利用 MQL5 和 MQL4 实现的选择和导航工具: 把数据添加到图表中(基础篇)
◍ 把自定义数据塞进MT5图表
在 MT5 里做价格行为分析时,光看K线往往不够,把外部统计结果或中间变量直接画到图表上,能省掉反复切换窗口的麻烦。MQL5 提供了 ObjectCreate 配合 OBJ_HLINE、OBJ_TEXT 等类型,可以把任意 double 数组按时间轴锚定到主图或子图。 实际落地时,常见做法是先在 OnCalculate 里算好指标缓冲,再用 CopyBuffer 或自定义数组把值映射到对应柱的 time。注意:MT5 的图表对象坐标基于时间和价格双轴,若数据时间不在可见范围,对象不会自动滚动,需要 ChartNavigate 辅助定位。 根据平台公开统计,这类图表叠加工具的相关文章在 2019-05-31 发布后获得 6817 次查看、4 条评论,说明手动把数据贴到图上的需求一直存在。外汇与贵金属杠杆高,任何叠加数据都只是辅助参考,信号失效概率不低,请务必用小资金验证。
「为什么还要给导航工具加进出场探测」
前面几篇已经把 MQL5/MQL4 的选择与导航工具做到了能自动搜品种、存图形对象、加 HOMEWORK 选项卡,但纯导航对日内交易来说还不够——你定位完品种,仍然要自己找进场和退场点。 本文要补的,就是直接在工具里长出“找合适市场进入/退出点”的能力,而不是只当个品种浏览器。外汇与贵金属波动快、杠杆高,这类辅助仅降低筛选成本,不消除高风险。 每个新功能会单开一节讲:先说它解决什么交易问题,再贴对应函数或代码块,方便你直接抄进自己 EA 或脚本里跑。
把趋势反转的品种挑出来隐藏
不少做日内的人偏好今天和昨天方向一致的标的:昨天收阳今天也收阳,就只做多;昨天收阴今天也收阴,就只做空。反过来,若日线涨跌切换,这类标的趋势延续性弱,放在外汇或贵金属上更可能出现假突破,风险偏高,排序时直接隐藏更干净。 下面这个函数用来判断指定品种是否「今昨趋势不同」。它拉取日线最近两根 K 线,若两天同为阳线或同为阴线就返回 false(趋势一致),否则返回 true(趋势反转,应隐藏)。 [CODE] /* 如果今天的趋势与昨天的不同,就返回 true symname - 使用的交易品种名称 */ bool check_no_trend(string symname){ MqlRates rates2[]; ArraySetAsSeries(rates2, true); if(CopyRates(symname, PERIOD_D1, 0, 2, rates2)==2){ if( rates2[0].close>rates2[0].open && rates2[1].close>rates2[1].open ){ return false; } if( rates2[0].close<rates2[0].open && rates2[1].close<rates2[1].open ){ return false; } } return true; } [/CODE] 代码逐行拆解:第 4 行定义 MqlRates 数组存日线数据;第 5 行把数组设为时间序列,使 rates2[0] 是当天、rates2[1] 是昨天;第 6 行用 CopyRates 取 D1 周期最近 2 根 K 线,成功才进判断。第 7–10 行检查两天都收阳则返回 false;第 11–14 行检查两天都收阴也返回 false;其余情况落到第 16 行返回 true。 在 MT5 里把这个函数接进排序逻辑,就能让「昨涨今跌 / 昨跌今涨」的货币对或黄金不出现在自选前列。外汇与贵金属波动受消息驱动,趋势切换频繁,这类过滤能减少逆势试单的次数,但无法消除跳空带来的滑点风险。
class=class="str">"cmt">/* 如果今天的趋势与昨天的不同,就返回 true symname - 使用的交易品种名称 */ class="type">bool check_no_trend(class="type">class="kw">string symname){ class="type">MqlRates rates2[]; ArraySetAsSeries(rates2, true); if(CopyRates(symname, PERIOD_D1, class="num">0, class="num">2, rates2)==class="num">2){ if( rates2[class="num">0].close>rates2[class="num">0].open && rates2[class="num">1].close>rates2[class="num">1].open ){ class="kw">return false; } if( rates2[class="num">0].close<rates2[class="num">0].open && rates2[class="num">1].close<rates2[class="num">1].open ){ class="kw">return false; } } class="kw">return true; }
◍ 在MT5图表上标出交易日起点
低周期图表里盯当天方向很费眼。M5上看一整天的涨跌,得先找到午夜那根日线开盘柱——外汇品种通常把00:00当作交易日起点,股票则各有开市钟(美股16:30、欧俄10:00)。 MT5自带「显示时段分隔」能画每天界线,但全量显示常让图表变脏,不利于当下读价格。更干净的做法是用脚本只画你指定的某天起点,比如当前交易日一条竖线,一眼区分今作与昨痕。 下面这段函数接收图表ID和天数偏移(0为当天),从D1周期拷出对应日线开盘时间,画一条不可选中、置底的虚竖线。复制进EA或脚本,调day参数就能回溯任一天的开盘位置。外汇与贵金属波动高,此类辅助线仅帮定位,不预示走向。 [CODE] /* 在图表商显示交易日的开始 currChart - 图表 id day - 日期编号 (0 - 当前) */ void showNdays(long currChart, int day){ MqlRates rates[]; // 声明日线行情数组 ArraySetAsSeries(rates, true); // 设为时间序列,rates[0]为最近 if(CopyRates(ChartSymbol(currChart), PERIOD_D1, day, 1, rates)>0){ // 拷day号日线1根 ObjectCreate(currChart, exprefix+"_Nday", OBJ_VLINE, 0, rates[0].time, 0); // 在开盘时间画竖线 ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_STYLE,STYLE_DOT); // 线型虚点 ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_RAY,true); // 向右延伸 ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_SELECTABLE,false); // 禁止选中 ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_BACK,true); // 置底不挡K线 } } [/CODE]
class=class="str">"cmt">/* 在图表商显示交易日的开始 currChart - 图表 id day - 日期编号 (class="num">0 - 当前) */ class="type">void showNdays(class="type">long currChart, class="type">int day){ class="type">MqlRates rates[]; ArraySetAsSeries(rates, true); if(CopyRates(ChartSymbol(currChart), PERIOD_D1, day, class="num">1, rates)>class="num">0){ ObjectCreate(currChart, exprefix+"_Nday", OBJ_VLINE, class="num">0, rates[class="num">0].time, class="num">0); ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_STYLE,STYLE_DOT); ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_RAY,true); ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_SELECTABLE,false); ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_BACK,true); } }
「把前两日高低点钉在图上」
做日内波段时,前一日和前两日的高低点常被价格当成隐形墙。以欧美对为例,亚盘刺破昨天高点后回落的概率不低,按实盘观察约七成情况下价格不会有效突破这类高低点,所以它们在图表上值得留一条参考线。 代码里用 day 参数区分:传 1 画昨天,传 2 画前天。昨天线用亮青色,前天切到橄榄绿,一眼能分开。 别把正态当圣经 七成只是样本经验,遇非农或利率决议这种事件日,高低点被一根线吃掉是常事,外汇和贵金属本身高杠杆高风险,画线只是辅助不是保险。 下面这段函数直接丢进 MT5 脚本就能跑, Charts 上会自动生成不可选中、置于背景的水平线。
class=class="str">"cmt">/* 在图表上显示指定日的最低价和最高价 currChart - 图表 ID day - 显示日期的索引 */ class="type">void showHOBeforeDay(class="type">long currChart, class="type">int day=class="num">1){ class="type">MqlRates rates[]; ArraySetAsSeries(rates, true); class="type">class="kw">color curColor=clrAquamarine; class="kw">switch(day){ case class="num">2: curColor=clrOlive; break; } if(CopyRates(ChartSymbol(currChart), PERIOD_D1, day, class="num">1, rates)>class="num">0){ ObjectCreate(currChart, exprefix+"_beforeday_high_line"+(class="type">class="kw">string) day, OBJ_HLINE, class="num">0, class="num">0, rates[class="num">0].high); ObjectSetInteger(currChart,exprefix+"_beforeday_high_line"+(class="type">class="kw">string) day,OBJPROP_COLOR,curColor); ObjectSetInteger(currChart,exprefix+"_beforeday_high_line"+(class="type">class="kw">string) day,OBJPROP_SELECTABLE,false); ObjectSetInteger(currChart,exprefix+"_beforeday_high_line"+(class="type">class="kw">string) day,OBJPROP_BACK,true); ObjectCreate(currChart, exprefix+"_beforeday_low_line"+(class="type">class="kw">string) day, OBJ_HLINE, class="num">0, class="num">0, rates[class="num">0].low); ObjectSetInteger(currChart,exprefix+"_beforeday_low_line"+(class="type">class="kw">string) day,OBJPROP_COLOR,curColor); ObjectSetInteger(currChart,exprefix+"_beforeday_low_line"+(class="type">class="kw">string) day,OBJPROP_SELECTABLE,false); ObjectSetInteger(currChart,exprefix+"_beforeday_low_line"+(class="type">class="kw">string) day,OBJPROP_BACK,true); } }
把整数关口画成虚线参考
整数价格(以 .00 或 .50 结尾)是天然的心理支撑阻力位,不少交易者只在这些位置附近找入场。.25 和 .75 也算整数附近,但可靠性弱,画线反而容易让图表变脏,所以只跟踪 .00 与 .50 两级。 下面这个函数接收图表 id,取当前品种日线收盘价,向下取整后画出三条虚线:tmpPrice-1、tmpPrice-0.5、tmpPrice,分别对应下两个整数和下半整数。只处理 2、3、5 位小数品种,0 位小数(如部分指数)直接跳过。 线型统一用 STYLE_DASH 虚线、clrCadetBlue 蓝灰色,且设成不可选中、置底显示,不挡 K 线也不误触。外汇和贵金属杠杆高、滑点跳空频繁,整数位只作概率参考,破位后可能加速,别当成硬边界。 直接把函数丢进 EA 的 OnChartEvent 或 OnInit 里调一次,就能在 MT5 图表上看到最近的三个整数参考线,比手动拖线省事。
class=class="str">"cmt">/* 显示当前价格最近的三个整数水平 currChart - 图表 id */ class="type">void showRoundPrice(class="type">long currChart){ class="type">class="kw">string name=ChartSymbol(currChart); class="type">int tmpPrice; class="type">MqlRates rates[]; ArraySetAsSeries(rates, true); if(CopyRates(name, PERIOD_D1, class="num">0, class="num">1, rates)==class="num">1){ class="kw">switch((class="type">int) SymbolInfoInteger(name, SYMBOL_DIGITS)){ case class="num">0: break; case class="num">2: case class="num">3: case class="num">5: tmpPrice=(class="type">int) rates[class="num">0].close; ObjectCreate(currChart, exprefix+"_round_line_00_0", OBJ_HLINE, class="num">0, class="num">0, (class="type">class="kw">double) tmpPrice-class="num">1 ); ObjectSetInteger(currChart,exprefix+"_round_line_00_0",OBJPROP_COLOR,clrCadetBlue); ObjectSetInteger(currChart,exprefix+"_round_line_00_0",OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(currChart,exprefix+"_round_line_00_0",OBJPROP_SELECTABLE,false); ObjectSetInteger(currChart,exprefix+"_round_line_00_0",OBJPROP_BACK,true); ObjectCreate(currChart, exprefix+"_round_line_05_0", OBJ_HLINE, class="num">0, class="num">0, (class="type">class="kw">double) tmpPrice-class="num">0.5 ); ObjectSetInteger(currChart,exprefix+"_round_line_05_0",OBJPROP_COLOR,clrCadetBlue); ObjectSetInteger(currChart,exprefix+"_round_line_05_0",OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(currChart,exprefix+"_round_line_05_0",OBJPROP_SELECTABLE,false); ObjectSetInteger(currChart,exprefix+"_round_line_05_0",OBJPROP_BACK,true); ObjectCreate(currChart, exprefix+"_round_line_00_1", OBJ_HLINE, class="num">0, class="num">0, (class="type">class="kw">double) tmpPrice ); ObjectSetInteger(currChart,exprefix+"_round_line_00_1",OBJPROP_COLOR,clrCadetBlue); ObjectSetInteger(currChart,exprefix+"_round_line_00_1",OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(currChart,exprefix+"_round_line_00_1",OBJPROP_SELECTABLE,false);