MQL5 细则手册:MetaTrader 5 交易事件的声音通知·进阶篇
按钮对象的属性落位与随机配色
在 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 种;外汇与贵金属波动剧烈、杠杆风险高,随机色仅用于区分对象,不代表任何方向暗示。
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 再编译验证。外汇与贵金属品种波动剧烈,这类界面参数只影响交互,不改变任何订单风险。
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 图表看按钮是否重叠或溢出。外汇与贵金属图表加载自定义指标存在脚本异常导致卡图的高风险,建议在模拟账户先跑。
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 策略测试器或离线图表验证音频路径与对象命名是否一致。
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 在图表上点击声音面板对象时,需要把对象名翻译成具体音频文件。下面这个函数遍历预设数组,命中即返回路径,未命中返回空串,调用方据此决定是否播放提示音。
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(""); }
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(""); }