MQL5 细则手册:MetaTrader 5 交易事件的声音通知·综合运用
🔊

MQL5 细则手册:MetaTrader 5 交易事件的声音通知·综合运用

(3/3)· 用 #resource 把 wav 封进 ex5,分发 EA 不再附带散装音频文件

实战向进阶 第 3/3 篇
很多交易者给 EA 配声音提醒,却把 wav 散落在 Files 目录,换电脑就失声。把音频编译进 EA 资源,分发一个 ex5 就能带着声音跑,省掉路径配置的麻烦。

用声音把成交结果钉进耳朵

EA 里把声音事件拆成了独立枚举:盈利平仓代号 3,亏损平仓代号 4,配合错误、开仓、改单共 5 类提示音。这样分离后,交易动作和反馈解耦,你想换音效只改字符串变量,不用动逻辑。 PlaySoundByID() 这个函数只在实时环境且 UseSound 为真时才响。它用 switch 把传入的 id 映射到具体 wav:比如 SOUND_CLOSE_WITH_PROFIT 就播 SoundCloseWithProfit,SOUND_CLOSE_WITH_LOSS 播 SoundCloseWithLoss,漏掉一个 case 就静音。 开仓函数 OpenPosition() 先把魔数设 0、滑点按品种精度修正为 10 点(CorrectValueBySymbolDigits(10))。关键点在执行模式判断:当 symb.execution_mode 是 INSTANT 或 MARKET 时,SL/TP 可在开仓同时提交——MT5 build 803 之后 MARKET 模式才支持这点,老版本会直接失败。 外汇和贵金属波动剧烈,滑点 10 点只是基准值,极端行情可能扩到数十点,声音提醒不能替代对风险的盯盘。

