QMT 委托状态跟踪与防重复下单示例

本示例展示如何通过 order_callback 委托回报回调和 get_trade_detail_data 委托查询函数,实现委托状态跟踪和防止重复下单。策略在发送委托后设置待成交标志,后续 Bar 中先检查委托是否已结束,未结束则跳过下单,从而避免同一信号反复触发导致的重复委托问题。示例中的真实委托代码默认关闭。

!!! danger "高风险警告" 本示例包含真实委托代码(passorder)。示例中的真实委托代码默认关闭,需将 ENABLE_REAL_ORDER 设为 True 才会发送委托。在仿真交易环境充分验证前,请勿在真实交易环境中启用。

示例目标

实现以下委托管理能力:

  • 发送委托后记录委托号并设置待成交标志
  • 通过 order_callback 回调实时获取委托号
  • 通过 get_trade_detail_data 查询委托状态
  • 在有待成交委托时阻止重复下单
  • 委托进入终态后恢复下单权限

适用环境

环境 是否支持
回测环境 不适用(回测无真实委托回报机制)
模拟信号 不适用(模拟信号不产生委托)
仿真交易环境 支持
真实交易环境 支持

使用周期

日线(1d)。

是否可能产生真实委托

是(默认关闭)。代码中包含 passorder 调用,但通过 ENABLE_REAL_ORDER 开关控制,默认值为 False(不发送委托)。设为 True 后将在仿真交易或真实交易环境中发送实际委托。

完整代码

# ============================================================
# 真实委托开关:默认关闭
# 设为 True 后将在仿真交易或真实交易环境中发送委托
# 示例中的真实委托代码默认关闭
# ============================================================
ENABLE_REAL_ORDER = False

# 委托终态状态码
# 57 = 全部成交
# 58 = 已撤
# 59 = 部分撤
# 废单等其他终态码以当前客户端版本为准
ORDER_TERMINAL_STATUS = [57, 58, 59]

def init(ContextInfo):
    """策略初始化"""
    ContextInfo.set_universe(['600000.SH'])
    ContextInfo.order_pending = False    # 是否有待成交委托
    ContextInfo.last_order_id = None     # 上一次委托号
    print(f'策略初始化完成 | 真实委托开关: {ENABLE_REAL_ORDER}')

def handlebar(ContextInfo):
    """主策略逻辑"""
    if not ContextInfo.is_last_bar():
        return

    # ---- 步骤1:检查待成交委托状态 ----
    if ContextInfo.order_pending:
        if check_order_finished(ContextInfo):
            ContextInfo.order_pending = False
            print('待成交委托已结束,恢复下单权限')
        else:
            return  # 有未结束的委托,不重复下单

    # ---- 步骤2:获取行情与持仓 ----
    close = ContextInfo.get_close_price()
    if close is None or close <= 0:
        return

    pos = ContextInfo.get_position('600000.SH')
    has_position = pos and pos.m_nVolume > 0

    # ---- 步骤3:交易信号判断与下单 ----
    # 示例信号:无持仓时买入 100 股
    if not has_position:
        if ENABLE_REAL_ORDER:
            # 发送买入委托
            # quickTrade=2 表示同步等待下单结果
            passorder(0, 0, ContextInfo.accountid, '600000.SH', 0,
                      close, 100, 'state_track', 2, 0)
            ContextInfo.order_pending = True
            print(f'已发送买入委托: 100 股 @ {close:.2f}')
        else:
            print(f'[信号] 可买入 100 股 @ {close:.2f}(真实委托代码已关闭)')

def order_callback(ContextInfo, order_info):
    """委托回报回调:委托状态变化时触发,用于获取实际委托号"""
    ContextInfo.last_order_id = str(getattr(order_info, 'm_strOrderID', ''))
    status = getattr(order_info, 'm_nOrderStatus', 0)
    stock = getattr(order_info, 'm_strStockCode', '')
    direction = '买入' if getattr(order_info, 'm_nOrderType', 0) == 1 else '卖出'
    print(f'委托回报: {stock} {direction} 委托号={ContextInfo.last_order_id} 状态={status}')

    # 如果委托已进入终态,清除待成交标志
    if status in ORDER_TERMINAL_STATUS:
        ContextInfo.order_pending = False
        print(f'委托 {ContextInfo.last_order_id} 已结束(状态码: {status})')

def check_order_finished(ContextInfo):
    """
    通过 get_trade_detail_data 查询委托状态
    返回 True 表示委托已结束,返回 False 表示仍未结束
    """
    orders = get_trade_detail_data(ContextInfo.accountid, 'ORDER', 'STOCK')
    if not orders:
        # 查不到任何委托记录,视为已结束
        return True

    target_id = str(ContextInfo.last_order_id) if ContextInfo.last_order_id else None

    for order in orders:
        order_id = str(getattr(order, 'm_strOrderID', ''))
        status = getattr(order, 'm_nOrderStatus', 0)

        if target_id is not None and order_id == target_id:
            if status in ORDER_TERMINAL_STATUS:
                print(f'查询确认: 委托 {order_id} 已结束 | 状态码: {status}')
                return True
            else:
                print(f'查询确认: 委托 {order_id} 未结束 | 状态码: {status}')
                return False

    # 未找到匹配的委托记录
    if target_id is not None:
        print(f'未找到委托 {target_id},视为已结束')
    return True

代码说明(状态管理逻辑)

整体流程

