🚀 AI 一键生成 qmt 策略代码

QMT 教程:如何使用 `cancel` 函数实现程序化撤单?

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

问题描述

如何通过代码撤销一笔未成交的订单?

解决方案

在 QMT 量化交易平台中,撤销未成交订单主要通过调用 cancel() 函数来实现。为了成功撤单,通常需要先获取当前的委托列表,找到未成交(或部分成交)的订单编号(Order ID),然后针对该编号执行撤单操作。

以下是具体的实现步骤和代码示例。

核心函数说明

  1. cancel(orderId, accountId, accountType, ContextInfo)

    • 功能:取消指定的委托。
    • 参数
      • orderId:委托编号(字符串),通常通过 get_trade_detail_data 获取到的对象属性 m_strOrderSysID 获得。
      • accountId:资金账号。
      • accountType:账号类型(如 'STOCK', 'FUTURE' 等)。
      • ContextInfo:策略全局对象。
  2. can_cancel_order(orderId, accountId, accountType)

    • 功能:查询委托是否处于可撤销状态(如已报、部成等)。
    • 返回True (可撤) 或 False (不可撤)。
  3. get_trade_detail_data(accountId, accountType, 'ORDER')

    • 功能:获取账号的委托详情列表,用于查找需要撤销的订单 ID。

完整代码示例

以下代码展示了一个完整的流程:在 handlebar 中遍历当前账号的所有委托,检查每一笔委托是否处于“可撤销”状态,如果是,则执行撤单操作。

# -*- coding: gbk -*-

def init(ContextInfo):
    # 1. 设置资金账号和类型
    ContextInfo.accID = '6000000248'  # 请替换为您真实的资金账号
    ContextInfo.accType = 'STOCK'     # 账号类型:'STOCK'股票, 'FUTURE'期货, 'CREDIT'信用等
    
    # 2. 绑定账号(接收回报消息必须)
    ContextInfo.set_account(ContextInfo.accID)
    print("策略初始化完成,账号已绑定")

def handlebar(ContextInfo):
    # 仅在最后一根K线(实时行情)执行,避免回测历史数据时重复触发
    if not ContextInfo.is_last_bar():
        return

    # 1. 获取当前账号的所有委托记录
    # 第三个参数 'ORDER' 表示获取委托信息
    order_list = get_trade_detail_data(ContextInfo.accID, ContextInfo.accType, 'ORDER')
    
    # 2. 遍历委托列表
    for order in order_list:
        # 获取委托编号
        order_id = order.m_strOrderSysID
        
        # 3. 判断该委托是否可以撤销
        # 使用平台提供的 can_cancel_order 函数进行判断
        if can_cancel_order(order_id, ContextInfo.accID, ContextInfo.accType):
            
            # 4. 执行撤单
            print(f"发现可撤销订单,委托号: {order_id},标的: {order.m_strInstrumentID}")
            cancel(order_id, ContextInfo.accID, ContextInfo.accType, ContextInfo)
            
        else:
            # 仅用于调试,实际策略中可注释掉
            # print(f"订单 {order_id} 当前状态不可撤销 (状态码: {order.m_nOrderStatus})")
            pass

代码逻辑解析

  1. 获取委托列表:使用 get_trade_detail_data 获取所有的委托对象。每个对象包含该笔委托的详细信息(如委托号、标的、状态等)。
  2. 筛选可撤单委托
    • 虽然可以通过 order.m_nOrderStatus 属性手动判断状态(如 50 已报, 55 部成),但直接使用 QMT 提供的 can_cancel_order 函数更加安全和简便。
  3. 执行撤单:调用 cancel 函数发送撤单指令。
  4. 实盘保护:代码中加入了 if not ContextInfo.is_last_bar(): return,确保撤单逻辑只在最新的实时行情中运行,防止在回测历史数据阶段错误地尝试操作。

常见问题 (Q&A)

Q: 如何只撤销特定股票的订单?
A: 在遍历 order_list 时增加判断条件。例如:

if order.m_strInstrumentID == '600000.SH' and can_cancel_order(order_id, ...):
    cancel(...)

Q: cancel 函数返回值是什么?
A: cancel 函数返回一个布尔值 (bool)。True 表示撤单指令发送成功(不代表撤单一定成功,需等待交易所确认),False 表示发送失败。

Q: 为什么调用了 cancel 但订单没有撤销?
A: 可能原因包括:

  1. 订单已经成交(在查询和撤单的微小时间差内成交)。
  2. 订单处于“待报”或“正报”状态,交易所尚未确认,此时无法立即撤单。
  3. 账号未登录或未在 init 中使用 set_account 绑定。

Q: 如何撤销所有任务(算法交易任务)?
A: 如果是算法交易产生的任务,需要使用 cancel_task(taskId, accountId, accountType, ContextInfo) 函数,而不是普通的 cancel。如果不指定 taskId,则会撤销该账号下所有可撤的任务。