第 4 章 · 01 alphalens 动量因子完整检验(对应 QS015)


文档摘要

第 4 章 · 01 alphalens 动量因子完整检验(对应 QS015) 速查摘要:alphalens 是 Quantopian 出品的因子检验套件,把一个因子序列喂进去,一键输出 IC、分位数组合收益、换手率、收益分析全套报告(tear sheet)。本节拿 5 只美股算 90 日动量,做一次标准因子评估,看"动量"这个信号对未来 1/5/10 日收益到底有没有预测力。是人工因子走向科学验证的入口工具。 涉及脚本:原项目 (约 56 行) ⚠️ 注意:脚本里 ,这里的 是前瞻对齐——把"未来 90 日的收益"挪到今天,纯研究用。任何回测/实盘代码绝对不能这么写,否则就是偷看未来。

第 4 章 · 01 alphalens 动量因子完整检验(对应 QS015)

速查摘要:alphalens 是 Quantopian 出品的因子检验套件,把一个因子序列喂进去,一键输出 IC、分位数组合收益、换手率、收益分析全套报告(tear sheet)。本节拿 5 只美股算 90 日动量,做一次标准因子评估,看"动量"这个信号对未来 1/5/10 日收益到底有没有预测力。是人工因子走向科学验证的入口工具。

涉及脚本:原项目 QS015-alphalens/01_alphalens_momentum.py(约 56 行)

⚠️ 注意:脚本里 momentum = prices.pct_change(90).shift(-90),这里的 shift(-90)前瞻对齐——把"未来 90 日的收益"挪到今天,纯研究用。任何回测/实盘代码绝对不能这么写,否则就是偷看未来。alphalens 的 get_clean_factor_and_forward_returns 内部会自己算 forward returns,本节这种"先 shift(-90) 再让 alphalens 算 1/5/10 日"是研究框架里的对齐写法,看懂逻辑即可,别照搬到策略。

工具与原理

截面因子(factor):每个时间点(每个 date)、每只股票(asset)上都有一个数值的信号,比如动量、估值、波动率。alphalens 评估的就是"这个数值排在前面的股票,未来收益是不是更好"。

forward returns(未来收益):对每个时点 t、每个标的,算 t+1、t+5、t+10 的收益率。alphalens 自动从价格序列里算。

因子 IC(信息系数):每个时间截面上,因子值与未来收益的 Spearman 秩相关:

IC_t = Spearman(factor_t, forward_return_t)

把所有截面的 IC 取平均,就是平均信息系数。|IC|>0.03 一般算"有点用",>0.1 已经很好。ICIR(IC 的稳定性,均值/标准差)看的是 IC 是不是稳定为正。

为什么用 Spearman(秩相关)而非 Pearson(线性相关)?因为金融数据有大量离群值(个别股票单日涨 20%),Pearson 容易被极端值带偏;Spearman 只看排名,对离群值鲁棒。alphalens 默认就是 Spearman IC。

IC 还分两种:正常 IC(因子 vs 同期收益,衡量因子自身质量)和 IR/ICIR(IC 的时序稳定性)。研究里两个都要看,前者看"预测力强度",后者看"预测力稳定性"。

分位数组合(quantile portfolio):每天把所有股票按因子值排序分 5(或 10)档,看第 1 档(top)和第 5 档(bottom)未来收益的差异。如果因子有效,最高档和最低档应该有明显价差,价差就是"分位数组合多空收益"。

分位数组合的优点是不依赖相关系数的线性假设——即使因子和收益是 U 形关系(最高最低档都涨),IC 可能算出 0,但分位数组合能识别出来。所以 IC 和分位数组合互补,两个都看才全面。

tear sheet:alphalens 的标准输出集合,包含 IC 时序图、IC 热力图(按持有期)、分位数组合累计收益、换手率、收益归因(按时段)。一行 create_full_tear_sheet 出全套。主要看四组图:

  1. IC 时序图:逐日 IC 折线 + 累计 IC,看 IC 是不是稳定为正(波动剧烈说明因子时灵时不灵)。
  2. IC 热力图(mean IC by forward window / quantile):不同持有期(1/5/10 日)× 不同分位的平均收益,看因子在哪个持有期最有效、单调性如何。
  3. 分位数组合累计收益:Q1~Q5(按因子值排序分 5 档)各自的累计收益曲线,有效因子应单调(因子值越大收益越高,或反之)。
  4. 换手率:每个分位组合的日均换手比例,反映交易成本——高换手策略实盘扣费后 alpha 容易消失。

ICIR(信息比率):IC 的均值除以 IC 的标准差,衡量 IC 的稳定性:

ICIR = mean(IC) / std(IC)

ICIR > 0.3 一般认为因子可用,> 0.5 已经不错。比单看 IC 均值更全面——一个 IC 均值 0.05 但标准差 0.4(波动剧烈)的因子,远不如 IC 均值 0.04 但标准差 0.1(稳定)的因子。

脚本精读

数据准备——拉 5 只股票,取 Adj Close 并前向填充缺失:

tickers = ["AAPL", "GOOGL", "MSFT", "JPM", "GE"] data = yf.download(tickers, start="2020-01-01", end="2023-01-01") prices = data['Adj Close'].ffill()

算动量——这里就是 shift(-90) 前瞻陷阱:

momentum_window = 90 momentum = prices.pct_change(periods=momentum_window).shift(-momentum_window)

pct_change(90) 是"今天相对 90 天前的收益率",.shift(-90) 把它整体向下挪 90 行(即"未来 90 天前"),本质是在每个时间点放"过去 90 日动量"的对齐版本。这种写法在纯研究里没问题,因为后续 alphalens 会重新对齐算未来收益。

