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=2 表示同步等待下单结果,会阻塞直到返回。如果对性能要求较高,可设为 0(异步)或 1(异步快速),但异步模式下委托号需通过 order_callback 获取。
安全注意事项
真实委托开关默认关闭:
ENABLE_REAL_ORDER默认为False,不会发送任何委托。请先在仿真交易环境中验证逻辑正确后再设为True。委托号获取时序:
passorder发送委托后,order_callback可能在下一个handlebar之前或之后触发。代码中通过check_order_finished函数查询委托状态作为兜底,即使回调未及时到达也能正确判断。状态码兼容性:不同客户端版本的委托状态码可能存在差异。使用前请在仿真交易环境中验证状态码的含义,以当前客户端版本为准。
异常处理:生产环境中应在
passorder调用和get_trade_detail_data查询周围添加try-except块,防止异常导致策略中断。资金与持仓检查:下单前应检查可用资金是否充足、可卖持仓是否充足,本示例为简化演示未包含完整的前置检查。
真实委托代码默认关闭说明
本示例中所有 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的触发时机和频率取决于客户端版本和网络状况,以当前客户端版本为准。- 本示例未实现撤单逻辑。如果委托长时间未成交,应考虑添加超时撤单机制。
passorder的prType=0在不同客户端版本中的行为可能略有差异,以当前客户端版本为准。- 在真实交易环境中使用时,建议添加更完善的资金检查、涨跌停检查和异常处理逻辑。