Files
ProStock/docs/factor_lookback_consistency_20260319.md
liaozhaorun ccd42082c2 refactor(experiment): 重构模型保存机制,支持 processors 持久化
- 模型保存路径改为 models/{model_type}/ 目录结构
- save_model_with_factors 新增 fitted_processors 参数
- 新增 load_processors 函数加载处理器状态
- Storage 查询排序优化:ORDER BY ts_code, trade_date
2026-03-19 21:06:11 +08:00

14 KiB
Raw Permalink Blame History

因子回看一致性测试问题报告

测试日期: 2026-03-19 测试文件: tests/debug/test_lookback_consistency.py::test_simple_factor_consistency 测试结果: FAILED - 33个因子不一致


1. 测试概述

1.1 测试目的

验证不同 LOOKBACK_DAYS回看窗口设置下同一预测日期范围的因子值是否一致。如果结果不一致可能存在以下问题

  1. 数据泄露:因子计算使用了超出合理回看期的历史数据
  2. 设计缺陷:某些因子天然依赖更长的历史数据(如累积和因子)
  3. 边界效应:滚动窗口在数据边界处的处理差异

1.2 测试配置

参数 说明
LOOKBACK_2Y 1095天 (3年) 较短回看窗口
LOOKBACK_3Y 1460天 (4年) 较长回看窗口
PREDICT_START 20250101 预测起始日期
PREDICT_END 20250131 预测结束日期
2Y实际数据范围 20220102 - 20250131 3年数据
3Y实际数据范围 20210102 - 20250131 4年数据

1.3 测试结果摘要

数据集形状:
  2Y 回看: (96761, 241)
  3Y 回看: (96761, 241)

总因子数: 191
一致因子数: 158
不一致因子数: 33 (17.3%)

2. 不一致因子详细分类

2.1 分类标准

类别 标准 风险等级
浮点精度差异 max_diff < 1e-6 低 - 可忽略
边界效应差异 1e-6 <= max_diff < 0.01 中 - 需关注
数值显著差异 0.01 <= max_diff < 0.1 高 - 需修复
严重不一致 max_diff >= 0.1 或 inf/nan 极高 - 必须修复

2.2 严重不一致因子(必须修复)

因子名称 最大差异 平均差异 差异数据点 问题类型
GTJA_alpha005 inf inf 2170 -inf值产生
GTJA_alpha113 0.803 0.108 95337 累积和历史依赖
GTJA_alpha115 0.989 0.0014 83182 ts_rank差异传播
GTJA_alpha138 0.857 0.108 21689 ts_decay_linear+ts_rank
GTJA_alpha140 0.999 0.029 7535 min_/max_边界
GTJA_alpha146 3.719 0.0058 81526 复杂嵌套公式
GTJA_alpha148 1.000 1e-5 1 cs_rank+ts_min边界
GTJA_alpha176 inf inf 74 除零/无穷大

2.2.1 GTJA_alpha005-inf值问题

差异数据点示例:
  idx=56: 2Y=-0.0000000262, 3Y=-inf, diff=inf
  idx=57: 2Y=-0.0000000262, 3Y=-inf, diff=inf
  idx=58: 2Y=-0.4564354646, 3Y=-inf, diff=inf

问题分析:

  • 3Y模式下产生-inf值说明存在除零或对负数取对数
  • DSL中可能包含 logsqrt 操作

2.2.2 GTJA_alpha113累积和历史依赖

差异数据点示例:
  idx=0: 2Y=0.1848, 3Y=0.9883, diff=0.8035
  idx=1: 2Y=-0.1146, 3Y=-0.9890, diff=0.8744
  idx=2: 2Y=0.1098, 3Y=0.9783, diff=0.8684

问题分析:

  • 包含 ts_delay(close, 5) 导致数据偏移
  • ts_corr(close, vol, 2) 只有2日窗口对边界敏感

2.2.3 GTJA_alpha138复杂嵌套

