news 2026/9/13 4:32:36

用 gs-quant 算出因子还能撑多久:IC 半衰期实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用 gs-quant 算出因子还能撑多久:IC 半衰期实战指南

用 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 4:32:12

AI辅助专业工作:技术边界与真实价值解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 4:30:27

Spring Boot 3集成Druid踩坑指南:四大报错与配置模板

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 4:30:03

RK3588边缘AI配置四层解耦架构:告别JSON定时炸弹

1. 为什么一个JSON文件在RK3588边缘AI项目里会成为“定时炸弹”我第一次在客户现场看到那个叫config.json的文件时,它正躺在RK3588板卡的/etc/ai/目录下,大小237KB,嵌套了17层对象,数组里套数组,数组里又塞对象&#x…

作者头像 李华
网站建设 2026/9/13 4:30:00

网址导航网站大全:从通用到垂直,实测推荐与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华