EA 交易中采用OnTrade() 函数处理交易事件(基础篇)
📘

EA 交易中采用OnTrade() 函数处理交易事件(基础篇)

第 1/3 篇

「用 OnTrade 接住 MT5 的成交回执」

在 MT5 里跑 EA,订单成交、修改、删除都不会自动提醒你的主循环,得靠交易事件钩子。OnTrade() 是系统在下单动作完成后回调的函数,适合在里面写仓位同步、日志落盘或风控复核,而不是塞进 OnTick 里轮询。 把交易后处理从行情 tick 中剥离,能少跑很多无效计算。一个常见的坑是:OnTrade 里再发订单可能触发递归回调,必须加标志位或判断交易上下文。 下面这段示例演示了在 OnTrade 中打印成交票据与当时点差,方便你回 MT5 后台核对每一笔执行质量。外汇与贵金属杠杆高,成交滑点可能放大亏损,任何自动化逻辑都先在策略测试器跑通再上实盘。

MQL5 / C++
class="type">int lastDeal=class="num">0;
class="type">void OnTrade()
{
   class=class="str">"cmt">// 取最近一笔成交ticket
   class="type">int deal=HistoryDealGetTicket(HistoryDealsTotal()-class="num">1);
   if(deal!=lastDeal)
   {
      class="type">class="kw">double spread=SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);
      Print("new deal #",deal," spread=",spread);
      lastDeal=deal;
   }
}

为什么 EA 必须会“报事”

写过 MQL5 EA 的人基本都会撞上同一个需求:把“我干了什么、市场发生了什么”告诉屏幕外的人。要么是给自己的手机发短信,要么是邮件推一条提醒,要么只是在日志里留痕,否则你根本没法事后复盘这单为什么开、为什么反手。 这件事的本质不复杂——你得在“市场出现某事件”或“EA 执行了某动作”的那一刻把它抓住,再转成通知。难点在于事件种类杂:持仓侧有开仓、加仓、改 SL/TP、反手、全平、平一部分;挂单侧有下单和修改挂单。漏掉一类,回测和实盘就对不上。 外汇和贵金属杠杆高、跳空频繁,事件触发可能集中在几秒内,通知逻辑若卡顿或漏捕,实盘风险会被放大。下面我会给出一套可跑的事件处理实现,你直接开 MT5 挂上就能验证捕捉是否完整。

◍ 交易事件怎么触发 OnTrade

MQL5 里的事件分预定义和自定义两类,做交易监控真正要盯的是预定义里的“交易”事件。 每次订单成交、修改或持仓变动,终端都会生成一个交易事件,随后必然回调 OnTrade() 函数。 也就是说,订单与持仓的具体处理逻辑只能写在 OnTrade() 里面才跑得起来;想验证这一点,开 MT5 新建 EA,在 OnTrade 里打印一句注释,手动下一单就能看到触发。 外汇和贵金属杠杆高,事件回调频率在剧烈行情下可能陡增,回测和实盘都要留意处理耗时。

「从向导里拼出可挂交易的 EA 空壳」

在 MT5 里点 File -> New 拉起 MQL5 向导,选 Expert Advisor 往后走,在 General properties 对话框填 Name 就行。我填的是 TradeControl,你也可以随便起,关键是不在向导里预置参数——参数留到写具体逻辑时再动态生成。 模板生成后默认只有 OnInit、OnDeinit、OnTick,要捕捉成交事件得自己把 OnTrade() 补进文件。加上这个函数,EA 才会在每笔订单成交或仓位变动时被触发,而不是干等下一根 tick。 下面这段就是补完 OnTrade 后的空模板,外汇和贵金属波动大、滑点频繁,这种骨架先跑通再填策略,能少踩很多运行时坑。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                              TradeControl_en.mq5 |
class=class="str">"cmt">//|                                             Copyright KlimMalgin |
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "KlimMalgin"
class="macro">#class="kw">property link      ""
class="macro">#class="kw">property version   "class="num">1.00"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA初始化函数                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//---
   
class=class="str">"cmt">//---
   class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA去初始化函数                                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(const class="type">int reason)
  {
class=class="str">"cmt">//---
   
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| OnTrade 函数                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTrade()
  {
class=class="str">"cmt">//---
class=class="str">"cmt">//---
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA订单函数                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
class=class="str">"cmt">//---
   
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

用 OnTrade 拆开一次开仓的四步事件

在 MT5 里点下 Buy 或 Sell 后,若于 OnTrade() 中挂一句 Alert("Trade事件发生了"),实测会连续弹出 4 次警报。这说明一次市价开仓会触发 4 次 Trade 事件:订单被创建、交易被执行、已完成订单送入历史、持仓正式建立。注意,官方文档称历史列表变更也会调用 OnTrade(),但与开发层确认后实际并非如此——只有已下订单与新建持仓列表变动才触发,历史写入存在延迟且 OnTrade() 运行期间读不到。 想在 EA 里精确捕捉「建仓那一刻」,就得在每次 OnTrade() 调用时比对订单列表长度。用 OrdersTotal() 取当前挂单数,再拿一个全局变量 OrdersPrev 存上一次的值。EA 启动时 OnInit() 里也要先令 OrdersPrev = OrdersTotal(),否则会误判初始化前就存在的挂单。 判定逻辑很直接:若 OrdersPrev 小于当前 OrdersTotal(),说明有新订单进列表;反之则说明订单已被执行或撤掉。几乎所有持仓操作都从这两个分支起手,唯独止损止盈修改要走独立通道。 针对 sl/tp 变动,可声明两个 _position 结构数组 PositionList[] 与 PrevPositionList[],分别在 OnTrade() 头尾刷新,循环比对每个持仓的获利止损值。不匹配就攒进 _alerts 用 Alert() 抛出,挂单修改同理。外汇与贵金属杠杆高,这类事件追踪仅作风控辅助,不预示任何方向。

MQL5 / C++
Alert("Trade事件发生了");
class="type">int OrdersPrev = class="num">0;        class=class="str">"cmt">// 上一次调用 OnTrade() 时订单的数量
class="type">int OrdersPrev = class="num">0;        class=class="str">"cmt">// 上一次调用OnTrade()时订单的数量
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA初始化函数                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
{
class=class="str">"cmt">//---
OrdersPrev = OrdersTotal();
class=class="str">"cmt">//---
class="kw">return(class="num">0);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| OnTrade 函数                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTrade()
{

常见问题

OnTrade由平台在成交、改仓、删单等事件发生后立即回调,比轮询更省资源也更实时,能第一时间接住回执做后续处理。
倾向触发四步事件:下单请求、订单被接受、成交返回、持仓更新;EA可在OnTrade里逐次拆开处理状态。
可以。小布能按你贴的EA空壳代码模拟开仓回执流程,标出哪一步事件没接住,省去你手动反复挂单测试。
向导只生成包含OnTrade等骨架的可编译空壳,具体开仓条件和事件处理要自己在函数体里补代码,否则不报事也不下单。
外汇贵金属波动大,回执可能因滑点部分成交;OnTrade里别假设每次都是全量成交,需读成交明细做容错。