"""
深入分析GTJA_alpha032差异来源

GTJA_alpha032 DSL: (-1 * ts_sum(cs_rank(ts_corr(cs_rank(high), cs_rank(vol), 3)), 3))

问题：该因子在不同LOOKBACK_DAYS下有8341个差异数据点，max_diff=0.605

目标：分析差异来源
"""

import numpy as np
import polars as pl
from datetime import datetime, timedelta

# =============================================================================
# 配置
# =============================================================================
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 analyze_data_boundary_effect():
    """
    分析数据边界效应

    问题：不同LOOKBACK_DAYS下，边界处的ts_corr/ts_sum计算结果可能不同
    """
    print("=" * 80)
    print("分析GTJA_alpha032的数据边界效应")
    print("=" * 80)

    actual_start_2y = get_lookback_start_date(PREDICT_START, LOOKBACK_2Y)
    actual_start_3y = get_lookback_start_date(PREDICT_START, LOOKBACK_3Y)

    print(f"\n2Y数据起始点: {actual_start_2y}")
    print(f"3Y数据起始点: {actual_start_3y}")
    print(f"差异: {LOOKBACK_3Y - LOOKBACK_2Y} 天 = {1460 - 1095} 天")

    print("\n关键发现:")
    print("-" * 60)
    print("ts_corr(window=3) 计算时：")
    print("  - 需要当前日期 + 前2天共3天数据")
    print("  - 对于预测日期20250101:")
    print("    * 2Y模式下需要: 20241230, 20241231, 20250101")
    print("    * 3Y模式下需要: 20241230, 20241231, 20250101")
    print("    * 两者从同一预测日期向前看，需要的历史数据相同")
    print("  - 但如果2Y在20221230有NA而3Y在20211230有数据，结果会不同")


def analyze_cs_rank_nesting_issue():
    """
    分析 cs_rank 嵌套问题

    GTJA_alpha032 = -1 * ts_sum(cs_rank(ts_corr(...)), 3)

    嵌套结构: cs_rank(ts_corr(...))
    - ts_corr输出可能有NA值（边界、数据不足等）
    - cs_rank(ts_corr(...)) 对截面进行排名
    - 问题：不同日期截面内可能有不同数量的NA值
    """
    print("\n" + "=" * 80)
    print("分析 cs_rank 嵌套问题")
    print("=" * 80)

    # 模拟截面数据：同一日期内不同股票有/无NA
    print("\n场景1：同一截面内有NA值")
    df_with_na = pl.DataFrame(
        {
            "trade_date": ["20250101"] * 5,
            "ts_code": ["001", "002", "003", "004", "005"],
            "corr_val": [0.5, 0.6, None, 0.8, 0.9],  # 003是NA
        }
    )

    print("数据:")
    print(df_with_na)

    result = (
        df_with_na.lazy()
        .with_columns(
            [
                pl.col("corr_val").rank().alias("rank"),
                pl.col("corr_val").count().over("trade_date").alias("count"),
            ]
        )
        .with_columns([(pl.col("rank") / pl.col("count")).alias("cs_rank")])
        .collect()
    )
    print("\ncs_rank结果 (有NA):")
    print(result.select(["ts_code", "corr_val", "rank", "count", "cs_rank"]))

    # 验证：004的值0.8在有NA时排名是3/4=0.75
    print("\n验证: 004的0.8是第3名，count=4，所以cs_rank=3/4=0.75")


