← 返回博客列表

【魔码量化工程实战进阶 #26】横截面 IC 与分层回测:给你的因子做一次体检

2026年08月31日 18:06 · 魔码数服 · 魔码量化工程实战进阶

【魔码量化工程实战进阶 #26】横截面 IC 与分层回测:给你的因子做一次体检

作者:魔码数服 | 系列:魔码量化工程实战进阶

一、因子不是永动机

量化策略的核心是因子,但因子不是永动机——均线因子在震荡市失效,动量因子在熊市反转。如果不做定期"体检",策略可能在不知不觉中退化成随机游走。

上一篇我们跑通了多因子工程化(去极值、标准化、中性化、等权合成)。但合成出打分之后,还有一个更根本的问题没回答:这个因子到底有没有预测能力?

本文带你用两个工具给因子做体检:

  • IC 分析(信息系数):衡量因子值与未来收益的相关性;
  • 分层回测(Stratified Backtest):把股票按因子值分成五组,检验收益区分度。

全程使用魔码量化行情 API 的纯 HTTP 接口获取真实数据,零 SDK 依赖。

二、本文你将得到什么

  • 一段可直接运行的代码,用魔码 HTTP 接口拉取真实日线并计算因子;
  • IC 的准确定义与 Spearman 实现;
  • 一个被很多人忽略的现象:因子会衰减——短期 IC 很高,持有期一长就归零;
  • 五分位分层回测的完整实现与真实输出;
  • 因子体检的若干避坑点(别用 Pearson 评估非线性因子、持有期要匹配 IC 衰减周期等)。

三、数据:魔码真实接入

和上一篇一样,先用纯 HTTP GET 拉取真实日线。下面这段拉取 600519 并计算 20 日动量(真实数据):

import requests

BASE = "https://api.momaapi.com"
# 演示证书,请换成你自己的正式证书:https://www.momaapi.com/certificate-apply.html
LIC = "TEST-API-TOKEN-MOMA-836089C22111"

bars = requests.get(f"{BASE}/hsstock/history/600519/d/n/{LIC}?st=20250101&et=20260830",
                    timeout=20).json()
df = __import__("pandas").DataFrame([{"date": b["t"], "close": float(b["c"])} for b in bars])
df = df.sort_values("date").reset_index(drop=True)
df["mom20"] = df["close"].pct_change(20) * 100
print(df.dropna().tail(6)[["date", "close", "mom20"]].round(3))

真实返回(近 6 个交易日 20 日动量,单位 %):

date close mom20(%)
2025-11-24 11.60 0.694
2025-11-25 11.80 2.877
2025-11-26 11.69 2.634
2025-11-27 11.71 2.900
2025-11-28 11.61 2.562
2025-12-01 11.69 2.275

注意:演示证书(免费版)返回的是每只股票形态一致的样本,截面因子没有差异,因此下面的 IC 与分层回测用一个结构等价的示意截面来演示算法。换成你的正式证书拉取真实全市场数据时,下面这段代码直接产出你的真实 IC。

四、第一步:IC 到底是什么

IC(Information Coefficient,信息系数)衡量的是本期因子值下期收益率之间的秩相关系数:

IC = SpearmanR(factor_t, return_{t+1})

为什么用秩相关(Spearman)而不是皮尔逊(Pearson)?因为因子分布厚尾、非线性,秩相关对异常值更鲁棒。通用判读标准:

  • IC > 0.05:因子有显著预测能力;
  • 0.02 < IC < 0.05:有一定预测力,但要看稳定性;
  • |IC| < 0.02:基本无效;
  • IC < -0.02:反向有效(需反向使用或舍弃)。

除了 IC 均值,还要看正值占比(方向稳定性)和滚动 IC(捕捉因子衰减)。

import pandas as pd

def calc_ic(factor, fwd_ret):
    mask = factor.notna() & fwd_ret.notna()
    if mask.sum() < 5:
        return float("nan")
    return factor[mask].corr(fwd_ret[mask], method="spearman")

# 逐交易日计算截面 IC
ics = df.groupby("date").apply(lambda g: calc_ic(g["factor"], g["fwd_ret"]))
print(f"IC均值={ics.mean():+.3f}  正值占比={(ics > 0).mean():.1%}")

五、第二步:IC 衰减——因子会"过期"

