问题描述
QMT 的回测只有 handler_bar 实在是很不方便。
很多时候,我们的策略需要指定时间执行,比如我需要每周执行一次,但是我希望在早上 10:00 的时候买入或者卖出。
如果只能用 handler_bar 函数,则需要调用分钟 K 线,然后每调用一次就判断是不是我的下单时间,不是就 PASS 掉,这太浪费资源了。
有没有更好的处理办法?
解决方案
QMT 回测机制解析
在 QMT 平台中,回测模式是严格基于 K 线行情驱动的。这意味着策略的推进完全依赖于历史 K 线数据的逐根回放,每走过一根 K 线,系统就会调用一次 handlebar(ContextInfo) 函数。
虽然 QMT 提供了定时器函数 ContextInfo.run_time(),但根据官方 API 文档说明:run_time() 在回测模式下是没有意义的(仅在实盘/模拟盘有效)。因此,在回测中,您必须通过调用包含目标时间的 K 线周期(如 1分钟、5分钟、15分钟、30分钟或 1小时线),并在 handlebar 中进行时间判断。
如何优化资源消耗?
虽然必须在 handlebar 中判断时间,但我们可以通过**“前置快速过滤(Fast Fail)”**的编程技巧,将资源浪费降到最低。只要在 handlebar 的最开头进行简单的时间戳比对,如果不符合条件直接 return,这种纯内存的简单判断对回测速度的影响微乎其微。
Python 实现代码:每周一早上 10:00 执行
以下是一个高效的实现模板,假设您在回测时选择的主图周期为 1分钟线 或 5分钟线:
#coding:gbk
import datetime
def init(ContextInfo):
# 初始化股票池
ContextInfo.set_universe(['000300.SH'])
# 设置回测起止时间
ContextInfo.start = '2023-01-01 09:00:00'
ContextInfo.end = '2023-12-31 15:00:00'
def handlebar(ContextInfo):
# 1. 获取当前 K 线的时间戳(毫秒)
timetag = ContextInfo.get_bar_timetag(ContextInfo.barpos)
# 2. 将时间戳转换为 datetime 对象
# 注意:QMT的时间戳是毫秒级,需要除以1000转换为秒
dt = datetime.datetime.fromtimestamp(timetag / 1000.0)
# 3. 前置快速过滤:判断是否为 10:00
# 如果主图是5分钟线,10:00的K线时间戳通常对应 10:00
if dt.hour != 10 or dt.minute != 0:
return # 不是10:00,直接跳过,极低资源消耗
# 4. 前置快速过滤:判断是否为每周的特定一天(例如周一,weekday() == 0)
if dt.weekday() != 0:
return # 不是周一,直接跳过
# ==========================================
# 以下为您的核心策略逻辑,每周一 10:00 才会执行到这里
# ==========================================
print(f"触发交易时间: {dt.strftime('%Y-%m-%d %H:%M:%S')}")
# 示例:获取最新价并下单
close_price = ContextInfo.get_market_data(['close'], stock_code=['000300.SH'], period='1d', count=1)
# passorder(23, 1101, ContextInfo.accid, '000300.SH', 5, -1, 100, ContextInfo)
实盘与回测的差异化处理
如果您希望策略在实盘/模拟盘中真正使用定时器,而在回测中使用 K 线判断,可以通过 ContextInfo.do_back_test 属性来区分运行环境:
def init(ContextInfo):
if not ContextInfo.do_back_test:
# 实盘/模拟盘模式:使用真正的定时器,每5秒执行一次自定义函数
ContextInfo.run_time("my_timer_func", "5nSecond", "2023-01-01 09:30:00", "SH")
def my_timer_func(ContextInfo):
# 实盘定时执行的逻辑
pass
def handlebar(ContextInfo):
if ContextInfo.do_back_test:
# 回测模式:使用 K 线时间戳判断逻辑
pass
总结
在 QMT 回测中,使用分钟 K 线配合 handlebar 顶部的快速 return 拦截,是目前唯一且最高效的指定时间执行方案。Python 的基础条件判断速度极快,只要不在此处调用复杂的 API(如获取历史数据、财务数据等),对整体回测性能的影响几乎可以忽略不计。