def analyze_ts_sum_boundary():
    """
    分析 ts_sum 边界效应

    GTJA_alpha032 = -1 * ts_sum(cs_rank(ts_corr(...)), 3)

    ts_sum(window=3) 是时间序列求和，不是截面求和
    问题：不同起始点导致ts_sum在边界处的有效窗口数量不同
    """
    print("\n" + "=" * 80)
    print("分析 ts_sum 边界效应")
    print("=" * 80)

    # 创建不同起始点的数据
    df1 = pl.DataFrame(
        {
            "trade_date": ["20250101", "20250102", "20250103", "20250104", "20250105"],
            "ts_code": ["001"] * 5,
            "value": [1.0, 2.0, 3.0, 4.0, 5.0],
        }
    )

    df2 = pl.DataFrame(
        {
            "trade_date": [
                "20241229",
                "20241230",
                "20241231",
                "20250101",
                "20250102",
                "20250103",
                "20250104",
                "20250105",
            ],
            "ts_code": ["001"] * 8,
            "value": [-1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
        }
    )

    print("\n数据集1（从20250101开始）:")
    print(df1)

    print("\n数据集2（从20241229开始）:")
    print(df2.filter(pl.col("trade_date") >= "20250101"))

    # 模拟ts_sum(window=3)的结果
    def rolling_sum(values, window):
        result = np.full(len(values), np.nan)
        for i in range(window - 1, len(values)):
            result[i] = np.nansum(values[i - window + 1 : i + 1])
        return result

    vals1 = df1["value"].to_numpy()
    vals2 = df2["value"].to_numpy()[3:]  # 取相同日期部分

    sum1 = rolling_sum(vals1, 3)
    sum2 = rolling_sum(vals2, 3)

    print("\nts_sum(window=3) 结果:")
    print(f"  数据集1: {sum1}")
    print(f"  数据集2: {sum2}")

    print("\n分析: 对于同一日期20250105:")
    print(f"  数据集1使用: [3,4,5] -> sum=12")
    print(
        f"  数据集2使用: [2,3,4] -> sum=9 (因为20241229=-1,20241230=0,20241231=1,20250101=2,...)"
    )
    print(
        "  但这里使用的是相同日期的数据[1,2,3,4,5] vs [2,3,4,5,6]，所以不同日期的数据本身不同"
    )


def identify_root_cause():
    """
    识别GTJA_alpha032差异的根本原因

    结论：
    1. GTJA_alpha032 = -1 * ts_sum(cs_rank(ts_corr(cs_rank(high), cs_rank(vol), 3)), 3)
    2. cs_rank(high) 和 cs_rank(vol) 是截面排名
    3. 如果2Y和3Y在某个日期的截面组成不同（股票池差异），cs_rank结果会不同
    4. ts_corr(window=3) 对嵌套的cs_rank值计算相关系数
    5. ts_sum(window=3) 对ts_corr结果进行滚动求和
    """
    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) - 截面排名（每天独立计算）
    L3: ts_corr(..., 3) - 滚动相关（3日窗口）
    L4: cs_rank(ts_corr(...)) - 对ts_corr结果再做截面排名
    L5: ts_sum(..., 3) - 滚动求和（3日窗口）
    L6: -1 * ... - 取反

    差异来源:
    1. 【主要】cs_rank(high) 和 cs_rank(vol) 是截面排名
       - 2Y和3Y的股票池可能在边界处有差异
       - 新上市/退市股票导致截面组成不同
       - 导致排名结果不同

    2. 【次要】ts_corr(window=3) 的边界效应
       - 不同起始点导致有效数据点不同
       - 但由于window=3较小，影响有限

    3. 【主要】cs_rank(ts_corr(...)) 嵌套排名
       - 每天对ts_corr结果再做截面排名
       - 如果2Y和3Y的ts_corr值不同，排名结果也不同
    """)


def analyze_cross_section_composition():
    """
    分析截面组成的差异

    如果2Y和3Y的股票池在边界处有差异，cs_rank结果会不同
    """
    print("\n" + "=" * 80)
    print("分析截面组成差异")
    print("=" * 80)

    print("""
    关键问题：cs_rank 是截面排名

    例如，对于日期20250101：
    - 2Y模式：股票池A（假设3000只）
    - 3Y模式：股票池B（假设3200只）

    如果股票池B包含一些股票A没有的早期股票（但这些股票在20250101也存在于A）
    那么在20250101的截面排名中：
    - 2Y: 对3000只股票排名
    - 3Y: 对3200只股票排名

    假设股票X在2Y模式下排名第1500/3000 = 0.5
    但在3Y模式下排名第1500/3200 = 0.47（因为分母变大了）

    这就是 cs_rank 嵌套导致差异的根本原因！
    """)


if __name__ == "__main__":
    analyze_data_boundary_effect()
    analyze_cs_rank_nesting_issue()
    analyze_ts_sum_boundary()
    identify_root_cause()
    analyze_cross_section_composition()
