🚀 AI 一键生成 qmt 策略代码

QMT 平台 get_trade_detail_data 获取委托数据慢怎么优化?(附超时撤单源码)

qmt | 发布时间: 2026/5/21
以下内容由 EasyQuant 生成。

问题描述

get_trade_detail_data获取“委托”数据的问题。

我是想实现对超过 N 秒的没成交的委托自动撤单功能:

先是用 get_trade_detail_data(A.acct,'STOCK','order')获取当天的委托明细

再循环每笔委托,用 can_cancel_order 判断该委托是否可撤。

但是发现当天的委托数量很大时(超过 1000 条),这个循环很耗时。

策略早上运行很快,下午会越来越慢。完成一次全循环需要十几秒。

请问 get_trade_detail_data 能否只获取最近的 100 条委托,获都只获取最近 5 分钟的委托数据?

解决方案

在 QMT 平台中,get_trade_detail_data 接口默认会返回指定账号当天的所有委托明细。API 底层并没有直接提供 limit(限制条数)或 start_time/end_time(限制时间)的参数。

你遇到的“下午越来越慢”的问题,根本原因通常不是 get_trade_detail_data 获取数据慢,而是对 1000 多条历史委托(包括已成、已撤、废单)进行了无意义的遍历,并且频繁调用 can_cancel_order 导致了极大的性能开销

为了实现高效的“超时 N 秒自动撤单”,你可以采用以下几种优化方案:

优化方案一:利用 Python 列表切片(最简单)

既然接口返回的是一个列表,且通常是按时间顺序追加的,你可以直接在 Python 层面截取最后 100 条数据进行遍历,避免遍历早上的历史订单。

order_list = get_trade_detail_data(ContextInfo.accid, 'STOCK', 'order')
# 只取最后 100 条委托
recent_orders = order_list[-100:] 

优化方案二:过滤委托状态(强烈推荐)

绝大多数历史委托已经是“已成(56)”、“已撤(54)”或“废单(57)”,对它们调用 can_cancel_order 是毫无意义且耗时的。你应该在循环的第一步,直接过滤掉这些状态,只处理“未报(48)”、“待报(49)”、“已报(50)”和“部成(55)”的活跃订单

优化方案三:在策略中自行维护“活跃订单字典”(最高效)

对于高频/大量订单的策略,最专业的做法是不依赖全量查询,而是在下单时将订单号和时间记录在全局字典中,成交或撤单后将其剔除。这样你每次只需要遍历几个真正未成交的订单。

综合优化代码示例(超时自动撤单)

下面结合了状态过滤时间比对的优化代码,能将十几秒的耗时降低到毫秒级:

import time
import datetime

def init(ContextInfo):
    ContextInfo.accid = '你的资金账号'
    ContextInfo.timeout_seconds = 60 # 设置超时时间 N = 60秒

def handlebar(ContextInfo):
    # 1. 获取当天所有委托
    order_list = get_trade_detail_data(ContextInfo.accid, 'STOCK', 'order')
    
    # 2. 优化:如果订单量极大,可先切片取最近 200 条
    if len(order_list) > 200:
        order_list = order_list[-200:]
        
    current_time = datetime.datetime.now()
    
    for order in order_list:
        # 3. 核心优化:只处理活跃状态的订单
        # 48:未报, 49:待报, 50:已报, 51:已报待撤, 52:部成待撤, 53:部撤, 55:部成
        # 如果是 54(已撤), 56(已成), 57(废单),直接跳过,极大地节省时间
        if order.m_nOrderStatus in [54, 56, 57, 86, 255]:
            continue
            
        # 4. 解析订单时间 (m_strInsertTime 格式通常为 'HH:MM:SS')
        try:
            # 拼接当天的日期和订单时间
            today_str = current_time.strftime('%Y-%m-%d')
            order_time_str = f"{today_str} {order.m_strInsertTime}"
            order_time = datetime.datetime.strptime(order_time_str, '%Y-%m-%d %H:%M:%S')
            
            # 计算时间差
            time_diff = (current_time - order_time).total_seconds()
            
            # 5. 判断是否超时 N 秒
            if time_diff > ContextInfo.timeout_seconds:
                # 6. 确认可撤后执行撤单
                if can_cancel_order(order.m_strOrderSysID, ContextInfo.accid, 'STOCK'):
                    cancel(order.m_strOrderSysID, ContextInfo.accid, 'STOCK', ContextInfo)
                    print(f"订单 {order.m_strOrderSysID} 超时 {time_diff} 秒,已发送撤单指令")
        except Exception as e:
            print(f"解析订单时间出错: {e}")

总结

  1. 不要依赖 API 去限制条数,直接用 Python 的 [-100:] 切片。
  2. 拦截无效状态:通过 order.m_nOrderStatus 过滤掉已成、已撤的单子,这是解决你循环耗时十几秒的最关键一步。
  3. 减少 API 调用can_cancel_order 内部有通信开销,只有在状态和时间都满足条件时,才去调用它。