问题描述
解决方案
在 QMT (Quantitative Trading Platform) 中,要实时获取每一笔委托的最新状态,最推荐且最高效的方式是使用 交易回报实时主推函数 order_callback。
该机制是事件驱动的:当交易所或柜台端的委托状态发生变化(如变为“已报”、“部成”、“已成”、“废单”等)时,系统会自动触发该函数,并将最新的委托信息对象 orderInfo 推送给策略。
以下是具体的实现步骤和代码示例。
核心步骤
- 绑定账号:在
init函数中,必须使用ContextInfo.set_account(accountID)绑定需要接收回报的资金账号。如果不绑定,主推函数将不会生效。 - 定义回调函数:在策略中定义
order_callback(ContextInfo, orderInfo)函数。 - 解析数据:通过
orderInfo对象获取委托号、状态码、成交量等信息。
完整代码示例
# -*- coding: gbk -*-
def init(ContextInfo):
# 1. 设置资金账号
# 请将 '6000000000' 替换为您实际的资金账号
account_id = '6000000000'
ContextInfo.accid = account_id
ContextInfo.set_account(account_id)
print(f"策略初始化完成,已绑定账号: {account_id}")
def handlebar(ContextInfo):
# 策略运行逻辑(此处仅为占位,实际交易逻辑写在这里)
pass
def order_callback(ContextInfo, orderInfo):
"""
委托状态变化主推函数
每当委托状态发生改变(如已报、成交、撤单等),系统自动调用此函数
"""
# 获取委托的关键信息
order_id = orderInfo.m_strOrderSysID # 委托编号
stock_code = orderInfo.m_strInstrumentID # 证券代码
direction = orderInfo.m_nDirection # 买卖方向 (48:买, 49:卖)
status = orderInfo.m_nOrderStatus # 委托状态码
status_msg = orderInfo.m_strStatus # 状态描述信息
traded_vol = orderInfo.m_nVolumeTraded # 已成交数量
total_vol = orderInfo.m_nVolumeTotalOriginal # 原始委托数量
remark = orderInfo.m_strRemark # 投资备注
# 打印调试信息
print("=" * 30)
print(f"【委托更新】 委托号: {order_id}")
print(f"标的: {stock_code}")
print(f"方向: {'买入' if direction == 48 else '卖出'}")
print(f"状态: {status} ({status_msg})")
print(f"进度: {traded_vol} / {total_vol}")
print(f"备注: {remark}")
# 示例:根据状态做进一步处理
# 56 代表 "已成" (ENTRUST_STATUS_SUCCEEDED)
if status == 56:
print(f"--> 委托 {order_id} 全部成交!")
# 54 代表 "已撤" (ENTRUST_STATUS_CANCELED)
elif status == 54:
print(f"--> 委托 {order_id} 已撤单。")
# 57 代表 "废单" (ENTRUST_STATUS_JUNK)
elif status == 57:
print(f"--> 委托 {order_id} 废单,原因: {orderInfo.m_strCancelInfo}")
print("=" * 30)
关键字段说明 (orderInfo 对象)
在 order_callback 中接收到的 orderInfo 对象包含以下常用属性:
| 属性名 | 类型 | 说明 |
|---|---|---|
m_strOrderSysID |
string | 委托编号,用于唯一标识一笔委托。 |
m_strInstrumentID |
string | 证券代码(如 '600000')。 |
m_nOrderStatus |
int | 委托状态码(详见下表)。 |
m_strStatus |
string | 委托状态的中文描述(如“已成”、“已报”)。 |
m_nVolumeTraded |
int | 当前已成交的数量。 |
m_nVolumeTotal |
int | 当前剩余未成交的数量。 |
m_nVolumeTotalOriginal |
int | 原始委托总数量。 |
m_dLimitPrice |
double | 委托价格。 |
m_dTradedPrice |
double | 成交均价。 |
m_strRemark |
string | 投资备注(下单时传入的 userOrderId 或 strategyName)。 |
委托状态码对照表 (m_nOrderStatus)
我们在代码逻辑中通常使用数字状态码来判断,以下是常见的状态码含义:
- 48: 未报 (Unreported)
- 49: 待报 (Wait Reporting)
- 50: 已报 (Reported)
- 51: 已报待撤 (Reported Cancel)
- 52: 部成待撤 (Part Success Cancel)
- 53: 部撤 (Part Cancel) - 部分成交,剩余部分已撤单
- 54: 已撤 (Canceled) - 完全未成交并已撤单
- 55: 部成 (Part Success) - 部分成交,剩余部分仍在挂单
- 56: 已成 (Succeeded) - 全部成交
- 57: 废单 (Junk) - 委托被柜台或交易所拒绝
注意事项
- 实盘模式:
order_callback仅在实盘或模拟交易模式下生效,回测模式下通常不会触发此回调(回测主要依赖handlebar的撮合机制)。 - 非阻塞:回调函数是由底层 C++ 线程推送的,尽量不要在回调函数中执行耗时过长的操作(如复杂的循环计算或网络请求),以免阻塞后续的行情或交易推送。
- 数据一致性:如果需要查询历史委托状态或在
handlebar中主动查询,可以使用get_trade_detail_data(accountID, accountType, 'ORDER')或get_value_by_order_id。
Q&A
Q: 为什么我写了 order_callback 但是没有收到任何打印信息?
A: 请检查以下两点:
- 确保在
init函数中正确调用了ContextInfo.set_account(account_id),且账号 ID 与实盘登录的账号一致。 - 确保当前策略处于“实盘运行”或“模拟交易”状态,且确实有委托产生(下单成功)。
Q: 如何判断一笔委托是“废单”?
A: 检查 orderInfo.m_nOrderStatus 是否等于 57。如果是废单,可以通过 orderInfo.m_strCancelInfo 获取具体的废单原因(如“资金不足”、“非交易时间”等)。
Q: order_callback 会被调用多少次?
A: 一笔委托的生命周期中,状态每发生一次变化,该函数就会被调用一次。例如,一笔委托从“已报”变为“部成”,再变为“已成”,order_callback 至少会被调用 3 次。