3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在量化交易中,IC (Information Coefficient) 和 Rank IC 是评估选股因子有效性的两个核心指标。
在 QMT 中计算这两个指标,通常需要结合 pandas 和 scipy.stats 库。
handlebar 周期内,获取股票池中所有股票的历史行情。handlebar 中,我们通常是在 $T$ 日收盘后计算。为了验证因子的有效性,我们通常计算 上一期因子值 ($T-1$) 与 当期收益率 ($T-1 \to T$) 的相关性。scipy.stats.pearsonr 计算 IC,使用 scipy.stats.spearmanr 计算 Rank IC。ContextInfo.paint 将结果绘制在副图上。以下代码展示了如何计算一个简单因子(20日动量因子)的 IC 和 Rank IC。
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
from scipy import stats
def init(ContextInfo):
# 1. 设置股票池,这里以沪深300为例
ContextInfo.set_universe(['000300.SH'])
# 2. 设置策略运行周期为日线
ContextInfo.period = '1d'
# 3. 设置回测时间范围(如果在回测模式下)
ContextInfo.start = '20220101'
ContextInfo.end = '20230101'
# 4. 必要的初始化变量
ContextInfo.ic_list = []
ContextInfo.rank_ic_list = []
def handlebar(ContextInfo):
# 跳过历史K线不足的情况,确保有足够数据计算因子
if ContextInfo.barpos < 25:
return
# 获取当前股票池
stock_list = ContextInfo.get_stock_list_in_sector('沪深300')
# =============================================
# 步骤 1: 获取历史行情数据
# 我们需要过去的数据来计算因子(T-1)和收益(T)
# count=22: 获取过去22根K线,足以计算昨天的20日动量和今天的收益
# =============================================
data_dict = ContextInfo.get_market_data_ex(
['close'],
stock_list,
period=ContextInfo.period,
count=22,
dividend_type='front'
)
# 将数据转换为 DataFrame 格式,方便处理
# 这里的逻辑是将 dict 转为 DataFrame,行是日期,列是股票代码
close_data = pd.DataFrame({k: v['close'] for k, v in data_dict.items()})
# 如果数据为空,直接返回
if close_data.empty:
return
# =============================================
# 步骤 2: 准备因子值 (Factor) 和 收益率 (Return)
# 逻辑:我们要看 "昨天的因子" 是否预测了 "今天的收益"
# =============================================
# 获取 昨天(T-1) 和 今天(T) 的收盘价
# iloc[-1] 是今天,iloc[-2] 是昨天,iloc[-22] 是20天前(相对于昨天)
# 1. 计算收益率 (Return): 今天收盘价 / 昨天收盘价 - 1
# Series, index为股票代码
returns_t = close_data.iloc[-1] / close_data.iloc[-2] - 1
# 2. 计算因子值 (Factor): 20日动量
# 定义:昨天的收盘价 / (昨天往前推20天的收盘价) - 1
# 注意:close_data.iloc[-2] 是昨天
# close_data.iloc[-22] 是昨天往前推20个交易日
factor_t_minus_1 = close_data.iloc[-2] / close_data.iloc[-22] - 1
# =============================================
# 步骤 3: 数据清洗与对齐
# =============================================
# 创建一个临时的 DataFrame 来对齐数据
df_calc = pd.DataFrame({
'factor': factor_t_minus_1,
'ret': returns_t
})
# 去除包含 NaN 或 Inf 的行
df_calc = df_calc.replace([np.inf, -np.inf], np.nan).dropna()
# 确保有足够的数据点进行相关性计算
if len(df_calc) < 10:
return
# =============================================
# 步骤 4: 计算 IC 和 Rank IC
# =============================================
# 1. 计算 Normal IC (Pearson 相关系数)
# pearsonr 返回 (correlation, p-value),我们取第一个值
ic_val, _ = stats.pearsonr(df_calc['factor'], df_calc['ret'])
# 2. 计算 Rank IC (Spearman 秩相关系数)
# spearmanr 返回 (correlation, p-value)
rank_ic_val, _ = stats.spearmanr(df_calc['factor'], df_calc['ret'])
# =============================================
# 步骤 5: 输出与绘图
# =============================================
# 打印日志 (可选)
# print(f"Time: {timetag_to_datetime(ContextInfo.get_bar_timetag(ContextInfo.barpos), '%Y-%m-%d')} | IC: {ic_val:.4f} | RankIC: {rank_ic_val:.4f}")
# 在副图绘制曲线
# 注意:paint 只能画数值,不能画字符串
ContextInfo.paint('IC', ic_val, -1, 0, 'red') # 红色线表示 IC
ContextInfo.paint('RankIC', rank_ic_val, -1, 0, 'blue') # 蓝色线表示 Rank IC
# 绘制 0 轴参考线
ContextInfo.paint('Zero', 0, -1, 0, 'gray', 'noaxis')
数据获取 (get_market_data_ex):
dividend_type='front':计算收益率和技术指标时,通常建议使用前复权数据,以消除分红配股造成的股价跳空影响。时间错位 (Lag):
handlebar 逐 K 线运行时,我们站在 $T$ 时刻。此时我们已知 $T$ 的收盘价。数据清洗 (dropna):
scipy.stats 会报错或返回 NaN。计算函数:
stats.pearsonr(x, y):计算线性相关系数,即 IC。stats.spearmanr(x, y):先将 x 和 y 转换为排名(Rank),再计算相关系数,即 Rank IC。绘图 (paint):
Q: 如何计算多因子合成后的 IC?
A: 您需要在步骤 2 中,先对多个因子进行标准化(Z-Score)和加权求和,得到一个合成因子值 composite_factor,然后用这个合成值去和 ret 计算相关性。
Q: 为什么我的 Rank IC 经常是 NaN?
A: 通常是因为数据清洗不彻底。请确保 df_calc 中没有 np.nan 或 np.inf。另外,如果当期有效股票数量太少(例如少于 2 只),相关系数也无法计算。
Q: 如何计算财务因子的 IC?
A: 可以使用 ContextInfo.get_financial_data 获取财务数据(如 PE、PB、ROE)。逻辑是一样的:取 $T-1$ 日已知的财务数据作为因子值,与 $T$ 日的收益率计算相关性。注意财务数据存在滞后性,必须确保使用的是当时已公告的数据。