问题描述
解决方案
这是一个基于 动量效应(Momentum) 的简单股债轮动策略。
策略逻辑
- 标的选择:
- 股票代表:沪深300 ETF(
510300.XSHG),代表大盘股表现。 - 债券代表:国债 ETF(
511010.XSHG),代表债券市场表现。
- 股票代表:沪深300 ETF(
- 信号计算:
- 每天开盘时,计算两个标的过去 20个交易日 的涨跌幅。
- 交易逻辑:
- 如果 股票涨幅 > 债券涨幅:全仓持有股票 ETF,卖出债券 ETF。
- 如果 债券涨幅 > 股票涨幅:全仓持有债券 ETF,卖出股票 ETF。
- 资金管理:每日轮动,持有强势资产。
策略代码
# -*- coding: utf-8 -*-
from jqdata import *
def initialize(context):
"""
初始化函数,设定基准、手续费、全局变量等
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 设定ETF交易手续费:无印花税,佣金设为万分之三
set_order_cost(OrderCost(open_tax=0, close_tax=0, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='fund')
# 定义全局变量
# 股票标的:沪深300ETF
g.stock_etf = '510300.XSHG'
# 债券标的:5年期国债ETF
g.bond_etf = '511010.XSHG'
# 动量回看窗口天数(例如过去20天)
g.lag = 20
# 每天9:30执行交易逻辑
run_daily(trade, '09:30')
def trade(context):
"""
交易逻辑函数
"""
# 获取历史数据
# 注意:attribute_history 获取的是不包含当前时刻的过去数据(避免未来函数)
# 获取过去 g.lag 天的收盘价
stock_data = attribute_history(g.stock_etf, g.lag, '1d', ['close'])
bond_data = attribute_history(g.bond_etf, g.lag, '1d', ['close'])
# 如果数据长度不足(例如刚上市),则不操作
if len(stock_data) < g.lag or len(bond_data) < g.lag:
return
# 计算收益率:(最新收盘价 - N天前收盘价) / N天前收盘价
# .iloc[-1] 取最新一天数据,.iloc[0] 取最早一天数据
stock_ret = (stock_data['close'].iloc[-1] - stock_data['close'].iloc[0]) / stock_data['close'].iloc[0]
bond_ret = (bond_data['close'].iloc[-1] - bond_data['close'].iloc[0]) / bond_data['close'].iloc[0]
# 记录日志,方便回测查看
log.info("股票20日涨幅: %.4f, 债券20日涨幅: %.4f" % (stock_ret, bond_ret))
# 确定目标持仓
target_security = ''
if stock_ret > bond_ret:
target_security = g.stock_etf
log.info("股票强于债券,计划持有股票")
else:
target_security = g.bond_etf
log.info("债券强于股票,计划持有债券")
# 执行交易
# 1. 获取当前持仓中非目标标的,如果持有则卖出
for security in context.portfolio.positions:
if security != target_security:
order_target_value(security, 0)
# 2. 全仓买入目标标的
# context.portfolio.total_value 包含现金和持仓总价值
if target_security:
order_target_value(target_security, context.portfolio.total_value)
代码解析
-
initialize:- 设置了
set_order_cost,因为 ETF 交易免印花税,这里专门针对type='fund'设置了低佣金,使回测更接近真实环境。 - 定义了两个 ETF 代码作为轮动对象。
- 设置了
-
trade:- 使用
attribute_history获取过去 20 天的收盘价。 - 计算公式为
(当前价 - 20天前价) / 20天前价。 - 比较逻辑:直接比较两个收益率的大小。
- 交易执行:
- 先遍历持仓,卖出非目标资产(例如当前持有债券但信号显示股票强,则卖出债券)。
- 使用
order_target_value将目标资产调整至账户总价值(即全仓买入)。
- 使用
建议回测设置
- 起始资金:100,000 元
- 频率:天 (Daily)
- 时间范围:建议包含牛熊周期,例如 2018-01-01 至今,以观察策略在不同市场环境下的表现。
Q&A
Q: 为什么要使用 ETF 而不是指数代码?
A: 指数(如 000300.XSHG)本身不能直接交易。在实盘或模拟交易中,我们需要买卖对应的基金产品(如 510300.XSHG)。虽然可以使用指数计算信号,但为了代码简洁和直接可交易,本策略直接使用 ETF 的历史价格来计算信号和交易。
Q: 如何降低交易频率?
A: 当前策略每天都会判断。如果两个资产收益率非常接近,可能会导致频繁买卖(震荡磨损)。可以通过以下方式优化:
- 增加阈值:只有当
股票收益 - 债券收益 > 1%时才切换。 - 延长调仓周期:将
run_daily改为run_weekly,每周只判断一次。
Q: 什么是“二八轮动”?
A: 这是一个经典的变种。通常是指在 沪深300(大盘股)和 中证500(小盘股)以及 国债 之间轮动。如果大盘或小盘哪个强就买哪个,如果都走弱(比如收益率为负),则空仓或买入国债避险。