构建自动运行的 EA(第 08 部分):OnTradeTransaction·进阶篇
📘

构建自动运行的 EA(第 08 部分):OnTradeTransaction·进阶篇

第 2/2 篇

「初始化与成交回调节流」

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 改成你的标识,能直接看到持仓对象只在真实交易事件后刷新,而不是每帧轮询。外汇与贵金属杠杆高,回调逻辑错配可能漏掉平仓信号,上线前务必用策略测试器跑一轮。

MQL5 / C++
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) 清掉挂单记录。实盘中外汇与贵金属波动剧烈、滑点频繁,这类回执分发若漏判魔法码,可能把别人的平仓当自己的更新,概率上会引发持仓计数错乱。

MQL5 / C++
(*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 分支。

MQL5 / C++
              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 老办法——启动时扫一遍持仓和挂单反推。所以真要落地,启动初始化那段必须保留仓位快照逻辑,光靠事件流会漏单。 下一步准备接最基础的自动化:盈亏平衡和尾随止损先挂上。外汇和贵金属杠杆高、滑点狠,这类自动移动止损只降手动负担,不承诺锁利,真跑起来先在策略测试器用历史数据验证触发节奏。

常见问题

在 OnInit 里只处理当前持仓与挂单快照,忽略启动前的成交流水;用时间或票据号做去重,避免回放旧回执触发逻辑。
按回执类型先分流:持仓类走持仓管理器,挂单类走挂单队列;用枚举判断比字符串比较更稳更快。
可以,把 EA 代码贴给小布,它会按回执类型帮你梳理分支覆盖,标出未处理的枚举值,省去手动排查。
default 至少写日志或计数,不画图可以但必须留痕;否则异常回执静默丢失,复盘时找不到原因。
只在持仓方向反转或止损触发时画标记,普通加仓不画;用开关变量控制频次,视觉噪音降下来才好盯盘。