问题描述
vba 的下单撤单
PASSORDER(OPTYPE, ORDERTYPE, ACCOUNTID, ORDERCODE, PRTYPE, PRICE, VOLUME, QUICKTRADE, 'strategyName1')
AA:=ORDERINGS(ACCOUNTID,'strategyName2');
FOR ORD IN AA DO CANCEL(ORD.ORDERSYSID,ACCOUNTID,'STOCK');
PASSORDER 指定 strategyName1 策略下单后,查询 strategyName2 策略的委托信息进行撤单,为什么会把 strategyName1 的委托单(实际上是所有的委托单)撤销呢
指定撤单是交易最基本的环节,vba 使用的人虽然不多(基本都是通达信用户),也是迅投的一大特色(其他平台没有 vba,通达信竟然用的是 py,没用最有优势的 vba),但知识库很久没更新了,论坛上的 vba 问题回复讨论的也很少,希望迅投这块可以做好吧,有工作人员可以处理这 bug
解决方案
问题现象分析
在 QMT 平台中使用 VBA 编写策略时,开发者通常希望通过 strategyName(策略名称)来隔离不同策略的委托单,避免互相干扰。您遇到的问题是:
PASSORDER(OPTYPE, ORDERTYPE, ACCOUNTID, ORDERCODE, PRTYPE, PRICE, VOLUME, QUICKTRADE, 'strategyName1')
AA:=ORDERINGS(ACCOUNTID,'strategyName2');
FOR ORD IN AA DO CANCEL(ORD.ORDERSYSID,ACCOUNTID,'STOCK');
预期 AA 只包含 strategyName2 的委托,但实际上 ORDERINGS 返回了该账号下的所有委托单(包括 strategyName1 的单子),导致执行了无差别的全局撤单。这确实是 VBA 接口在特定版本中存在的一个已知过滤失效问题(或底层参数传递未严格匹配)。
为什么会出现这种情况?
- 本地标识局限性:在 QMT 底层,
strategyName是一个纯本地客户端的标识(不会发送到柜台),主要用于本地界面的区分和 API 的二次过滤。 - VBA 接口封装遗漏:VBA 的
ORDERINGS函数在底层调用 C++ 接口时,可能未能正确将第二个参数(策略名)作为严格的过滤条件,导致退化为查询该账号下的所有未报/待撤委托。
解决方案与建议
方案一:VBA 下的临时替代方案(手动过滤)
既然 ORDERINGS 无法自动过滤,我们可以在 FOR 循环中手动增加一层判断,检查订单的备注或策略标识(通常映射在订单对象的特定属性中,如 REMARK 或自定义字段),只有匹配时才执行撤单:
// 假设订单对象中有 REMARK 或 STRATEGYNAME 属性(具体视 QMT VBA 对象字典而定)
AA := ORDERINGS(ACCOUNTID, ''); // 直接获取所有
FOR ORD IN AA DO
BEGIN
// 手动判断策略名是否匹配
IF ORD.REMARK = 'strategyName2' THEN
CANCEL(ORD.ORDERSYSID, ACCOUNTID, 'STOCK');
END;
(注:具体属性名称请参考 QMT VBA 的内置对象说明,若 VBA 无法读取该属性,则此方法受限。)
方案二:迁移至 Python API(官方主推,完美支持)
正如您所说,QMT 目前的核心优势和官方维护重心都在 Python API 上。Python 接口对 strategyName 的支持非常完善,且文档更新及时。如果您希望实现精细化的订单管理,强烈建议使用 Python。
在 Python 中,实现您上述逻辑的代码如下,绝对不会出现误撤单:
#encoding:gbk
def init(ContextInfo):
ContextInfo.set_account('您的资金账号')
def handlebar(ContextInfo):
account = '您的资金账号'
# 1. 使用 strategyName1 下单
# passorder(opType, orderType, accountid, orderCode, prType, price, volume, strategyName, quickTrade, userOrderId, ContextInfo)
passorder(23, 1101, account, '000001.SZ', 5, -1, 100, 'strategyName1', 1, 'order_1', ContextInfo)
# 2. 精确获取 strategyName2 的委托单并撤单
# get_trade_detail_data(accountID, strAccountType, strDatatype, strategyName)
orders_strategy2 = get_trade_detail_data(account, 'stock', 'order', 'strategyName2')
for ord_obj in orders_strategy2:
# 仅对 strategyName2 的未成委托进行撤单
if ord_obj.m_nOrderStatus in [48, 49, 50, 55]: # 未报、待报、已报、部成等状态
cancel(ord_obj.m_strOrderSysID, account, 'stock', ContextInfo)
print(f"成功撤销 strategyName2 的委托: {ord_obj.m_strOrderSysID}")
总结
QMT 的 VBA 确实是通达信用户的福音,但在复杂交易逻辑(如多策略并发、精细化订单管理)上,VBA 的底层维护频率已不如 Python。建议您:
- 向迅投官方客服或客户经理提交该 VBA
ORDERINGS函数的 BUG 反馈,附上复现代码。 - 逐步将核心的自动化交易逻辑向 Python 迁移,Python 的
get_trade_detail_data配合strategyName参数可以完美解决您的痛点。