问题描述
解决方案
这是一个基于 QMT Python API 实现的 R-Breaker 交易策略。
R-Breaker 是一种经典的日内交易策略,结合了趋势跟踪(突破交易)和均值回归(反转交易)两种逻辑。它根据前一个交易日的最高价、最低价和收盘价计算出 6 个关键价位,以此来决定当天的交易信号。
策略逻辑简介
-
计算关键价位(基于昨日数据):
Pivot(枢轴点) = (High + Low + Close) / 3Bbreak(突破买入价) = High + 2 * (Pivot - Low)Ssetup(观察卖出价) = Pivot + (High - Low)Senter(反转卖出价) = 2 * Pivot - LowBenter(反转买入价) = 2 * Pivot - HighBsetup(观察买入价) = Pivot - (High - Low)Sbreak(突破卖出价) = Low - 2 * (High - Pivot)
-
交易信号:
- 趋势策略(突破):
- 若价格 >
Bbreak,开多单。 - 若价格 <
Sbreak,开空单。
- 若价格 >
- 反转策略:
- 若日内最高价 >
Ssetup且 价格跌破Senter,开空单(做空)。 - 若日内最低价 <
Bsetup且 价格突破Benter,开多单(做多)。
- 若日内最高价 >
- 趋势策略(突破):
-
风控与退出:
- 日内策略,收盘前(如 14:55)强制平仓。
QMT 策略代码实现
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
import time
def init(ContextInfo):
# ================= 策略参数设置 =================
# 交易标的:这里以沪深300股指期货主力合约为例
# 如果是股票,请修改为股票代码,如 '600000.SH'
ContextInfo.stock = 'IF00.IF'
# 资金账号 (请修改为您自己的实盘或模拟账号)
ContextInfo.account_id = 'YOUR_ACCOUNT_ID'
# 账号类型:'FUTURE' (期货), 'STOCK' (股票)
ContextInfo.account_type = 'FUTURE'
# 策略运行周期,建议 1分钟 或 5分钟
ContextInfo.period = '1m'
# 设置股票池
ContextInfo.set_universe([ContextInfo.stock])
# 绑定账号 (实盘/模拟盘必须)
ContextInfo.set_account(ContextInfo.account_id)
# 每日收盘平仓时间 (格式 HHMM)
ContextInfo.close_time = "1455"
# 记录当前持仓状态: 0-空仓, 1-多头, -1-空头
ContextInfo.position_status = 0
def handlebar(ContextInfo):
# 获取当前K线索引
index = ContextInfo.barpos
# 获取当前时间戳
timetag = ContextInfo.get_bar_timetag(index)
# 转换为时间字符串 HHMM
current_time = timetag_to_datetime(timetag, '%H%M')
# ================= 1. 每日收盘强平逻辑 =================
if current_time >= ContextInfo.close_time:
if ContextInfo.position_status != 0:
print(f"尾盘强平: {current_time}")
close_position(ContextInfo)
return
# ================= 2. 获取数据 =================
stock = ContextInfo.stock
# 获取日线数据(取过去2根,index=-2为昨日,index=-1为今日)
# 注意:get_market_data_ex 返回的是字典 {code: dataframe}
daily_data = ContextInfo.get_market_data_ex(
['high', 'low', 'close'],
[stock],
period='1d',
count=2,
dividend_type='none'
)
if stock not in daily_data or len(daily_data[stock]) < 2:
return # 数据不足,不运行
df_daily = daily_data[stock]
# 昨日数据
prev_high = df_daily['high'].iloc[-2]
prev_low = df_daily['low'].iloc[-2]
prev_close = df_daily['close'].iloc[-2]
# 今日至今的最高价和最低价 (用于反转逻辑判断)
# 注意:这里取日线数据的最后一行作为今日实时的High/Low
today_high = df_daily['high'].iloc[-1]
today_low = df_daily['low'].iloc[-1]
# 获取当前周期的最新收盘价 (用于触发信号)
current_data = ContextInfo.get_market_data_ex(
['close'],
[stock],
period=ContextInfo.period,
count=1,
dividend_type='none'
)
if stock not in current_data or len(current_data[stock]) < 1:
return
current_price = current_data[stock]['close'].iloc[-1]
# ================= 3. 计算 R-Breaker 关键价位 =================
pivot = (prev_high + prev_low + prev_close) / 3
# 突破买入价 (Breakout Buy)
b_break = prev_high + 2 * (pivot - prev_low)
# 观察卖出价 (Setup Sell)
s_setup = pivot + (prev_high - prev_low)
# 反转卖出价 (Enter Sell)
s_enter = 2 * pivot - prev_low
# 反转买入价 (Enter Buy)
b_enter = 2 * pivot - prev_high
# 观察买入价 (Setup Buy)
b_setup = pivot - (prev_high - prev_low)
# 突破卖出价 (Breakout Sell)
s_break = prev_low - 2 * (prev_high - pivot)
# ================= 4. 交易逻辑判断 =================
# 获取当前持仓 (为了回测准确,这里使用变量记录,实盘建议配合 get_trade_detail_data 使用)
pos = ContextInfo.position_status
# --- 信号判断 ---
# 1. 趋势突破买入 (空仓 -> 多仓)
# 条件:当前价格 > 突破买入价
if pos == 0 and current_price > b_break:
print(f"触发趋势买入: 价格 {current_price} > Bbreak {b_break}")
open_long(ContextInfo)
# 2. 趋势突破卖出 (空仓 -> 空仓)
# 条件:当前价格 < 突破卖出价
elif pos == 0 and current_price < s_break:
print(f"触发趋势卖出: 价格 {current_price} < Sbreak {s_break}")
open_short(ContextInfo)
# 3. 反转做空 (多仓 -> 空仓 或 空仓 -> 空仓)
# 条件:今日最高价 > 观察卖出价 且 当前价格跌破 反转卖出价
elif (pos == 0 or pos == 1) and (today_high > s_setup and current_price < s_enter):
print(f"触发反转做空: 今日高点 {today_high} > Ssetup {s_setup} 且 现价 {current_price} < Senter {s_enter}")
if pos == 1:
close_position(ContextInfo) # 平多
open_short(ContextInfo) # 开空
# 4. 反转做多 (空仓 -> 多仓 或 空仓 -> 多仓)
# 条件:今日最低价 < 观察买入价 且 当前价格突破 反转买入价
elif (pos == 0 or pos == -1) and (today_low < b_setup and current_price > b_enter):
print(f"触发反转做多: 今日低点 {today_low} < Bsetup {b_setup} 且 现价 {current_price} > Benter {b_enter}")
if pos == -1:
close_position(ContextInfo) # 平空
open_long(ContextInfo) # 开多
# 5. 止损平仓 (可选,这里简单示例)
# 如果持多仓,价格跌破突破卖出价,止损
elif pos == 1 and current_price < s_break:
print("多头止损")
close_position(ContextInfo)
# 如果持空仓,价格突破突破买入价,止损
elif pos == -1 and current_price > b_break:
print("空头止损")
close_position(ContextInfo)
# ================= 交易辅助函数 =================
def open_long(ContextInfo):
"""开多单"""
# 期货开仓:opType=0 (开多), orderType=1101 (单股/单账号/普通/股手)
# 股票买入:opType=23
op_type = 0 if ContextInfo.account_type == 'FUTURE' else 23
passorder(op_type, 1101, ContextInfo.account_id, ContextInfo.stock, 5, -1, 1, ContextInfo)
ContextInfo.position_status = 1
def open_short(ContextInfo):
"""开空单 (仅期货)"""
if ContextInfo.account_type != 'FUTURE':
print("股票账户无法直接开空")
return
# 期货开空:opType=3 (开空)
passorder(3, 1101, ContextInfo.account_id, ContextInfo.stock, 5, -1, 1, ContextInfo)
ContextInfo.position_status = -1
def close_position(ContextInfo):
"""平仓所有头寸"""
if ContextInfo.position_status == 1:
# 平多:期货 opType=1 (平昨多) 或 2 (平今多),这里简化处理
# 股票卖出:opType=24
op_type = 1 if ContextInfo.account_type == 'FUTURE' else 24
passorder(op_type, 1101, ContextInfo.account_id, ContextInfo.stock, 5, -1, 1, ContextInfo)
elif ContextInfo.position_status == -1:
# 平空:期货 opType=4 (平昨空) 或 5 (平今空)
passorder(4, 1101, ContextInfo.account_id, ContextInfo.stock, 5, -1, 1, ContextInfo)
ContextInfo.position_status = 0
代码关键点解析
-
数据获取 (
get_market_data_ex):- R-Breaker 的核心在于利用昨日的日线数据计算 Pivot 点。
- 代码中使用了
period='1d', count=2。在返回的 DataFrame 中,iloc[-2]代表昨日完整数据,iloc[-1]代表今日截止目前的数据(用于获取今日实时的 High 和 Low)。 - 同时使用
period='1m'(或您设置的周期) 获取当前最新的close价格来触发信号。
-
反转逻辑:
- 反转信号不仅看当前价格,还必须确认今日的极值(最高价或最低价)已经触及了观察区(Setup)。
- 例如做空反转:必须是今日最高价已经超过了
Ssetup,证明市场已经冲高过,然后价格回落跌破Senter,才确认为反转。
-
交易执行 (
passorder):- 代码中针对期货(Future)和股票(Stock)做了简单的区分。
opType参数:期货开多为 0,开空为 3,平多为 1,平空为 4。股票买入为 23,卖出为 24。prType=5代表使用最新价下单,price=-1在最新价模式下无效。
-
尾盘强平:
- R-Breaker 是典型的日内策略,不留隔夜仓。代码中设置了
1455(14:55) 检测时间,一旦到达该时间且有持仓,立即平仓。
- R-Breaker 是典型的日内策略,不留隔夜仓。代码中设置了
注意事项
- 账号设置: 请务必将
ContextInfo.account_id替换为您实际的 QMT 资金账号。 - 标的: 示例使用的是股指期货主力合约
IF00.IF。如果是股票,R-Breaker 只能做多(除非融券),需要调整open_short的逻辑。 - 实盘持仓同步: 示例代码使用
ContextInfo.position_status变量来记录持仓。在实盘中,建议在handlebar开头使用get_trade_detail_data获取真实的账户持仓,以防止程序重启后状态丢失。
Q&A
Q: 如何将此策略用于股票交易?
A: 将 ContextInfo.stock 改为股票代码(如 600000.SH),将 ContextInfo.account_type 改为 'STOCK'。注意股票通常只能做多(T+1限制和缺乏做空机制),因此代码中的 open_short 部分在纯股票账户中将无法执行,策略将退化为只做多头的 R-Breaker。
Q: 为什么使用 get_market_data_ex 而不是 get_market_data?
A: get_market_data_ex 是 QMT 推荐的新版接口,返回的数据结构更清晰(字典包含 DataFrame),且支持订阅模式,效率更高。
Q: 策略中的 timetag_to_datetime 是哪里来的?
A: 这是 QMT 内置的辅助函数,用于将毫秒时间戳转换为可读的日期时间字符串。无需导入,直接在 QMT 环境中即可调用。