精华帖子

最大化 ICIR 的滚动训练多因子选股策略

由bq5973r5创建,最终由neoblackxt 被浏览 47 用户

1. 策略概览

本策略面向 A 股沪深300成分股,采用多因子选股 + 指数增强思路:每次调仓前都用过去一段训练窗口的历史数据,重新训练因子权重,目标是让组合信号的 ICIR(Information Coefficient Information Ratio)最大化,从而得到更稳定的预测信号,并据此构建可交易组合。

策略特点:

  • 滚动训练:权重不是固定的,每个交易日都可重新训练,适应市场风格变化;
  • 显式最大化ICIR:权重通过优化器直接提升 ICIR,而不是仅计算后展示;
  • 可交易:股票池过滤可交易性,TopN 等权持仓,按固定周期调仓,并计入手续费。

2. 股票池与数据过滤

数据来源:cn_stock_prefactors(BigQuant预处理因子库)

股票池及过滤条件(SQL中体现):

  • is_hs300 = 1:沪深300范围;
  • st_status = 0:剔除 ST;
  • suspended = 0:剔除停牌,保证可交易;
  • pe_ttm > 0、pb > 0:估值因子有效;
  • roe_avg_ttm IS NOT NULL:ROE有效。

这些过滤的目的不是“提升回测”,而是保证信号在实盘环境可落地:数据有效、标的可交易。

3. 因子体系与方向统一

策略使用 5 个因子,分别覆盖价值、质量、拥挤度、动量:

  • 价值:pe_ttm、pb
  • 质量:roe_avg_ttm
  • 拥挤度:turn(换手率)
  • 动量:momentum_5

训练阶段将因子方向统一为“越大越好”,便于权重解释:

  • f1 = -1/pe_ttm(PE越低越好)
  • f2 = -pb(PB越低越好)
  • f3 = roe_avg_ttm(ROE越高越好)
  • f4 = -turn(换手越低越不拥挤)
  • f5 = momentum_5(动量越高越好)

4. 训练标签:未来 H 日收益(排序预测)

策略的预测周期为 H=5(未来5个交易日),训练标签定义为:

代码实现:

df_all['fwd_ret'] = df_all.groupby('instrument')['close'].shift(-H) / df_all['close'] - 1

说明:

  • shift(-H) 把未来第5个交易日的收盘价对齐到今天这一行;
  • 得到的是“从今天买入持有到第5天”的区间收益;
  • 策略并不追求对收益率做点预测,而是通过因子对股票做排序,判断“谁更可能在未来5天表现更好”。

5. IC、ICIR 与训练数据结构

5.1 每日IC的计算

在训练窗口内,对每个交易日做一次横截面计算:

  1. 对每个因子做截面 rank(分位排序)以增强稳健性;
  2. 计算因子 rank 与未来收益 fwd_ret 的相关系数,得到当天该因子的 IC。

代码核心:

df_day[f'{col}_rank'] = df_day[col].rank(pct=True)
ic = df_day[f'{col}_rank'].corr(df_day['fwd_ret'])

这样,每一天会得到一个 5 维 IC 向量;过去 TRAIN_WINDOW=252 天会堆成一个 IC 矩阵:

  • ic_mat 形状约为 [N_days, 5]
  • 行:日期;列:5个因子

5.2 μ 与 Σ(协方差矩阵)的意义

对 ic_mat 做统计估计:

  • μ:各因子 IC 的均值(有效性)
  • Σ:因子 IC 的协方差矩阵(稳定性 + 重复信息结构)

代码实现:

mu = np.nanmean(ic_mat, axis=0)      # shape [5]
cov = np.cov(ic_mat.T, ddof=1)       # shape [5,5]

解释:

  • μ 越大:该因子在过去窗口里平均更“有用”;
  • Σ 对角线:因子IC波动大不大(稳不稳);
  • Σ 非对角线:因子是否经常一起强/一起弱(信息重复程度)。

