问题描述
解决方案
在 QMT 平台中,交易下单是一个异步过程。要捕获下单失败或委托异常的情况,不能仅依赖下单函数的返回值,而必须使用 交易回报回调函数。
主要有两个核心回调函数用于处理错误:
orderError_callback:专门用于捕获下单时的直接错误(如参数错误、资金不足被柜台直接拒绝等)。order_callback:用于监控委托状态的变化(例如委托变为“废单”状态)。
核心步骤
- 初始化账号:在
init中必须调用set_account,否则无法接收任何回调。 - 实现
orderError_callback:获取具体的错误信息errMsg。 - 实现
order_callback:检查m_nOrderStatus是否为废单状态(通常为 57),并读取m_strCancelInfo(废单原因)。
策略代码实现
以下是一个完整的示例代码,展示了如何捕获和处理下单错误:
# -*- coding: gbk -*-
def init(ContextInfo):
# 1. 设置资金账号 (必须步骤)
# 请将 '6000000000' 替换为您真实的资金账号
ContextInfo.accID = '6000000000'
ContextInfo.set_account(ContextInfo.accID)
print("策略初始化完成,已绑定账号接收回报")
def handlebar(ContextInfo):
# 示例:在最后一根K线尝试下单
if not ContextInfo.is_last_bar():
return
# 这里模拟一个下单操作
# 实际策略中,这里是您的交易逻辑
# order_shares(stock_code, amount, style, price, ContextInfo, accountID)
# 下面这行代码仅作演示,运行时请确保有对应的行情数据
# order_shares('600000.SH', 100, 'fix', 10.0, ContextInfo, ContextInfo.accID)
pass
# ==============================================================================
# 错误处理核心函数 1:orderError_callback
# ==============================================================================
def orderError_callback(ContextInfo, orderArgs, errMsg):
"""
当账号下单出现直接异常时(如参数错误、未登录、柜台拒单),触发此函数
"""
print("=" * 30)
print("【警告】捕获到下单异常!")
print(f"错误信息 (errMsg): {errMsg}")
# 解析下单参数 (orderArgs 是一个对象,包含下单时的参数)
# 注意:orderArgs 的具体属性取决于具体的下单接口,通常包含 code, price 等
# 这里可以打印出来辅助调试
print(f"涉及账号: {ContextInfo.accID}")
# --- 错误处理逻辑 ---
# 1. 记录日志
# 2. 停止策略运行 (可选)
# 3. 发送报警通知 (可选)
print("=" * 30)
# ==============================================================================
# 错误处理核心函数 2:order_callback
# ==============================================================================
def order_callback(ContextInfo, orderInfo):
"""
当委托状态发生变化时触发(如:已报、已成、废单、已撤)
"""
# 获取委托状态
status = orderInfo.m_nOrderStatus
# 状态码 57 代表 "废单" (ENTRUST_STATUS_JUNK)
# 状态码 54 代表 "已撤" (ENTRUST_STATUS_CANCELED)
# 状态码 52 代表 "拒单/部成待撤" (视柜台而定)
if status == 57:
print("=" * 30)
print(f"【警告】委托变为废单!委托号: {orderInfo.m_strOrderSysID}")
print(f"证券代码: {orderInfo.m_strInstrumentID}")
print(f"废单原因: {orderInfo.m_strCancelInfo}") # 关键:获取废单的具体原因
# --- 废单处理逻辑 ---
# 例如:如果是价格超出涨跌停,可以尝试重新计算价格下单
# 例如:如果是额度不足,停止后续买入
print("=" * 30)
elif status == 52:
print(f"【提示】委托被拒绝或部成待撤: {orderInfo.m_strCancelInfo}")
代码详解
-
ContextInfo.set_account(ContextInfo.accID):- 这是最关键的一步。如果不调用此函数,QMT 认为该策略不需要接收交易回报,因此
orderError_callback和order_callback永远不会被触发。
- 这是最关键的一步。如果不调用此函数,QMT 认为该策略不需要接收交易回报,因此
-
orderError_callback(ContextInfo, orderArgs, errMsg):- 触发时机: 当下单指令发送那一刻,如果系统检测到错误(例如:账号未登录、资金账号格式错误、下单参数非法),会立即回调此函数。
errMsg: 包含具体的错误描述字符串,例如 "账号未登录" 或 "资金不足"。
-
order_callback(ContextInfo, orderInfo):- 触发时机: 委托发送到交易所或柜台后,状态发生变更时。
orderInfo对象: 包含了委托的详细信息。m_nOrderStatus: 这是一个整数,代表状态。57 是最常见的“废单”状态(Junk)。m_strCancelInfo: 如果是废单,这个字段通常包含交易所或柜台返回的具体拒绝原因(例如 "价格超出涨跌停限制"、"没有持仓" 等)。
常见状态码参考 (m_nOrderStatus)
- 48: 未报
- 50: 已报
- 56: 已成
- 54: 已撤
- 57: 废单 (重点关注)
通过结合这两个回调函数,您可以覆盖绝大多数下单失败的场景,并根据 errMsg 或 m_strCancelInfo 的内容编写具体的重试或风控逻辑。
Q&A
Q: 为什么我写了 orderError_callback 却从来没有触发过?
A: 请检查 init 函数中是否调用了 ContextInfo.set_account()。如果没有绑定账号,策略无法接收任何交易回报。另外,确保您的运行模式是“实盘”或“模拟交易”,回测模式下对某些错误的模拟可能不完全。
Q: orderError_callback 和 order_callback 有什么区别?
A: orderError_callback 通常捕获的是下单请求发出时的错误(比如API调用参数不对,或者本地风控拦截)。order_callback 捕获的是委托产生后的状态变化(比如委托发到了交易所,但因为价格超限被交易所退回,变成了废单)。
Q: 如何在捕获错误后自动重单?
A: 在回调函数中,您可以根据错误信息判断是否需要重单。如果需要,可以再次调用 passorder 或 order_shares 等函数。但请务必设置一个计数器或标志位,防止因为持续错误导致死循环无限下单(例如资金不足时无限重试)。