问题描述
比如如下的代码,回测下单后,可以读取到股票代码,但读取证券名称和投资备注时,返回却是空字符串。
def handlebar(C):
msg = f"投资备注字符串 用来区分不同委托"
passorder(23, 1101, g.account, '600000.SH', 5, -1, 100, '测试下单', 2, msg, C)
passorder(23, 1101, g.account, '600000.SH', 5, -1, 200, '测试下单', 2, msg, C)
orders=get_trade_detail_data(g.account,'STOCK','ORDER')
print('查询委托结果:')
for o in orders:
print(f'股票代码:{o.m_strInstrumentID}',
f'证券名称: {o.m_strInstrumentName}',
f'投资备注: {o.m_strRemark}')
解决方案
在 QMT 平台中,出现回测时 Order 对象的 m_strInstrumentName(证券名称)和 m_strRemark(投资备注)为空字符串的现象,是由 QMT 的底层回测机制 决定的。
核心原因分析
- 回测引擎的性能优化:在回测模式下,QMT 使用的是本地的虚拟撮合引擎。为了极大提升历史数据的回测速度并降低内存消耗,虚拟撮合引擎只记录计算策略净值、盈亏所必需的核心字段(如:证券代码
m_strInstrumentID、委托价格、委托数量、买卖方向、成交状态等)。 - 非核心字段被忽略:像
m_strInstrumentName(中文名称)和m_strRemark(投资备注)这类描述性、非计算性的字段,在回测的虚拟订单生成时会被省略,因此读取时返回的是空字符串。 - 实盘与回测的差异:如果您将策略切换到实盘模式或模拟交易模式(连接了真实的交易柜台或模拟柜台),柜台在接收到
passorder传过去的userOrderId(即投资备注)后,会完整地生成包含名称和备注的委托回报,此时您再调用get_trade_detail_data就能正常读取到这些字段了。
回测中的替代解决方案
既然回测引擎不保存这些字段,我们可以通过 Python 代码在策略层面自行维护和获取这些信息:
1. 获取证券名称
不要依赖 Order 对象自带的名称属性,而是直接使用 QMT 提供的 API ContextInfo.get_stock_name() 动态获取:
stock_code = o.m_strInstrumentID
stock_name = ContextInfo.get_stock_name(stock_code)
2. 记录和区分投资备注
如果您的策略强依赖“投资备注”来区分不同的委托逻辑,可以在 ContextInfo 中维护一个全局字典,利用委托号 (m_strOrderSysID) 或 代码+时间 作为映射的 Key,自行记录备注信息。
改进后的代码示例:
def init(C):
# 初始化一个字典,用于在回测中记录自定义备注
C.my_order_remarks = {}
def handlebar(C):
msg1 = "测试下单_100股"
msg2 = "测试下单_200股"
# 下单
passorder(23, 1101, g.account, '600000.SH', 5, -1, 100, '测试下单', 2, msg1, C)
# 获取最新委托号并记录备注
order_id_1 = get_last_order_id(g.account, 'STOCK', 'ORDER')
if order_id_1 != '-1':
C.my_order_remarks[order_id_1] = msg1
passorder(23, 1101, g.account, '600000.SH', 5, -1, 200, '测试下单', 2, msg2, C)
order_id_2 = get_last_order_id(g.account, 'STOCK', 'ORDER')
if order_id_2 != '-1':
C.my_order_remarks[order_id_2] = msg2
# 查询委托结果
orders = get_trade_detail_data(g.account, 'STOCK', 'ORDER')
print('查询委托结果:')
for o in orders:
stock_code = o.m_strInstrumentID
# 替代方案 1:动态获取股票名称
stock_name = C.get_stock_name(stock_code)
# 替代方案 2:从自定义字典中获取备注,实盘中可以直接用 o.m_strRemark
remark = C.my_order_remarks.get(o.m_strOrderSysID, o.m_strRemark)
print(f'委托号:{o.m_strOrderSysID}',
f'股票代码:{stock_code}',
f'证券名称:{stock_name}',
f'投资备注:{remark}')
总结
- 回测时:
m_strInstrumentName和m_strRemark为空是正常现象(引擎优化所致)。 - 实盘/模拟时:这两个字段会有正常的值。
- 建议:在编写兼容回测和实盘的策略时,尽量使用
ContextInfo.get_stock_name()获取名称,并通过全局变量辅助记录回测时的订单状态。