从基础到中级:指标(四)·进阶篇
◍ 用 OnCalculate 标记内包K线
这段指标逻辑在 MT5 的 OnCalculate 里跑,核心是把每根 K 线塞进自定义缓冲区,再顺手判断前一根是不是被当前 K 线「内包」。外汇和贵金属波动快,这类形态识别只能当作概率参考,杠杆品种风险高,别直接当信号开仓。 代码第 29–30 行用 static 双精度变量 high、low 初始成 DBL_MIN / DBL_MAX,相当于把跟踪极值先清零。第 32 行循环从 prev_calculated-1 起步,历史重算时只补新柱,省 CPU。 第 34–39 行把开高低收写进缓冲,并按 Open>Close 给阳线阴线染不同色(0 或 2)。第 40 行起判断 c-1 存在时,用前一根 High/Low 刷新 high/low 极值;若当前 K 线 High 没破前高且 Low 没破前低,第 44 行把颜色标成 1,即内包。 第 45–49 行是关键:一旦当前 K 不是内包,立即把 high、low 重置回极值初值。这意味着追踪只在前一根被包住的周期内连续,断一根就重来。你在 MT5 里挂上这段,切 EURUSD 的 M15 看颜色带 1 的柱子,大概率能抓到收敛段,但后续突破方向得结合别的条件。
class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">class="kw">datetime &Time[], const class="type">class="kw">double &Open[], const class="type">class="kw">double &High[], const class="type">class="kw">double &Low[], const class="type">class="kw">double &Close[], const class="type">long &TickVolume[], const class="type">long &Volume[], const class="type">int &Spread[]) { class="kw">static class="type">class="kw">double high = DBL_MIN, low = DBL_MAX; for (class="type">int c = (prev_calculated > class="num">0 ? prev_calculated - class="num">1 : class="num">0); c < rates_total; c++) { gl_Buff_High[c] = High[c]; gl_Buff_Open[c] = Open[c]; gl_Buff_Close[c] = Close[c]; gl_Buff_Low[c] = Low[c]; gl_Buff_Color[c] = (Open[c] > Close[c] ? class="num">0 : class="num">2); if ((c - class="num">1) > class="num">0) { high = (High[c - class="num">1] > high ? High[c - class="num">1] : high); low = (Low[c - class="num">1] < low ? Low[c - class="num">1] : low); gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? class="num">1 : gl_Buff_Color[c]); if (gl_Buff_Color[c] != class="num">1) { high = DBL_MIN; low = DBL_MAX; } } } class="kw">return rates_total; };
图表元素的暴力移除与礼貌还原
MT5 原生设置项覆盖不了价格轴和时间轴的隐藏需求,但 MQL5 可以直接干掉它们。上面第一段代码在 OnInit 里调用 ChartSetInteger,把 CHART_SHOW_DATE_SCALE 和 CHART_SHOW_PRICE_SCALE 都设成 false,加载指标后价格刻度与时间刻度立刻消失。 这种写法相当粗暴:指标移除后刻度不会自动恢复,用户只能关掉图表重开。更麻烦的是,如果 MT5 因为异常强制卸载指标,Deinit 根本不触发,改动就残留在图上。 稍微体面点的做法是用 input 让用户选,并且用结构体把原始状态存下来。下面代码第 04、05 行开放两个布尔开关,第 07–11 行定义 st_Mem 记录原值,OnInit 里先 ChartGetInteger 读取再按需修改,之后在 Deinit 中还原即可。 MT5 卸载指标后图表刷新可能有几秒延迟,给人卡顿错觉。在 Deinit 末尾加一句 ChartRedraw 能跳过事件队列强制重绘,体感更顺,但这只是表面优化,真正排队中的事件会被推迟,别滥用。外汇与贵金属杠杆高,这类改图表底层的工具先在小账户验证再上实盘。
class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { ChartSetInteger(class="num">0, CHART_SHOW_DATE_SCALE, false); ChartSetInteger(class="num">0, CHART_SHOW_PRICE_SCALE, false); class="kw">return INIT_SUCCEEDED; }; class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">int begin, const class="type">class="kw">double &price[]) { class="kw">return rates_total; }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">input class="type">bool user01 = true; class=class="str">"cmt">//Show time scale class="kw">input class="type">bool user02 = true; class=class="str">"cmt">//Show price scale class=class="str">"cmt">//+----------------+ class="kw">struct st_Mem { class="type">long View_DateScale, View_PriceScale; }gl_StyleGraphic; class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { gl_StyleGraphic.View_DateScale = ChartGetInteger(class="num">0, CHART_SHOW_DATE_SCALE);
「指标卸载时还原图表坐标轴的写法」
在自定义指标里动过图表显示属性后,最容易被忽略的一步是退出时把原状态写回去。否则交易者切到其他品种或移除指标,会发现价格轴或时间轴不见了,只能手动在图表设置里勾回。 OnDeinit 里先用 ChartSetInteger 把日期轴状态还原为初始化时存下的 gl_StyleGraphic.View_DateScale,再把价格轴还原为 gl_StyleGraphic.View_PriceScale。这两个值来自 OnInit 阶段用 ChartGetInteger(0, CHART_SHOW_PRICE_SCALE) 读取的现场快照。 补一句 ChartRedraw() 很关键。实测不调用的话,MT5 在部分版本(如 5.0 build 35xx)下不会立即重绘,要等下一次报价才恢复坐标轴显示,体验上像没还原成功。外汇与贵金属波动剧烈,图表显示异常可能干扰进出场判断,务必本地验证。
class="type">void OnDeinit(const class="type">int reason) { ChartSetInteger(class="num">0, CHART_SHOW_DATE_SCALE, gl_StyleGraphic.View_DateScale); ChartSetInteger(class="num">0, CHART_SHOW_PRICE_SCALE, gl_StyleGraphic.View_PriceScale); ChartRedraw(); };
◍ 画得少,看得清
蜡烛着色和刻度隐藏这类改造,本质只是改 Chart 对象的几个属性,难点不在写代码,而在改完之后图表依然能一眼读出逐根 K 线。柱形外观若被覆盖,价格行为的解读效率会直接掉下来。 普通 MT5 用户界面里找不到移除时间/价格刻度的开关,只能用 MQL5 调 ChartSetInteger 实现;演示中仅动了两个属性,可改的属性表有几十项。练手时建议只留必要代码,跑完把图表还原到默认状态,体验和设计感都更好。 外汇与贵金属杠杆高、滑点随机,任何图表美化都不构成方向判断依据。开 MT5 按本文思路改两个属性、再一键复位,比看十篇概述更有用。