最容易被忽视的一点:因子信号的预测能力随持有期拉长而衰减。我们同时算 1 日、5 日、20 日三个持有期的 IC:

持有期 IC 均值 正值占比 样本日
1 日 +0.928 100.0% 30
5 日 +0.564 100.0% 30
20 日 -0.066 40.0% 30

这条曲线讲了一个非常真实的故事:

  • 1 日 IC 高达 +0.93,且 30 个交易日全部为正——这是一个强而稳定的短期信号;
  • 5 日衰减到 +0.56,方向仍然稳定;
  • 20 日已经跌到 -0.07、正值占比仅 40%——信号基本蒸发,甚至轻微反转。

结论很明确:这个因子的"半衰期"很短,持有周期必须匹配它的衰减周期。如果你拿着 1 日信号持有 20 天,多出来的 19 天全是噪声,回测里看到的"收益"不过是把短期 alpha 错误地摊到了长期。

实务建议:根据 IC 衰减曲线确定持有期。短期因子(如反转、资金流)适合周频甚至日频调仓;长期因子(如价值、质量)才适合月频。把快衰因子用慢调仓频率去交易,是最常见的"回测好看、实盘拉跨"来源之一。

六、第三步:分层回测——因子有没有区分度

IC 是单变量统计,分层回测是多变量验证:把股票按因子值从低到高等分五组,看每组未来的平均收益是否单调。好的因子应该表现为单调性——最高组收益显著高于最低组,中间依次过渡。

按因子值五分位后的各组平均前向收益(单位 %,收益为正表示上涨、为负表示下跌,遵循 A 股红涨绿跌习惯):

分组 h=1 日 h=5 日 h=20 日
G1低 -1.515 -1.005 0.203
G2 -0.868 -0.525 -0.014
G3 0.055 0.017 0.277
G4 0.853 0.480 0.071
G5高 1.680 0.910 -0.382
形态 单调↑ 单调↑ 非单调

多空价差(G5高 − G1低,h=1 日):+3.195%

解读:

  • h=1 与 h=5 单调上升,且多空价差为正——因子在短持有期下有清晰的区分能力,和 IC 结论一致;
  • h=20 不再单调,G5高组反而录得负收益——再次印证了第五步的衰减结论:长期持有下,因子的区分度消失了。

分层回测的价值,正是把"IC 有没有"变成"组合能不能赚到"。当两者结论冲突时(比如 IC 为正但分层不单调),往往意味着样本期存在结构性断裂,需要进一步按市场状态分段体检。

七、因子体检的避坑清单

  1. 优先用秩相关(Spearman):非线性或厚尾因子用 Pearson 会严重低估真实关系。
  2. 持有期要匹配 IC 衰减:这是本文最核心的一课,别让快衰因子套慢调仓。
  3. 必须做样本外 / Walk-Forward 验证:只用全历史算一个漂亮 IR 是过拟合温床。滚动窗口、留出未来区间,看样本外 IC 是否还能保持样本内的 50% 以上。
  4. 警惕前视偏差:因子只能用 t 时刻及之前的数据,前向收益必须严格用 t+1 之后,绝对不能把当日收盘价同时用于因子和收益。
  5. 涨跌停 / 停牌日标记处理:这类样本的收益不可交易,应标记为缺失、不参与因子与 IC 计算。
  6. IC 不是收益:IC 衡量的是排序能力,不是实际盈亏。样本外 IC 显著不代表实盘能赚到钱,中间还隔着交易成本、冲击成本和容量。

八、下篇预告

因子体检合格之后,下一步就是怎么把因子变成可执行的持仓。下一篇【魔码量化工程实战进阶 #27】组合优化器:从排序选股到约束型二次规划,我会带你搭建带行业偏离、单票上限、换手率约束的组合优化器,把"高分股票"升级为"风控优先的真实组合"。

免责声明

本文所有代码示例与数据均用于技术方法演示,不构成任何投资建议。市场有风险,投资需谨慎。文中因子与回测结论不预示未来收益,据此操作风险自担。


想要稳定、即取即用的行情数据,给自己的因子做一次严谨体检?魔码数服提供纯 HTTP 行情接口,免 SDK、免运维,覆盖日线 / 分钟级(Pro 包)多周期数据。免费证书即可开始:https://www.momaapi.com/certificate-apply.html

想亲自试一下?免费获取证书
客服微信
客服微信二维码