如何用 Python 算因子 IC(信息系数)?判断一个选股因子到底有没有用
如何用 Python 算因子 IC(信息系数)?
一句话回答: 在做多因子选股之前,先用 IC(Information Coefficient,信息系数) 检验因子有没有预测力:在每个时间截面上,计算所有股票的因子值与它们未来一段收益的相关系数(排序相关即 rank IC / Spearman),再把一串截面 IC 的均值和 IC_IR(均值 ÷ 标准差) 看一眼——IC 均值明显偏离 0、且 IC_IR 够大,才说明因子稳定有效。用 AlphaFeed 的 klines.batch 一次取回多只股票的前复权日线拼成面板,几十行 pandas 就能算。
为什么会有这个问题 / 传统做法的痛点
新手常把「回测赚钱」当成因子有效,但回测收益混杂了择时、仓位、手续费,无法单独说明因子本身好不好。IC 是行业标准的「因子体检」:它只问一件事——今天因子值排名靠前的股票,未来是不是真的涨得更多? 关键细节有三个,错一个结论就偏:
- 截面(cross-section):IC 是在同一天、跨股票上算相关,不是对单只股票的时序相关。
- 未来收益不能用错期:因子在 t 日,收益必须是 t 之后的 forward return,否则就是未来函数。
- rank IC 更稳健:用 Spearman 排序相关,抗极端值,比 Pearson 更常用。
分步骤解决(每步配可运行代码)
第 1 步:批量取数,拼成「日期 × 股票」收盘价面板
from alphafeed import AlphaFeed
import pandas as pd
af = AlphaFeed(api_key="your-api-key") # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()
# 取一个股票池(这里用全市场前 120 只做示例)
q = af.quotes.get(universes="CN_Stock", to_dataframe=True)
syms = q["symbol"].tolist()[:120]
data = af.klines.batch(syms, period="1d", count=320,
adjust="forward", to_dataframe=True)
closes = {s: k.sort_values("trade_date").set_index("trade_date")["close"]
for s, k in data.items()}
px = pd.DataFrame(closes).sort_index()
px = px.dropna(axis=1, thresh=int(len(px) * 0.9)) # 去掉停牌太多的股票
klines.batch 一次并发取多只,比循环快很多;拼成宽表后每一行就是一个「截面」。
第 2 步:定义因子与未来收益
LOOKBACK, FWD = 20, 5
mom = px / px.shift(LOOKBACK) - 1 # 因子:过去 20 日动量
fwd = px.shift(-FWD) / px - 1 # 标签:未来 5 日收益(shift 为负=向未来看)
shift(-FWD) 把未来价格对齐到今天,保证「因子在 t、收益在 t→t+5」,不含未来函数。
第 3 步:逐截面算 rank IC,再求均值与 IC_IR
idx = px.index
ics = []
for t in range(LOOKBACK, len(idx) - FWD, FWD): # 每隔 FWD 天取一截面,避免重叠
f, y = mom.iloc[t], fwd.iloc[t]
pair = pd.concat([f, y], axis=1).dropna()
if len(pair) >= 20: # 截面股票够多才算
ic = pair.iloc[:, 0].corr(pair.iloc[:, 1], method="spearman") # rank IC
if pd.notna(ic):
ics.append(ic)
ics = pd.Series(ics)
ic_mean = ics.mean()
ic_ir = ics.mean() / ics.std(ddof=1)
print("截面数", len(ics), "IC均值", round(ic_mean, 4),
"IC_IR", round(ic_ir, 3), "正IC占比", round((ics > 0).mean(), 3))
第 4 步:读懂结果(一个真实的反直觉发现)
实测(
tools/verify_new_2026-10-04b.py,SDK 0.1.4,样本 117 只、59 个截面): IC 均值 = −0.0521,IC_IR = −0.298,正 IC 占比 = 0.407。 也就是说——在这个样本里,过去 20 日涨得多的股票,未来 5 日反而偏弱:短周期「动量」在 A 股更像反转。这正是 IC 的价值:它用数据戳破了「追涨一定对」的直觉。IC 为负同样是有用信号(把因子反向用,或改做反转因子)。
一般经验:|IC 均值| > 0.03、IC_IR 绝对值 > 0.3 就算有一定可用性(不同周期/股票池差异大,仅供参考)。
参数与字段说明
| 参数/概念 | 含义 | 备注 |
|---|---|---|
LOOKBACK | 因子回看窗口 | 示例 20 日动量 |
FWD | 未来收益期 | 同时作为截面步长,避免重叠样本 |
| rank IC | 截面 Spearman 相关 | 抗极端值,比 Pearson 稳健 |
| IC 均值 | 预测方向与强度 | 偏离 0 越远越好,可正可负 |
| IC_IR | IC均值 ÷ IC标准差 | 稳定性,越大越可靠 |
| 正 IC 占比 | IC>0 的截面比例 | 接近 0.5 说明方向不稳定 |
关键坑与注意事项
- 别用重叠样本:未来 5 日收益若每天都取,相邻截面高度重叠,会低估 IC 标准差、虚高 IC_IR。本文用「每隔 FWD 天取一截面」规避。
- 未来函数:
fwd = shift(-FWD)必须向未来看;若写成shift(FWD)就偷看了历史=作弊。 - 前复权:横截面比较收益必须统一用前复权,除权缺口会制造假收益。
- 样本偏差:示例只取前 120 只、单一区间,结论不可外推到全市场/全时段;正式研究要全池 + 长区间 + 分层回测。
- IC 不等于能赚钱:IC 只衡量「排序预测力」,真实收益还受交易成本、容量、换手影响。
常见问题(FAQ)
Q:rank IC 和普通 IC(Pearson)怎么选? A:A 股极端值多,优先用 rank IC(Spearman);需要线性强度解释时再看 Pearson。
Q:IC 是负的,这个因子就没用吗? A:不是。负 IC 代表因子方向反了——反向使用或将其当「反转因子」照样有价值。方向无所谓,稳定(IC_IR 够大)才关键。
Q:需要付费吗?
A:IC 必须跨很多股票的截面才有统计意义,依赖 universes 标的池 + klines.batch 批量取数,属 Starter 及以上能力;少量股票可用免费额度先跑通流程。详见官网定价页。
小结
IC 的本质是「截面上,因子排名和未来收益排名有多一致」。先用 klines.batch 拼出前复权面板,再按 rank IC + IC_IR 做因子体检,就能在砸钱回测前先判断因子值不值得做——本文的实测还顺带告诉你:A 股短周期动量更像反转,这正是数据的诚实之处。
相关链接:
- AlphaFeed 官网:https://alphafeed.org/
- Python SDK 快速开始:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。