策略名称:动量因子加RSRS择时和ETF轮动策略
策略概述:
该策略结合了动量因子和RSRS择时模型,通过ETF轮动的方式实现资产配置优化。策略的核心逻辑是通过动量因子筛选出表现优异的ETF,并结合RSRS择时模型判断市场趋势,动态调整持仓,以实现收益最大化。
策略核心逻辑:
-
动量因子选股:
-
计算ETF的年化收益率和判定系数(R²),作为动量评分。
-
根据动量评分对ETF进行排序,选择评分最高的ETF作为投资标的。
-
-
RSRS择时模型:
-
使用OLS回归分析计算ETF的最高价和最低价之间的斜率(RSRS斜率)。
-
通过计算RSRS斜率的Z分数,判断市场趋势(买入、卖出或持有信号)。
-
结合均线比较(MA)进一步确认趋势信号。
-
-
ETF轮动:
-
根据动量因子和RSRS择时信号,动态调整持仓。
-
每次调仓时,卖出当前持仓的ETF,买入动量评分最高的ETF。
-
-
风险控制:
-
当RSRS择时模型发出卖出信号时,清空所有持仓,避免市场下跌风险。
-
策略特点:
-
动量驱动:通过动量因子筛选表现优异的ETF,捕捉市场趋势。
-
择时优化:结合RSRS择时模型,动态判断市场趋势,避免逆势操作。
-
ETF轮动:通过轮动方式优化资产配置,分散风险。
-
自动化交易:策略实现全自动化交易,减少人为干预。
策略参数:
| 参数名称 | 参数值 | 说明 |
|---|---|---|
| 动量天数 | 25 | 计算动量评分的周期 |
| RSRS计算周期(N) | 18 | 计算RSRS斜率的周期 |
| RSRS历史周期(M) | 600 | 计算RSRS斜率Z分数的历史周期 |
| 均线计算周期 | 20 | 计算均线的周期 |
| 均线比较周期 | 3 | 比较均线的前后周期差 |
| RSRS阈值 | 0.7 | RSRS Z分数的阈值,用于判断买卖信号 |
| 最大持仓数 | 1 | 每次调仓时持有的ETF数量 |
策略流程:
-
初始化:
-
设置基准指数(沪深300)。
-
定义ETF池(黄金ETF、纳指ETF、创业板ETF、上证180ETF)。
-
初始化动量因子和RSRS择时模型的参数。
-
-
每日调仓:
-
计算ETF的动量评分,选择评分最高的ETF。
-
根据RSRS择时模型判断市场趋势(买入、卖出或持有)。
-
如果信号为买入且持仓与目标不一致,则调仓;如果信号为卖出,则清仓。
-
-
动量评分计算:
-
使用对数收益率和线性回归计算ETF的年化收益率和判定系数。
-
动量评分 = 年化收益率 × 判定系数。
-
-
RSRS择时模型:
-
使用OLS回归计算最高价和最低价的斜率。
-
计算斜率的Z分数,结合均线比较生成买卖信号。
-
-
调仓操作:
-
卖出当前持仓的ETF。
-
买入动量评分最高的ETF。
-
from jqdata import * # 从jqdata模块导入所有内容
import numpy as np # 导入numpy库
def initialize(context):
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 用真实价格交易
set_option('use_real_price', True)
# 将滑点设置为0
set_slippage(FixedSlippage(0.004))
# 设置交易成本万分之五
set_order_cost(OrderCost(open_tax=0, close_tax=0, open_commission=0.0005, close_commission=0.0005, close_today_commission=0, min_commission=5),
type='fund')
log.set_level('order', 'error') # 过滤order中低于error级别的日志
# 初始化全局变量g.index_pool,用于存放策略中关注的指数或ETF
g.index_pool = [
'518880.XSHG', #黄金ETF(大宗商品)'518880.XSHG'
'513100.XSHG', #纳指100(海外资产)
'159915.XSHE', #创业板100(成长股,科技股,题材性,中小盘)
# '515080.XSHG', #红利ETF(价值股,蓝筹股,防御性,中大盘) '515080.XSHG'
# '159928.XSHE', # 中证消费ETF
'510180.XSHG',#上证180(价值股,蓝筹股,中大盘)
# '159981.XSHE'#能源化工期货
]
# 初始化全局变量,设置最大持仓数为1
g.stock_num = 1
# 设置动量天数为25天,用于计算股票的动量
g.momentum_day = 25
# 设置股票代码为沪深300指数
g.stock = '000300.XSHG'
# 设置N周期为18天,用于计算RS指标
g.N = 18
# 设置M周期为600天,用于计算RS指标
g.M = 600
# 设置均值计算天数为20天
g.mean_day = 20
g.mean_diff_day = 3 # 设置比较均线时的前后天数差为3天
g.score_threshold = 0.7 # 设置RS标准分指标阈值为0.7
g.slope_series = initial_slope_series()[:-1] # 初始化斜率序列,这里调用initial_slope_series函数并去掉序列中的第一个元素
# 设置交易时间,每周任意时间执行一次
# run_weekly(trade, weekday=3, time='9:45',reference_security='000300.XSHG')
run_daily(trade,time='9:45') # 设置交易时间,这里设置为每个交易日的9:45执行trade函数
# 定义 trade 函数,用于执行策略的交易逻辑
def trade(context):
# 获取当前持仓的股票集合
stock_hold = set(context.portfolio.positions.keys())
# 调用 get_stock_pool 函数获取策略选定的股票池
stock_pool = get_stock_pool()
# 设置要持有的股票数量,不超过预设的最大持仓数 g.stock_num,也不超过股票池的大小
g.stock_number = g.stock_num if (len(stock_pool) > g.stock_num) else len(stock_pool)
# print(g.stock_number)
# 选择股票池中的前 g.stock_num 只股票作为最终要买入的股票集合
set_stock_pool=set(stock_pool[:g.stock_num])
print(f'持仓股票{stock_hold}') # 打印当前持仓的股票集合
# print(f'所有要买入的股票{stock_pool}')
print(f'最后要买入股票{set_stock_pool}')
signal = get_signal() # 获取交易信号
print(signal) # 打印交易信号
# 如果有符合买入条件的股票池,并且当前交易信号不是卖出信号
if stock_pool and signal != "SELL":
# 如果当前持仓的股票集合与要买入的股票集合相同,且非空,不进行调仓
if stock_hold == set_stock_pool and len(set_stock_pool) != 0:
print("当前持仓和需买入集合一致,不进行调仓")
else:
# 否则,调用 change_position 函数调整持仓
change_position(context, set_stock_pool)
else:
# 如果交易信号是卖出信号
if signal == "SELL":
print("RSRS择时模型发出清仓信号!")
# 卖出当前持仓的所有股票
for stock in stock_hold:
# 将每只股票的目标持仓量设置为0,即全部卖出
order_target_value(stock, 0)
log.info('美好的一天结束')
log.info('##############################################################')
关键函数解锁后查看:
# 定义 change_position 函数,用于调整持仓
def change_position(context, target_list):
# 获取当前持仓列表
hold_list = list(context.portfolio.positions)
# 卖出当前持仓的所有股票
for etf in hold_list:
order_target_value(etf, 0) # 将持仓量调整为0,即全部卖出
print('卖出' + str(etf))
# 如果目标股票数量大于0,则进行买入操作
if g.stock_number > 0:
# 计算每只股票的买入金额
value = context.portfolio.available_cash / g.stock_number
# 遍历目标股票列表,买入每只股票
for etf in target_list:
# 如果该股票不在当前持仓中,则买入
if context.portfolio.positions[etf].total_amount == 0:
order_target_value(etf, value) # 将目标金额调整为value
print('买入' + str(etf))
# 定义 get_signal 函数,用于产生交易信号
def get_signal():
''' 产生交易信号
Returns:
str: "BUY" or "SELL" or "KEEP"
'''
# 获取过去 g.mean_day + g.mean_diff_day 天的收盘价数据
close_data = attribute_history(g.stock, g.mean_day + g.mean_diff_day, '1d', ['close'])
# 计算今天及之前 g.mean_diff_day 天的收盘价均值
today_MA = close_data.close[g.mean_diff_day:].mean()
# 计算之前 g.mean_day 天的收盘价均值
before_MA = close_data.close[:-g.mean_diff_day].mean()
# 获取过去 g.N 天的最高价和最低价数据
data = attribute_history(g.stock, g.N, '1d', ['high', 'low'])
# 调用 get_ols 函数进行 OLS 回归分析,得到截距、斜率和判定系数
intercept, slope, r2 = get_ols(data.low, data.high)
# 将新计算的斜率添加到 g.slope_series 列表中
g.slope_series.append(slope)
# 计算 RS/RS 指标的 z 分数,并乘以判定系数 r2
rsrs_score = get_zscore(g.slope_series[-g.M:]) * r2
# 根据 RS/RS 指标的 z 分数和均线比较结果产生交易信号
if rsrs_score > g.score_threshold and today_MA > before_MA:
return "BUY"
elif rsrs_score < -g.score_threshold and today_MA < before_MA:
return "SELL"
else:
return "KEEP"
# 定义 get_ols 函数,用于对输入的自变量和因变量建立 OLS 回归模型
def get_ols(x, y):
''' 对输入的自变量和因变量建立OLS回归模型
Args:
x (series of x): 每日最低价
y (series of y): 每日最高价
Returns:
tuple: (截距,斜率,判定系数)
'''
# 使用 numpy 的 polyfit 函数进行一次线性回归,得到斜率和截距
slope, intercept = np.polyfit(x, y, 1)
# 计算判定系数 R^2
r2 = 1 - (sum((y - (slope * x + intercept))**2) / ((len(y) - 1) * np.var(y, ddof=1)))
# 返回截距、斜率和判定系数
return (intercept, slope, r2)
# 定义 initial_slope_series 函数,用于初始化前 M 日内的斜率时间序列
def initial_slope_series():
''' 初始化前M日内的斜率时间序列
Returns:
list of slope (float)
'''
# 获取过去 g.N + g.M 天的最高价和最低价数据
data = attribute_history(g.stock, g.N + g.M, '1d', ['high', 'low'])
# 使用列表推导式计算序列中每 g.N 天的斜率
# 对于序列中的每个 i,计算从 i 到 i+g.N 的最高价和最低价的 OLS 回归斜率
return [get_ols(data.low[i:i+g.N], data.high[i:i+g.N])[1] for i in range(g.M)]
# 定义 get_zscore 函数,用于通过斜率序列计算标准分
def get_zscore(slope_series):
''' 通过斜率序列计算标准分
Returns:
float
'''
# 计算斜率序列的均值
mean = np.mean(slope_series)
# 计算斜率序列的标准差
std = np.std(slope_series)
# 计算最后一个斜率与序列均值的差,然后除以标准差,得到标准分
return (slope_series[-1] - mean) / std
策略风险:
-
市场风险:
-
如果市场整体下跌,动量因子可能失效,导致策略表现不佳。
-
-
择时风险:
-
RSRS择时模型的信号可能滞后或误判,导致错失机会或错误止损。
-
-
流动性风险:
-
ETF的流动性不足可能导致买卖价差较大,影响策略收益。
-
-
参数敏感性:
-
策略对动量天数、RSRS阈值等参数较为敏感,参数设置不当可能导致策略失效。
-
策略优化方向:
-
多因子结合:
-
引入更多因子(如波动率、成交量等)优化选股模型。
-
-
动态参数调整:
-
根据市场环境动态调整动量天数和RSRS阈值。
-
-
风险控制增强:
-
加入止损机制,进一步控制回撤。
-
-
ETF池扩展:
-
增加更多类型的ETF(如行业ETF、债券ETF等),丰富资产配置。
-
策略适用场景:
-
市场环境:适用于趋势明显的市场环境,动量因子和RSRS择时模型能够有效捕捉趋势。
-
投资者类型:适合风险承受能力中等、追求长期稳定收益的投资者。
策略回测表现:
-
年化收益率:根据历史回测数据,策略年化收益率较高(具体数值需通过回测验证)。
-
最大回撤:通过RSRS择时模型和动量因子优化,策略能够有效控制回撤。
总结:
“动量因子加RSRS择时和ETF轮动策略”通过动量因子和择时模型的结合,实现了对ETF的动态配置和趋势跟踪。策略具备较强的自动化能力和风险控制机制,适合在趋势明显的市场环境中运行。通过进一步优化和扩展,策略有望在更多市场环境中表现出色。
2025-02-21
