2. 因子IC的计算方法:Spearman秩相关系数、Pearson相关系数、IC计算实战
聊到因子IC的计算,很多新手第一反应就是「直接算相关系数呗」。嗯,这话没错,但具体用哪种相关系数,什么时候用,坑在哪里,这里面的门道可不少。我个人习惯把IC计算分成两派:一派是Pearson,一派是Spearman。今天咱们就把这两兄弟掰开揉碎了讲清楚。
2.1 Pearson相关系数:线性关系的「尺子」
Pearson相关系数,说白了就是衡量两个变量之间线性关系的强度和方向。公式长这样:
ρ = Cov(X, Y) / (σ_X * σ_Y)
其中Cov是协方差,σ是标准差。取值范围在[-1, 1]之间。1代表完全正相关,-1代表完全负相关,0代表没有线性关系。
但这里有个大坑——Pearson对异常值极其敏感。我在项目中遇到过一回,某只小盘股因为财报数据异常,因子值突然飙到天上,结果整个IC序列都被带偏了。后来我花了整整一个下午排查,才发现是这一个点搞的鬼。
另外,Pearson只能捕捉线性关系。你想想看,如果因子和收益率之间是U型关系(比如低因子值和高因子值都表现好,中间表现差),Pearson算出来可能接近0,但这不代表因子没用。嗯,这种情况我建议你画个散点图先看看。
2.2 Spearman秩相关系数:稳健的「排名派」
Spearman秩相关系数,说白了就是把原始数据换成排名再算Pearson。公式其实一样,只不过输入的是秩次(rank):
ρ_s = 1 - (6 * Σd_i²) / (n * (n² - 1))
其中d_i是第i个样本在两个变量上的排名差,n是样本数。
为什么我更喜欢Spearman? 三个字:稳健性。它不关心具体的数值大小,只关心谁排在前面谁排在后面。这就意味着,哪怕某只股票的因子值因为数据错误变成了100倍,只要它的排名没变,Spearman的结果就不受影响。
我记得有一次做多因子模型回测,用Pearson算出来的IC序列波动特别大,换成Spearman之后,序列变得平滑多了,而且选股效果反而更好。后来复盘发现,原来是数据源里有些股票的价格调整没做干净,导致收益率计算有偏差。Spearman因为只看排名,天然免疫了这类问题。
2.3 两种方法的对比
| 对比维度 | Pearson相关系数 | Spearman秩相关系数 |
|---|---|---|
| 数据类型 | 连续数值 | 排名/顺序 |
| 异常值敏感度 | 高 | 低 |
| 关系类型 | 线性关系 | 单调关系(线性或非线性) |
| 数据分布要求 | 近似正态 | 无要求 |
| 选股场景适用性 | 一般 | 推荐 |
你可能会问:「那我是不是永远都用Spearman就行了?」也不尽然。如果你在做因子择时,或者需要精确衡量因子值与收益率之间的线性弹性,Pearson还是有它的用武之地的。但如果是做截面选股,我个人建议无脑上Spearman。
2.4 IC计算实战:手把手写代码
光说不练假把式。咱们直接上代码,看看在Python里怎么算IC。
import pandas as pd
import numpy as np
from scipy.stats import spearmanr, pearsonr
def calc_ic(factor_series, return_series, method='spearman'):
"""
计算因子IC值
Parameters:
-----------
factor_series : pd.Series
因子值,index为股票代码
return_series : pd.Series
下期收益率,index为股票代码
method : str
'spearman' 或 'pearson'
Returns:
--------
ic_value : float
IC值
p_value : float
显著性p值
"""
# 对齐数据,去掉缺失值
common_idx = factor_series.dropna().index.intersection(
return_series.dropna().index
)
f = factor_series[common_idx]
r = return_series[common_idx]
if method == 'spearman':
ic, p = spearmanr(f, r)
elif method == 'pearson':
ic, p = pearsonr(f, r)
else:
raise ValueError("method must be 'spearman' or 'pearson'")
return ic, p
# 示例:计算某一天的截面IC
# 假设我们有2024-01-05的因子值和2024-01-06的收益率
factor_data = pd.Series({
'000001': 2.5, '000002': 1.8, '000003': 3.2,
'000004': 0.9, '000005': 2.1
}, name='factor')
return_data = pd.Series({
'000001': 0.03, '000002': 0.01, '000003': 0.05,
'000004': -0.02, '000005': 0.02
}, name='return')
ic_spearman, p_spearman = calc_ic(factor_data, return_data, 'spearman')
ic_pearson, p_pearson = calc_ic(factor_data, return_data, 'pearson')
print(f"Spearman IC: {ic_spearman:.4f}, p-value: {p_spearman:.4f}")
print(f"Pearson IC: {ic_pearson:.4f}, p-value: {p_pearson:.4f}")
这段代码看起来简单,但有几个细节我得提醒你:
- 一定要对齐数据。因子值和收益率必须对应同一批股票,而且要去掉缺失值。我曾经因为忘记dropna,结果IC算出来全是NaN,排查了半天才发现是某只股票停牌导致收益率缺失。
- 注意时间窗口。因子值通常是T日收盘后的数据,收益率应该是T+1日到T+N日的收益。别搞反了,否则就是「用未来预测过去」,属于典型的过拟合。
- p值也很重要。IC值高不代表因子有效,还得看p值是否显著。一般p值小于0.05才算统计显著。
2.5 滚动IC序列:看因子是否「稳定」
单日的IC只能告诉你这一天因子有没有选股能力。但我们要的是因子能不能持续稳定地赚钱。所以,我们需要计算滚动IC序列——也就是每天算一个IC,然后连成一条线。
def calc_rolling_ic(factor_df, return_df, window=20, method='spearman'):
"""
计算滚动IC序列
Parameters:
-----------
factor_df : pd.DataFrame
因子值矩阵,index为日期,columns为股票代码
return_df : pd.DataFrame
收益率矩阵,index为日期,columns为股票代码
window : int
滚动窗口大小(天数)
method : str
'spearman' 或 'pearson'
Returns:
--------
ic_series : pd.Series
滚动IC序列,index为日期
"""
dates = factor_df.index
ic_values = []
for i in range(len(dates)):
# 取当天的因子值和收益率
f = factor_df.iloc[i]
r = return_df.iloc[i]
# 计算IC
ic, _ = calc_ic(f, r, method)
ic_values.append(ic)
ic_series = pd.Series(ic_values, index=dates)
# 计算滚动均值(可选)
ic_ma = ic_series.rolling(window=window).mean()
return ic_series, ic_ma
# 示例:假设我们有100天的数据
# factor_df 和 return_df 的形状都是 (100, 500)
# 代表100天,500只股票
ic_daily, ic_ma20 = calc_rolling_ic(factor_df, return_df, window=20)
print(f"IC均值: {ic_daily.mean():.4f}")
print(f"IC标准差: {ic_daily.std():.4f}")
print(f"IC>0占比: {(ic_daily > 0).mean():.2%}")
print(f"ICIR: {ic_daily.mean() / ic_daily.std():.4f}")
这里有个关键指标——ICIR(信息系数比率),也就是IC的均值除以标准差。ICIR越高,说明因子越稳定。我个人习惯把ICIR大于0.5的因子列为「可用」,大于1.0的列为「优质」。
2.6 知识体系总览
说了这么多,咱们用一张图把IC计算的核心逻辑串起来:
这张图把IC计算的整个流程串起来了。从输入数据开始,到选择计算方法,再到输出结果,最后用于评估因子选股能力。你写代码的时候,也可以按照这个流程来组织逻辑。
好了,IC的计算方法就讲到这里。记住一句话:IC是因子选股能力的「体检报告」,算对了,你就能知道因子到底行不行;算错了,后面所有的分析都是白搭。下一节咱们聊聊怎么用IC序列来评估因子的稳定性,到时候我会分享一个我踩过的坑——关于IC序列的「假稳定」现象。
公众号:蓝海资料掘金营,微信deep3321