MQL5 / C++
  SOUND_CLOSE_WITH_PROFIT = class="num">3,  class=class="str">"cmt">// Position closing at profit
  SOUND_CLOSE_WITH_LOSS    = class="num">4   class=class="str">"cmt">// Position closing at loss
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Playing sounds                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void PlaySoundByID(ENUM_SOUNDS id)
  {
class=class="str">"cmt">//--- If it is the real-time mode and sounds are enabled
   if(IsRealtime() && UseSound)
     {
class=class="str">"cmt">//--- Play the sound based on the identifier passed
      class="kw">switch(id)
        {
         case SOUND_ERROR             : PlaySound(SoundError);           break;
         case SOUND_OPEN_POSITION     : PlaySound(SoundOpenPosition);    break;
         case SOUND_ADJUST_ORDER      : PlaySound(SoundAdjustOrder);     break;
         case SOUND_CLOSE_WITH_PROFIT : PlaySound(SoundCloseWithProfit); break;
         case SOUND_CLOSE_WITH_LOSS   : PlaySound(SoundCloseWithLoss);   break;
        }
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Opening a position                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OpenPosition(class="type">class="kw">double lot,
                  ENUM_ORDER_TYPE order_type,
                  class="type">class="kw">double price,
                  class="type">class="kw">double sl,
                  class="type">class="kw">double tp,
                  class="type">class="kw">string comment)
  {
class=class="str">"cmt">//--- Set the magic number in the trading structure
   trade.SetExpertMagicNumber(class="num">0);
class=class="str">"cmt">//--- Set the slippage in points
   trade.SetDeviationInPoints(CorrectValueBySymbolDigits(class="num">10));
class=class="str">"cmt">//--- The Instant Execution and Market Execution modes
class=class="str">"cmt">//    *** Starting with build class="num">803, Stop Loss and Take Profit            ***
class=class="str">"cmt">//    *** can be set upon opening a position in the SYMBOL_TRADE_EXECUTION_MARKET mode ***
   if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT ||
      symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET)
     {
class=class="str">"cmt">//--- If the position failed to open
      if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment))
        {
class=class="str">"cmt">//--- Play the error sound and print the relevant message
         PlaySoundByID(SOUND_ERROR);

「用成交回history给平仓上声音锁」

EA 想在真实盘面里给平仓动作挂个提示音,核心不是监听 OrderSend 返回值,而是回头扫 HistoryDealsTotal 里的成交单。逻辑里用 static ulong last_ticket 记住上一次扫到的票号,避免同一笔平仓被重复播报——这是实盘里最容易漏的细节。 代码先 HistorySelect(0, TimeCurrent()+1000) 把截至当前+1000秒的成交全拉进列表,再从尾向前遍历。只处理 deal.symbol==_Symbol 的条目,且 deal.entry 属于 DEAL_ENTRY_OUT 或 DEAL_ENTRY_INOUT(即平仓、减仓、反手)才触发声音判断。 若 ticket==last_ticket 或 last_ticket==0(首次初始化),就更新 last_ticket 并退出。外汇与贵金属波动剧烈,这类声音告警只能当辅助,不能替代你对点差与滑点的实时判断,误触发概率始终存在。 让小布替你跑这套 把 UseSound 开关接进你的面板参数,回测时关掉、实盘只留平仓提示,能少听一半噪音。

MQL5 / C++
class="type">void SoundNotification()
  {
class=class="str">"cmt">//--- If it is the real-time mode and sounds are enabled
   if(IsRealtime() && UseSound)
     {
      class="type">class="kw">ulong       ticket      =class="num">0; class=class="str">"cmt">// Deal ticket
      class="type">int         total       =class="num">0; class=class="str">"cmt">// Total deals
      class="kw">static class="type">class="kw">ulong last_ticket =class="num">0; class=class="str">"cmt">// Last ticket prior to this check
      class=class="str">"cmt">//--- Get the complete history
      if(!HistorySelect(class="num">0,TimeCurrent()+class="num">1000))
         class="kw">return;
      class=class="str">"cmt">//--- Get the number of deals in the obtained list
      total=HistoryDealsTotal();
      class=class="str">"cmt">//--- In the obtained list, iterate over all deals from the last one to the first one
      for(class="type">int i=total-class="num">1; i>=class="num">0; i--)
        {
         class=class="str">"cmt">//--- If the deal ticket by its position in the list has been obtained
         if((ticket=HistoryDealGetTicket(i))>class="num">0)
           {
            class=class="str">"cmt">//--- get the symbol of the deal
            GetHistoryDealProperties(ticket,D_SYMBOL);
            class=class="str">"cmt">//--- If the symbol of the deal and the current symbol are the same
            if(deal.symbol==_Symbol)
              {
               class=class="str">"cmt">//--- get the direction of the deal
               GetHistoryDealProperties(ticket,D_ENTRY);
               class=class="str">"cmt">//--- If it is position closing, volume decrease or reversal
               if(deal.entry==DEAL_ENTRY_OUT || deal.entry==DEAL_ENTRY_INOUT)
                 {
                  class=class="str">"cmt">//--- If the ticket of the current deal from the list(the last deal for the symbol) is equal to the previous ticket
                  class=class="str">"cmt">//    or this is the initialization of the ticket of the last deal
                  if(ticket==last_ticket || last_ticket==class="num">0)
                    {
                     class=class="str">"cmt">//--- Save the ticket and exit

◍ 平仓盈亏的声音反馈与初始化钩子

这段逻辑把每笔成交的盈亏结果直接映射到声音提示上。在 SoundNotification() 里,先通过 GetHistoryDealProperties(ticket,D_PROFIT) 取出该 ticket 的浮动/已实现盈亏,再用 deal.profit>=0deal.profit<0 两个分支决定播放 SOUND_CLOSE_WITH_PROFIT 还是 SOUND_CLOSE_WITH_LOSS,同时把 last_ticket 记下来并 return,避免重复处理同一单。 初始化阶段 OnInit() 只做两件事:调用 CheckNewBar() 重置新 K 线状态,以及跑一次 SoundNotification() 把当前品种的历史末单 ticket 同步进 last_ticket,返回 INIT_SUCCEEDED 即代表加载无误。 实时监听靠 OnTrade() 事件,只要终端发生成交或平仓,就会再次触发 SoundNotification()。外汇与贵金属杠杆高、滑点可能让 deal.profit 与预期有偏差,声音提示仅作辅助,不能替代对账单核对。 开 MT5 把下面代码贴进 EA 的对应函数,改 SOUND_CLOSE_WITH_PROFIT 为你自定义的 wav 名,就能在黄金剥头皮时靠耳朵分辨平仓方向。

MQL5 / C++
last_ticket=ticket;
   class="kw">return;
   }
   class=class="str">"cmt">//--- Get the result of the deal
   GetHistoryDealProperties(ticket,D_PROFIT);
   class=class="str">"cmt">//--- In case of profit
   if(deal.profit>=class="num">0)
     {
     class=class="str">"cmt">//--- Profit sound
     PlaySoundByID(SOUND_CLOSE_WITH_PROFIT);
     class=class="str">"cmt">//--- Save the ticket number
     last_ticket=ticket;
     class="kw">return;
     }
   class=class="str">"cmt">//--- In case of loss
   if(deal.profit<class="num">0)
     {
     class=class="str">"cmt">//--- Loss sound
     PlaySoundByID(SOUND_CLOSE_WITH_LOSS);
     class=class="str">"cmt">//--- Save the ticket number
     last_ticket=ticket;
     class="kw">return;
     }
   }
      }
         }
       }
     }
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Initialization                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- Initialize the new bar
   CheckNewBar();
class=class="str">"cmt">//--- Initialize tickets of the last deals for the symbol
   SoundNotification();
class=class="str">"cmt">//--- Initialization completed successfully
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Monitoring trade events                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTrade()
  {
class=class="str">"cmt">//--- Sound notification
   SoundNotification();
  }

别急着下结论

这套把交易事件映射成声音的思路,附件里给了可直接拖进 MT5 的 soundpanel.mq5(14.07 KB)和两组音频包 sounds.zip(321.8 KB)、soundtradeevents.zip(17.29 KB),实测在终端 Sounds 目录下丢进文件就能被 EA 调用。 想玩进阶的,代码库里搜 CMIDI 这个类能直接在 MetaTrader 5 播 MIDI,把报警写成一段旋律也不是不行,外汇和贵金属波动剧烈,声音提醒只是辅助,真下单前还是得自己看盘口。 读者常卡在两个地方:脚本和 EA 分不清——脚本拖上图表只跑一次 OnStart,EA 才吃每个 tick;声音文件找不到就认准 C:\Program Files\平台目录\Sounds,别在杂乱子目录里乱塞。

让小布替你听盘外音
这些事件声音的触发逻辑,小布盯盘已内置到品种页的异动提醒里,你只需勾选通知方式,不必自己写 PlaySound 封装。

常见问题

Alert() 固定播系统警示声并弹消息窗;PlaySound() 可指定资源内 wav 播独特音效,不弹窗,适合区分多类事件。
PlaySound 走资源播放不受该选项卡控制;若用 Alert 触发才需在选项里启用事件声音。
对象创建仅在初始化做一次,运行时仅响应点击,开销低;但按钮数过多会挤占图表空间,按事件类别精简更稳。
小布盯盘在云端做事件诊断与推送,不依赖你本地 EA 资源;若你自建 EA,可参考本篇把音效封进 ex5 减少部署依赖。
双冒号用于按名引资源,路径须与 #resource 声明一致且文件已编译进 ex5,漏写前缀或拼错名会播不出声。