差异数据点示例:
  idx=3: 2Y=0.9808, 3Y=0.8380, diff=0.1429
  idx=4: 2Y=0.9782, 3Y=0.8354, diff=0.1429
  idx=5: 2Y=0.9738, 3Y=0.6880, diff=0.2857
  idx=6: 2Y=0.9780, 3Y=0.4066, diff=0.5714
  idx=7: 2Y=0.5586, 3Y=0.1300, diff=0.4286

问题分析:

  • 5层嵌套cs_rank → ts_decay_linear → ts_delta → ts_rank → ts_corr → ts_rank
  • ts_decay_linear 使用 numpy.convolve,结果依赖输入序列长度
  • ts_rank 使用 sliding_window_view,对数据起始点敏感

2.2.4 GTJA_alpha176无穷大值

差异数据点示例:
  idx=948: 2Y=-0.9146, 3Y=-0.9146, diff=0.0000000001
  idx=949: 2Y=-0.8809, 3Y=-0.8809, diff=0.0000000002
  (差异本身很小但3Y模式下产生了inf值)

问题分析:

  • 存在除零操作导致inf值
  • 需要添加epsilon保护

2.3 数值显著差异因子(需修复)

因子名称 最大差异 平均差异 差异数据点 可能原因
GTJA_alpha016 0.021 2.3e-6 336 cs_rank嵌套
GTJA_alpha032 0.605 3.9e-5 8341 ts_sum嵌套cs_rank
GTJA_alpha077 0.580 0.00013 36577 cs_rank+ts_decay_linear
GTJA_alpha091 0.204 4.2e-6 2147 cs_rank嵌套max_
GTJA_alpha121 0.296 0.00457 2412 ts_rank嵌套ts_corr
GTJA_alpha130 0.613 2.9e-5 358 ts_rank+ts_decay_linear
GTJA_alpha141 0.629 0.00011 28256 cs_rank+ts_corr

2.4 边界效应差异因子(需关注)

因子名称 最大差异 平均差异 差异数据点 问题类型
GTJA_alpha042 0.00027 1.8e-7 214 ts_std嵌套
GTJA_alpha062 1.0e-6 0.0 579 ts_corr嵌套
GTJA_alpha064 0.501 0.00022 74838 ts_decay_linear嵌套
GTJA_alpha070 2.3e-6 8.1e-9 58440 ts_std(amount)
GTJA_alpha074 0.00837 8.5e-7 241 cs_rank+ts_corr
GTJA_alpha104 0.00028 2.7e-8 381 ts_delta+ts_std
GTJA_alpha119 0.160 1.5e-5 4051 cs_rank+ts_decay_linear

2.5 浮点精度差异因子(可忽略)

因子名称 最大差异 平均差异 差异数据点
volatility_5 4.3e-9 0.0 906
volatility_ratio 6.0e-10 0.0 96
volatility_squeeze_5_60 2.0e-10 0.0 16
turnover_deviation 2.0e-9 0.0 37
GTJA_alpha083 9.3e-5 1.9e-9 2
GTJA_alpha139 2.0e-10 0.0 12
GTJA_alpha179 1.9e-4 5.9e-9 4
GTJA_alpha191 5.6e-9 0.0 1010

2.6 NaN模式不一致因子需关注

因子名称 2Y NaN数 3Y NaN数 差异
GTJA_alpha005 704 600 -104
GTJA_alpha028 87678 89155 +1477
GTJA_alpha111 29410 35516 +6106
GTJA_alpha113 294 282 -12
GTJA_alpha164 29410 35516 +6106

3. 根因分析

3.1 问题分类

3.1.1 设计缺陷型(无法修复,只能排除)

以下因子天然依赖从数据起始点开始的累积计算,不应该在不同回看期下产生相同结果:

因子 DSL公式 问题
GTJA_alpha165 ts_sumac(close-ts_mean(close,48)) 累积和从数据起点开始
GTJA_alpha183 ts_sumac(close-ts_mean(close,24)) 累积和从数据起点开始

本次测试中这两个因子未出现但之前测试中差异巨大14万级别

