为 MetaTrader 5 开发 MQTT 客户端:TDD 方法 - 最终篇(基础篇)
📘

为 MetaTrader 5 开发 MQTT 客户端:TDD 方法 - 最终篇(基础篇)

第 1/3 篇

「先把抽象层级钉死再写代码」

给 MT5 写 MQTT 客户端,第一件事不是急着调库,而是先确认问题的抽象边界。C++ 圈有句话很实在:在给定问题和解法约束下,尽量把抽象层级拉到最高。 落到 MQL5 这边,约束很硬——终端是事件驱动的单线程环境,网络 IO 不能阻塞主线程,否则行情刷新和 EA 逻辑会卡死。

  • 年 11 月 28 日那篇系列终章里,作者开篇就引用了 Stroustrup 的原话作为工程信条,说明这套 MQTT 封装从设计起就没打算堆砌底层 socket 细节,而是先抽象出「连接 / 订阅 / 收发」三层接口。

你开 MT5 新建一个 EA,先别写业务,把 MQTT 类头文件过一遍,看它是不是把网络异常和重连策略都收进了高层方法里——如果是,说明抽象到位,后续换 broker 或换主题都只改参数。

回顾 MQTT 在 MT5 信号共享里的来路

这是系列收尾的一篇,先把前面六篇的技术脉络快速串一下,方便直接跳到落地代码的人补齐上下文。 第一篇确认了 MQTT 是 pub/sub 模型、轻量且抗网络抖动,3.1.1 与 5.0 两个版本都能用于机器间实时解耦数据共享;第二篇搭了客户端代码骨架并用 WSL 本地代理测出 CONNECT 类发错包;第三篇啃 CONNECT 标志语义与受保护方法测试;第四篇逐条拆 MQTT 5.0 属性特别是用户属性的扩展用法;第五篇在位的层级上演示 PUBLISH 固定头标志与 QoS 0/1/2 的包交换;第六篇做了首次大重构并补了 PUBACK 原因码。 最后这篇要给出的实战代码,解决的是 EA 指标信号一个很具体的坑:交易账户里根本没有指标需要的交易品种。 一种可行思路是用自定义交易品种,加一对在 MT5 以服务形式跑的 MQTT 客户端;演示虽只在单终端跑,但靠代理解耦的特性,理论上能扩到任意设备数和品种数。外汇与贵金属杠杆高、滑点大,这种跨终端同步方案仅降低数据缺失概率,不消除交易风险。 文中 MUST / MAY 沿自 OASIS,而 OASIS 又引自 IETF RFC 2119,引文未注明者均出自 OASIS 标准。

◍ 跨账户拿不到报价,EA 就瞎了

设想你做加密货币,发现 AncapCoin 和标普 500 稳定负相关:标普涨它就跌,标普跌它就涨。你靠这个关系手动赚了钱,现在想写成 EA 丢到 VPS 里全天候跑,但手里的加密货币券商根本没有标普 500 这个品种,有标普的券商又不给 AncapCoin。EA 要在下单账户里实时拿到标普报价才能决策,可两家即使都给 MT5,普通零售用户想把异地报价拉进交易账户,几乎做不到。 手动交易时这不算事——接个副屏或者开个独立窗口看另一个商的行情就行。自动化就卡死了:决策发生在交易账户,数据却在别家账户,实时桥接对普通人门槛极高。这类不匹配在商品和对应 CFD 之间也常见,你能合法看数据,但数据散在不同提供商。 从开发视角看,这就是个典型的发布/订阅需求。MQTT 是成熟开源规范里最轻量、最便宜也最稳的一个,天生干这事:以极小网络开销,把地理分散的源数据实时收集并分发。后面我们会用 MT5 把数据源账户的标普报价直送交易账户,那边建个自定义品种就能喂指标。 具体落点:交易账户侧建自定义标普品种、写 SUBSCRIBE 服务收代理推送、用 MQL5 刷新品种;数据源账户侧写 PUBLISH 服务往代理发报价。账户数理论无限,只受内存、CPU、带宽和代理限制。本文不给你成品方案,只拆主要步骤,顺便点醒你这模式能用的地方远不止搬虚拟币报价。

「用 GUI 在 MT5 里捏一个自定义品种」

自定义交易品种等于你自己定义规格、自己喂报价的交易对。它最实际的用途是合成指数、回测 EA 和指标——因为报价源完全可控,你可以让历史数据按想要的方式重播。 在挂着交易账户的 MT5 里,点「查看 > 交易品种 > 创建自定义交易品种」就能走图形界面,不必写代码。弹窗中「复制自」选 S&P 500,再把名字写成比如 MySPX500、填一句描述,确定后左侧树列表就会出现这个新品种。 有个坑得说清楚:如果你交易账户本身没有 S&P 500,那就没法直接「复制自」它。原文示例里图省事才这么写,真实环境要手动把标准普尔 500 的合约规格抄进自定义品种参数里,否则点出来的是个空壳。外汇和贵金属本身杠杆高、滑点大,自定义品种若规格填错,回测和实盘偏差会放大,动手前核对小数点位数和最小价位变动。 新品种建完还得加到「市场报价」窗口,不然只能看树列表、不能画图也不能收分时。MQL5 参考里写明了:CustomTicksAdd 只认市场报价里已打开的自定义品种;没选中的得用 CustomTicksReplace 去塞报价。建好后报价区是空的,下一步就是让这个账户连上 MQTT 代理,把实时 tick 灌进 MySPX500。

常见问题

先用 MQTT 把报价发布到中间代理,另一个账户的 EA 订阅主题即可拿到数据;抽象层先钉死消息结构,再写 EA 逻辑,避免信号中断导致 EA 误判。
可以,用 MT5 自带的 GUI 对象在图表上画面板,把接收到的 MQTT 数据写进自定义品种的离线报价,就能在原生界面里看。
小布可接管跨账户信号订阅与异常预警,你只需设定品种和主题,盘口异动它直接推给你,不用自己盯代理状态。
采用测试驱动开发,先写最小可用抽象接口并用假代理跑通单测,再接真实 broker,能大幅降低联调时的隐藏 bug。
大概率是的,MT5 要求严格递增的 MqlDateTime;发送端统一用 UTC 毫秒并做去重,重新同步历史就能正常绘制。