2133 字
11 分钟

因子研究框架:IC、RankIC 与分层回测

本课目标:建立一套可复用的因子检验框架,学会用 IC、RankIC 和分层回测判断一个因子”是否值得继续研究”。

入门阶段我们跑的是策略回测:一套买卖规则,输出一条资金曲线。进入因子研究后,视角要换一下——我们不再直接问”这个策略赚不赚钱”,而是问”这个变量对未来收益有没有预测力”。本文不构成投资建议,所有示例仅用于说明研究方法。

什么是截面因子#

截面因子(cross-sectional factor)是在同一个时点上,给股票池里每只股票打一个分数的变量。比如”过去 20 日换手率”,在 2026 年 6 月 30 日这一天,全市场每只股票都有一个值,这一列数据就是当天的因子截面。

关键在”截面”两个字:我们比较的是同一时点上股票之间的相对高低,而不是一只股票自己的时间序列变化。因子研究的核心假设是:

  • 在时点 T,因子值的高低排序,与
  • 从 T 到 T+N(比如未来 20 个交易日)的收益率排序

之间存在稳定的关系。这个关系可以是正向的(因子值越高、未来收益越高),也可以是负向的,方向不重要,稳定才重要。

还要区分因子和信号:因子是原始变量(换手率、市盈率倒数、过去收益),信号是加工后的交易指令。因子研究停留在”变量有没有预测力”这一层,离实盘还隔着组合构建、成本、风控好几步。先把这一层做扎实,后面的每一步才有可靠的地基。

检验思路:从因子值到未来收益#

把上面的假设翻译成可计算的流程:

  1. 在每个调仓时点 T,取出全部股票的因子值向量 f_T
  2. 计算每只股票从 T 到 T+N 的收益率向量 r_{T→T+N}(注意是未来收益,避免用到 T 之后才知道的信息);
  3. 度量 f_Tr 之间的相关性;
  4. 在很多个时点上重复,观察这个相关性的均值、波动和稳定性。

第 3 步的度量方式,就是 IC。

两个容易踩的细节值得单独强调。第一,未来收益的区间要和调仓频率匹配:月度调仓就看未来一个月收益,不要用月度因子去对齐未来一年的收益,否则相邻期的收益区间大量重叠,统计上会高估稳定性。第二,收益率的起点应设在”你实际能交易的时刻”——比如用 T+1 开盘价而不是 T 日收盘价计算,因为你在 T 日收盘后才算得出因子值,按当日收盘成交是做不到的。这类细节单看都不起眼,叠加起来足以让一个平庸因子在回测里显得出色。

IC 与 RankIC 的含义与计算#

IC(Information Coefficient,信息系数)是某一期因子值与下一期收益的截面相关系数

  • IC:用 Pearson 相关系数,衡量线性关系,对极端值敏感;
  • RankIC:用 Spearman 秩相关,先把因子值和收益都换成排名再算相关,只关心排序关系,对离群值稳健。

A 股个股收益分布长尾明显,涨跌停也会截断收益,所以实务中通常更看 RankIC。

import pandas as pd
def calc_rank_ic(factor: pd.DataFrame, fwd_ret: pd.DataFrame) -> pd.Series:
"""factor/fwd_ret: index=日期, columns=股票代码, 已对齐"""
ics = {}
for dt in factor.index:
f = factor.loc[dt].dropna()
r = fwd_ret.loc[dt].reindex(f.index).dropna()
f = f.reindex(r.index)
ics[dt] = f.corr(r, method="spearman")
return pd.Series(ics)
ic_series = calc_rank_ic(factor_df, fwd_ret_df)
print(ic_series.mean(), ic_series.std())
print(ic_series.mean() / ic_series.std()) # ICIR

读数时关注三个量:

指标含义经验参考
IC 均值预测力的平均强度和方向绝对值持续在 0.02~0.05 以上通常值得关注
ICIR(IC 均值/IC 标准差)预测力的稳定性越高越稳定
IC 胜率IC 与均值同号的期数占比明显高于 50% 更好

