🚀 AI 一键生成 qmt 策略代码

QMT 策略盘中运行机制:如何正确处理 Tick 数据与 K 线更替?

qmt | 发布时间: 2025/11/28
以下内容由 EasyQuant 生成。

问题描述

在做分钟级别策略时,如何保证我的交易信号只在新 K 线生成时触发一次?

解决方案

在 QMT(Quant Trader)平台进行分钟级别(或任何 K 线周期)的实盘或模拟交易时,handlebar(ContextInfo) 函数在最后一根 K 线(实时行情)上是基于 Tick 驱动的。这意味着每来一个分笔数据(Tick),handlebar 就会被调用一次。

如果不加控制,简单的 if condition: buy() 逻辑会导致在同一分钟内重复下单。

要保证交易信号只在新 K 线生成时触发一次,主要有以下三种常用方法:

方法一:使用全局变量记录 K 线位置(推荐)

这是最稳健的方法。通过在 ContextInfo 中记录上一次交易或运行逻辑时的 K 线索引(barpos)或时间戳(timetag),来判断当前是否进入了新的 K 线。

逻辑:

  1. init 中初始化一个变量记录 K 线位置。
  2. handlebar 中,获取当前 K 线位置。
  3. 比较当前位置与记录的位置,如果不同,说明是新 K 线,执行逻辑并更新记录。

代码示例:

# -*- coding: gbk -*-

def init(ContextInfo):
    # 1. 初始化全局变量,用于记录上一次处理的 K 线索引
    # -1 表示尚未处理过任何 K 线
    ContextInfo.last_processed_barpos = -1
    
    # 设置账号等其他初始化操作
    # ContextInfo.set_account('您的账号')

def handlebar(ContextInfo):
    # 获取当前 K 线索引
    current_barpos = ContextInfo.barpos
    
    # 2. 判断是否为新的一根 K 线
    # 如果当前索引 大于 上次处理的索引,说明新 K 线生成了(或者刚启动策略)
    if current_barpos > ContextInfo.last_processed_barpos:
        
        # --- 策略逻辑开始 ---
        # 获取行情数据
        close_price = ContextInfo.get_market_data(['close'], period=ContextInfo.period, count=1)
        print(f"新 K 线生成,当前索引: {current_barpos}, 最新收盘价: {close_price}")
        
        # 在这里编写你的交易信号逻辑
        # if condition:
        #     passorder(...)
        
        # --- 策略逻辑结束 ---
        
        # 3. 关键步骤:更新已处理的 K 线索引
        # 标记当前 K 线已经处理过,防止同根 K 线后续 Tick 重复触发
        ContextInfo.last_processed_barpos = current_barpos

    else:
        # 如果索引没有变化,说明还是在同一根 K 线的不同 Tick 中
        # 这里可以什么都不做,或者只做一些盘口观察逻辑
        pass

方法二:使用 ContextInfo.is_new_bar()

QMT API 提供了一个原生函数 is_new_bar(),用于判断当前 Tick 是否为当前 K 线的第一个 Tick。

逻辑:
直接调用 ContextInfo.is_new_bar(),如果返回 True,则执行交易逻辑。

优缺点:

  • 优点:代码简洁。
  • 缺点:在实盘网络波动极大或数据包丢失的极端情况下,如果“第一个 Tick”丢失,可能会导致整根 K 线漏单(虽然概率较低)。

代码示例:

# -*- coding: gbk -*-

def init(ContextInfo):
    pass

def handlebar(ContextInfo):
    # 判断是否为新 K 线的第一个 Tick
    if ContextInfo.is_new_bar():
        print(f"检测到新 K 线,当前时间: {ContextInfo.get_bar_timetag(ContextInfo.barpos)}")
        
        # 执行交易逻辑
        # passorder(...)

方法三:使用 do_order() 配合收盘价模型

如果你习惯于**“K 线走完确认信号,在下一根 K 线开盘立即下单”**(即收盘价模型),QMT 提供了 do_order 机制。

逻辑:

  1. 策略逻辑写在 handlebar 中,基于最后一根 K 线(实时变动)计算信号。
  2. 只有当这根 K 线彻底走完(即下一根 K 线的第一个 Tick 到来时),QMT 才会真正发出信号。
  3. 需要配合 do_order(ContextInfo) 函数使用,或者在非日线周期下依赖平台机制。

注意:这种方式通常用于日线策略,或者不需要盘中立即抢单的策略。对于分钟级策略,通常建议使用方法一,配合 quickTrade=1(立即下单)参数,以便在信号触发的瞬间立即成交,而不是等到下一分钟。

总结与建议

对于分钟级别的量化策略,强烈建议使用【方法一:全局变量记录 K 线位置】

理由:

  1. 可控性强:你可以完全控制信号触发的逻辑,不受 API 底层 Tick 推送机制的偶发影响。
  2. 避免重复:通过 barpos 的严格比对,物理上杜绝了同一根 K 线重复执行交易代码块的可能性。
  3. 兼容性:这种写法在回测和实盘中逻辑一致性较好。

关于下单函数的补充:
在使用 passorderorder_shares 等下单函数时,请确保 quickTrade 参数设置为 1(立即下单),否则系统可能会默认等到 K 线结束才报单,这会违背你“新 K 线生成即触发”的初衷。

例如:

# quickTrade=1 表示立即触发,不等待 K 线结束
passorder(23, 1101, account, code, 5, -1, 100, "strategyName", 1, "remark", ContextInfo)

Q&A

Q: ContextInfo.barpos 在实盘中是实时更新的吗?
A: 是的。每当时间跨过 K 线切分点(例如 10:00 到 10:01),barpos 会自动加 1。

Q: 如果策略重启了,last_processed_barpos 会重置吗?
A: 是的,策略重启后 init 会重新运行,变量会重置。如果不想在重启瞬间重复交易,可以在 init 中先获取一次当前的 barpos 赋值给 last_processed_barpos,这样策略会从“下一根”K 线开始交易。

Q: 这种方法支持多只股票吗?
A: 支持。handlebar 是由主图行情驱动的。如果你交易多只股票,且它们都在同一个周期下,主图的 barpos 变化代表时间推进,你可以在逻辑块内循环遍历你的股票池进行信号计算。