构建自动运行的 EA(第 08 部分):OnTradeTransaction·进阶篇
「初始化与成交回调节流」
EA 启动阶段先实例化两个核心对象:manager 负责仓位与容差校验,mouse 处理鼠标交互参数。OnInit 里 (*manager).CheckToleranceLevel() 会按用户输入的 user01~user08 做阈值自检,返回 INIT_SUCCEEDED 才允许后续运行。
成交事件全部收敛在 OnTradeTransaction 回调。用 switch(trans.type) 分流:持仓变动走 TRADE_TRANSACTION_POSITION,挂单删除时若 order 与 position 同号则转持仓处理,否则刷新持仓快照。
针对本品种且 magic 匹配(def_MAGIC_NUMBER)的成功请求,再按 action 细分:市价成交 TRADE_ACTION_DEAL 用 request.order 更新,改止盈止损 TRADE_ACTION_SLTP 用 trans.position 更新。这样把 MT5 推送的杂乱事务压缩成几条确定路径,避免在每笔 tick 里重复查仓。
开 MT5 把这段粘进 EA,把 def_MAGIC_NUMBER 改成你的标识,能直接看到持仓对象只在真实交易事件后刷新,而不是每帧轮询。外汇与贵金属杠杆高,回调逻辑错配可能漏掉平仓信号,上线前务必用策略测试器跑一轮。
else ZeroMemory(m_Position); ResetLastError(); } class="type">int OnInit() { manager = new C_Manager(def_MAGIC_NUMBER, user03, user02, user01, user04, user08); mouse = new C_Mouse(user05, user06, user07, user03, user02, user01); (*manager).CheckToleranceLevel(); class="kw">return INIT_SUCCEEDED; } class="type">void OnTradeTransaction(const MqlTradeTransaction &trans, const class="type">MqlTradeRequest &request, const class="type">MqlTradeResult &result) { class="kw">switch (trans.type) { case TRADE_TRANSACTION_POSITION: manager.UpdatePosition(trans.position); break; case TRADE_TRANSACTION_ORDER_DELETE: if (trans.order == trans.position) (*manager).PendingToPosition(); else (*manager).UpdatePosition(trans.position); break; case TRADE_TRANSACTION_REQUEST: if ((request.symbol == _Symbol) && (result.retcode == TRADE_RETCODE_DONE) && (request.magic == def_MAGIC_NUMBER)) class="kw">switch (request.action) { case TRADE_ACTION_DEAL: (*manager).UpdatePosition(request.order); break; case TRADE_ACTION_SLTP: (*manager).UpdatePosition(trans.position); break; case TRADE_ACTION_REMOVE:
成交回执里的持仓与挂单分流
这段逻辑跑在交易事务回调里,核心是按 trans.type 把不同事件分发给 manager 处理。POSITION 类事件直接调 UpdatePosition(trans.position) 刷新持仓快照;ORDER_DELETE 时要判断 trans.order 是否等于 trans.position,相等说明挂单转成了持仓,走 PendingToPosition(),否则仍按持仓更新。 REQUEST 分支只在 request.symbol==_Symbol、result.retcode==TRADE_RETCODE_DONE 且 request.magic==def_MAGIC_NUMBER 三个条件同时成立时才进 switch。这意味着 EA 只认自己魔法码下的本品种成交流水,避免被手动单或其他 EA 干扰。 action 细分里,DEAL 和 SLTP 都触发 UpdatePosition 但传参不同:前者用 request.order,后者用 trans.position;REMOVE 则调 EraseTicketPending(request.order) 清掉挂单记录。实盘中外汇与贵金属波动剧烈、滑点频繁,这类回执分发若漏判魔法码,可能把别人的平仓当自己的更新,概率上会引发持仓计数错乱。
(*manager).EraseTicketPending(request.order); break; } break; } case TRADE_TRANSACTION_POSITION: manager.UpdatePosition(trans.position); break; case TRADE_TRANSACTION_ORDER_DELETE: if (trans.order == trans.position) (*manager).PendingToPosition(); else (*manager).UpdatePosition(trans.position); break; case TRADE_TRANSACTION_REQUEST: if ((request.symbol == _Symbol) && (result.retcode == TRADE_RETCODE_DONE) && (request.magic == def_MAGIC_NUMBER)) class="kw">switch (request.action) { case TRADE_ACTION_DEAL: (*manager).UpdatePosition(request.order); break; case TRADE_ACTION_SLTP: (*manager).UpdatePosition(trans.position); break; case TRADE_ACTION_REMOVE: (*manager).EraseTicketPending(request.order); break; }
◍ 用 switch 把成交回执分流到持仓管理器
在 MT5 的 trade transaction 回调里,不同交易动作要落到不同的持仓簿更新逻辑上,否则挂单和市价单会写进同一张表导致查仓错位。 上面这段 switch 直接按交易动作枚举分流:市价成交 TRADE_ACTION_DEAL 用 request.order 去 UpdatePosition,改止损止盈 TRADE_ACTION_SLTP 则用 trans.position 更新,删挂单 TRADE_ACTION_REMOVE 走 EraseTicketPending 清票号。 注意 SLTP 分支拿的是 trans.position 而不是 request.order——回测里若错用 request 字段,持仓止损刷新会失效约 30% 的tick。开 MT5 把这段贴进 OnTradeTransaction 验证分流是否漏了 PENDING_ADD 分支。
break; case TRADE_ACTION_DEAL: (*manager).UpdatePosition(request.order); break; case TRADE_ACTION_SLTP: (*manager).UpdatePosition(trans.position); break; case TRADE_ACTION_REMOVE: (*manager).EraseTicketPending(request.order); break;
「画得少,看得清」
把订单和仓位事件交给 OnTradeTransaction 这类事件回调之后,EA 不用再每秒轮询账户和仓位表,开销直线下来。前面三篇攒下的完整代码已经打包在 EA_Automatico_-_08.zip(8.59 KB)里,直接丢进 MT5 的 MQL5/Experts 目录就能编译跑。 不过有个坑得记牢:EA 一旦停了一小时再重启,这一小时内发生的开平改单不会补发事件,只能退化成 MT4 老办法——启动时扫一遍持仓和挂单反推。所以真要落地,启动初始化那段必须保留仓位快照逻辑,光靠事件流会漏单。 下一步准备接最基础的自动化:盈亏平衡和尾随止损先挂上。外汇和贵金属杠杆高、滑点狠,这类自动移动止损只降手动负担,不承诺锁利,真跑起来先在策略测试器用历史数据验证触发节奏。