问题描述
smart_algo_passorder 智能算法(TWAP)下单,通过 order_callback 返回的投资备注的值 .m_strRemark 为空值是什么原因?
解决方案
在 QMT 平台中使用 smart_algo_passorder 进行智能算法(如 TWAP、VWAP)下单时,如果在 order_callback 回调中发现委托对象 orderInfo 的投资备注属性 .m_strRemark 为空值,通常是由底层算法引擎的母子单拆单机制导致的。
以下是具体原因分析及相应的解决方案:
一、 为什么 .m_strRemark 会为空?
-
母子单拆单机制未透传备注
智能算法交易在 QMT 底层是以**“任务 (Task)”(即母单)的形式存在的。当你调用smart_algo_passorder时,实际上是向算法引擎提交了一个任务。算法引擎会根据你设定的参数(如 TWAP、量比等),在一段时间内自动将这个任务拆分成多个实际的“委托单 (Order)”(即子单)发送到交易所。
order_callback捕获并推送的是这些子单**的状态变化。在 QMT 的底层实现中,算法引擎在自动生成子单时,通常不会将母单的投资备注(userid/remark)透传给每一个子单,这就导致了你在回调中获取到的子单.m_strRemark为空。 -
参数传递位置错误(需排除)
请检查你的smart_algo_passorder参数顺序是否完全符合官方文档。与普通的passorder不同,智能算法下单的策略名 (strageName) 和 投资备注 (userid) 都是不可缺省的。正确的参数顺序示例:# 注意参数顺序:... volume, strageName, quickTrade, userid(备注), smartAlgoType ... smart_algo_passorder(23, 1101, '资金账号', '000001.SZ', 5, -1, 50000, "我的策略名", 0, "我的投资备注", "TWAP", 20, 0, ContextInfo)如果参数错位,也可能导致备注未能正确记录到母单中。
二、 替代解决方案
既然子单无法直接获取到备注,我们可以通过以下几种方式来绕过这个限制,实现对算法订单的追踪和区分:
方案 1:利用“策略名 (strategyName)”传递信息(推荐)
虽然子单可能不继承备注,但通常会继承策略名。你可以将需要传递的备注信息编码到 strageName 参数中。在 order_callback 中,通过解析策略名来识别订单。
示例代码:
def handlebar(ContextInfo):
# 将备注信息合并到策略名中,例如:"TWAP策略_建仓批次A"
custom_strategy_name = "TWAP_Strategy_Batch_A"
smart_algo_passorder(23, 1101, ContextInfo.accid, '000001.SZ', 5, -1, 50000,
custom_strategy_name, 0, "备注信息", "TWAP", 20, 0, ContextInfo)
def order_callback(ContextInfo, orderInfo):
# 通过 orderInfo.m_strRemark 获取可能为空
# 但可以通过 get_trade_detail_data 获取该订单对应的策略名(如果 QMT 版本支持在回调中直接获取策略名则更好)
print(f"收到委托回调,代码:{orderInfo.m_strInstrumentID}")
(注:具体子单是否完美继承策略名,建议在模拟盘中打印测试确认。)
方案 2:通过“任务 (Task)”状态进行跟踪
既然智能算法本质上是一个任务,你可以放弃在 order_callback 中死磕子单,转而监控任务状态。使用 get_trade_detail_data 获取任务列表,任务对象 (CTaskDetail) 中包含了算法执行的整体进度。
示例代码:
def handlebar(ContextInfo):
# 定期查询任务状态
task_list = get_trade_detail_data(ContextInfo.accid, 'STOCK', 'TASK')
for task in task_list:
# CTaskDetail 对象包含:m_nTaskId, m_eStatus, m_strMsg, m_nBusinessNum(已成交量) 等
print(f"任务ID: {task.m_nTaskId}, 状态: {task.m_eStatus}, 标的: {task.m_stockCode}, 已成交: {task.m_nBusinessNum}")
方案 3:在 Python 内存中自行维护映射表
在调用 smart_algo_passorder 时,在全局字典中记录下当前时间、标的代码、买卖方向和你的自定义备注。当 order_callback 触发时,通过标的代码和方向去全局字典中匹配,从而推断出该子单属于哪个算法任务。
示例代码:
def init(ContextInfo):
ContextInfo.algo_orders_map = {} # 维护映射表
def handlebar(ContextInfo):
stock = '000001.SZ'
remark = "TWAP建仓_001"
# 记录映射
ContextInfo.algo_orders_map[stock] = remark
smart_algo_passorder(23, 1101, ContextInfo.accid, stock, 5, -1, 50000,
"MyAlgo", 0, remark, "TWAP", 20, 0, ContextInfo)
def order_callback(ContextInfo, orderInfo):
stock = orderInfo.m_strInstrumentID
# 从映射表中找回备注
if stock in ContextInfo.algo_orders_map:
inferred_remark = ContextInfo.algo_orders_map[stock]
print(f"推断出该子单 {stock} 属于任务:{inferred_remark}")
总结
smart_algo_passorder 生成的子单 .m_strRemark 为空是底层算法引擎拆单机制导致的正常现象。建议通过将关键信息写入策略名或在本地维护代码与任务的映射关系来解决订单识别的问题。