问题描述
解决方案
Hans123 策略 QMT 实现指南
Hans123 是一种经典的日内突破策略。其核心逻辑是在开盘后的一段时间内(如开盘前30分钟)建立一个高低点区间,当价格突破该区间的上轨时做多,跌破下轨时做空,并在收盘前平仓。
以下是在 QMT 平台上实现该策略的详细代码和说明。
策略逻辑概述
- 设定时间窗口:例如 09:30 - 10:00。
- 计算上下轨:记录时间窗口内的最高价(上轨)和最低价(下轨)。
- 交易信号:
- 做多:10:00 后,价格突破上轨。
- 做空:10:00 后,价格跌破下轨。
- 出场规则:
- 止损:价格反向突破(如做多后跌破下轨,或固定点数止损)。
- 收盘平仓:在每日收盘前(如 14:55)强制平仓。
QMT Python 策略代码
本代码以**股指期货(IF)**为例,因为 Hans123 是典型的 T+0 策略。如果您用于股票,请注意 A 股的 T+1 限制(当日买入无法当日卖出)。
# -*- coding: gbk -*-
import pandas as pd
import time
def init(ContextInfo):
"""
策略初始化函数
"""
# 1. 设定交易标的 (此处以沪深300主力合约为例,实盘请修改为具体合约代码)
ContextInfo.asset = 'IF00.IF'
# 2. 设定账户 (请修改为您的资金账号)
ContextInfo.account_id = 'YOUR_ACCOUNT_ID'
ContextInfo.account_type = 'FUTURE' # FUTURE: 期货, STOCK: 股票
# 3. 策略参数设置
ContextInfo.setup_time_end = "10:00:00" # 突破区间建立结束时间
ContextInfo.close_time = "14:55:00" # 尾盘平仓时间
ContextInfo.trade_qty = 1 # 每次交易手数
# 4. 全局变量初始化
ContextInfo.day_high = -1 # 当日区间最高价
ContextInfo.day_low = 999999 # 当日区间最低价
ContextInfo.current_date = '' # 当前日期记录
ContextInfo.trade_triggered = False # 当日是否已开仓标记
# 5. 设定运行周期 (建议使用 1分钟 或 5分钟 线)
# 注意:在界面上运行策略时,请选择相应的周期
def handlebar(ContextInfo):
"""
K线周期运行函数
"""
# 获取当前K线位置
index = ContextInfo.barpos
# 获取当前时间
timetag = ContextInfo.get_bar_timetag(index)
# 将时间戳转换为日期和时间字符串
current_dt_str = timetag_to_datetime(timetag, '%Y%m%d %H:%M:%S')
current_date = current_dt_str[0:8]
current_time = current_dt_str[9:]
# --- 1. 新的一天,重置变量 ---
if ContextInfo.current_date != current_date:
ContextInfo.current_date = current_date
ContextInfo.day_high = -1
ContextInfo.day_low = 999999
ContextInfo.trade_triggered = False
print(f"New Day: {current_date}, Reset parameters.")
# 获取最新行情数据 (开高低收)
# 使用 get_market_data_ex 获取数据更高效
data_dict = ContextInfo.get_market_data_ex(
['open', 'high', 'low', 'close'],
[ContextInfo.asset],
period=ContextInfo.period,
count=1,
subscribe=True
)
if ContextInfo.asset not in data_dict:
return
df = data_dict[ContextInfo.asset]
if df.empty:
return
# 获取当前K线的最高、最低、收盘价
current_high = df.iloc[-1]['high']
current_low = df.iloc[-1]['low']
current_close = df.iloc[-1]['close']
# --- 2. 建立突破区间 (开盘 - 10:00) ---
if current_time <= ContextInfo.setup_time_end:
# 更新区间最高价
if current_high > ContextInfo.day_high:
ContextInfo.day_high = current_high
# 更新区间最低价
if current_low < ContextInfo.day_low:
ContextInfo.day_low = current_low
# 在图表上画出区间线 (仅供回测观察)
ContextInfo.paint('Setup_High', ContextInfo.day_high, -1, 0)
ContextInfo.paint('Setup_Low', ContextInfo.day_low, -1, 0)
# --- 3. 交易阶段 (10:00 - 14:55) ---
elif current_time < ContextInfo.close_time:
# 绘制延续的区间线
ContextInfo.paint('Setup_High', ContextInfo.day_high, -1, 0)
ContextInfo.paint('Setup_Low', ContextInfo.day_low, -1, 0)
# 如果今日尚未交易,检查突破信号
if not ContextInfo.trade_triggered:
# 向上突破
if current_close > ContextInfo.day_high:
print(f"Breakout UP: Price {current_close} > High {ContextInfo.day_high}")
# 期货开多 (23:买入, 1101:单股/手)
# 注意:期货开仓通常用 buy_open,这里演示通用 passorder
# opType: 23(买入/开多), 24(卖出/开空) - 股票逻辑
# 期货逻辑: 0(开多), 3(开空)
# 这里使用期货专用函数 buy_open 更加清晰
buy_open(ContextInfo.asset, ContextInfo.trade_qty, "LATEST", current_close, ContextInfo, ContextInfo.account_id)
ContextInfo.trade_triggered = True
# 向下突破
elif current_close < ContextInfo.day_low:
print(f"Breakout DOWN: Price {current_close} < Low {ContextInfo.day_low}")
# 期货开空
sell_open(ContextInfo.asset, ContextInfo.trade_qty, "LATEST", current_close, ContextInfo, ContextInfo.account_id)
ContextInfo.trade_triggered = True
# --- 止损逻辑 (可选) ---
# 如果持多仓,价格跌破下轨,止损平仓
# 如果持空仓,价格突破上轨,止损平仓
# 此处需要获取持仓信息,为简化代码,此处略过复杂持仓判断,仅展示核心逻辑
# --- 4. 尾盘平仓 (14:55 以后) ---
else:
# 获取当前持仓
positions = get_trade_detail_data(ContextInfo.account_id, ContextInfo.account_type, 'POSITION')
for pos in positions:
if pos.m_strInstrumentID == ContextInfo.asset:
# 如果有多单,平多
if pos.m_nVolume > 0 and pos.m_nDirection == 48: # 48代表多头
print(f"Closing Long Position at {current_time}")
sell_close_tdayfirst(ContextInfo.asset, pos.m_nVolume, "LATEST", current_close, ContextInfo, ContextInfo.account_id)
# 如果有空单,平空
elif pos.m_nVolume > 0 and pos.m_nDirection == 49: # 49代表空头
print(f"Closing Short Position at {current_time}")
buy_close_tdayfirst(ContextInfo.asset, pos.m_nVolume, "LATEST", current_close, ContextInfo, ContextInfo.account_id)
代码关键点解析
-
时间处理:
- QMT 的
get_bar_timetag返回的是毫秒级时间戳。 - 使用
timetag_to_datetime转换为字符串(如'10:00:00'),方便进行字符串比较来判断当前处于哪个交易阶段(建立区间、交易、平仓)。
- QMT 的
-
数据获取 (
get_market_data_ex):- 这是 QMT 推荐的高效数据接口。
count=1表示只取最新的一根 K 线数据,用于实时判断。- 返回的是一个字典,Key 是股票代码,Value 是 Pandas DataFrame。
-
状态管理:
ContextInfo.day_high和day_low用于存储当天的关键点位。ContextInfo.current_date用于检测日期变更,确保第二天开盘时重置策略状态。
-
交易函数:
- 代码中使用了
buy_open(买入开仓) 和sell_open(卖出开仓),这是针对期货的便捷函数。 - 尾盘平仓使用了
sell_close_tdayfirst(卖出平仓,平今优先),适合日内交易,优先平掉当天的仓位以节省手续费(针对某些品种)。
- 代码中使用了
注意事项
- 股票 vs 期货:
- 上述代码逻辑是针对期货(T+0)设计的。
- 如果是股票(T+1),你不能在当天买入后当天卖出。要在股票上实现 Hans123,通常需要你有“底仓”(即昨天已经持有的股票),通过“做T”的方式(高抛低吸)来实现日内价差,或者将策略改为波段策略(持有过夜)。
- 回测与实盘:
- 在回测模式下,QMT 会模拟撮合。
- 在实盘模式下,请务必先在模拟账户测试,确认
ContextInfo.account_id设置正确。
- 周期选择:
- Hans123 对时间敏感,建议在策略编辑器界面选择 1分钟 或 5分钟 周期运行。
Q&A
Q: 如何修改止损逻辑?
A: 在“交易阶段”的代码块中,可以使用 get_trade_detail_data 获取当前持仓成本。如果 (现价 - 成本) / 成本 < -0.01 (亏损1%),则触发平仓函数。
Q: 为什么使用 get_market_data_ex 而不是 get_market_data?
A: get_market_data_ex 是 QMT 的新版接口,返回 Pandas 格式,处理数据更方便,且支持订阅模式,效率更高。
Q: 如何在股票上做空?
A: 在 A 股市场,普通账户无法直接做空。如果是信用账户(融资融券),可以使用 passorder 函数配合 opType=28 (融券卖出) 来实现做空逻辑。