MQL5 细则手册:MetaTrader 5 交易事件的声音通知·进阶篇
📘

MQL5 细则手册:MetaTrader 5 交易事件的声音通知·进阶篇

第 2/3 篇

按钮对象的属性落位与随机配色

在 MT5 里用 ObjectSetInteger 给 GUI 按钮定尺寸和坐标时,OBJPROP_YSIZE 管高度、OBJPROP_XDISTANCE / OBJPROP_YDISTANCE 管相对图表的偏移量,这几个值直接决定按钮会不会被价格曲线吃掉。 OBJPROP_SELECTABLE 设 false 后用户点不到该对象,OBJPROP_STATE 标记按钮当前是否被按下,OBJPROP_ZORDER 控制层叠顺序——想让按钮压在指标线之上就把 z_order 调大。 OBJPROP_TOOLTIP 传 "\n" 可彻底关掉悬浮提示,避免鼠标掠过图表时弹出无用信息。 配色函数用 MathRand()%26 从 0~25 里挑预设色,case 0 到 10 已映射 clrOrange、clrGold、clrChocolate 等 11 种;外汇与贵金属波动剧烈、杠杆风险高,随机色仅用于区分对象,不代表任何方向暗示。

MQL5 / C++
ObjectSetInteger(chart_id,name,OBJPROP_YSIZE,y_size); class=class="str">"cmt">// setting height Y
ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x_distance); class=class="str">"cmt">// setting X-coordinate
ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y_distance); class=class="str">"cmt">// setting Y-coordinate
ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,class="kw">false); class=class="str">"cmt">// cannot select the object if FALSE
ObjectSetInteger(chart_id,name,OBJPROP_STATE,class="kw">false); class=class="str">"cmt">// button state(clicked/unclicked)
ObjectSetInteger(chart_id,name,OBJPROP_ZORDER,z_order); class=class="str">"cmt">// higher/lower Z-order
ObjectSetString(chart_id,name,OBJPROP_TOOLTIP,"\n"); class=class="str">"cmt">// no tooltip if "\n"
}
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Returning a random class="type">color                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">color GetRandomColor()
  {
class=class="str">"cmt">//--- Select a random class="type">color from class="num">0 to class="num">25
  class="kw">switch(MathRand()%class="num">26)
    {
    case class="num">0   : class="kw">return(clrOrange);       break;
    case class="num">1   : class="kw">return(clrGold);         break;
    case class="num">2   : class="kw">return(clrChocolate);    break;
    case class="num">3   : class="kw">return(clrChartreuse);   break;
    case class="num">4   : class="kw">return(clrLime);         break;
    case class="num">5   : class="kw">return(clrSpringGreen);  break;
    case class="num">6   : class="kw">return(clrMediumBlue);   break;
    case class="num">7   : class="kw">return(clrDeepSkyBlue);  break;
    case class="num">8   : class="kw">return(clrBlue);         break;
    case class="num">9   : class="kw">return(clrSeaGreen);     break;
    case class="num">10  : class="kw">return(clrRed);          break;

◍ 颜色映射与声音面板的初始化参数

在指标或 EA 的配色逻辑里,用 switch-case 把整数索引映射到具体颜色常量是一种直白做法。上面片段覆盖了 11 到 25 号共 15 个枚举值,分别返回 clrSlateGray、clrPeru、clrBlueViolet 等预设色;任何未列出的索引都会落到 default 分支返回 clrGold,保证绘图不会因越界而报错。 声音面板的布局则由 SetSoundPanel() 里的几个局部变量定死。column_count 从 0 起算,x_dist=10 和 y_dist=15 是相对图表左上角的像素缩进,x_size=100、y_size=20 决定单个按钮的点击热区,button_color 初始化为 clrNONE 意味着先不填色,后续按状态再覆写。 开 MT5 把这段塞进自己的脚本时,重点看 y_size=20 和 y_dist=15 的组合——若图表缩放比例偏大,20 像素高按钮在 4K 屏上会显得偏窄,可把 y_size 调到 24 再编译验证。外汇与贵金属品种波动剧烈,这类界面参数只影响交互,不改变任何订单风险。

MQL5 / C++
case class="num">11 : class="kw">return(clrSlateGray);       break;
case class="num">12 : class="kw">return(clrPeru);            break;
case class="num">13 : class="kw">return(clrBlueViolet);      break;
case class="num">14 : class="kw">return(clrIndianRed);       break;
case class="num">15 : class="kw">return(clrMediumOrchid);    break;
case class="num">16 : class="kw">return(clrCrimson);         break;
case class="num">17 : class="kw">return(clrMediumAquamarine); break;
case class="num">18 : class="kw">return(clrDarkGray);        break;
case class="num">19 : class="kw">return(clrSandyBrown);      break;
case class="num">20 : class="kw">return(clrMediumSlateBlue); break;
case class="num">21 : class="kw">return(clrTan);             break;
case class="num">22 : class="kw">return(clrDarkSalmon);      break;
case class="num">23 : class="kw">return(clrBurlyWood);       break;
case class="num">24 : class="kw">return(clrHotPink);         break;
case class="num">25 : class="kw">return(clrLightSteelBlue);  break;
class=class="str">"cmt">//---
class="kw">default : class="kw">return(clrGold);
  }
class=class="str">"cmt">//---
  class="kw">return(clrGold);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Adding the sound panel to the chart                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void SetSoundPanel()
  {
  class="type">int   column_count =class="num">0;       class=class="str">"cmt">// Column counter
  class="type">int   x_dist       =class="num">10;      class=class="str">"cmt">// Indent from the left side of the chart
  class="type">int   y_dist       =class="num">15;      class=class="str">"cmt">// Indent from the top of the chart
  class="type">int   x_size       =class="num">100;     class=class="str">"cmt">// Button width
  class="type">int   y_size       =class="num">20;      class=class="str">"cmt">// Button height
  class="type">color button_color =clrNONE; class=class="str">"cmt">// Button class="type">color

「声音面板的排布与销毁逻辑」

这段实现把一组声音按钮按两列自动换行铺到图表左上角。循环里每画一个按钮 column_count 加一,当计数到 2 时把 x_dist 复位成 10、y_dist 累加 20,否则 x_dist 顺着 x_size 往右挪,等于用纯坐标算术替代了网格容器。 CreateButton 调用里写死了字号 8、字体 Arial、文字色 clrWhite,按钮底色由 GetRandomColor 随机给,这意味着每次加载指标面板配色都不同,回测或截图比对时得留意这点。 DeleteSoundPanel 遍历 name_sound_object 数组逐个调 DeleteObjectByName,后者先用 ObjectFind 确认对象存在再 ObjectDelete,删失败就 Print 出 GetLastError 码——开 MT5 挂这个指标后从导航栏拖走,若图表残留按钮,大概率能在专家日志里看到错误号。 OnInit 只干一件事:调 SetSoundPanel。想验证排布效果,直接把 ARRAY_SIZE、x_size、y_dist 步进改小,重新编译挂 EURUSD 图表看按钮是否重叠或溢出。外汇与贵金属图表加载自定义指标存在脚本异常导致卡图的高风险,建议在模拟账户先跑。

MQL5 / C++
for(class="type">int i=class="num">0; i<ARRAY_SIZE; i++)
  {
  class=class="str">"cmt">//--- Increase the column counter
  column_count++;
  class=class="str">"cmt">//--- Get the button class="type">color
  button_color=GetRandomColor();
  class=class="str">"cmt">//--- Draw a button
  CreateButton(class="num">0,class="num">0,sound_names[i],sound_texts[i],
               ANCHOR_LEFT_UPPER,CORNER_LEFT_UPPER,"Arial",class="num">8,
               clrWhite,button_color,button_color,x_size,y_size,x_dist,y_dist,class="num">1);
  class=class="str">"cmt">//--- If two buttons have already been set in the same row
  if(column_count==class="num">2)
    {
     x_dist=class="num">10;       class=class="str">"cmt">// Move the X-coordinate to the initial position
     y_dist+=class="num">20;      class=class="str">"cmt">// Set the Y-coordinate for the next row
     column_count=class="num">0;  class=class="str">"cmt">// Zero out the counter
    }
  else
  class=class="str">"cmt">//--- Set the X-coordinate for the next button 
     x_dist+=x_size;
  }
class=class="str">"cmt">//--- Refresh the chart
 ChartRedraw(class="num">0);
}
class="type">void DeleteSoundPanel()
  {
  for(class="type">int i=class="num">0; i<ARRAY_SIZE; i++)
    DeleteObjectByName(name_sound_object[i]);
  ChartRedraw();
  }
class="type">void DeleteObjectByName(class="type">class="kw">string name)
  {
  if(ObjectFind(ChartID(),name)>=class="num">0)
    {
    if(!ObjectDelete(ChartID(),name))
      Print("Error("+IntegerToString(GetLastError())+") when deleting the object!");
    }
  }
class="type">void OnInit()
  {
  SetSoundPanel();
  }

声音面板的事件响应与配色刷新

指标卸载时,OnDeinit 直接调用 DeleteSoundPanel() 清理图形对象,避免残留按钮留在图表上占用资源。 点击音效按钮后的视觉反馈由 ChangeColorsOnSoundPanel() 负责:它用 for 循环遍历 ARRAY_SIZE 个按钮,逐个取随机色并改写背景与边框,每次改完调用 ChartRedraw(0) 重绘,再用 Sleep(20) 制造 20 毫秒间隔,形成闪烁扫过效果。 真正的入口在 OnChartEvent。当 id 等于 CHARTEVENT_OBJECT_CLICK 且 sparam 里能找到 "sound_button" 子串,就通过 GetSoundPath(sparam) 定位音频文件并 PlaySound 播放;若返回失败,Print 打出 GetLastError() 便于排查(例如 5019 代表文件不存在)。播放完后顺手调一次 ChangeColorsOnSoundPanel() 重置所有按钮颜色。 外汇与贵金属品种波动剧烈、滑点频发,这类面板仅作交互辅助,实盘前请在 MT5 策略测试器或离线图表验证音频路径与对象命名是否一致。

MQL5 / C++
class="type">void OnDeinit(const class="type">int reason)
  {
class=class="str">"cmt">//--- Delete the sound panel
   DeleteSoundPanel();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Changing colors on the sound panel                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void ChangeColorsOnSoundPanel()
  {
   class="type">color clr=clrNONE; class=class="str">"cmt">// Button class="type">color
class=class="str">"cmt">//--- Iterate over all buttons in a loop and change their class="type">color
   for(class="type">int i=class="num">0; i<ARRAY_SIZE; i++)
     {
      class=class="str">"cmt">//--- Get the new class="type">color
      clr=GetRandomColor();
      class=class="str">"cmt">//--- Set the border class="type">color
      ObjectSetInteger(class="num">0,sound_names[i],OBJPROP_BGCOLOR,clr);
      class=class="str">"cmt">//--- Set the background class="type">color
      ObjectSetInteger(class="num">0,sound_names[i],OBJPROP_BORDER_COLOR,clr);
      class=class="str">"cmt">//--- Unclicked button
      ObjectSetInteger(class="num">0,sound_names[i],OBJPROP_STATE,class="kw">false);
      class=class="str">"cmt">//--- Refresh the chart
      ChartRedraw(class="num">0);
      class=class="str">"cmt">//--- Wait for class="num">20 ms(lag)
      Sleep(class="num">20);
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Event handler                                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnChartEvent(const class="type">int     id,     class=class="str">"cmt">// Event identifier
                  const class="type">long&   lparam, class=class="str">"cmt">// Parameter of the event of type class="type">long
                  const class="type">class="kw">double& dparam, class=class="str">"cmt">// Parameter of the event of type class="type">class="kw">double
                  const class="type">class="kw">string& sparam) class=class="str">"cmt">// Parameter of the event of type class="type">class="kw">string
  {
class=class="str">"cmt">//--- If there was an event of left-clicking on the object
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      class=class="str">"cmt">//--- If the object name contains "sound_button"
      if(StringFind(sparam,"sound_button",class="num">0)>=class="num">0)
        {
         class=class="str">"cmt">//--- Play the sound based on the object name
         class=class="str">"cmt">//     class="num">5019 - ERR_FILE_NOT_EXIST - The file does not exist
         if(!PlaySound(GetSoundPath(sparam)))
            Print("Error: ",GetLastError());
         class=class="str">"cmt">//--- Change colors of all buttons
         ChangeColorsOnSoundPanel();
        }
     }
  }

◍ 把成交事件映射成声音反馈

EA 在图表上点击声音面板对象时,需要把对象名翻译成具体音频文件。下面这个函数遍历预设数组,命中即返回路径,未命中返回空串,调用方据此决定是否播放提示音。

MQL5 / C++
class="type">class="kw">string GetSoundPath(class="type">class="kw">string object_name)
  {
   for(class="type">int i=class="num">0; i<ARRAY_SIZE; i++)
     {
      if(object_name==name_sound_object[i])
        class="kw">return(path_sound_object[i]);
     }
   class="kw">return("");
  }
逐行拆解:函数入口拿到图表传来的 object_name;for 循环从 0 跑到 ARRAY_SIZE-1,挨个比对 name_sound_object 数组;一旦名字相等,直接 return 对应 path_sound_object[i] 的音源地址;循环走完没匹配就回空串,避免播放无效资源。 实际挂 EA 前,要在代码里用 #resource 把 WAV 编进 ex5:AHOOGA.WAV 留给报错,CASHREG.WAV 对应开仓或加仓,WHOOSH.WAV 管挂单与止损止盈改动,VERYGOOD.WAV 是盈利平仓,DRIVEBY.WAV 是亏损平仓。路径统一写成 "::Files\\Sounds\\XXX.WAV" 这种双冒号内联格式。 外部参数里 UseSound 默认 true,关掉就完全静音;配合 Lot=0.1、TakeProfit=100、StopLoss=50、TrailingStop=10 这类默认值,你可以在 MT5 里改完直接编译运行,听一声 CASHREG 就知道反向单(Reverse=true)有没有触发。外汇与贵金属杠杆高,声音提醒只是辅助,不代表信号胜率,下单前仍须自验风险。

MQL5 / C++
class="type">class="kw">string GetSoundPath(class="type">class="kw">string object_name)
  {
   for(class="type">int i=class="num">0; i<ARRAY_SIZE; i++)
     {
      if(object_name==name_sound_object[i])
        class="kw">return(path_sound_object[i]);
     }
   class="kw">return("");
  }

常见问题

在声音面板初始化里把事件映射改成自定义音频,并把随机配色关掉,避免视觉干扰,只留听觉反馈。
关闭随机配色逻辑,在颜色映射参数里写死买卖方向对应的 RGB 值,刷新时只重绘不重算。
小布可在对应品种页内置 AIGC 诊断,把成交事件直接映射成声音反馈与配色刷新,打开即可用。
在销毁逻辑里依次释放按钮对象、颜色句柄与事件钩子,确认无残留引用再退出,防止盘口卡顿。
多是事件响应里做了同步重绘,改为异步刷新配色、只播关键成交声音,可把延迟压到毫秒级。