用 gs-quant 算出因子还能撑多久:IC 半衰期实战指南
【免费下载链接】gs-quantPython toolkit for quantitative finance项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant
上周组里刚评完一批候选因子,紧接着就有人追问:动量因子按月调仓够不够?它的因子有效性还能"撑"多久?这正是 IC 半衰期要回答的问题。拿 gs-quant 的风险模型和时序工具走一遍,二十分钟就能把答案算出来。
🔋 先说人话:IC 和半衰期在衡量什么
打个比方,因子就像一块给策略供电的电池。IC(信息系数)就是它此刻的输出电流——每个交易日,因子暴露对下期收益的预测有多准。电流强,预测可信;电流掉到 0.05 以下,基本等于没在供电。
电池的电还会随时间漏掉,漏到只剩一半要多久?这就是半衰期。假设因子 IC 按指数规律衰减:$IC(t) = IC_0 e^{-\lambda t}$,那么半衰期 $t_{1/2} = \ln 2 / \lambda$。用大白话拆一下符号:$IC_0$ 是初始电量,$\lambda$ 是漏电速度,漏电越快,因子越"不耐放"。
数据侧,因子暴露由风险模型提供,gs_quant/markets/factor.py 里的 Factor 类负责把模型中的因子取出来。说白了,半衰期就是因子有效性的保质期,它直接框定了调仓频率的上限:保质期 21 个交易日的因子,按月调仓大致刚好,按周调仓多半是白花钱。
三步把半衰期算出来
整条链路就三件事:数据拿进来、IC 序列滚出来、衰减参数解出来。
1. 加载因子暴露与收益数据
先回答"数据从哪来":Marquee 这类风险模型,暴露和收益都能直接取。
import datetime as dt from gs_quant.models.risk_model import FactorRiskModel # 用模型 ID 取模型对象(按实际权限替换) model = FactorRiskModel.get("MODEL_ID") # 取一段区间内的因子数据,并带上因子收益曲线 factor_perf = model.get_factor_data( start_date=dt.date(2022, 1, 1), end_date=dt.date(2024, 12, 31), include_performance_curve=True, )个股维度的暴露可用 get_universe_exposure 批量取,单名收益用 get_daily_return,入口都集中在 gs_quant/models/risk_model.py。
2. 滚动计算 IC 序列
这一步把"预测力"变成一个随时间走的序列。
from gs_quant.timeseries import correlation # exposure_ret:当日因子暴露收益;next_ret:下期组合收益 # w=60 表示 60 个交易日滚动窗口;若输入的是收益序列, # 用 SeriesType.RETURNS 显式声明,别让它当价格再求一次收益 ic = correlation(exposure_ret, next_ret, w=60)correlation 定义在 gs_quant/timeseries/econometrics.py,窗口参数 w 支持 Window 对象,前 N 天可设 ramp-up,避开冷启动带来的噪声。
3. 拟合衰减系数,解出半衰期
把干净的 IC 点拟合成一条指数曲线,λ 一出来,半衰期就是道算术题。
import numpy as np from scipy.optimize import curve_fit def exp_decay(t, ic0, lam): # 指数衰减模板:ic0 初始值,lam 衰减速度 return ic0 * np.exp(-lam * t) clean = ic.dropna() t = np.arange(len(clean)) (ic0, lam), _ = curve_fit(exp_decay, t, clean.values, p0=[clean.iloc[0], 0.05]) half_life = np.log(2) / lam # 半衰期 = ln2 ÷ 衰减速度 print(f"拟合半衰期约 {half_life:.0f} 个交易日")p0 一定要给初值:ic0 用首值、lam 给个小正数,能明显降低拟合跑飞的概率。
📈 结果怎么读:只盯两个数字
画图只是把两条曲线叠在一起:实线是滚动 IC,虚线是拟合的 IC 衰减曲线。
import matplotlib.pyplot as plt plt.plot(clean.index, clean, label="滚动 IC") plt.plot(t, exp_decay(t, ic0, lam), "r--", label=f"拟合 半衰期 {half_life:.0f} 天") plt.legend() plt.show()看完图,真正要带走的是两个数。
- 拟合半衰期本身:它是调仓频率的天花板。半衰期 15 天的因子按季调仓,等于大部分时间捏着块漏电的电池;半衰期 60 天以上,月度调仓绰绰有余。
- 残差的方向:拟合线长期压在实际点上方,说明真实衰减"前慢后快",单指数会把半衰期估长;持续在下方则相反。残差有系统性方向时,把样本切成两段分别再拟一次。
仓库里这张图展示的也是同类"随时间走弱"的相关性曲线,读法一致:先找衰减起点,再看衰减斜率。
经验清单:四个误区对照
- 误区:IC 窗口越大越稳 → 正确:大窗口会把衰减本身抹平,20~60 交易日是常用区间,再用两档窗口交叉验证半衰期是否稳定。
- 误区:拟合一次就定案 → 正确:残差有方向就分样本重拟,必要时给衰减函数加漂移项或改分段拟合。
- 误区:半衰期等于调仓频率 → 正确:它只是上限,叠上交易成本后,多数情况按不高于半衰期调仓才划算。
- 误区:拿单只股票的时间序列直接算 → 正确:IC 本质是截面统计量,先按日在股票间做秩相关得到 IC 序列,再谈衰减。
收尾
下一步可以按因子分组:把每个因子的半衰期作为权重输入,长寿命因子降频、短寿命因子提频,就是一条动态调仓的路线。gs-quant 的 markets 模块里有现成的组合与优化接口,接上优化器,这次分享就能长成一个完整策略。
【免费下载链接】gs-quantPython toolkit for quantitative finance项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考