MT5 交易事件模型:一次请求为何触发多个 OnTrade
从 OrderSend 到成交回包,理清异步交易事件的来龙去脉
理解交易事件模型,是写出健壮 EA 的前提。MT5 的异步特性意味着:你发的“一个请求”和客户端收到的“事件”并不是一一对应的。本文顺着请求到成交的完整链路,把这件事讲透。
一、请求如何从客户端到服务器
手动交易按 F9 打开请求窗口,程序用 OrderSend() 发送。发往服务器前,客户端先做强正确性检查(如买几百万手、负价),错误请求根本不会出门——这是为保护服务器不被程序 bug 刷爆。可用 OrderCheck() 预检,结果存入 MqlTradeCheckResult。
二、服务器的初步检查
请求到达后,服务器校验资金是否充足、价格/止损止盈是否正确、数量(最小/最大/步长/持仓限额)、品种状态与交易时段、账户限制等。不通过即拒绝,回包写入 MqlTradeResult(通过 OrderSend 第二参数取回)。通过检查的请求进入等待队列,寿命 3 分钟,超时删除。
三、OnTrade:交易事件处理函数
MQL5 的事件模型会在订单/持仓/成交/历史变化时调用预定义的 OnTrade()。注意:OnTrade 仅供 EA 使用,你在指标或脚本里写同名函数也不会被触发。
四、关键真相:一个请求 → 多个事件
例:一个买 10 手 EURUSD 的挂单,被 1+4+5 手三笔反向成交吃掉。作为结果,客户端会收到 7 个 Trade 事件(每笔成交、每次数量变化、订单移入历史各算一个)。
五、消息是独立到达的
来自服务器的消息各自独立到达:订单单证可能先到,而 Trade 事件、活动列表更新稍后才到;生至出现“成交已执行、订单已进历史”的情况也属正常,因为服务器处理远快于网络传输。
六、可靠的事件处理算法:对比状态
唯一能确定“账户到底变了什么”的方法,是比较前后快照。做法:全局声明 orders/positions/deals/history_orders 计数器;OnInit 里用 HistorySelect 初始化;在 OnTick/OnTrade 中重新 HistorySelect 并比较前后数量与状态,差异即变化;用 CheckStartDateInTradeHistory 滚动历史窗口。
小结
- 发单前客户端先强校验保护服务器
- OrderSend 返回 true≠成交,看 MqlTradeResult
- OnTrade 仅 EA 可用
- 一个操作可触发多个 Trade 事件
- 服务器消息独立到达、可能乱序
- 最稳做法:维护计数器快照并比较差异