上表的”经验参考”只是常见的口头约定,不同股票池、不同频率下标准差异很大,不要当成硬性门槛。

分层回测:怎么做、怎么读#

IC 只给出一个相关系数,分层回测(分组回测)能让你看到因子在整个截面上的形态。做法:

  1. 每个调仓日,按因子值把股票池等分成 K 组(常用 5 组或 10 组),第 1 组因子值最低,第 K 组最高;
  2. 每组内等权持有到下一个调仓日,滚动下去,得到 K 条净值曲线;
  3. 同时构造”多空组合”:做多第 K 组、做空第 1 组(A 股做空受限,这条曲线主要用于研究而非实盘)。
def layer_test(factor, fwd_ret, n_groups=5):
labels = factor.rank(axis=1, pct=True).apply(
lambda row: pd.cut(row, n_groups, labels=False), axis=1
)
group_ret = {
g: fwd_ret[labels == g].mean(axis=1) for g in range(n_groups)
}
return pd.DataFrame(group_ret)

读图要点:

  • 单调性:从第 1 组到第 K 组,平均收益是否单调递增(或递减)。单调说明因子在全截面都有区分度;如果只有最高组异常突出、中间乱序,因子可能只是捕捉了少数极端股票;
  • 多空曲线的平稳性:是长期缓慢爬升,还是靠一两段行情堆出来的;
  • 两端组的构成:极端组里如果长期挤满微盘股、ST 股,因子可能只是市值或流动性的代理;
  • 分年度表现:把分层收益按年份切开看,如果多空收益集中在某一两年、其余年份平平,说明因子对特定市场环境依赖很重。

IC 和分层回测是互补而非替代的关系:IC 概括了整个截面的平均相关性,但会被中间段的噪声稀释;分层回测能暴露”只在两端有效""只有一端有效”这类形态。一个只在最低分组有区分度的因子,做纯多头组合时几乎帮不上忙——A 股缺乏便利的做空工具,“负端有效”的价值会大打折扣。这也是为什么两套工具都要跑、对照着读。

因子检验的标准流程清单#

把上面内容整理成每次拿到新因子都要过一遍的清单:

  • 明确因子定义、计算所需数据和可得时间(防止未来函数);
  • 确定股票池(如剔除上市不满一年、ST、停牌股)与调仓频率;
  • 因子预处理:去极值、缺失值处理(标准化留到多因子课详谈);
  • 计算 RankIC 序列:看均值、ICIR、胜率、逐年分段表现;
  • 做 5 组或 10 组分层回测:看单调性、多空曲线、各组换手率;
  • 检查因子与市值、行业的相关性,判断是否只是老因子换皮;
  • 记录全部参数与结论,无论结果好坏都归档。

最后一条尤其重要:被否掉的因子记录下来,能避免几个月后换个马甲重测一遍。归档还有一个更深层的作用——控制多重检验偏差。当你测过一百个因子后,即使全是噪声,也大概率能挑出几个历史表现”显著”的。只有完整记录测试过多少个候选,你才能对幸存者的成色保持清醒:测得越多,对单个好结果就应该越怀疑。

本课小结#

  • 因子研究检验的是”截面排序对未来收益排序的预测力”,不是单只股票的走势;
  • RankIC 比 IC 更稳健,读数时要同时看均值、ICIR 和胜率,别只看均值;
  • 分层回测是 IC 的放大镜,重点看单调性和多空曲线的平稳性;
  • 极端组的成分检查能识别”伪因子”——很多因子只是市值或流动性的代理;
  • 建立固定的检验清单并归档每次结果,是因子研究工程化的第一步。

动手练习:选一个最简单的因子——过去 20 日收益率(动量/反转的原材料),在你熟悉的股票池上按月调仓,计算 RankIC 序列和 5 组分层净值,判断它在你的样本里是单调的吗?方向是动量还是反转?把结论和图存进你的研究档案。