把因子序列塞进 pd.MultiIndex(date, asset)——这是 alphalens 的硬性要求:

factor_data = { (date, ticker): value for date in momentum.index for ticker, value in momentum.loc[date].items() if np.isfinite(value) } factor_index = pd.MultiIndex.from_tuples(factor_data.keys(), names=['date', 'asset']) factor_values = pd.Series(factor_data.values(), index=factor_index, name='momentum').to_frame()

价格也要对齐到同一份索引:

aligned_prices = prices.stack().reindex(factor_index).unstack()

prices.stack() 把"日期 × 股票"宽表压成长 Series,.reindex(factor_index) 只保留因子存在的(date, asset),.unstack() 再摊回宽表。

一键洗数据 + 一键出报告:

factor_data_al = al.utils.get_clean_factor_and_forward_returns( factor=factor_values['momentum'], prices=aligned_prices, periods=[1, 5, 10], max_loss=0.5 ) al.tears.create_full_tear_sheet(factor_data_al)

periods=[1, 5, 10] 让它算 1/5/10 日未来收益,max_loss=0.5 允许丢弃最多 50% 的数据(因 NaN 或对齐问题被 alphalens 内部清理),超过就报错。create_full_tear_sheet 出全部图表。

关键技巧

  1. MultiIndex(date, asset) 是 alphalens 的硬通货。因子 Series 必须是两层索引,名字必须叫 dateasset(写法见上)。错了它直接报错。
  2. get_clean_factor_and_forward_returns 是洗数据中枢。它内部把因子和价格按日期对齐、丢 NaN、按分位数分组,返回一个统一的"factor + 多列 forward return"的大 DataFrame,后续所有图表都基于它。
  3. max_loss 容忍比例。设 0.5 表示允许丢一半数据,设 0 严格不允许丢。研究阶段建议 0.3~0.5,真要发策略再收紧。
  4. shift 方向陷阱:shift(n) 是把数据向移 n 行(把过去的值挪到现在),shift(-n) 是向移(把未来的值挪到现在)。回测里几乎永远只用 shift(n),看到 shift(-n) 要立刻警觉是否在偷看未来。
  5. 一行出全套报告:create_full_tear_sheet 是 alphalens 的招牌,几乎不用配置就能出 IC、分组、换手、收益归因四类图。
  6. 解读顺序建议:先看 IC 均值是否显著(>0.03)、再看分位数组合是否单调(Q1→Q5 是否单调递增/递减)、再看最高档减最低档收益是否可观、最后看换手率是否过高(高换手扣费后 alpha 可能消失)。
  7. 5 只股票样本太少:本节只为演示流程,实战因子检验至少用全市场(数百只以上)的股票池,IC 才有统计意义。5 只股票的 IC 噪声极大,结论不可靠——这是教学样本,别当实战结果。
  8. prices.stack().reindex(factor_index).unstack() 的对齐技巧:把宽表压成长 Series,reindex 只保留因子存在的(date,asset)对,再摊回宽表。alphalens 要求 prices 列索引 = factor 第二级 asset 集合的子集,这一步保证对齐。
  9. NaN 在 factor 阶段就过滤:if np.isfinite(value) 在构造 MultiIndex 时丢掉因子里的 NaN(动量头 90 天无法计算),比让 alphalens 内部 max_loss 吞掉更可控、错误更易诊断。

💡 速查要点:想验证一个因子有没有 alpha,套路是"算因子 → MultiIndex → get_clean_factor_and_forward_returns → create_full_tear_sheet"。看 IC 均值(>0.03 有点用)、看分位数组合单调性(从 Q1 到 Q5 是否单调)、看最高最低档价差。Shift 负数 = 前视,回测禁用。

本节要点

  1. alphalens 是因子检验标准工具,核心是 IC(因子值与未来收益的相关)+ 分位数组合(按因子值分档看未来收益差异)。
  2. 因子序列必须塞进 pd.MultiIndex(date, asset),price 要 stack().reindex().unstack() 对齐。
  3. get_clean_factor_and_forward_returns(periods=[1,5,10], max_loss=0.5) 洗数据,create_full_tear_sheet 一行出全套报告。
  4. shift(-90) 是前瞻对齐,纯研究用,回测严禁——这是本节最大的坑。
  5. 评估一个因子有没有 alpha:IC 均值、ICIR、分位数组合单调性、最高档减最低档收益,综合看。

实战补充

因子中性化:本节只算"裸动量",实战中应该把因子对市值、行业、贝塔中性化,排除这些常见风险因子的干扰,剩下的才是"纯 alpha"。alphalens 不直接做中性化,需要研究者在喂入前用线性回归 factor = alpha + beta_market * market_cap + beta_industry * industry + ε 残差化。

分层抽样:5 只股票无法体现因子在全市场的真实表现。实战应选 300-3000 只股票的池(如 S&P 1500、A 股全市场),IC 才有统计意义。本节代码逻辑可原样复用,只需把 tickers 列表扩大。

多因子叠加:单因子检验完后,可以把多个因子(动量 + 反转 + 估值)合成一个综合因子(等权、IC 加权、回归加权),再过 alphalens 检验合成因子的有效性。综合因子通常比单因子稳定。

实盘前最后一关:alphalens 检验只是研究阶段,通过 alphalens 的因子还要过 Zipline(第 02 节)等回测引擎,加上交易成本、滑点、冲击成本,看扣费后 alpha 还在不在。研究通过的因子 ≈ 30% 能在回测中存活,回测通过的 ≈ 30% 能在实盘存活——研究到实盘层层筛选是常态。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U