MQL5 Cookbook: 处理典型图表事件·综合运用
🎛️

MQL5 Cookbook: 处理典型图表事件·综合运用

(3/3)·从事件分类到处理器模板,一套可套用的 MQL5 交互框架收尾

偏理论进阶 第 3/3 篇
很多交易者写了 OnChartEvent 却只处理鼠标点击,键盘与对象拖拽事件常年闲置。半自动系统卡在「手动改了挂单却没被 EA 识别」的坑里,往往就是事件类型没覆盖全。外汇贵金属杠杆高,事件逻辑漏一处就可能误触发下单。

◍ 用两条竖线驱动矩形区重绘

在 MT5 脚本里做价格区间可视化,常见做法是手拖两条竖线当时间边界,再让矩形自动贴合。下面这段逻辑就是先抓这两条线的坐标,再决定是否刷新矩形。 代码先用 ObjectFind 按名字找两条竖线,找到就把 OBJPROP_TIME 读进 datetime 变量。若 time_limit1 和 time_limit2 都大于 0,说明两条线都在图上,且当 time_limit1 < time_limit2 时才认为区间方向正确,进入重绘分支。 重绘前调用 RefreshRecPoints 算出新起止时间,随后用 ObjectMove 改矩形四个角点。任意一次 Move 返回 false 就 Print 报错并 return,避免半截矩形留在图上。外汇与贵金属波动剧烈,这类手动画线工具仅作参考,实际信号误判概率不低。 别把对象名写死在多处 gRectLimit1_name、gTimeLimit1_name 这类全局名如果在别的函数也被引用,改一处漏一处就会让 ObjectFind 永远返回 -1。建议把名字集中到宏或全局常量,开 MT5 跑一遍找线失败的情况就能复现。

MQL5 / C++
class=class="str">"cmt">//--- the time coordinate of vertical lines
class="type">class="kw">datetime time_limit1=class="num">0;
class="type">class="kw">datetime time_limit2=class="num">0;
class=class="str">"cmt">//--- find the first vertical line
if(ObjectFind(class="num">0,gTimeLimit1_name)>-class="num">1)
   time_limit1=(class="type">class="kw">datetime)ObjectGetInteger(class="num">0,gTimeLimit1_name,OBJPROP_TIME);
class=class="str">"cmt">//--- find the second vertical line
if(ObjectFind(class="num">0,gTimeLimit2_name)>-class="num">1)
   time_limit2=(class="type">class="kw">datetime)ObjectGetInteger(class="num">0,gTimeLimit2_name,OBJPROP_TIME);
class=class="str">"cmt">//--- if vertical lines are found
if(time_limit1>class="num">0 && time_limit2>class="num">0)
   if(time_limit1<time_limit2)
     {
      class=class="str">"cmt">//--- update properties of rectangles
      class="type">class="kw">datetime start_time=time_limit1;
      class="type">class="kw">datetime finish_time=time_limit2;
      class=class="str">"cmt">//---
      if(RefreshRecPoints(start_time,finish_time))
        {
         class=class="str">"cmt">//---
         if(!ObjectMove(class="num">0,gRectLimit1_name,class="num">0,gRec1_time1,gRec1_pr1))
           {
            Print("Failed to move the 1st point!");
            class="kw">return;
           }
         if(!ObjectMove(class="num">0,gRectLimit1_name,class="num">1,gRec1_time2,gRec1_pr2))
           {
            Print("Failed to move the 2nd point!");
            class="kw">return;
           }
         class=class="str">"cmt">//---
         if(!ObjectMove(class="num">0,gRectLimit2_name,class="num">0,gRec2_time1,gRec2_pr1))
           {

「图表事件里的矩形拖拽与文本下单」

在 MT5 的 OnChartEvent 里处理 CHARTEVENT_OBJECT_DRAG 时,若用户拖动了矩形限制框的两个锚点,需要分别用 ObjectMove 重写坐标。第一段点移动失败就打印 "Failed to move the 1st point!" 并 return,第二段点失败则提示 "Failed to move the 2nd point!",这说明两个锚点独立校验,任一失效都不继续后续逻辑。 到了 CHARTEVENT_OBJECT_ENDEDIT 分支,系统先取 sparam 里的对象名,与全局编辑框 gEdit_name 比对。若一致,就通过 ObjectGetString 读 OBJPROP_TEXT,拿到用户输入的串。 读到文本后做不区分大小写比对:输入 "Buy" 且 TryToBuy() 返回真,编辑框染蓝(clrBlue);输入 "Sell" 且 TryToSell() 返回真,染红(clrRed)。外汇与贵金属杠杆高,这类一键下单框误触可能瞬间建仓,实盘前务必在策略测试器用历史 tick 跑通 TryToBuy/TryToSell 的返回路径。

MQL5 / C++
if(!ObjectMove(class="num">0,gRectLimit2_name,class="num">1,gRec2_time2,gRec2_pr2))
  {
   Print("Failed to move the 2nd point!");
   class="kw">return;
  }
case CHARTEVENT_OBJECT_ENDEDIT:
  {
   class="type">class="kw">string curr_obj_name=sparam;
   if(!StringCompare(curr_obj_name,gEdit_name))
     {
      class="type">class="kw">string obj_text=NULL;
      if(ObjectGetString(class="num">0,curr_obj_name,OBJPROP_TEXT,class="num">0,obj_text))
        {
         if(!StringCompare(obj_text,"Buy",false))
           {
            if(TryToBuy())
             ObjectSetInteger(class="num">0,gEdit_name,OBJPROP_COLOR,clrBlue);
           }
         else if(!StringCompare(obj_text,"Sell",false))
           {
            if(TryToSell())
             ObjectSetInteger(class="num">0,gEdit_name,OBJPROP_COLOR,clrRed);
           }
        }
     }
  }

图表尺寸不变时强锁显示属性

在 CHARTEVENT_CHART_CHANGE 事件里,先取 ChartHeightInPixelsGet() 与 ChartWidthInPixels() 的实时返回值,再和全局变量 gChartHeight、gChartWidth 比对。若两者都相等,说明用户只是切了品种或刷新,并没有拖拽改变窗口大小,此时脚本会强行把网格、图表类型、背景色按输入参数复位。 这段逻辑的核心价值在于:MT5 图表在重绘或切换标的时,经常把用户手动改过的显示属性冲掉。用「尺寸未变」作为判断闸门,可以避免每次重绘都反复写属性,减少无谓的 ChartRedraw 调用。 代码里对 ChartShowGridSet(InpToShowGrid)、ChartModeSet(InpMode)、ChartBackColorSet(InpBackColor) 三个调用都做了失败检测,任一返回 false 就 Print 报错并 return。你在 EA 里套用时,可以把 InpBackColor 设成 clrBlack 做暗色模板,外汇与贵金属波动剧烈、杠杆高,参数误设可能导致视觉误判,建议先在模拟盘验证。 若尺寸确实变了,则只更新 gChartHeight 与 gChartWidth 两个全局值,不做属性重置——相当于记一下新窗口大小,等下次事件再来比对。

MQL5 / C++
         {
                  class=class="str">"cmt">//--- set text class="type">color
                  ObjectSetInteger(class="num">0,gEdit_name,OBJPROP_COLOR,clrGray);
                }
              class=class="str">"cmt">//--- redraw chart
              ChartRedraw();
             }
          }
        class=class="str">"cmt">//---
        break;
      }
