3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在量化投资中,传统的单一动量(如单纯看过去N天的涨幅)容易在市场拐点处失效,且无法识别趋势的平滑度与波动风险。为了构建一个更稳健的 ETF 轮动策略,我们可以将以下四个维度合成为一个多重动量评分(Multi-Momentum Score):
对过去 $N$ 天的复权收盘价取对数 $\ln(P_t)$,对时间 $t$ 进行一元线性回归:
$$\ln(P_t) = \alpha + \beta \cdot t + \epsilon_t$$
计算过去 $M$ 天(如 5 天或 10 天)的简单收益率:
$$Score_{short} = \frac{P_{today} - P_{t-M}}{P_{t-M}}$$
计算过去 $N$ 天日收益率的标准差并年化:
$$\sigma_{annual} = Std(Return_{daily}) \times \sqrt{250}$$
将各项指标进行标准化(Z-Score)或直接加权合成:
$$Final_Score = w_1 \cdot Z(Score_{long}) + w_2 \cdot Z(Score_{short}) - w_3 \cdot Z(\sigma_{annual})$$
(通常权重可设为:$w_1 = 0.5, w_2 = 0.3, w_3 = 0.2$)
以下代码展示了如何在聚宽平台中实现该多重评分系统,并在每月初进行 ETF 轮动调仓:
import jqdata
import numpy as np
import pandas as pd
from scipy.stats import linregress
def initialize(context):
# 开启真实价格模式
set_option('use_real_price', True)
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 策略参数设置
g.etf_pool = [
'510300.XSHG', # 沪深300ETF
'510500.XSHG', # 中证500ETF
'159915.XSHE', # 创业板ETF
'518880.XSHG', # 黄金ETF
'513100.XSHG', # 纳指ETF
'511010.XSHG' # 国债ETF
]
g.long_window = 60 # 长期回归窗口
g.short_window = 10 # 短期动量窗口
g.hold_num = 2 # 每次持有的ETF数量
# 每月第一个交易日开盘运行
run_monthly(handle_rotation, monthday=1, time='9:30')
def get_rotation_score(security, end_date):
# 获取历史收盘价数据
hist = attribute_history(security, g.long_window, '1d', ['close'], skip_paused=True)
if len(hist) < g.long_window:
return -999
closes = hist['close'].values
# 1. 计算长期回归斜率和 R2
log_closes = np.log(closes)
x = np.arange(len(log_closes))
slope, intercept, r_value, p_value, std_err = linregress(x, log_closes)
annual_slope = (np.exp(slope * 250) - 1) * 100
r_squared = r_value ** 2
long_score = annual_slope * r_squared
# 2. 计算短期动量
short_return = (closes[-1] - closes[-g.short_window]) / closes[-g.short_window]
# 3. 计算年化波动率
daily_returns = np.diff(closes) / closes[:-1]
ann_vol = np.std(daily_returns) * np.sqrt(250)
return {
'long_score': long_score,
'short_score': short_return,
'volatility': ann_vol
}
def handle_rotation(context):
score_list = []
# 计算每个 ETF 的各项指标
for etf in g.etf_pool:
metrics = get_rotation_score(etf, context.previous_date)
if metrics != -999:
metrics['code'] = etf
score_list.append(metrics)
df = pd.DataFrame(score_list)
if df.empty:
return
# 对各项指标进行 Z-Score 标准化
df['z_long'] = (df['long_score'] - df['long_score'].mean()) / (df['long_score'].std() + 1e-6)
df['z_short'] = (df['short_score'] - df['short_score'].mean()) / (df['short_score'].std() + 1e-6)
df['z_vol'] = (df['volatility'] - df['volatility'].mean()) / (df['volatility'].std() + 1e-6)
# 合成最终评分 (长期趋势 50% + 短期动量 30% - 波动率惩罚 20%)
df['final_score'] = 0.5 * df['z_long'] + 0.3 * df['z_short'] - 0.2 * df['z_vol']
# 按评分降序排列,选择前 N 个
df = df.sort_values(by='final_score', ascending=False)
target_list = df['code'].head(g.hold_num).tolist()
# 调仓执行
# 1. 卖出不在目标列表中的持仓
for etf in list(context.portfolio.positions.keys()):
if etf not in target_list:
order_target(etf, 0)
log.info(f"卖出 {etf}")
# 2. 等权重买入目标持仓
if len(target_list) > 0:
target_value = context.portfolio.total_value / len(target_list)
for etf in target_list:
order_target_value(etf, target_value)
log.info(f"调仓/买入 {etf} 至目标价值 {target_value:.2f}")
-0.2 * z_vol)在市场剧烈震荡时,会自动将仓位导向国债 ETF、黄金 ETF 等低波动避险资产,起到天然的防守作用。long_window(长期窗口)和权重系数进行网格搜索,以适应不同宏观周期下的市场特征。