handlebar 触发
    │
    ├── order_pending == True?
    │   ├── 是 → 查询委托状态 → 已结束? → 清除标志,继续
    │   │                       └─ 未结束? → return(跳过下单)
    │   └── 否 → 继续执行交易逻辑
    │
    ├── 获取行情与持仓
    │
    └── 信号触发?
        ├── ENABLE_REAL_ORDER == True → passorder 下单 → 设置 order_pending = True
        └── ENABLE_REAL_ORDER == False → 仅输出信号日志

状态变量

变量 类型 初始值 说明
ENABLE_REAL_ORDER bool False 真实委托开关,默认关闭
ContextInfo.order_pending bool False 是否有待成交委托
ContextInfo.last_order_id str/None None 上一次委托号

委托号获取机制

def order_callback(ContextInfo, order_info):
    ContextInfo.last_order_id = str(getattr(order_info, 'm_strOrderID', ''))
  • passorder 发送委托后,order_callback 在委托状态变化时被触发。
  • 通过 order_info.m_strOrderID 获取委托号并保存到 ContextInfo.last_order_id
  • 注意:passorder 的返回值与 order_callback 中的委托号可能不同步,以 order_callback 中的为准。

委托状态查询

orders = get_trade_detail_data(ContextInfo.accountid, 'ORDER', 'STOCK')
参数 说明
accountid ContextInfo.accountid 账户 ID
datatype 'ORDER' 查询委托记录
market 'STOCK' 股票市场

get_trade_detail_data 返回委托对象列表,每个对象包含以下常用属性:

属性 说明
m_strOrderID 委托号
m_strStockCode 证券代码
m_nOrderType 委托类型(1=买入,2=卖出)
m_nOrderStatus 委托状态码
m_nVolume 委托数量
m_dPrice 委托价格
m_nTradedVolume 已成交数量

委托状态码

状态码 说明 是否终态
50 委托已提交
55 委托已确认
56 部分成交
57 全部成交
58 已撤
59 部分撤
提示

废单等其他终态状态码可能因客户端版本不同而异,以当前客户端版本为准。建议在 ORDER_TERMINAL_STATUS 列表中补充已知的废单状态码。

防重复下单机制

if ContextInfo.order_pending:
    if check_order_finished(ContextInfo):
        ContextInfo.order_pending = False
    else:
        return  # 有未结束的委托,不重复下单
  • 发送委托后立即设置 order_pending = True
  • 后续每次 handlebar 触发时,先检查是否有待成交委托。
  • 如果委托未结束(非终态),直接 return 跳过下单逻辑。
  • 如果委托已结束(终态),清除标志并继续执行交易逻辑。

passorder 参数说明

passorder(0, 0, ContextInfo.accountid, '600000.SH', 0,
          close, 100, 'state_track', 2, 0)
位置 参数 说明
1 opType 0 股票
2 orderType 0 买入
3 accountid ContextInfo.accountid 账户 ID
4 orderCode '600000.SH' 证券代码
5 prType 0 价格类型
6 price close 委托价格(最新收盘价)
7 volume 100 委托数量
8 strategyName 'state_track' 策略名称
9 quickTrade 2 同步等待下单结果
10 userOrderId 0 用户自定义委托号
quickTrade 参数

quickTrade=2 表示同步等待下单结果,会阻塞直到返回。如果对性能要求较高,可设为 0(异步)或 1(异步快速),但异步模式下委托号需通过 order_callback 获取。

安全注意事项

  1. 真实委托开关默认关闭ENABLE_REAL_ORDER 默认为 False,不会发送任何委托。请先在仿真交易环境中验证逻辑正确后再设为 True

  2. 委托号获取时序passorder 发送委托后,order_callback 可能在下一个 handlebar 之前或之后触发。代码中通过 check_order_finished 函数查询委托状态作为兜底,即使回调未及时到达也能正确判断。

  3. 状态码兼容性:不同客户端版本的委托状态码可能存在差异。使用前请在仿真交易环境中验证状态码的含义,以当前客户端版本为准。

  4. 异常处理:生产环境中应在 passorder 调用和 get_trade_detail_data 查询周围添加 try-except 块,防止异常导致策略中断。

  5. 资金与持仓检查:下单前应检查可用资金是否充足、可卖持仓是否充足,本示例为简化演示未包含完整的前置检查。

真实委托代码默认关闭说明

本示例中所有 passorder 调用均受 ENABLE_REAL_ORDER 开关控制:

ENABLE_REAL_ORDER = False  # 默认关闭

if ENABLE_REAL_ORDER:
    passorder(...)  # 仅当开关打开时执行
else:
    print('[信号] ...(真实委托代码已关闭)')
  • 默认状态False,不发送委托,仅输出信号日志。
  • 启用方式:将 ENABLE_REAL_ORDER 改为 True
  • 启用前提:已在仿真交易环境中充分验证委托状态跟踪逻辑正确无误。
  • 启用风险:设为 True 后将在对应环境中发送实际委托,仿真交易环境产生仿真委托,真实交易环境产生真实委托。

已知限制

  • 本示例的交易信号仅为演示用途(无持仓即买入),不构成任何交易建议。
  • get_trade_detail_data 查询的是当日委托记录,跨日的未成交委托可能无法查到。
  • order_callback 的触发时机和频率取决于客户端版本和网络状况,以当前客户端版本为准。
  • 本示例未实现撤单逻辑。如果委托长时间未成交,应考虑添加超时撤单机制。
  • passorderprType=0 在不同客户端版本中的行为可能略有差异,以当前客户端版本为准。
  • 在真实交易环境中使用时,建议添加更完善的资金检查、涨跌停检查和异常处理逻辑。