"""
验证问题2.3因子差异根本原因的测试

理论分析：
1. GTJA_alpha032: cs_rank(high)和cs_rank(vol)是截面排名
   - 不同LOOKBACK_DAYS下载面股票数量可能不同（3Y包含更多历史股票）
   - cs_rank = rank/count，count不同导致排名分母不同

2. GTJA_alpha077: ts_decay_linear + cs_rank
   - ts_decay_linear使用np.convolve，边界效应
   - cs_rank嵌套导致排名基准变化

3. GTJA_alpha121: ts_rank嵌套ts_corr
   - ts_rank使用滑动窗口，边界敏感
   - ts_corr的边界效应叠加

验证方法：直接计算2Y和3Y数据下的截面股票数量差异
"""

import polars as pl
from datetime import datetime, timedelta
from src.factors import FactorEngine
import numpy as np


# =============================================================================
# 配置
# =============================================================================
PREDICT_START = "20250101"
PREDICT_END = "20250131"
LOOKBACK_2Y = 365 * 3
LOOKBACK_3Y = 365 * 4


def get_lookback_start_date(start_date: str, lookback_days: int) -> str:
    start_dt = datetime.strptime(start_date, "%Y%m%d")
    lookback_dt = start_dt - timedelta(days=lookback_days)
    return lookback_dt.strftime("%Y%m%d")


def verify_cross_section_stock_count():
    """
    验证：不同LOOKBACK_DAYS下载面股票数量是否不同

    关键问题：如果3Y数据包含更多历史股票，那么在相同日期D，
    2Y和3Y的截面股票数量可能不同，导致cs_rank分母不同
    """
    print("=" * 80)
    print("验证截面股票数量差异")
    print("=" * 80)

    # 加载2Y数据
    actual_start_2y = get_lookback_start_date(PREDICT_START, LOOKBACK_2Y)
    engine_2y = FactorEngine()
    data_2y = engine_2y.compute(
        factor_names=["close"],
        start_date=actual_start_2y,
        end_date=PREDICT_END,
    )
    data_2y = data_2y.filter(data_2y["trade_date"] >= PREDICT_START)

    # 加载3Y数据
    actual_start_3y = get_lookback_start_date(PREDICT_START, LOOKBACK_3Y)
    engine_3y = FactorEngine()
    data_3y = engine_3y.compute(
        factor_names=["close"],
        start_date=actual_start_3y,
        end_date=PREDICT_END,
    )
    data_3y = data_3y.filter(data_3y["trade_date"] >= PREDICT_START)

    # 统计截面股票数量
    stocks_per_date_2y = (
        data_2y.group_by("trade_date")
        .agg(pl.col("ts_code").count().alias("stock_count"))
        .sort("trade_date")
    )

    stocks_per_date_3y = (
        data_3y.group_by("trade_date")
        .agg(pl.col("ts_code").count().alias("stock_count"))
        .sort("trade_date")
    )

    print("\n2Y数据 - 每天截面股票数量:")
    print(stocks_per_date_2y)

    print("\n3Y数据 - 每天截面股票数量:")
    print(stocks_per_date_3y)

    # 比较差异
    comparison = stocks_per_date_2y.join(
        stocks_per_date_3y, on="trade_date", suffix="_3y"
    ).with_columns(
        [(pl.col("stock_count_3y") - pl.col("stock_count")).alias("count_diff")]
    )

    print("\n股票数量差异 (3Y - 2Y):")
    print(comparison)

    diff_count = comparison.filter(pl.col("count_diff") != 0).height
    print(f"\n有差异的日期数: {diff_count}")
    if diff_count > 0:
        print("结论：2Y和3Y的截面股票数量确实不同！")
    else:
        print("结论：2Y和3Y的截面股票数量相同，cs_rank分母应该一致")


def verify_cs_rank_formula_behavior():
    """
    验证cs_rank在不同count下的行为

    场景：如果2Y截面有3000只股票，3Y截面有3100只股票
    假设股票X的rank都是1500（中间位置）
    - 2Y: cs_rank = 1500/3000 = 0.5
    - 3Y: cs_rank = 1500/3100 ≈ 0.484
    """
    print("\n" + "=" * 80)
    print("验证 cs_rank 在不同 count 下的行为")
    print("=" * 80)

    # 模拟：相同rank但不同count的情况
    df = pl.DataFrame(
        {
            "trade_date": ["20250101"] * 2,
            "ts_code": ["001", "002"],
            "rank": [1500.0, 1500.0],  # 相同rank
            "count_2y": [3000, 3000],  # 2Y count
            "count_3y": [3100, 3100],  # 3Y count
        }
    )

    result = df.with_columns(
        [
            (pl.col("rank") / pl.col("count_2y")).alias("cs_rank_2y"),
            (pl.col("rank") / pl.col("count_3y")).alias("cs_rank_3y"),
        ]
    )

    print("模拟结果（rank=1500的情况）:")
    print(
        result.select(
            ["ts_code", "rank", "count_2y", "cs_rank_2y", "count_3y", "cs_rank_3y"]
        )
    )
    print(f"\n差异: cs_rank_2y - cs_rank_3y = {1500 / 3000 - 1500 / 3100:.6f}")


