🚀 AI 一键生成 qmt 策略代码

QMT 策略中如何保证每个 K 线周期只执行一次交易逻辑?

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

问题描述

请帮我编写一个交易策略,让它在每个新的K线开始时只执行一次操作,避免在K线中间的每个tick都重复执行。

解决方案

在 QMT (Quant Trader) 平台中,handlebar(ContextInfo) 函数在实时行情(运行模式)下是基于 Tick 驱动的。这意味着在同一根 K 线(Bar)内,每当有新的分笔成交(Tick)推送过来,handlebar 都会被调用一次。

如果不加限制,交易逻辑会在同一根 K 线内重复触发。为了实现**“每个新的 K 线开始时只执行一次”**,最标准的方法是使用 QMT API 提供的 ContextInfo.is_new_bar() 函数。

核心逻辑说明

  1. ContextInfo.is_new_bar():
    • 该函数用于判断当前处理的数据是否为一根新的 K 线的第一个 Tick。
    • 返回值:True 表示是新 K 线的开始,False 表示仍在当前 K 线内部更新。
  2. 替代方案(手动判断 barpos:
    • 如果需要更复杂的控制,也可以通过记录上一次执行的 K 线索引 (ContextInfo.barpos) 来判断索引是否发生了变化。

策略代码实现

以下是一个完整的 Python 策略示例。该策略会在每根新 K 线出现的第一个 Tick 打印日志并执行模拟下单操作。

# -*- coding: gbk -*-
import pandas as pd

def init(ContextInfo):
    """
    初始化函数,策略启动时执行一次
    """
    # 设置资金账号 (请替换为您的真实账号)
    ContextInfo.accid = '600000248'
    ContextInfo.set_account(ContextInfo.accid)
    
    # 设置股票池 (示例:平安银行)
    ContextInfo.set_universe(['000001.SZ'])
    
    # 初始化一个自定义变量,用于记录上一次操作的 Bar 索引(作为双重验证,非必须但推荐)
    ContextInfo.last_processed_barpos = -1
    
    print("策略初始化完成")

def handlebar(ContextInfo):
    """
    行情事件函数,每根 K 线或每个 Tick 运行一次
    """
    # 获取当前 K 线的索引位置
    current_barpos = ContextInfo.barpos
    
    # 获取当前主图品种代码
    stock_code = ContextInfo.stockcode
    
    # --- 核心逻辑:判断是否为新 K 线 ---
    # 方法 A:使用官方 API is_new_bar() (推荐)
    is_new = ContextInfo.is_new_bar()
    
    # 方法 B:手动比较 barpos (作为补充理解)
    # is_new_manual = current_barpos > ContextInfo.last_processed_barpos
    
    if is_new:
        # 更新自定义的状态变量
        ContextInfo.last_processed_barpos = current_barpos
        
        # 获取当前时间,用于日志展示
        # 注意:get_bar_timetag 获取的是 K 线时间戳,timetag_to_datetime 转换为字符串
        timetag = ContextInfo.get_bar_timetag(current_barpos)
        time_str = timetag_to_datetime(timetag, '%Y-%m-%d %H:%M:%S')
        
        print(f"检测到新 K 线生成!时间: {time_str}, 索引: {current_barpos}")
        
        # --- 在此处编写您的交易逻辑 ---
        
        # 示例:获取最新价
        last_price = ContextInfo.get_market_data_ex(
            ['close'], 
            [stock_code], 
            period=ContextInfo.period, 
            count=1
        )[stock_code].iloc[-1]['close']
        
        print(f"当前 {stock_code} 价格: {last_price},执行开仓操作...")
        
        # 示例下单:按最新价买入 100 股
        # 注意:实盘中请确保账号设置正确且有资金
        # order_shares(stock_code, 100, 'LATEST', last_price, ContextInfo, ContextInfo.accid)
        
    else:
        # 如果不是新 K 线,则跳过,避免重复执行
        pass

代码详解

  1. # -*- coding: gbk -*-:

    • QMT 的 Python 编辑器默认使用 GBK 编码,这行代码是必须的,否则中文注释会导致乱码或报错。
  2. ContextInfo.is_new_bar():

    • 这是控制频率的关键。在实时行情中,一根 1 分钟的 K 线可能会触发几十次 handlebar 调用,但 is_new_bar() 只有在第一次调用时返回 True
  3. ContextInfo.barpos:

    • 这是当前 K 线在历史数据中的索引位置。随着时间推移,新 K 线生成时,这个数字会增加。
  4. timetag_to_datetime:

    • QMT 内部时间通常是毫秒级时间戳,使用此函数将其转换为可读的日期字符串,方便调试。

常见问题 (Q&A)

Q: is_new_bar() 在回测模式下如何工作?
A: 在回测模式下,handlebar 本身就是逐根 K 线运行的(除非开启了 Tick 回测),所以每一根 K 线 is_new_bar() 都会返回 True。这个逻辑在回测和实盘中是兼容的。

Q: 如果我在 K 线中间启动策略,会立即下单吗?
A: 如果您在 K 线中间启动策略,handlebar 会立即运行。对于当前这根 K 线,如果它是策略启动后接收到的第一个数据包,is_new_bar() 可能会返回 True(视具体版本和启动时机而定)。如果您希望严格限制只在下一根完整的 K 线开始时才交易,可以结合 ContextInfo.is_last_bar() 和本地时间判断,或者在 init 中记录启动时的 barpos,在 handlebar 中要求 current_barpos > init_barpos 才开始交易。

Q: 为什么有时候 is_new_bar() 触发了,但是成交价和 K 线收盘价不一样?
A: 因为 is_new_bar() 对应的是新 K 线的第一个 Tick(即 Open 开盘时刻附近的数据)。此时 K 线刚刚生成,价格通常接近开盘价,而不是这根 K 线结束时的收盘价。如果您需要在 K 线走完时交易(即收盘价确定后),通常的做法是在 is_new_bar() 触发时,读取上一根 K 线(barpos - 1)的数据进行信号计算和下单。