MQL5 Cookbook: 处理典型图表事件·综合运用
(3/3)·从事件分类到处理器模板,一套可套用的 MQL5 交互框架收尾
◍ 用两条竖线驱动矩形区重绘
在 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 跑一遍找线失败的情况就能复现。
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 的返回路径。
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 两个全局值,不做属性重置——相当于记一下新窗口大小,等下次事件再来比对。
{
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 广播才解决——这类坑只有自己开三个图表实测才会暴露。 写代码时别把例程当万能模板,先想清楚你要捕获的是哪一类事件,再决定用定时器还是消息队列。