🚀 AI 一键生成 qmt 策略代码

QMT 多标的策略:如何处理不同股票行情更新不一致的问题?

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

问题描述

编写多股票交易策略时,如何确保获取到的所有股票行情都是最新的?

解决方案

在 QMT (Quantitative Trading Platform) 中编写多股票交易策略时,默认的 handlebar(ContextInfo) 函数是由主图行情(即策略界面上选择的那个标的)驱动的。这意味着如果主图标的没有成交或更新,handlebar 就不会运行,或者当它运行时,其他非主图股票的行情可能不是毫秒级最新的。

为了确保在多股票策略中获取到的所有股票行情都是最新的,通常有以下两种最有效的方案:

  1. 使用 subscribe_quote 进行独立行情订阅(推荐):这是最实时的方式。通过订阅指定股票列表的 Tick 数据,每当任何一只股票有行情更新时,都会触发独立的回调函数,而不是依赖主图驱动。
  2. 使用 get_full_tick 获取全推快照:如果在定时任务或主图驱动的逻辑中需要下单,可以使用此函数一次性拉取股票池中所有股票当前时刻的最新 Tick 快照(盘口、最新价等)。

下面我将提供一个完整的策略代码示例,展示如何结合这两种方法来确保数据的实时性。

策略代码示例

该策略演示了如何订阅多只股票的实时行情,并在数据更新时立即获取最新价格,同时展示了如何在主逻辑中获取一篮子股票的最新快照。

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

def init(ContextInfo):
    """
    初始化函数
    """
    # 1. 设定需要交易或监控的股票池
    ContextInfo.stock_list = ['600000.SH', '000001.SZ', '600519.SH']
    
    # 2. 设置账号(实盘必须设置)
    # ContextInfo.set_account('您的资金账号')
    
    print("策略初始化启动,开始订阅行情...")
    
    # 3. 【核心方法1】使用 subscribe_quote 订阅独立行情
    # 这种方式不依赖主图,任何一只股票有Tick更新都会触发 on_quote_callback
    for stock in ContextInfo.stock_list:
        # 参数说明:代码, 周期('tick'为最实时), 复权方式, 回调函数
        ContextInfo.subscribe_quote(stock, 'tick', 'follow', on_quote_callback)

def on_quote_callback(datas):
    """
    【核心方法1的回调】行情推送回调函数
    当 subscribe_quote 订阅的股票有新行情时,系统自动调用此函数
    datas: {code: dataframe} 结构
    """
    for stock_code, df in datas.items():
        # 获取最新的一行数据
        last_row = df.iloc[-1]
        current_price = last_row['lastPrice'] # 最新价
        timestamp = last_row.name # 时间戳
        
        # 在这里编写针对该股票的实时交易逻辑
        # print(f"【实时推送】{stock_code} 最新价: {current_price}, 时间: {timestamp}")
        
        # 示例:如果价格触发条件则下单(此处仅为演示,未实际开启交易)
        # if current_price > 100:
        #     passorder(23, 1101, ContextInfo.accid, stock_code, 5, -1, 100, ContextInfo)

def handlebar(ContextInfo):
    """
    K线驱动函数(由主图行情驱动)
    """
    # 如果是回测模式或需要基于K线走完才交易,逻辑写在这里
    if not ContextInfo.is_last_bar():
        return

    # 4. 【核心方法2】使用 get_full_tick 获取全推快照
    # 这种方式适合在某个时间点(如定时任务或主图更新时)一次性查看所有股票的最新状态
    # 它返回的是调用该函数那一刻的最新盘口数据,确保数据是最新的
    
    print("=" * 30)
    print("主图驱动触发,正在拉取股票池最新快照...")
    
    # 获取股票池中所有股票的最新 Tick 数据
    tick_data_map = ContextInfo.get_full_tick(ContextInfo.stock_list)
    
    for stock_code in ContextInfo.stock_list:
        if stock_code in tick_data_map:
            tick_data = tick_data_map[stock_code]
            
            # 解析 Tick 数据
            last_price = tick_data.get('lastPrice')
            ask_price_1 = tick_data.get('askPrice')[0] # 卖一价
            bid_price_1 = tick_data.get('bidPrice')[0] # 买一价
            
            print(f"【快照检查】{stock_code} -> 最新价: {last_price}, 卖一: {ask_price_1}, 买一: {bid_price_1}")
            
            # 在这里可以进行篮子交易逻辑判断
            # ...

    print("=" * 30)

# 注意:
# 1. subscribe_quote 适合高频、事件驱动的策略,响应速度最快。
# 2. get_full_tick 适合轮询、定时任务或低频策略,确保获取那一刻的最新切片。
# 3. 请勿使用 get_market_data 来获取"最新"的实时价格,因为它通常返回的是K线数据,
#    在实时性上不如上述两个接口。

核心原理解析

1. subscribe_quote (事件驱动模式)

这是确保多股票行情最新的最佳方案

  • 机制:它告诉 QMT 系统:“不要只管主图,请盯着这几只股票,一旦它们有动静,立刻通知我。”
  • 优势:解耦了主图行情。即使主图(如上证指数)1分钟没动,只要你订阅的股票(如某只活跃的小盘股)有成交,on_quote_callback 就会被触发。
  • 数据格式:回调返回的是 pandas.DataFrame,包含最新的 Tick 数据。

2. get_full_tick (快照轮询模式)

这是在特定时间点同步数据的方案。

  • 机制:当你决定要进行一次逻辑判断(例如每隔3秒,或者主图K线走完时),你调用此函数,QMT 会直接从内存中读取这些股票当前这一毫秒的最新状态。
  • 优势:数据结构是字典(Dict),包含详细的五档盘口(askPrice, bidPrice)、持仓量等,非常适合做截面分析(Cross-sectional analysis)。

常见误区提醒

  • 不要依赖 get_market_data 获取最新价get_market_data 主要用于获取历史序列数据(如过去100天的收盘价)。虽然它也能获取最新数据,但在实盘的高频场景下,它的更新机制不如 get_full_tick 直接和高效。
  • 主图依赖:如果你的策略只写在 handlebar 里且没有使用上述两种方法,那么如果主图停牌或交易不活跃,你的整个策略逻辑都会停止运行,导致错过其他股票的行情。

Q&A

Q: subscribe_quote 订阅的股票数量有限制吗?
A: QMT 对订阅数量通常没有硬性限制,但订阅过多(如几千只)会消耗大量内存和CPU资源,导致回调处理变慢。建议根据硬件配置合理控制,一般几百只以内问题不大。

Q: get_full_tickget_market_data_ex 有什么区别?
A: get_full_tick 专门用于获取当前时刻的 Tick 快照(盘口、最新价),返回字典格式,速度极快。get_market_data_ex 更通用,可以获取历史K线序列,返回 DataFrame。如果只是为了看最新价下单,get_full_tick 更合适。

Q: 回测模式下 subscribe_quote 有效吗?
A: 在回测模式下,subscribe_quote 的行为会模拟实盘,但受限于回测数据的精度(通常是分钟线或日线,除非有Tick级回测数据)。在回测中,通常主要依赖 handlebar 配合 ContextInfo.get_market_data 进行逻辑验证。上述提到的实时性问题主要针对实盘交易