问题描述
我用的券商版 miniqmt 模拟环境,想在委托失败的回调中查询并重置该笔失败委托占用的资金,我这里测试没反应
解决方案
在 QMT 或 miniqmt (xtquant) 的委托失败回调(如 on_order_error 或 orderError_callback)中,理论上是可以调用查询接口的,但强烈不建议直接在回调函数中进行同步的资产和持仓查询。这通常是你测试“没反应”或卡死的主要原因。
为什么在回调中查询会“没反应”?
- 线程阻塞与死锁:miniqmt (xtquant) 的回调函数通常运行在底层的 C++ 回调线程中。如果你在这个线程里调用同步的查询接口(如
query_stock_asset),可能会阻塞该线程,甚至与底层通信机制产生死锁,导致程序无响应。 - 查询频率限制:券商柜台对资产和持仓的查询有严格的频率限制(通常建议间隔 2-3 秒以上)。如果在短时间内产生大量失败委托,触发密集查询,会被柜台拒绝或导致连接异常。
- 数据延迟:委托失败后,柜台的资金解冻可能存在微小的延迟,立即查询可能获取不到最新的可用资金。
正确的解决姿势:异步处理(生产者-消费者模式)
为了避免阻塞回调线程,最佳实践是:在回调函数中仅记录失败的委托信息(放入队列),然后在主线程或专门的业务线程中消费这些信息,并进行资产查询和状态重置。
基于 miniqmt (xtquant) 的异步处理示例:
import time
import threading
import queue
from xtquant import xtdata
from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
from xtquant.xttype import StockAccount
# 创建一个线程安全的队列,用于存放失败的委托
error_order_queue = queue.Queue()
class MyTraderCallback(XtQuantTraderCallback):
def on_order_error(self, error):
# 回调中只做最简单的操作:将错误信息放入队列
print(f"[回调] 收到委托失败回报: {error.order_remark}, 错误信息: {error.error_msg}")
error_order_queue.put(error)
def process_error_orders(trader, account):
"""
专门处理失败委托的线程函数
"""
while True:
try:
# 阻塞等待队列中的错误委托,超时时间1秒
error_order = error_order_queue.get(timeout=1.0)
print(f"[处理线程] 开始处理失败委托: {error_order.order_sysid}")
# 稍微等待一下,确保柜台资金已解冻
time.sleep(0.5)
# 在这里进行资产查询
asset = trader.query_stock_asset(account)
if asset:
print(f"[处理线程] 当前可用资金: {asset.cash}")
# 在这里执行你的资金重置或重新下单逻辑
# reset_my_strategy_fund(error_order, asset.cash)
except queue.Empty:
continue
except Exception as e:
print(f"[处理线程] 处理异常: {e}")
if __name__ == '__main__':
# 初始化 trader 和 account (此处省略具体的初始化路径和账号)
path = r'D:\国信证券QMT交易端\userdata_mini'
session_id = int(time.time())
trader = XtQuantTrader(path, session_id)
account = StockAccount('你的资金账号')
callback = MyTraderCallback()
trader.register_callback(callback)
trader.start()
trader.connect()
trader.subscribe(account)
# 启动专门处理错误委托的后台线程
processor_thread = threading.Thread(target=process_error_orders, args=(trader, account))
processor_thread.daemon = True
processor_thread.start()
# 保持主线程运行
while True:
time.sleep(1)
总结
- 不要在
on_order_error或orderError_callback中直接调用query_stock_asset或get_trade_detail_data。 - 应该使用
queue.Queue将回调数据传递给主线程或独立的工作线程。 - 注意查询频率,避免因频繁查询导致账号被限制。