🚀 AI 一键生成 qmt 策略代码

如何在 QMT 策略中高效管理多个行情订阅 (`subscribe_quote`)?

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

问题描述

如何在策略中管理多个行情数据订阅?

解决方案

在 QMT 平台中,默认的策略运行机制是基于“主图驱动”的,即策略挂载在哪个标的上,就由该标的的行情(Tick 或 Bar)来驱动 handlebar 函数的执行。

如果需要在策略中同时监控和交易多个标的(例如多因子选股、配对交易或篮子交易),仅依赖主图驱动可能会导致非主图标的的信号延迟。此时,我们需要使用 ContextInfo.subscribe_quote 接口来进行独立的数据订阅,并通过回调函数来处理实时推送的数据。

以下是关于如何在 QMT 策略中管理多个行情数据订阅的详细说明和代码示例。

核心机制说明

  1. 订阅接口ContextInfo.subscribe_quote(stock_code, period, dividend_type, callback)
    • 该函数用于订阅指定合约的行情。
    • callback:这是关键参数,当订阅的标的有行情更新时,平台会自动调用这个回调函数,而不是等待 handlebar
  2. 数据获取:在回调函数中,系统会传入 datas 参数,包含当前更新的数据。也可以结合 ContextInfo.get_market_data_ex 获取更详细的历史数据。
  3. 运行模式:此功能主要用于实盘全推行情模式。

策略代码实现

以下代码展示了如何订阅一个股票列表,并在数据更新时实时打印最新价格。

# -*- coding: gbk -*-

def init(ContextInfo):
    """
    初始化函数
    """
    # 1. 定义需要订阅的股票池
    ContextInfo.stock_list = ['600000.SH', '000001.SZ', '000300.SH']
    
    # 2. 定义订阅的周期 (例如 'tick', '1m', '1d')
    # 注意:实盘中通常订阅 'tick' 以获得最快响应
    period = 'tick'
    
    # 3. 遍历股票池进行订阅
    print("开始订阅行情...")
    for stock in ContextInfo.stock_list:
        # subscribe_quote 返回订阅号,如果需要反订阅可以使用该返回值
        # 参数说明: 代码, 周期, 复权方式('follow'跟随主图), 回调函数
        ContextInfo.subscribe_quote(stock, period, 'follow', on_quote_callback)
        print(f"已订阅: {stock}")

def on_quote_callback(datas):
    """
    行情推送回调函数
    当订阅的股票有新行情时,此函数会被触发
    datas 格式: {code: dataframe}
    """
    # 遍历推送过来的数据(通常一次推送可能包含一个或多个标的)
    for stock_code, df in datas.items():
        # 获取最新的一行数据
        if not df.empty:
            last_price = df.iloc[-1]['close']
            # 获取当前时间 (毫秒时间戳转字符串,需自行实现或使用简单打印)
            # 这里直接打印演示
            print(f">>> 行情推送 [{stock_code}]: 最新价 {last_price}")
            
            # 在此处编写您的交易逻辑
            # 例如:如果价格突破某条线,则下单
            # check_signal_and_order(stock_code, last_price)

def handlebar(ContextInfo):
    """
    主图驱动函数
    """
    # 如果策略完全依赖 subscribe_quote 的回调驱动,handlebar 可以留空
    # 或者在这里处理一些定时任务、收盘后的逻辑
    if ContextInfo.is_new_bar():
        print(f"主图 {ContextInfo.stockcode} K线更新")

# 辅助函数示例:简单的交易逻辑封装
def check_signal_and_order(stock_code, price):
    # 示例逻辑:仅作演示
    pass

关键点解析

  1. 回调函数的定义 (on_quote_callback)

    • 必须接受一个参数(示例中为 datas)。
    • datas 是一个字典,Key 是合约代码,Value 是包含该合约最新数据的 pandas.DataFrame
    • 注意:回调函数中的逻辑应尽量简洁高效,避免进行大量的耗时计算,以免阻塞行情推送线程。
  2. 订阅与主图的关系

    • 使用了 subscribe_quote 后,策略就变成了多源驱动
    • handlebar 依然会随主图(策略界面上选择的那个标的)更新而运行。
    • on_quote_callback 会随订阅列表中的任意标的更新而运行。
  3. 反订阅 (unsubscribe_quote)

    • 如果策略运行过程中需要动态移除某个标的的监控,可以使用 ContextInfo.unsubscribe_quote(subId)
    • subIdsubscribe_quote 调用时的返回值。
  4. 数据获取建议

    • 虽然 datas 参数带有了最新的行情,但如果需要计算复杂的指标(如 MA, MACD),建议在回调函数内部调用 ContextInfo.get_market_data_ex 来获取指定长度的历史数据窗口。

常见问题 Q&A

Q: subscribe_quote 在回测模式下有效吗?
A: 在回测模式下,QMT 主要依赖 handlebar 逐根 K 线驱动。虽然接口在回测中不会报错,但回测的核心逻辑通常是基于 handlebar 遍历历史时间轴。subscribe_quote 的主要应用场景是实盘模拟交易中的实时行情监控。

Q: 订阅的股票数量有限制吗?
A: QMT 平台本身对订阅数量有一定的内部限制,且订阅过多标的(例如几千只)会消耗大量内存和 CPU 资源,导致策略响应变慢。建议根据硬件配置合理控制订阅数量,通常几十到几百只是可以接受的。

Q: 为什么我在 on_quote_callback 里下单没有反应?
A: 请检查是否正确设置了交易账号 (ContextInfo.set_account)。此外,确保回调函数内部的逻辑没有报错(可以使用 try...except 捕获异常并打印日志),因为回调函数是在后台线程运行的,有时错误信息不如主线程明显。

Q: 如何获取 Level-2 数据(如买一卖一队列)?
A: 如果您开通了 Level-2 权限,可以在 subscribe_quote 的周期参数中使用 'l2quoteaux' (快照指标) 或 'tick' (分笔) 并配合 get_market_data_ex 获取更深度的盘口数据。