3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在量化交易中,限价单(Limit Order)常常因为市场价格波动而无法立即成交。为了避免资金被长时间占用或防止错过更好的交易时机,我们通常需要实现超时自动撤单功能。本文将详细介绍如何在国信证券 QMT 极速策略交易系统中,利用 cancel 等相关 API 实现这一功能。
在 QMT 中实现自动撤单,主要依赖以下四个 API 函数:
cancel()cancel(orderId, accountId, accountType, ContextInfo)orderId (string):委托号(合同编号)。accountId (string):资金账号。accountType (string):账号类型,如 'STOCK'(股票)、'FUTURE'(期货)。ContextInfo:策略运行环境对象。True(已发出撤单信号)或 False。get_last_order_id()get_last_order_id(accountID, strAccountType, strDatatype)can_cancel_order()can_cancel_order(orderId, accountID, strAccountType)get_value_by_order_id()get_value_by_order_id(orderId, accountID, strAccountType, strDatatype)m_nVolumeTraded、委托状态 m_nOrderStatus 等属性。passorder 发送限价单,随后立即调用 get_last_order_id 获取该笔订单的委托号,并记录当前的时间戳。handlebar 或定时器中,检查未成交订单的等待时间。can_cancel_order 确认订单可撤,则调用 cancel 函数执行撤单。以下是一个完整的 QMT 超时自动撤单策略模板:
#encoding:gbk
import time
def init(ContextInfo):
# 设定交易账号
ContextInfo.accid = '6000000248'
ContextInfo.set_account(ContextInfo.accid)
# 存储未成交订单信息的字典 { 委托号: 下单时间戳 }
ContextInfo.pending_orders = {}
# 设定超时时间(单位:秒)
ContextInfo.timeout_limit = 60
def handlebar(ContextInfo):
# 仅在最后一根 Bar(最新行情)处理撤单逻辑
if not ContextInfo.is_last_bar():
return
# 示例:满足某种条件时发送限价买入单(此处仅作演示,实际策略中需替换为您的信号)
if ContextInfo.barpos == ContextInfo.time_tick_size - 1 and not ContextInfo.pending_orders:
target_stock = '600000.SH'
limit_price = 7.50 # 假设一个不易立即成交的限价
# 发送限价买入单 (opType=23, orderType=1101)
passorder(23, 1101, ContextInfo.accid, target_stock, 11, limit_price, 100, '超时撤单策略', 1, ContextInfo)
# 立即获取刚刚生成的委托号
order_id = get_last_order_id(ContextInfo.accid, 'STOCK', 'ORDER')
if order_id and order_id != '-1':
# 记录委托号及当前时间戳
ContextInfo.pending_orders[order_id] = time.time()
print(f'已发送限价单,委托号: {order_id},开始计时...')
# --- 轮询检查超时订单 ---
current_time = time.time()
orders_to_remove = []
for order_id, start_time in ContextInfo.pending_orders.items():
# 计算已等待时间
elapsed_time = current_time - start_time
# 获取订单当前状态
order_obj = get_value_by_order_id(order_id, ContextInfo.accid, 'STOCK', 'ORDER')
if order_obj:
# 如果订单已经全部成交 (ENTRUST_STATUS_SUCCEEDED = 56) 或已撤销,则无需再监控
if order_obj.m_nOrderStatus in [54, 56]:
print(f'订单 {order_id} 已结束(已成/已撤),移出监控列表。')
orders_to_remove.append(order_id)
continue
# 若超时且可撤销
if elapsed_time > ContextInfo.timeout_limit:
if can_cancel_order(order_id, ContextInfo.accid, 'STOCK'):
print(f'订单 {order_id} 已等待 {elapsed_time:.1f} 秒,触发超时撤单!')
cancel_result = cancel(order_id, ContextInfo.accid, 'STOCK', ContextInfo)
if cancel_result:
print(f'撤单信号发送成功。')
orders_to_remove.append(order_id)
else:
print(f'订单 {order_id} 超时,但当前状态不可撤销。')
# 移除已处理的订单记录
for order_id in orders_to_remove:
if order_id in ContextInfo.pending_orders:
del ContextInfo.pending_orders[order_id]
cancel、can_cancel_order 等函数主要在实盘/模拟盘运行模式下生效。在历史 K 线回测中,限价单通常在当期 K 线结束时按收盘价撮合,撤单 API 无实际撮合意义。init 中调用 ContextInfo.set_account() 绑定资金账号,否则无法正常接收交易主推和执行撤单。enum_EEntrustStatus 委托状态码(如 54 代表已撤,56 代表已成)。