2. Alpha衰减的度量方法:信息系数(IC)衰减、收益衰减曲线、衰减半衰期计算
好,咱们接着聊Alpha衰减的度量。说实话,这部分内容是我在实际工作中反复打磨过的。你想想看,一个策略好不好,不能光看它赚不赚钱,还得看它能赚多久的钱。这就是Alpha衰减要回答的问题。
我个人习惯把度量方法分成三个维度:IC衰减、收益衰减曲线、半衰期计算。这三个东西,说白了就是从不同角度去观察同一个现象——你的Alpha信号到底能活多久。
2.1 信息系数(IC)衰减
IC衰减,是我最常用的一个指标。为什么?因为它干净、直接。
IC(信息系数)衡量的是你的预测值和实际收益之间的相关性。那IC衰减呢,就是看这个相关性随时间怎么变化。
核心逻辑:如果今天你的IC是0.05,明天变成0.03,后天变成0.01,那你的信号衰减得很快。如果一个月后还有0.04,那说明你的信号很持久。
我在项目中遇到过这样的情况:一个基于高频订单流的因子,IC在T+0时高达0.08,但到了T+1就掉到0.02,T+2直接归零。嗯,这就是典型的「短命因子」。
计算IC衰减的步骤其实不复杂:
- 对每个时间点t,计算因子值和未来k期的收益之间的秩相关系数
- k从1取到N(比如20个交易日)
- 观察IC值随k的变化趋势
import pandas as pd
import numpy as np
from scipy.stats import spearmanr
def calc_ic_decay(factor_series, forward_returns, max_lag=20):
"""
计算IC衰减序列
factor_series: 因子值,index为时间,columns为股票
forward_returns: 未来收益,index为时间,columns为股票
"""
ic_decay = []
for lag in range(1, max_lag + 1):
# 对齐数据
aligned_factor = factor_series.shift(lag)
aligned_ret = forward_returns
# 逐日计算IC再取均值
daily_ics = []
for date in aligned_factor.index:
if date in aligned_ret.index:
f = aligned_factor.loc[date].dropna()
r = aligned_ret.loc[date].dropna()
common = f.index.intersection(r.index)
if len(common) > 30: # 至少30只股票
ic, _ = spearmanr(f[common], r[common])
daily_ics.append(ic)
ic_decay.append(np.mean(daily_ics))
return ic_decay
# 使用示例
ic_curve = calc_ic_decay(my_factor, forward_ret_1d, max_lag=20)
print(f"T+0 IC: {ic_curve[0]:.4f}")
print(f"T+5 IC: {ic_curve[4]:.4f}")
print(f"T+10 IC: {ic_curve[9]:.4f}")
我的小技巧:IC衰减曲线最好画出来看。如果曲线下降得像个陡坡,那你的策略得频繁调仓。如果像个缓坡,那你可以躺平久一点。
2.2 收益衰减曲线
收益衰减曲线,这个更直观。说白了就是看你的策略收益随时间怎么变化。
我记得有一次给老板汇报,他问我:「你这个策略到底能管几天?」我当时就画了张收益衰减曲线图,他一看就明白了。
收益衰减曲线的做法是这样的:
- 把股票按因子值分成10组(Decile)
- 计算每组在未来1天、2天、3天...N天的累计收益
- 看多头组和空头组的收益差随时间的变化
这里有个关键点——你要区分「累计收益」和「边际收益」。累计收益是越滚越大的,但边际收益是逐渐减小的。我们真正关心的是边际收益什么时候归零。
def calc_return_decay(factor_df, return_df, n_groups=10, max_hold=20):
"""
计算收益衰减曲线
返回:每个持有期下,多空组合的累计收益
"""
results = []
for hold_period in range(1, max_hold + 1):
# 计算未来hold_period期的累计收益
future_ret = return_df.rolling(window=hold_period).sum().shift(-hold_period)
# 分组
factor_rank = factor_df.rank(axis=1, pct=True)
groups = pd.qcut(factor_rank, n_groups, labels=False, axis=1)
# 计算每组平均收益
group_returns = []
for g in range(n_groups):
mask = (groups == g)
ret = future_ret[mask].mean(axis=1).mean()
group_returns.append(ret)
# 多空收益差(第10组 - 第1组)
long_short = group_returns[-1] - group_returns[0]
results.append(long_short)
return results
我曾经踩过的坑:收益衰减曲线一定要用「等权」计算,别用市值加权。市值加权会把大票的影响放大,小票的信号被淹没。我一开始没注意这个问题,结果画出来的曲线特别平滑,还以为策略很稳定,后来才发现是权重搞错了。
2.3 衰减半衰期计算
衰减半衰期,这个概念是从物理学借来的。它指的是你的Alpha信号衰减到初始强度一半所需要的时间。
为什么要算这个?因为半衰期是一个很直观的数字。你说「这个策略半衰期是5天」,别人一听就懂了。比看一堆曲线图要方便得多。
计算半衰期的方法有好几种,我推荐用指数衰减模型来拟合:
from scipy.optimize import curve_fit
import numpy as np
def half_life_calculation(ic_decay_series):
"""
通过指数衰减模型计算半衰期
模型: IC(t) = IC_0 * exp(-lambda * t)
半衰期: T_1/2 = ln(2) / lambda
"""
def exp_decay(t, ic0, lam):
return ic0 * np.exp(-lam * t)
t = np.arange(1, len(ic_decay_series) + 1)
y = ic_decay_series
# 拟合
try:
popt, _ = curve_fit(exp_decay, t, y, p0=[y[0], 0.1])
ic0, lam = popt
half_life = np.log(2) / lam
return half_life, ic0, lam
except:
return None, None, None
# 使用
ic_curve = [0.05, 0.042, 0.035, 0.029, 0.024, 0.02, 0.017, 0.014]
hl, ic0, lam = half_life_calculation(ic_curve)
print(f"初始IC: {ic0:.4f}")
print(f"衰减速率: {lam:.4f}")
print(f"半衰期: {hl:.1f} 天")
经验之谈:我个人觉得,半衰期低于3天的因子,基本只能做高频交易。半衰期在5-10天的,适合做中频。超过20天的,那就可以考虑做低频或者基本面策略了。
2.4 三种方法的对比与选择
这三种方法,各有各的用处。我一般会这样用:
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| IC衰减 | 干净、统计意义强 | 对异常值敏感 | 因子筛选、信号质量评估 |
| 收益衰减曲线 | 直观、贴近实际收益 | 受市场环境影响大 | 策略调仓频率决策 |
| 半衰期计算 | 单一数字、易于比较 | 假设指数衰减,不一定符合实际 | 因子横向对比、组合管理 |
你想想看,这三种方法其实是从不同角度回答同一个问题。IC衰减告诉你信号质量怎么变,收益衰减告诉你钱怎么变,半衰期告诉你时间尺度是多少。
我的建议:别只看一种。我习惯的做法是:先用IC衰减看信号质量,再用收益衰减曲线验证,最后算个半衰期给老板汇报。三管齐下,基本不会出大问题。
2.5 知识体系总览
下面这张图,是我自己整理的知识体系。你可以看到,三种度量方法不是孤立的,它们共同构成了Alpha衰减分析的核心框架。
嗯,这张图基本把这一章的核心内容都串起来了。你从中心往外看,先选度量方法,再看具体指标,最后落到调仓决策上。这就是我平时做Alpha衰减分析的完整流程。
最后提醒一句:别迷信半衰期这个数字。它只是一个参考。我见过半衰期10天的因子,在某个市场环境下3天就失效了。也见过半衰期3天的因子,换个参数设定又能活两周。所以,度量方法只是工具,真正的判断还得靠你对市场和策略的理解。
公众号:蓝海资料掘金营,微信deep3321