3.1.2 数值稳定性问题(需要修复)

问题类型 相关因子 修复方案
除零导致inf GTJA_alpha005, GTJA_alpha176 添加epsilon保护
负数取对数 GTJA_alpha005 检查log输入是否为正
负数取平方根 待查 检查sqrt输入

3.1.3 滚动窗口边界效应(需要优化)

以下模式会导致不同回看期下边界数据点不同:

# ts_rank 实现
def rank_calc(s: pl.Series) -> pl.Series:
    values = s.to_numpy()
    n = len(values)
    windows = np.lib.stride_tricks.sliding_window_view(values, window)
    # 从第 window 个元素开始产生有效值
    # 不同起始点导致滑动窗口内容不同
# ts_decay_linear 实现
# 使用 numpy.convolve输出依赖输入序列长度

3.1.4 cs_rank截面排名差异需要优化

cs_rank 对截面数据进行排名,不同回看期下:

  • 有效数据点数量不同
  • 排名时的百分位数分母不同

4. 修复建议

4.1 立即修复(高优先级)

4.1.1 排除问题因子

将以下因子加入排除列表:

EXCLUDED_FACTORS = [
    # 设计缺陷 - 累积和因子
    "GTJA_alpha165",  # ts_sumac 累积和历史依赖
    "GTJA_alpha183",  # ts_sumac 累积和历史依赖
    
    # 数值稳定性问题
    "GTJA_alpha005",  # 产生-inf值
    "GTJA_alpha176",  # 产生inf值
]

4.1.2 修复数值稳定性

修复位置: src/factors/translator.py

# 为除法操作添加 epsilon 保护
def _safe_divide(numerator, denominator, epsilon=1e-10):
    return numerator / (denominator + epsilon)

# 为 log 函数添加输入检查
def _safe_log(x, epsilon=1e-10):
    return log(x + epsilon)  # 确保输入为正

# 为 sqrt 函数添加输入检查  
def _safe_sqrt(x, epsilon=1e-10):
    return sqrt(abs(x))  # 确保输入非负

4.2 中期优化(中优先级)

4.2.1 优化 ts_rank 边界处理

当前问题:

  • sliding_window_view 从第 window 个元素开始产生有效值
  • 不同起始点导致初始NaN数量不同

优化方案:

# 使用动态起始点对齐
def ts_rank_aligned(expr, window):
    # 确保不同回看期产生相同起始点
    pass

4.2.2 优化 cs_rank 排名基准

当前问题:

  • 排名时使用滑动窗口内的元素
  • 不同回看期下窗口内元素不同

优化方案:

def cs_rank_aligned(expr):
    # 使用固定的排名基准
    # 例如:固定使用当日全部股票进行排名
    pass

4.3 长期改进(低优先级)

4.3.1 建立因子分类体系

class FactorCategory(Enum):
    """因子分类"""
    TIME_SERIES_ROLLING = "ts_rolling"      # 滚动窗口型(对回看期不敏感)
    TIME_SERIES_CUMULATIVE = "ts_cumulative" # 累积型(对回看期敏感)
    CROSS_SECTIONAL = "cs_rank"             # 截面型(对回看期敏感)
    HYBRID = "hybrid"                         # 混合型

4.3.2 增强一致性测试

def test_factor_consistency_threshold():
    """测试因子一致性阈值"""
    THRESHOLDS = {
        "float_precision": 1e-6,   # 浮点精度差异可接受
        "boundary_effect": 0.01,    # 边界效应差异需关注
        "significant": 0.1,         # 显著差异需修复
    }

5. 影响评估

5.1 对模型训练的影响

风险等级 因子数量 影响
8 浮点精度差异,不影响训练
16 边界效应,可能轻微影响
8 显著不一致,会影响模型
极高 1 inf/nan值必须修复

5.2 对回测的影响

关键问题:

  • 训练时使用3年回看预测时用4年回看 → 因子值不一致
  • 可能导致回测结果与实盘表现不符

