从基础到中级:指标(四)·进阶篇
📘

从基础到中级:指标(四)·进阶篇

第 2/2 篇

◍ 用 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 的柱子,大概率能抓到收敛段,但后续突破方向得结合别的条件。

MQL5 / C++
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 能跳过事件队列强制重绘,体感更顺,但这只是表面优化,真正排队中的事件会被推迟,别滥用。外汇与贵金属杠杆高,这类改图表底层的工具先在小账户验证再上实盘。

MQL5 / C++
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)下不会立即重绘,要等下一次报价才恢复坐标轴显示,体验上像没还原成功。外汇与贵金属波动剧烈,图表显示异常可能干扰进出场判断,务必本地验证。

MQL5 / C++
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 按本文思路改两个属性、再一键复位,比看十篇概述更有用。

常见问题

在自定义指标里用OnCalculate逐根比较高低点,当前K线高点低于前一根高点且低点高于前一根低点时,用箭头或色块标记即可,开软件加载指标就能验证。
用ObjectDelete按前缀批量移除旧对象最干净;若只想临时隐藏,用ObjectSetInteger改可见性,比暴力删更安全。
小布可读取你品种页的指标加载状态,提示坐标轴被改写或对象未清理的异常,你再对照代码修OnDeinit里的还原写法。
在OnDeinit里用ChartSetInteger把自动缩放和值域还原成默认,否则手动拖过的坐标会卡住,下次加载图还歪。
只标关键结构(如内包K线)能降噪,过多辅助线会掩盖价格行为;先跑通单信号标记,再决定是否加第二层。