3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在多因子选股策略中,因子的表现往往会随着市场环境的变化而发生轮动。传统的固定权重法无法适应这种变化。通过计算各因子在过去一段时间(如12个月)的滚动IC(Information Coefficient,信息系数),并以IC值(或其IR值)作为权重分配的依据,可以实现因子权重的动态调整,从而提升策略的超额收益和稳定性。
以下是在聚宽(JoinQuant)平台上实现该策略的完整思路与核心代码。
get_fundamentals 获取,例如 valuation.pe_ratio(市盈率)或 indicator.roe_ttm(净资产收益率)。momentum(传统动量)或 Price3M(3个月股价变动)。Variance20(20日年化收益方差)或 residual_volatility(残差波动率)。winsorize)和标准化(standardlize)。以下是一个简化但结构完整的聚宽策略代码示例,展示了如何获取数据、计算滚动IC并动态调仓:
import numpy as np
import pandas as pd
from jqdata import *
from jqfactor import get_factor_values, winsorize, standardlize
def initialize(context):
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 策略参数设置
g.factor_list = ['roe_ttm', 'momentum', 'Variance20'] # 候选因子:ROE、动量、20日方差
g.holding_num = 20 # 持股数量
g.rolling_months = 12 # 滚动IC计算窗口(月)
# 每月第一个交易日开盘运行
run_monthly(rebalance, monthday=1, time='09:30')
def rebalance(context):
# 1. 获取股票池(以中证500成分股为例)
stock_pool = get_index_stocks('000905.SH', date=context.previous_date)
# 2. 计算滚动IC并获取动态权重
weights = get_dynamic_weights(stock_pool, context.previous_date)
log.info(f"当前调仓日因子动态权重: {weights}")
# 3. 获取当前交易日的因子值
current_date = context.previous_date
factor_data = {}
# 获取基本面因子 (ROE)
q = query(valuation.code, indicator.roe_ttm).filter(valuation.code.in_(stock_pool))
df_fundamental = get_fundamentals(q, date=current_date).set_index('code')
factor_data['roe_ttm'] = df_fundamental['roe_ttm']
# 获取量价因子 (动量与波动率)
factor_val = get_factor_values(securities=stock_pool, factors=['momentum', 'Variance20'], end_date=current_date, count=1)
factor_data['momentum'] = factor_val['momentum'].iloc[0]
factor_data['Variance20'] = factor_val['Variance20'].iloc[0]
# 4. 因子数据处理与综合打分
score_series = pd.Series(0.0, index=stock_pool)
for factor in g.factor_list:
val = factor_data[factor].reindex(stock_pool).fillna(0.0)
# 去极值与标准化
clean_val = standardlize(winsorize(val, scale=3))
# 负向因子处理(如方差/波动率通常为负向因子,IC计算后权重自然会调整正负,此处直接加权)
score_series += clean_val * weights[factor]
# 5. 排序并执行调仓
buy_list = score_series.sort_values(ascending=False).head(g.holding_num).index.tolist()
# 卖出不在买入列表中的持仓
for stock in list(context.portfolio.positions.keys()):
if stock not in buy_list:
order_target(stock, 0)
# 等权重买入目标持仓
position_value = context.portfolio.total_value / g.holding_num
for stock in buy_list:
order_target_value(stock, position_value)
def get_dynamic_weights(stock_pool, end_date):
"""
计算过去12个月的滚动IC,并返回归一化后的因子权重
"""
# 获取过去12个月的历史月度交易日列表
trade_days = get_trade_days(end_date=end_date, count=g.rolling_months * 21)
monthly_dates = [trade_days[i] for i in range(0, len(trade_days), 21)][-g.rolling_months:]
ic_history = {factor: [] for factor in g.factor_list}
# 循环计算历史每个月的IC值
for i in range(len(monthly_dates) - 1):
t_date = monthly_dates[i]
t_next_date = monthly_dates[i+1]
# 获取t期的因子值
# ROE
q = query(valuation.code, indicator.roe_ttm).filter(valuation.code.in_(stock_pool))
df_fundamental = get_fundamentals(q, date=t_date).set_index('code')
# 动量与波动率
factor_val = get_factor_values(securities=stock_pool, factors=['momentum', 'Variance20'], end_date=t_date, count=1)
# 获取t期到t+1期的个股收益率
prices = get_price(stock_pool, start_date=t_date, end_date=t_next_date, fields=['close'], fq='pre')['close']
returns = (prices.iloc[-1] - prices.iloc[0]) / prices.iloc[0]
# 计算各因子的Rank IC
for factor in g.factor_list:
if factor == 'roe_ttm':
f_series = df_fundamental['roe_ttm'].reindex(stock_pool)
else:
f_series = factor_val[factor].iloc[0].reindex(stock_pool)
f_series = f_series.fillna(f_series.median())
# 计算秩相关系数
rank_ic = f_series.corr(returns, method='spearman')
if not np.isnan(rank_ic):
ic_history[factor].append(rank_ic)
# 计算滚动IC均值
mean_ic = {}
total_abs_ic = 0.0
for factor in g.factor_list:
mean_ic[factor] = np.mean(ic_history[factor]) if len(ic_history[factor]) > 0 else 0.01
total_abs_ic += abs(mean_ic[factor])
# 归一化权重
weights = {}
for factor in g.factor_list:
weights[factor] = mean_ic[factor] / total_abs_ic if total_abs_ic > 0 else 1.0 / len(g.factor_list)
return weights
Weight = Mean(IC) / Std(IC),这样可以惩罚那些波动巨大、方向不稳定的因子。neutralize 函数对因子进行行业和市值中性化,避免选股策略过度暴露在某一特定行业或小市值风格上。