Caloogy 文章
English

因子检验:IC、分层回测与换手率

日期
2025 年 3 月 5 日
系列
量化
阅读
4 分钟
主题
QuantFactor testingInformation coefficientIcir

一句话版本

做好了一个因子,怎么判断它有没有用?标准的检验有三件套:

  1. IC(信息系数):每一期,因子值和下一期收益的(秩)相关系数。它衡量"排序预测得准不准";
  2. 分层回测:每一期按因子值把股票分成 5 组(或 10 组),看各组的未来收益是否单调;
  3. 换手率与衰减:因子排名变化得多快,预测力能维持多久。这决定了它在扣除交易成本后还剩多少。

一个好因子的典型样子是:IC 不大(0.03 到 0.05 就已经很好),但稳定(ICIR 高);分组收益单调;换手适中。

免责

本文仅为技术科普,不构成任何投资建议。文中代码使用模拟数据。


第一层:通俗理解

1.1 天气预报员的成绩单

评价一个天气预报员,不是看他某一天有没有说对,而是看:

因子检验做的是同样的事:IC 衡量"平均有多准",ICIR 衡量"稳不稳定",分层回测检查"分档是否合理"。

1.2 为什么 0.05 的相关就算好

日常生活中,相关系数 0.05 几乎等于没有关系。但在股票市场:

这就是 Grinold 提出的"主动管理基本定律"的直觉:

IR≈IC×Breadth,\text{IR} \approx \text{IC} \times \sqrt{\text{Breadth}},

信息比率约等于"每次预测的准确度"乘以"独立下注次数的平方根"。


第二层:三件套详解

2.1 IC

每一期 tt:

ICt=Corrrank(fi,t, ri,t+1),\text{IC}_t = \text{Corr}_{\text{rank}}\big(f_{i,t},\ r_{i,t+1}\big),

用 Spearman 秩相关(而不是 Pearson),对极端值更稳健。

指标 含义 参考
IC 均值 平均预测力 绝对值 0.02–0.03 可以考虑,0.05 以上很少见
IC 标准差 波动 —
ICIR = 均值 / 标准差 稳定性 年化后 0.5 以上不错
IC > 0 的比例 胜率 60% 以上较好
IC 的 t 值 统计显著性 注意多重检验问题(见《多重检验与因子动物园》)

2.2 分层回测

每一期按因子值分成 5 组,等权持有到下一期,记录每组收益:

2.3 换手率与衰减

概念 计算 意义
换手率 每期组合成分的变化比例 换手越高,交易成本越高(见《交易成本建模》)
因子自相关 本期因子值与上期的秩相关 越高说明因子越"慢",换手越低
IC 衰减 因子与未来第 1、2、3…期收益的 IC 衰减越慢,越可以降低调仓频率

第三层:代码实现

3.1 构造模拟面板并计算 IC

模拟 120 个月、每月 300 只股票,因子和下一期收益之间藏着大约 0.04 的真实相关:

import numpy as np, pandas as pd
rng = np.random.default_rng(1)
T, N = 120, 300
dates = pd.period_range("2015-01", periods=T, freq="M")
rows = []
for t in dates:
    f = rng.normal(size=N)
    r = 0.04 * f + rng.normal(size=N)        # 真实 IC 约 0.04
    rows.append(pd.DataFrame({"date": t, "factor": f, "fwd_ret": r * 0.08}))
panel = pd.concat(rows, ignore_index=True)

ic = panel.groupby("date").apply(lambda g: g["factor"].corr(g["fwd_ret"], method="spearman"))
print("IC均值", round(ic.mean(), 4), "IC标准差", round(ic.std(), 4),
      "ICIR(年化)", round(ic.mean() / ic.std() * np.sqrt(12), 2), "IC>0占比", round((ic > 0).mean(), 2))

输出:

IC均值 0.0465 IC标准差 0.06 ICIR(年化) 2.69 IC>0占比 0.77

3.2 五分组回测

panel["q"] = panel.groupby("date")["factor"].transform(lambda x: pd.qcut(x, 5, labels=False) + 1)
qret = panel.groupby(["date", "q"])["fwd_ret"].mean().unstack()
print("各分组月均收益(%)", (qret.mean() * 100).round(3).to_dict())
ls = qret[5] - qret[1]
print("多空年化收益(%)", round(ls.mean() * 12 * 100, 2), "多空夏普", round(ls.mean() / ls.std() * np.sqrt(12), 2))

输出:

各分组月均收益(%) {1: -0.573, 2: -0.373, 3: -0.063, 4: -0.026, 5: 0.53}
多空年化收益(%) 13.24 多空夏普 2.74

3.3 解读与现实校准

3.4 换手率的计算

def top_quintile_turnover(panel):
    tops = panel[panel["q"] == 5].groupby("date")["stock_id"].apply(set)
    prev, out = None, []
    for s in tops:
        if prev is not None:
            out.append(1 - len(s & prev) / len(s))   # 本期新进入的比例
        prev = s
    return np.mean(out)

(这段需要面板里有 stock_id 列;上面的模拟数据每期股票独立生成,没有身份延续,所以换手率会接近 80%,这正是"因子没有自相关"时的情况。)

3.5 一份检验报告应包含什么

项目 内容
基本统计 覆盖率、分布、与已有因子(市值、行业、动量……)的相关性
IC 分析 均值、ICIR、胜率、IC 时间序列图、分年度 IC
分层回测 各组净值曲线、单调性、多空收益与回撤
稳定性 不同市场环境(牛市、熊市、高波动)、不同股票池(大盘、小盘)下的表现
换手与衰减 换手率、IC 衰减曲线、扣除成本后的收益
增量信息 对已有因子做回归,看新因子是否提供了新的信息

总结

工具 回答的问题
IC / ICIR 平均预测力有多强、多稳定
分层回测 排序是否单调、多空收益如何
换手与衰减 扣除成本后还剩多少、调仓多快合适
主动管理基本定律 IR ≈ IC × √广度:微弱但稳定的优势可以积少成多