问题描述
解决方案
这是一个基于聚宽(JoinQuant)平台的螺纹钢(RB)期货趋势跟踪策略。
策略思路
- 标的选择:选取螺纹钢期货的主力合约(
get_dominant_future),并自动处理主力合约换月。 - 核心逻辑:双均线策略(Golden Cross/Death Cross)。
- 做多信号:短期均线(如10日)上穿长期均线(如30日)。
- 做空信号:短期均线下穿长期均线。
- 平仓逻辑:趋势反转时,先平掉原有仓位,再反向开仓。
- 资金管理:每次开仓使用账户可用资金的一定比例(如 30%),防止爆仓。
- 移仓换月:每日检查持仓,如果持有的合约不再是主力合约,则强制平仓,并在新主力合约上按信号开仓。
策略代码
# -*- coding: utf-8 -*-
from jqdata import *
def initialize(context):
"""
初始化函数,设定基准、手续费、全局变量等
"""
# 设定基准为螺纹钢主力连续合约
set_benchmark('RB9999.XSGE')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 设定期货交易手续费(根据实际情况调整,这里设为万分之1)
set_order_cost(OrderCost(open_commission=0.0001,
close_commission=0.0001,
close_today_commission=0.0001), type='futures')
# 设定保证金比例(这里设为15%,具体视交易所标准而定)
set_option('futures_margin_rate', 0.15)
# 定义全局变量
g.future_code = 'RB' # 品种代码:螺纹钢
g.short_window = 10 # 短期均线周期
g.long_window = 30 # 长期均线周期
g.position_ratio = 0.3 # 每次开仓资金比例
# 每天开盘时运行
run_daily(trade_func, time='open')
def trade_func(context):
"""
交易主函数
"""
# 1. 获取当前日期的主力合约代码
dom_contract = get_dominant_future(g.future_code)
# 2. 移仓换月处理:检查持仓,如果持有非主力合约,则平仓
# 遍历多头持仓
for security in list(context.portfolio.long_positions.keys()):
# 如果持仓品种是螺纹钢,但不是当前主力合约
if security.startswith(g.future_code) and security != dom_contract:
order_target(security, 0, side='long')
log.info("移仓换月:平掉旧合约多单 %s" % security)
# 遍历空头持仓
for security in list(context.portfolio.short_positions.keys()):
if security.startswith(g.future_code) and security != dom_contract:
order_target(security, 0, side='short')
log.info("移仓换月:平掉旧合约空单 %s" % security)
# 3. 获取行情数据
# 获取过去 g.long_window + 2 天的收盘价,确保数据足够计算均线
# 注意:这里直接获取主力合约的数据
hist_data = attribute_history(dom_contract, g.long_window + 2, '1d', ['close'])
close_prices = hist_data['close']
# 如果数据长度不足,无法计算均线,则跳过
if len(close_prices) < g.long_window:
return
# 4. 计算均线
# 短期均线
ma_short = close_prices[-g.short_window:].mean()
# 长期均线
ma_long = close_prices[-g.long_window:].mean()
# 获取当前主力合约的持仓情况
long_pos = context.portfolio.long_positions[dom_contract].total_amount
short_pos = context.portfolio.short_positions[dom_contract].total_amount
# 获取可用资金
cash = context.portfolio.available_cash
# 5. 交易逻辑
# 情况A:短均线 > 长均线 (看多)
if ma_short > ma_long:
# 如果有空单,先平空
if short_pos > 0:
order_target(dom_contract, 0, side='short')
log.info("趋势转多:平空单 %s" % dom_contract)
# 如果没有多单,则开多
if long_pos == 0:
# 按资金比例开仓
order_value(dom_contract, cash * g.position_ratio, side='long')
log.info("趋势转多:开多单 %s" % dom_contract)
# 情况B:短均线 < 长均线 (看空)
elif ma_short < ma_long:
# 如果有多单,先平多
if long_pos > 0:
order_target(dom_contract, 0, side='long')
log.info("趋势转空:平多单 %s" % dom_contract)
# 如果没有空单,则开空
if short_pos == 0:
# 按资金比例开仓
order_value(dom_contract, cash * g.position_ratio, side='short')
log.info("趋势转空:开空单 %s" % dom_contract)
代码关键点解析
-
get_dominant_future('RB'):- 这是期货策略最关键的函数。它会自动返回当前日期的主力合约代码(例如
RB2405.XSGE)。 - 策略中包含了一个循环检查,如果账户里持有旧的主力合约(例如上个月的主力),会自动将其平仓,实现“移仓换月”。
- 这是期货策略最关键的函数。它会自动返回当前日期的主力合约代码(例如
-
attribute_history:- 用于获取历史收盘价。这里我们获取了比长期均线稍多几天的数据,以防计算边界问题。
-
order_targetvsorder_value:order_target(security, 0, side='...'): 用于将仓位调整为0,即平仓。order_value(security, cash, side='...'): 用于按金额下单。这里使用cash * g.position_ratio来控制仓位,避免满仓操作导致风险过大。
-
side='long'/side='short':- 期货交易必须指定方向。
long代表多头(买入开仓/卖出平仓),short代表空头(卖出开仓/买入平仓)。
- 期货交易必须指定方向。
常见问题 (Q&A)
Q1: 为什么使用 RB9999 作为基准,但交易时用 get_dominant_future?
A: RB9999 是聚宽合成的指数或主力连续数据,适合用来观察大趋势或作为回测的业绩基准。但在实盘或模拟交易中,你必须对具体的合约(如 RB2410.XSGE)下单,交易所不支持直接对 RB9999 下单。
Q2: 策略中的 g.position_ratio = 0.3 是什么意思?
A: 这意味着每次开仓只使用账户可用资金的 30%。期货自带杠杆(通常 10倍左右),全仓交易(100%资金)风险极高,一旦方向判断错误极易爆仓。30% 是一个相对稳健的起始仓位。
Q3: 如何调整均线参数?
A: 修改 initialize 函数中的 g.short_window (短期) 和 g.long_window (长期) 即可。例如改为 5日和 20日均线。
Q4: 这个策略适合高频交易吗?
A: 不适合。这是一个日线级别的趋势策略,交易频率较低。如果需要更高频,需要将 run_daily 改为 run_daily(..., time='every_bar') 并修改数据获取频率为分钟级。