6. 目标函数:用 μ 与 Σ 计算 ICIR(w)

组合因子的 ICIR 通过二次型写成:

对应代码函数 icir_from_mu_cov:

  • 分子 mu @ w:组合IC均值
  • 分母 sqrt(w @ cov @ w):组合IC波动

该形式的好处是:不需要每次都回到全样本序列重新计算ICIR,可以直接在 μ/Σ 层面优化权重。

7. 权重约束:sum(|w|)=1(L1 归一化)

由于 ICIR 对权重整体缩放不敏感(w 乘常数,分子分母同比放大),优化过程中权重规模可能漂移,因此加入约束:

实现函数 project_l1:

w = w / np.sum(np.abs(w))

这使权重规模可控、便于解释,也更贴近实盘“总风险暴露”固定的风格。

8. 显式最大化ICIR:梯度上升 + 回溯步长

8.1 初始权重 w0:解 Σ·w = μ

为了加速收敛,先用线性方程求一个“方向很合理”的初值:

代码实现(含奇异矩阵兜底):

try:
    w0 = np.linalg.solve(cov, mu)
except np.linalg.LinAlgError:
    w0, *_ = np.linalg.lstsq(cov, mu, rcond=None)
w0 = project_l1(w0)

说明:这一步给的是“好起点”,最终权重仍然通过后续优化得到。

8.2 maximize_icir:从 w0 出发把 ICIR 推到最大

优化器目标:

方法:

  • 计算 ICIR 的解析梯度;
  • 用 w ← w + η·grad 做梯度上升;
  • 采用回溯步长:若更新后ICIR不升,就把步长减半;
  • 每次更新后执行 project_l1 以保持约束。

关键点:

  • 初始步长 lr=0.2,若不提升则依次变为 0.1、0.05...(最多尝试20次)
  • 迭代上限 max_iter=200
  • 提升幅度小于 tol=1e-9 认为收敛

调用处(滚动训练里):

w, icir_mu_cov = maximize_icir(mu, cov, w0=w0, max_iter=200, lr=0.2, tol=1e-9)

9. 滚动训练主循环(权重随时间变化)

训练窗口长度 TRAIN_WINDOW=252,对回测期每个日期 current_date:

  1. 取过去252天作为训练集;
  2. 每天计算5维IC向量,形成 ic_mat;
  3. 估计 μ 与 Σ;
  4. 求初值 w0,并显式最大化ICIR得到 w*;
  5. 把 w* 存入 weights_history[current_date],供后续打分选股。

同时,为了便于监控与解释,代码还计算了“基于真实组合IC序列”的ICIR:

ic_combo = ic_mat @ w
icir = np.nanmean(ic_combo) / np.nanstd(ic_combo)

日志里同时输出两种ICIR:

  • icir_mu_cov:用 μ/Σ 二次型公式算的(优化器口径)
  • icir:用训练窗口真实序列算的(直观口径)

二者应方向一致,差异主要来自样本估计误差与协方差估计方式。

10. 生成交易信号:打分、TopN、等权、调仓

训练得到每日权重后,进入交易信号生成:

  1. 取回测期因子数据,并对每个因子做横截面标准化 c_normalize,保证量纲一致;
  2. 综合得分:
  • 选择 TOPN=30 只股票,等权配置 1/TOPN;
  • 用 TradingDaysRebalance(REBALANCE_DAYS=5) 只在调仓日输出持仓,降低换手、对齐预测周期。

这套策略用“未来5日收益”作为训练目标,在每次调仓前用过去一年滚动估计因子IC分布,通过显式优化器直接最大化组合因子的ICIR,从而得到更稳定的综合信号,并据此构建Top30等权、5日调仓、计入成本的可交易组合。

https://bigquant.com/codesharev3/47104882-5666-4a34-9a46-ab7a1b17eb9a

\

评论
  • 👍
  • 讲解和代码质量很高
  • 有两个可以改进的位置:1、369行:
{link}