MT5 Manager API 服务与实时风控监控
基于 MetaQuotes 原生 Manager API 的 C#/.NET 服务,以及在数秒内执行每个交易账户亏损规则的实时监控,服务于线上自营交易平台。
- 角色
- 全栈工程师,MT5 集成负责人
- 类型
- 为 TradersFlow (TRFC) 做的自由职业合同项目
- 时间
- 2025.11 至 2026.06
- 状态
- 已投产
平台原本通过 MetaTrader 5 的 HTTP Web API 读取交易数据,一次持仓查询可能需要好几秒;违规检查由后端定时任务执行,交易者可能在超限后仍继续交易数分钟。我用原生 Manager API 构建了 C#/.NET 7 服务,从其实时推送缓存中读取数据,并实现每隔几秒检查所有活跃账户亏损规则的监控器。在此之外,我还构建了后端客户端、健康监控、营销归因,以及运营团队每天使用的管理工具。
问题
一家运营中的自营交易公司(prop firm),必须对每个交易者的账户执行亏损限额。它的平台原本通过 MetaTrader 5 的 HTTP Web API 读取交易数据。一次持仓查询可能要好几秒,违规检查由后端定时任务执行,所以交易者可能在超限之后还继续交易好几分钟。对自营交易公司来说,这就是实实在在的钱。
难点在哪里
- 原生的 MT5 Manager API 是一套 Windows SDK,有自己的连接生命周期和本地推送缓存(pump)。它很快,但它不是线程安全的。
- 风控引擎会根据读到的数据采取行动:禁止交易,判定账户失败。一个错的数字比一个慢的数字更糟。
- 规则因账户类型和阶段而不同,而监控和后端对“账户是否失败”绝不能得出不同结论。
- 这次改动必须在一个正在运行、有真实交易者的平台上发布,模拟盘和实盘还是分开的服务器。
架构
一个 C#/.NET 7 服务持有 Manager API 连接和它的本地推送缓存,通过 HTTP 提供账户、持仓、成交、订单和历史接口,用 API Key 和 IP 白名单保护。原有的 TypeScript 后端通过带类型的客户端调用它,并把规则快照发布到 Redis。服务内部的监控每隔几秒按这些规则检查每个活跃账户,并通过幂等回调把违规上报给后端。模拟盘和实盘作为两个独立的 Windows 服务运行。
- C#
- .NET 7
- ASP.NET Core
- MT5 Manager API
- Serilog
- Redis
- TypeScript
- AdonisJS 5
- MySQL
- React 19
- React Query
- Zustand
- Tailwind CSS
- Windows Server
- AWS
我做了什么
全栈工程师,MT5 集成负责人
- 01用 C#/.NET 7 基于原生 Manager API 构建 MetaTrader 5 集成服务:账户、持仓、成交、订单与每日历史接口,批量读取和交易权限控制;通过 API Key 与 IP 白名单保护,以独立的模拟盘和实盘 Windows 服务运行。
- 02把持仓读取从耗时数秒的网络请求,改为读取 Manager API 的本地推送缓存。
- 03设计实时违规监控:每隔几秒,依据后端发布到 Redis 的规则快照,检查每个活跃账户的日亏损、最大亏损和即时账户规则,并通过幂等的后端回调判定违规账户失败。
- 04构建端到端营销归因:在官网和哈希路由的交易者 App 中采集首次触达的 UTM 与点击 ID,活动短链接,点击 → 注册 → 试用 → 购买的漏斗,以及同期群留存报表。
- 05用 React 19、TypeScript 与 React Query 重建管理后台:KYC 审核、带安全校验的 MT5 账户操作(手动通过/失败、冻结、一键平仓)、联盟报表和促销向导。
- 06新增登录 IP 与地理位置历史及重复 IP 风险标记,用于反欺诈审核;为 MT5 服务加入健康监控、告警邮件、崩溃通知和自动重连。
决策与挑战
从本地推送缓存读取,保留网络请求作为后备
持仓读取从耗时数秒的网络请求,改为读取 Manager API 持续更新的本地副本。较慢的请求方式保留下来,用于缓存还没准备好的少数时刻。
串行化每一次 SDK 调用
并发负载下,部分账户读到了别的账户的数据。我查到原因是 SDK 不是线程安全的,于是把所有调用放到同一把锁后面,连接和关闭也加了锁,并加了一道校验:返回的登录号必须就是请求的登录号。批量读取因此变慢,但为了读得正确,这个代价可以接受。
数字对不上就不动手
数据看起来不对时,监控会跳过这个账户,而不是判定它失败。任何账户要晋级,它的净值都必须在两个独立的 MT5 数据源上一致。
失败流程要幂等,出错要报出来
违规通过一个可以安全重复调用的回调上报。如果 MT5 拒绝权限变更,失败流程会直接抛错,数据库和 MT5 就不会悄悄地不一致。这也堵住了一个 bug:权限位掩码被取反,导致已判定失败的账户还能继续交易。
失败判定只算一处
这套计算只放在一个共享服务里。隔夜利息被重复计算造成的误判失败,以及没有日限额的即时账户被误判失败,都就此结束。
部署时先关掉监控
部署手册的步骤是:停掉两个服务,部署,在监控关闭的状态下手动验证一个已知违规的账户和一个已知正常的账户,最后才打开监控。
值得一讲的故事
最难的一次:高并发下,部分账户读到了别的账户的数据。我定位到 MT5 SDK 本身不是线程安全的,于是串行化所有 SDK 调用,对连接与关闭生命周期加锁,并加入登录号不一致保护。现在任何账户晋级前,其净值都必须在两个独立的 MT5 数据源上一致。
“幽灵违规”问题:权限位掩码被取反,导致已判定失败的账户仍能交易。我修正位运算并在服务器上验证,同时让失败流程在 MT5 拒绝修改时直接抛错,保证数据库与 MT5 不会悄悄不一致。
把失败判定统一到一个共享服务中,消除了隔夜利息重复计算以及无日限额的即时账户被误判失败的问题。
编写部署手册:先停止两个服务,在监控关闭的情况下用一个已知违规和一个正常账户验证,最后才开启监控。
经过验证的结果
- 持仓读取从耗时数秒的网络请求改为读取本地推送缓存。
- 每个活跃账户大约每 5 秒按亏损规则检查一次,以前这类检查由定时任务执行。
- 净值在两个独立的 MT5 数据源上一致之前,任何账户都不会晋级。
- 账户之间数据串读的事故已查明原因,并加了防护。
- 已在生产环境运行,模拟盘和实盘是两个独立的服务。
数字
- 6 个月约 250 次提交
- 5 个代码仓库
- 约每 5 秒一次违规检查
- 晋级前比对 2 个净值来源
这对客户说明了什么
我能负责一家交易公司里风险最高的集成,找出数据一致性事故的真正原因,并交付运营团队信得过的修复。同样的能力也适用于投资者门户、券商后台,以及任何必须与 MetaTrader 5 保持一致的系统。
告诉我你在做什么。
几句话就够了:做的是什么、目前进展到哪一步、需要工程师做什么。表单就在下方,也可以直接发邮件。我会在两个工作日内回复。