从MiniQMT换到大QMT,再把桥接层从零搭起来,这条路我走了将近两年。期间试过各种方案,也踩过无数坑,今天这篇就把我最真实的横评结果写出来:四种主流的大QMT桥接方案到底各自适合谁,为什么最后我All In了HTTP API。
先交代一下背景。我做的是A股日内策略,订单频率不算极端,但行情订阅、条件触发、自动拆单、回撤风控这些环节一个都不能少。最早图省事直接用MiniQMT,xtquant一套下来确实简单,后来实盘规模上来,发现它的边界很明显,于是切到完整版QMT,结果发现大QMT自带的东西也没好到哪去,反而多了一层"怎么把策略和终端对接起来"的问题。这才有了做桥接的折腾。下面这篇文字,适合手里已经有QMT环境、正在纠结桥接选型的人看,也适合刚入门、想一次选对技术路线的人参考。
1. 为什么我要从MiniQMT迁移到完整版QMT
1.1 MiniQMT的便利与天花板
MiniQMT的最大魅力就是轻。装个xtquant,Python里直接from xtquant import xttrader就能连上券商终端,下单、撤单、查持仓,几乎零成本上手。它对散户真的太友好了,不需要理解复杂的通信协议,也不需要处理终端进程管理,甚至连客户端都不用保持在最前台。我在早期实盘里就是靠这套东西快速跑通了第一版策略,从写代码到真实下单,前后不到三天。
但它的天花板也很明显。首先是进程依赖问题,xtquant本质上是跟QMT终端建立一个本地通信通道,终端一旦卡死、断线或者被系统睡眠打断,整个策略就跟着瘫痪。我遇到过几次深夜挂机后第二天一看,数据流早就断了,订单状态还卡在"已报"没回报,这种体验非常要命。其次是对多账户和并发支持弱,MiniQMT在你只有一个资金账号、策略逻辑简单的时候很舒服,可一旦涉及多策略并行、多账号轮询、动态风控拦截这类需求,它的接口抽象层级就有点不够用了,很多逻辑你得自己在外面包一层状态机,代码越写越绕。
1.2 促使我迁移的几个刺痛点
真正让我决定切完整版QMT的,是三个具体问题。
第一个是接口文档和版本不一致。xtquant不同版本之间函数签名存在差异,有次券商升级终端后,我这边xt_trader的连接参数直接失效,排查了一整个晚上,最后发现是内部协议版本对不上。第二个是策略与终端强耦合,MiniQMT的行情回调、订单回报全部依赖那个本地进程活着,而QMT终端本身是GUI程序,你没法保证它在无人值守环境下永不弹窗、永不卡顿,事实上它真的会。第三个是我想在策略里接入更多数据源和风控逻辑,MiniQMT那套接口虽然能用,但扩展性有限,想自己加一层缓存、加一个权限控制、对订单做二次校验,都会受限于它既有的调用方式,感觉像是在一个不够大的房间里硬塞家具,怎么摆都不顺。
切到完整版QMT之后,我确实拿到了更强的终端能力和稳定的行情源,但也立刻撞上一个新问题:完整版QMT没有像xtquant那样轻量的Python接口。它没有直连SDK,你只能用它的内置Python环境跑策略,或者靠各种桥接手段从外部去操作它。于是就有了下面这四种方案的大横评。
2. 四种主流桥接方案全景对比
2.1 方案一:MiniQMT官方xtquant直连(基线方案)
严格来说,第一个方案不算"大QMT桥接",而是MiniQMT本身。但我把它放在对比里,是因为很多人坚持用MiniQMT的原因就是"官方支持好",实际上完整版QMT和MiniQMT在很多券商那边是同一个终端的两种模式,核心逻辑也没变。
xtquant直连的优势在于简单、官方维护、有现成Python生态,适合快速验证想法、低频率交易、个人单账户策略。它的局限在于进程生命周期绑死终端,对并发、多账户、自定义风控支持不足。我在实际使用中还发现它有一个隐蔽缺点:行情数据在极端行情下会有延迟,尤其在开盘瞬间tick洪峰时,xtquant回调堆积会导致策略反应滞后,这对于抢反弹、打板这类对时效要求高的策略是不小的隐患。
所以我的结论是:MiniQMT不是不好,它是"够用但不够深"。如果你只是做低频、单账户、单策略,MiniQMT完全够用;但如果你要往中高频、多策略、精细化风控方向走,它那层壳就遮不住需求了。
2.2 方案二:QMT客户端内嵌Python策略脚本
完整版QMT自带了Python运行环境,你可以把策略脚本放进它的策略编辑器里,让它跑在终端的托管模式下。这个方案好处是不用管进程连接问题,因为策略跟终端同生共死,写起来最省事,券商客服也会告诉你"建议这么用"。
但它的缺点也很致命。首先是生态封闭,自带Python环境版本老旧,缺少大量第三方库,你想装个pandas、numpy新版本都得想办法,更别提requests、websocket这些网络库了,装起来麻烦不说,还经常跟内置模块冲突。其次是策略更新麻烦,每次修改策略逻辑都要在终端GUI里重新加载,没法做脚本热更新,更没法方便地做单元测试。
我见过不少人在这个模式里写复杂策略,最终代码全堆在一个Python文件里,回调函数嵌套回调函数,出了bug不好定位,想加日志分析也很难受。这个方案适合策略简单、不需要太多外部依赖、愿意接受终端托管模式的人。但对我而言,它最大的问题是限制了我的工具箱,我外面有那么多现成的库和框架,不能因为一个桥接方案就全部放弃。
2.3 方案三:VBA/COM外挂控制方案
这个方案属于"老股民智慧"。QMT终端是Windows下的GUI程序,基于某些组件技术,理论上你可以用VBA或者通过COM接口去操作它的窗口、按钮、列表控件,实现"模拟人工操作"的效果。
实际操作中,这种方案通常是通过Windows的UI自动化框架或者COM接口去点击下单按钮、读取持仓列表。它的优势是不依赖官方接口,对终端版本不敏感,哪怕官方改了内部协议,只要界面没变,你就能继续用。另外一个好处是,它操作的是"真实用户操作路径",所以在合规性上跟人工操作没有本质区别,不会触发一些风控限制。
但它的劣势太明显了:性能差、不稳、难维护。UI自动化本质上是模拟人的操作,不管是定位控件、发送点击消息还是读取表格数据,延迟都在数百毫秒到秒级之间,根本没法应对行情剧烈波动时的快速下单需求。而且它非常脆弱,终端界面上一个弹窗、一个按钮位置变化,都可能让你的桥接脚本失效。我在测试阶段就遇到过一个低版本终端和高版本终端控件名不同的问题,排查起来非常消耗精力。它更适合做"兜底工具",比如手动下单辅助、监控告警这类场景,而不是量化策略的主通道。
2.4 方案四:自建HTTP API桥接服务(最终选择)
这个方案的核心思路是:在QMT终端所在的Windows机器上跑一个本地服务,这个服务负责跟QMT终端通信(通常是走QMT内置的接口或者文件监听方式),对外暴露HTTP API,策略代码通过HTTP请求来下单、查持仓、订阅行情。相当于在QMT外面包了一层"翻译官"。
这样做的好处非常明显:
- 策略语言不再受限,你外面可以用Python、C++、Go、Node.js,随便什么技术栈,只要会发HTTP请求就行。
- 策略跟终端解耦,终端崩溃了我可以检测、可以重启,策略本身不会跟着挂。
- 容易做风控和审计,所有请求都经过HTTP层,你可以在这一层记录日志、做限额校验、速率控制、敏感操作审批。
- 部署灵活,行情订阅和交易接口可以分离开来,多个策略服务共享同一个交易通道。
我在实际落地时,把QMT终端装在Windows服务器上,用Python写了一个FastAPI服务,内部用xtquant连接MiniQMT模式(因为完整版QMT在MiniQMT模式下也能跑),对外暴露一套RESTful API。策略代码跑在另一台Linux服务器上,通过局域网调用这套API。这个方案既保留了MiniQMT官方接口的稳定性,又绕开了"策略和终端绑死"的困局,还能把策略层和交易层彻底分离。这也是为什么标题里我说"All In HTTP API"——它不是简单换一个库,而是整个架构思路的转变。
3. HTTP API方案的架构设计与落地细节
3.1 整体架构与请求链路
我的最终架构分为三层。最底层是Windows上运行的QMT终端,负责跟券商柜台通信;中间层是一台Windows服务器上的桥接服务(Python FastAPI),负责跟终端通信并对外提供HTTP接口;最上层是Linux上跑的策略服务,负责处理行情、生成信号、通过HTTP调用桥接服务下单。
这里有个关键点:桥接服务如何跟QMT通信。我采用的是MiniQMT模式下的xtquant接口,也就是在Windows服务器上启动一个Python进程,用xtquant连接QMT客户端,这个进程常驻内存,持有XtQuantTrader实例,同时启动一个HTTP服务。外部策略发来下单请求,HTTP服务接受后,把参数转换成xtquant的OrderStock结构体,调用交易接口,再把结果封装成JSON返回。
这样做的好处是内部还用官方接口,稳定有保障;外部的是标准HTTP协议,通用性强。坏处是需要处理两个进程之间的生命周期同步,以及终端异常时的自动重启逻辑。具体我用了一个看门狗线程,定时检查QMT终端进程和xtquant连接状态,发现异常就执行重启脚本,同时把状态上报给策略端,策略端这时候会自动切换到"只读模式",暂停开新仓但允许撤单,避免失控。
3.2 交易接口设计:从下单到回报的回环
HTTP API设计是整个桥接层好用的关键。我一开始偷懒,只暴露了/order、/cancel、/position几个基础接口,后来实盘发现远远不够。下面是我最终沉淀下来的接口清单和设计思路。
下单接口是最核心的。POST /api/order,参数包括:账户ID、股票代码、方向(buy/sell)、价格类型(限价/市价)、价格、数量、策略ID、订单备注。策略ID我会拿来作为幂等键,同一个策略对同一只股票的重复下单请求在10秒内会被幂等拦截,防止网络重试导致重复下单。这一点非常重要,HTTP请求天然可能超时重发,如果没有幂等保护,一次下单可能变成两笔甚至三笔。
撤单接口DELETE /api/order/{order_id},逻辑上用xtquant的cancel_order_stock。注意要处理好"订单可能已经成交完毕"的情况,此时xtquant会返回"无法撤单",桥接服务要把这个错误映射成HTTP的409 Conflict,而不是笼统地返回500,这样策略端才能做正确判断。
持仓查询GET /api/position/{account_id},这是一个高频调用接口,我加了10秒缓存,因为日内策略经常查持仓,但其实并不需要每次都穿透到终端。资金查询GET /api/asset/{account_id}同理,也做了缓存和过期时间。
回报这块我用了两种方式。一种是主动拉取,提供GET /api/order/list?status=partial接口,策略轮询未完成订单。另一种是主动推送,桥接服务建立WebSocket通道,xtquant的订单回报回调会被实时推送到策略端。实盘下来,主动轮询对低频策略足够,WebSocket推送对中高频策略更稳。不过WebSocket增加了复杂度,网络闪断还涉及重连和补拉历史回报,我建议初版先做轮询,跑通后再增加WebSocket。
3.3 行情订阅与数据对齐
行情是量化策略的血液。HTTP API方案里,行情这块有两种处理方式。
第一种是由桥接层订阅行情,把tick数据推送给策略端。这适合策略对行情实时性要求高的场景。我用xtquant的subscribe_quote接口订阅了自选股列表中的股票,收到tick回调后,通过WebSocket推送给策略服务。这里需要注意,行情推送的频率非常高,如果策略端处理不过来,会导致数据堆积和内存上涨。我的处理是加了一层有界队列,队列满了就丢弃旧数据,只保留最新tick,保证策略拿到的永远是当前快照,而不是堆积的历史。
第二种方式是策略端直接在Linux上订阅其他数据源的行情,桥接层只负责交易。这种方式更灵活,数据质量也更容易把控,但需要解决"策略看到的行情"和"QMT终端看到的行情"之间的对齐问题。比如策略从数据源A判断涨停价是10.01元,但QMT终端计算出来是10.00元,你按10.01元下市价单就可能出意外。我的做法是每次下单前,先通过桥接层获取当前交易状态和昨收价,跟策略端数据源的快照做一次一致性校验,偏差超过阈值就拒绝下单并告警。
3.4 关键参数与调优:超时、重试、并发
HTTP API桥接的关键工程参数,我用一个实际表格来说明:
| 参数 | 我的取值 | 说明 |
|---|---|---|
| 下单HTTP超时 | 5秒 | 超过5秒直接判断异常,避免策略线程卡死 |
| 查询HTTP超时 | 3秒 | 查询类接口普遍较快,超时短一点 |
| 下单重试次数 | 0次 | 不允许自动重试,靠幂等键和人工确认 |
| 查询重试次数 | 2次 | 查询可重试,但间隔至少1秒 |
| 同日单账户并发下单数 | 最多5笔/秒 | 超过就排队,防止柜台限流 |
| WebSocket心跳间隔 | 30秒 | 定期ping,断线及时感知 |
| 终端看门狗检查间隔 | 5秒 | 检查进程和连接状态 |
超时设置这块,我踩过一个大坑。最初我把下单超时设成30秒,想着"时间长一点总比断掉好",结果遇到一次终端假死,xtquant的调用一直没有返回,整个HTTP线程池被订单请求占满,其他策略的查询也跟着被堵死。后来改成5秒超时,配合线程池隔离,虽然有时会出现"请求超时但实际订单已提交"的情况,但因为做了幂等保护,这种问题反而更好处理了。
并发控制也很重要。QMT终端的交易接口其实不是线程安全的,多个线程同时调用xtquant的order_stock,轻则报错,重则导致内部状态错乱。我的桥接层里用一个threading.Lock()锁住所有交易类调用,宁可牺牲一点并发度,也要保证交易通道稳定。
4. 横评结论:为什么HTTP API最终胜出
4.1 五维评分对比
这一节我把四种方案放到五个维度上打分,每个维度满分5分,分数是我个人主观判断,只代表我的使用场景(A股日内、多策略、需要外部风控)。
| 方案 | 易用性 | 稳定性 | 扩展性 | 性能 | 可维护性 | 综合 |
|---|---|---|---|---|---|---|
| MiniQMT xtquant直连 | 5 | 3 | 2 | 3 | 3 | 16 |
| 客户端内嵌Python | 4 | 4 | 1 | 3 | 2 | 14 |
| VBA/COM外挂控制 | 2 | 2 | 2 | 1 | 1 | 8 |
| 自建HTTP API桥接 | 3 | 4 | 5 | 4 | 4 | 20 |
这个打分很能说明问题。MiniQMT的唯一优势就是易用性,其他维度都不突出。内嵌Python胜在"不太会崩",但扩展性和可维护性太差。VBA/COM方案全面落后,只适合极个别场景。HTTP API方案虽然初期搭建成本高,易用性只有3分,但后续扩展性和可维护性拉满,配合合理的设计,稳定性也可以做到很高。
4.2 我踩过的坑和你可能也会踩的坑
自建HTTP API桥接这条路,听着优雅,实际走起来处处是坑。我分享几个印象最深的。
坑一:QMT终端休眠判定导致连接假死。Windows服务器默认几分钟无操作就会锁屏/睡眠,QMT终端虽然是GUI程序,但在锁屏状态下部分通信接口会变得极不稳定,xtquant连接时好时坏。解决办法是在电源设置里把睡眠和硬盘休眠全部设为"从不",同时用一个小脚本定期模拟鼠标移动,让系统认为始终有人在操作。
坑二:xtquant的connect方法不能反复调用。我第一次做断线重连时,简单粗暴地在异常发生后调用xt_trader.connect()重建连接,结果发现连接根本不会真正建立,返回一直失败,或者建立成功但订单回报收不到。后来排查发现,xtquant内部对同一个终端的连接是有状态缓存的,正确做法是重建XtQuantTrader实例,而不是复用旧实例去重连。这个案例说明,桥接层做"进程级重启"比做"函数级重连"要可靠得多。
坑三:策略端和桥接层的时间不同步。两台服务器时间差个几秒,订单回报里的时间戳跟策略本地的调度时间对不上,排查问题时会非常混乱。我在桥接服务里把所有时间字段统一转换成Unix时间戳,并在HTTP响应头里加了X-Server-Time,方便策略端校准。
4.3 什么情况下不应该照搬HTTP API方案
HTTP API方案不是万能药。下面三种情况,我建议别照搬我的做法。
第一种是纯手工/半手工交易者,你只偶尔用QMT手动下单,那完全没有必要搭桥接服务,老老实实打开终端点鼠标最省心。第二种是策略非常简单、频率极低的用户,一天就几笔交易,用MiniQMT直连就够了,多一套HTTP层反而增加故障点。第三种是团队有专职运维但没有编程能力的情况,HTTP API方案需要人写代码、维护、监控,如果团队不具备这个能力,硬上HTTP API只会造成"桥接层比交易策略还容易出问题"的尴尬局面。
归根结底,技术选型是收益和成本的权衡。我能All In HTTP API,是因为我有独立的策略服务器、有多套子策略共享交易通道的需求、有足够的开发精力去维护这套桥接层。如果你不具备这些前提,那选择MiniQMT直连甚至内嵌Python,反而更明智。
5. 从"能用"到"好用":几条实战经验
5.1 处理好QMT终端的状态依赖
很多人用HTTP API桥接时遇到的首个错误就是client is null。这个报错通常出现在调用xt_trader.order_stock时,提示客户端连接对象是空的,本质上是xtquant还没有成功连接上QMT终端,或者之前的连接已经断开了。
我的处理方式是加了一个状态机:disconnected -> connecting -> connected -> trading,桥接服务启动后先进入connecting状态,尝试连接终端;连上后做一次账户信息预取,确认能正常通信才切换到connected;客户端首次下单前,再主动检查一次状态,如果是not connected就返回503 Service Unavailable,而不是让策略端等一个永远不返回的下单响应。同时,连接过程本身要加资源锁,防止多个HTTP请求同时触发连接逻辑,导致多个连接实例打架。
5.2 认证与会话管理
HTTP API服务如果只监听本机,一般不用太担心安全问题。但如果像我一样跑在局域网里供多台策略服务器调用,就必须做认证。我遇到过策略代码里密码错写、导致桥接服务日志里反复出现401 Unauthorized的情况,那其实就是token验证没通过。
我的方案是:桥接服务启动时生成一个长期token,写在配置文件中,策略端请求时放在Authorization头里。所有接口通过FastAPI的依赖注入做统一校验。对于WebSocket连接,在建立连接时校验token,之后用JWT方式做心跳续期。这套做法的好处是简单、可控、不必维护独立的用户体系,很适合小团队。
5.3 报错500的排查套路
桥接层返回500 Internal Server Error是家常便饭,关键是触发了别瞎猜原因。我总结出一套排查顺序:先看桥接服务日志里有没有打印出具体的异常堆栈,再确认是不是QMT终端流连接断开,再查是不是参数格式不对。
举个例子,我遇到过一种情况:POST /api/order请求返回500,查看服务日志发现是xtquant执行order_stock时抛了STK_ORDER_ERROR,原因是"该股票可能不在当前账户可交易范围内"。这种情况常见于科创板/北交所股票,账户权限没开全。所以我在桥接层里对股票代码做了规则校验,不在可交易板块列表里的直接返回422 Unprocessable Entity,把参数错误和系统错误区分开,策略端看到422就知道是自己选股或权限的问题,而不是桥接层故障。
5.4 稳定运行的小技巧
最后分享几个让整套系统长期稳定跑下去的小技巧。一是日志分级,桥接层把每笔下单、成交回报、异常事件都记录到独立的日志文件中,并加上请求ID和策略ID字段,后续排查问题时能快速串联整个链路。二是定期健康检查,我写了个简单的监控脚本,每5分钟模拟一次查询持仓,如果连续3次失败就触发告警,并在群里发一条通知。三是重启策略,我用Windows任务计划程序每天凌晨4点重启一次桥接服务,这个时间点没有夜盘也没有隔夜单,重启能清掉积累的线程和内存碎片,显著降低长期运行后的稳定性风险。
我个人在实际操作中体会最深的一点是:桥接方案没有绝对的对错,只有合不合适。我见过的很多量化交易者,一开始都被MiniQMT的低门槛吸引,但真正想做出点规模时,最大的瓶颈往往不是策略逻辑,而是工程架构撑不住需求。HTTP API方案虽然多写了很多代码,但它把交易通道变成了一个标准化的内部服务,让后续的策略迭代、多账户扩展、风控接入都变得非常自然。如果你也在纠结怎么给QMT做桥接,希望这篇横评能帮你少走一些弯路,选一条真正适合自己的路。