def analyze_alpha032_deep():
    """
    深入分析GTJA_alpha032 = -1 * ts_sum(cs_rank(ts_corr(cs_rank(high), cs_rank(vol), 3)), 3)

    差异来源层级：
    1. cs_rank(high) - 截面排名，count不同导致差异
    2. cs_rank(vol) - 截面排名，count不同导致差异
    3. ts_corr(..., 3) - 滚动相关，使用上述排名结果作为输入
    4. cs_rank(ts_corr(...)) - 嵌套：对ts_corr结果再做截面排名
    5. ts_sum(..., 3) - 滚动求和
    """
    print("\n" + "=" * 80)
    print("GTJA_alpha032 深入分析")
    print("=" * 80)

    print("""
    公式: (-1 * ts_sum(cs_rank(ts_corr(cs_rank(high), cs_rank(vol), 3)), 3))

    差异传递链:
    ┌─────────────────────────────────────────────────────────────┐
    │ L1: high, vol                                               │
    │     ↓                                                        │
    │ L2: cs_rank(high), cs_rank(vol)                              │
    │     问题: 2Y和3Y的截面股票数量可能不同                        │
    │     例如: 股票X在2Y下cs_rank=1500/3000=0.5                   │
    │           股票X在3Y下cs_rank=1500/3100=0.484                │
    │     ↓                                                        │
    │ L3: ts_corr(cs_rank(high), cs_rank(vol), 3)                 │
    │     问题: 输入的cs_rank值已经不同，ts_corr结果自然不同         │
    │     ↓                                                        │
    │ L4: cs_rank(ts_corr(...))                                    │
    │     问题: 每天对ts_corr结果做截面排名，count可能不同           │
    │     ↓                                                        │
    │ L5: ts_sum(..., 3)                                           │
    │     问题: ts_sum对L4的排名结果求和，边界效应                  │
    │     ↓                                                        │
    │ L6: -1 * ...                                                │
    └─────────────────────────────────────────────────────────────┘

    结论：GTJA_alpha032的差异主要来自L2的cs_rank嵌套问题
    """)


def analyze_alpha077_deep():
    """
    深入分析GTJA_alpha077 = min_(cs_rank(ts_decay_linear(...)), cs_rank(ts_decay_linear(...)))

    涉及：ts_decay_linear（np.convolve边界效应）+ cs_rank嵌套
    """
    print("\n" + "=" * 80)
    print("GTJA_alpha077 深入分析")
    print("=" * 80)

    print("""
    公式: min_(cs_rank(ts_decay_linear(DECAY1)), cs_rank(ts_decay_linear(DECAY2)))

    其中:
    DECAY1 = ((((high + low) / 2) + high) - ((amount / vol) + high))
    DECAY2 = ts_corr(((high + low) / 2), ts_mean(vol, 40), 3)

    差异来源:
    1. ts_decay_linear使用np.convolve
       - mode='valid' 只返回完全重叠的结果
       - 边界处(前window-1个)数据会是NaN
       - 但2Y和3Y的边界位置相同，结果应该一样

    2. 真正的问题：cs_rank(ts_decay_linear(...))
       - 每天对ts_decay_linear结果做截面排名
       - 2Y和3Y截面股票数量不同 → cs_rank分母不同 → 结果不同

    结论：差异主要来自cs_rank嵌套问题
    """)


def analyze_alpha121_deep():
    """
    深入分析GTJA_alpha121 = (cs_rank(...) ** ts_rank(ts_corr(...), 3)) * -1

    涉及：ts_rank（滑动窗口边界敏感）+ ts_corr + cs_rank嵌套
    """
    print("\n" + "=" * 80)
    print("GTJA_alpha121 深入分析")
    print("=" * 80)

    print("""
    公式: ((cs_rank(((amount / vol) - min_((amount / vol), 12))) ** ts_rank(ts_corr(...), 3)) * -1)

    差异来源:
    1. ts_rank(ts_corr(...), 3)
       - ts_rank使用sliding_window_view
       - 对边界敏感：不同起始点导致边界处有效窗口数量不同
       - ts_corr本身也有边界效应

    2. cs_rank((amount / vol) - min_(..., 12))
       - 截面排名问题
       - 2Y和3Y截面股票数量不同

    3. cs_rank(...) ** ts_rank(...)
       - 嵌套：截面排名结果作为指数
       - 两个差异来源叠加

    结论：差异来自ts_rank/ts_corr边界效应 + cs_rank嵌套问题
    """)


def summarize_root_causes():
    """
    总结问题2.7因子的根本原因
    """
    print("\n" + "=" * 80)
    print("问题2.3因子差异根本原因总结")
    print("=" * 80)

    print("""
    ┌─────────────────┬──────────────────────────────────────────────────────┐
    │ 因子            │ 根本原因                                              │
    ├─────────────────┼──────────────────────────────────────────────────────┤
    │ GTJA_alpha016   │ cs_rank嵌套：ts_corr结果再做截面排名，count不同       │
    │ GTJA_alpha032   │ cs_rank嵌套：cs_rank(high)和cs_rank(vol)截面count不同 │
    │ GTJA_alpha077   │ ts_decay_linear边界效应 + cs_rank嵌套                 │
    │ GTJA_alpha091   │ cs_rank嵌套：max_(close,5)结果再做截面排名            │
    │ GTJA_alpha121   │ ts_rank/ts_corr边界效应 + cs_rank嵌套                 │
    │ GTJA_alpha130   │ ts_decay_linear边界效应 + cs_rank嵌套                  │
    │ GTJA_alpha141   │ cs_rank嵌套：ts_corr结果再做截面排名                  │
    └─────────────────┴──────────────────────────────────────────────────────┘

    核心问题：cs_rank是截面排名函数，当不同LOOKBACK_DAYS下截面股票数量
    不同时，cs_rank的分母(count)不同，导致归一化排名结果不同。

    这是一个结构性问题，与具体实现无关。
    """)


if __name__ == "__main__":
    verify_cross_section_stock_count()
    verify_cs_rank_formula_behavior()
    analyze_alpha032_deep()
    analyze_alpha077_deep()
    analyze_alpha121_deep()
    summarize_root_causes()