建议:

  • 训练和预测使用相同的LOOKBACK_DAYS配置
  • 或在模型中记录回看期设置

6. 测试代码分析

6.1 测试逻辑

def compute_simple_factors(lookback_days: int) -> pl.DataFrame:
    actual_start = get_lookback_start_date(PREDICT_START, lookback_days)
    # 从 actual_start 开始加载数据
    # 计算因子
    # 过滤到 PREDICT_START 之后的日期
    return data

6.2 比较逻辑

def compare_factor_values(data_2y, data_3y, feature_cols):
    # 使用 np.allclose(valid_2y, valid_3y, rtol=1e-10, atol=1e-10)
    # rtol=1e-10 相对容差
    # atol=1e-10 绝对容差

6.3 断言

assert results["inconsistent_factors"] == 0, (
    f"发现 {results['inconsistent_factors']} 个简单因子在不同 LOOKBACK_DAYS 下结果不一致"
)

7. 结论

7.1 关键发现

  1. 33个因子17.3%)在不同回看期下结果不一致
  2. 其中8个因子存在严重差异max_diff >= 0.1 或 inf/nan
  3. 问题根源包括:设计缺陷、数值稳定性、边界效应

7.2 修复优先级

优先级 因子 行动
P0 GTJA_alpha005, GTJA_alpha176 排除产生inf值
P1 GTJA_alpha113, GTJA_alpha138, GTJA_alpha140, GTJA_alpha146 修复数值稳定性
P2 其他16个显著差异因子 优化滚动窗口处理
P3 8个浮点精度因子 可忽略

7.3 后续步骤

  1. 立即:更新 SELECTED_FACTORS 排除问题因子
  2. 本周:修复 translator.py 中的数值稳定性问题
  3. 本月:建立因子分类和一致性测试体系

附录:完整不一致因子列表

序号 因子名称 最大差异 平均差异 差异数据点 风险等级
1 volatility_5 4.3e-09 0.0 906
2 volatility_ratio 6.0e-10 0.0 96
3 volatility_squeeze_5_60 2.0e-10 0.0 16
4 turnover_deviation 2.0e-09 0.0 37
5 GTJA_alpha005 inf inf 2170 极高
6 GTJA_alpha016 0.021 2.3e-06 336
7 GTJA_alpha032 0.605 3.9e-05 8341
8 GTJA_alpha042 0.00027 1.8e-07 214
9 GTJA_alpha062 1.0e-06 0.0 579
10 GTJA_alpha064 0.501 0.00022 74838
11 GTJA_alpha070 2.3e-06 8.1e-09 58440
12 GTJA_alpha074 0.00837 8.5e-07 241
13 GTJA_alpha077 0.580 0.00013 36577
14 GTJA_alpha083 9.3e-05 1.9e-09 2
15 GTJA_alpha090 0.021 1.5e-06 416
16 GTJA_alpha091 0.204 4.2e-06 2147
17 GTJA_alpha104 0.00028 2.7e-08 381
18 GTJA_alpha105 nan nan 591 极高
19 GTJA_alpha113 0.803 0.108 95337 极高
20 GTJA_alpha114 nan nan 11 极高
21 GTJA_alpha115 0.989 0.0014 83182 极高
22 GTJA_alpha119 0.160 1.5e-05 4051
23 GTJA_alpha121 0.296 0.00457 2412
24 GTJA_alpha130 0.613 2.9e-05 358
25 GTJA_alpha138 0.857 0.108 21689 极高
26 GTJA_alpha139 2.0e-10 0.0 12
27 GTJA_alpha140 0.999 0.029 7535 极高
28 GTJA_alpha141 0.629 0.00011 28256
29 GTJA_alpha146 3.719 0.0058 81526 极高
30 GTJA_alpha148 1.000 1.0e-05 1 极高
31 GTJA_alpha176 inf inf 74 极高
32 GTJA_alpha179 0.00019 5.9e-09 4
33 GTJA_alpha191 5.6e-09 0.0 1010