class=class="str">"cmt">//--- class="num">10
      case CHARTEVENT_CHART_CHANGE:
        {
         class=class="str">"cmt">//--- current height and width of the chart
         class="type">int curr_ch_height=ChartHeightInPixelsGet();
         class="type">int curr_ch_width=ChartWidthInPixels();
         class=class="str">"cmt">//--- if chart height and width have not changed
         if(gChartHeight==curr_ch_height && gChartWidth==curr_ch_width)
           {
            class=class="str">"cmt">//--- fix the properties:
            class=class="str">"cmt">//--- display grid
            if(!ChartShowGridSet(InpToShowGrid))
              {
               Print("Failed to show grid!");
               class="kw">return;
              }
            class=class="str">"cmt">//--- type of chart display
            if(!ChartModeSet(InpMode))
              {
               Print("Failed to set mode!");
               class="kw">return;
              }
            class=class="str">"cmt">//--- background class="type">color
            if(!ChartBackColorSet(InpBackColor))
              {
               Print("Failed to set background сolor!");
               class="kw">return;
              }
           }
         class=class="str">"cmt">//--- store window dimensions
         else
           {
            gChartHeight=curr_ch_height;
            gChartWidth=curr_ch_width;
           }
         class=class="str">"cmt">//---
         comment+="class="num">10) modify chart";
         class=class="str">"cmt">//---
         break;
        }

◍ 别急着下结论

这套图表事件处理的例程,核心价值在于把 MT5 里散落的鼠标、键盘、自定义消息等事件归到统一的处理框架里。刚接触 MQL5 的人直接抄文里的事件分类代码,能在半小时内跑通一个响应 ChartEvent 的脚本。 外汇与贵金属市场高杠杆、高波动,任何基于事件触发的自动化逻辑都只是概率优势,实盘前务必在策略测试器里用历史数据验证延迟与漏触发。 社区里有人提到多图表同步用全局对象加 OnTimer 轮询会有延迟,后来改走 ChartEvent 广播才解决——这类坑只有自己开三个图表实测才会暴露。 写代码时别把例程当万能模板,先想清楚你要捕获的是哪一类事件,再决定用定时器还是消息队列。

交给小布看盘口事件流
这些图表事件的诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到哪些交互被 EA 捕获、哪些被静默丢弃,你专注决策就好。

常见问题

id 来自 ENUM_CHART_EVENT 枚举,涵盖按键、对象创建删除、点击、拖拽、文本编辑等;自定义事件段保留 65535 个标识符,但本篇未展开。
可以,小布盯盘内置了事件流诊断,能列出当前品种页 EA 捕获与忽略的图表交互,省去你手动加 Print 调试。
EventChartCustom 按参数向指定图表发自定义事件,目标图表 OnChartEvent 用对应 id 接收,适合多品种协同的半自动逻辑。
OnChartEvent 入参为常量保证事件信息不被篡改,模板内用局部变量做分支判断,避免副作用影响其他事件处理路径。