问题描述
解决方案
在 QMT 量化交易平台中,撤销未成交订单主要通过调用 cancel() 函数来实现。为了成功撤单,通常需要先获取当前的委托列表,找到未成交(或部分成交)的订单编号(Order ID),然后针对该编号执行撤单操作。
以下是具体的实现步骤和代码示例。
核心函数说明
-
cancel(orderId, accountId, accountType, ContextInfo)- 功能:取消指定的委托。
- 参数:
orderId:委托编号(字符串),通常通过get_trade_detail_data获取到的对象属性m_strOrderSysID获得。accountId:资金账号。accountType:账号类型(如'STOCK','FUTURE'等)。ContextInfo:策略全局对象。
-
can_cancel_order(orderId, accountId, accountType)- 功能:查询委托是否处于可撤销状态(如已报、部成等)。
- 返回:
True(可撤) 或False(不可撤)。
-
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
代码逻辑解析
- 获取委托列表:使用
get_trade_detail_data获取所有的委托对象。每个对象包含该笔委托的详细信息(如委托号、标的、状态等)。 - 筛选可撤单委托:
- 虽然可以通过
order.m_nOrderStatus属性手动判断状态(如50已报,55部成),但直接使用 QMT 提供的can_cancel_order函数更加安全和简便。
- 虽然可以通过
- 执行撤单:调用
cancel函数发送撤单指令。 - 实盘保护:代码中加入了
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: 可能原因包括:
- 订单已经成交(在查询和撤单的微小时间差内成交)。
- 订单处于“待报”或“正报”状态,交易所尚未确认,此时无法立即撤单。
- 账号未登录或未在
init中使用set_account绑定。
Q: 如何撤销所有任务(算法交易任务)?
A: 如果是算法交易产生的任务,需要使用 cancel_task(taskId, accountId, accountType, ContextInfo) 函数,而不是普通的 cancel。如果不指定 taskId,则会撤销该账号下所有可撤的任务。