MQL5 简介(第 15 部分):构建自定义指标的初学者指南(四)·进阶篇
📘

MQL5 简介(第 15 部分):构建自定义指标的初学者指南(四)·进阶篇

第 2/3 篇

「用三重循环锁定上升结构」

识别一段上涨趋势中的 Higher High / Higher Low,核心是先抓到一个摆动高点,再往后找更高的低点,最后确认更高的高点。下面这段逻辑用三层嵌套 for 循环把顺序和价格关系都卡死,避免在 MT5 上把震荡误判为多头排列。 外层先把摆动高点存下来:H 记价格、H_time 记时间、H_letter 用 StringFormat 拼出 "High%d" 方便后续在图上标字母。紧接着第二层从 j 开始扫,要求 IsSwingLow 成立且 time[k] > H_time,也就是低点必须出现在高点之后,才把它当作 HL 存起来。 第三层再往后找摆动高点,条件 time[l] > HL_time 保证高点晚于那个更高的低点。最终用 L < H && HL < H && HL > L && HH > H 做结构校验:原始低点低于前高、更高低点低于前高但高于原始低点、更高高点突破前高,四个条件全满足才倾向认定为有效上升段。外汇与贵金属波动剧烈,这种结构也只是概率上的多头信号,实盘须结合止损。 把 LookbackBars 调小(比如 3)会让摆动点更敏感、信号更密;调大(比如 10)则只抓大级别拐点。开 MT5 把这段代码塞进指标 OnCalculate,肉眼对比 EURUSD 日线,能直接看到标错字母的假结构少了一截。

MQL5 / C++
         class=class="str">"cmt">// Store the values for the swing high
         H = high[j];
         H_time = time[j];
         H_letter = StringFormat("High%d", j);
         class=class="str">"cmt">// Loop further to find a higher low after the swing high
         for(class="type">int k = j; k < rates_total - LookbackBars; k++)
           {
            class=class="str">"cmt">// Check if the current bar is a swing low and occurs after the swing high
            if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time)
              {
               class=class="str">"cmt">// Store the values for the higher low
               HL = low[k];
               HL_time = time[k];
               HL_letter = StringFormat("Higher Low%d", j);
               class=class="str">"cmt">// Loop further to find a higher high after the higher low
               for(class="type">int l = j ; l < rates_total - LookbackBars; l++)
                 {
                  class=class="str">"cmt">// Check if the current bar is a swing high and occurs after the higher low
                  if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time)
                    {
                     class=class="str">"cmt">// Store the values for the higher high
                     HH = high[l];
                     HH_time = time[l];
                     HH_letter = StringFormat("Higher High%d", l);
                     class=class="str">"cmt">// Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High
                     if(L < H && HL < H && HL > L && HH > H)
                       {

在图上标出 L、H、HL、HH 的文字对象

把波段低点、高点、更高的低点、更高的高点直接画在 MT5 图表上,比肉眼去数 K 线省事得多。下面这段逻辑就是一次性创建四个文本对象,并统一用深绿色、15 号字标注,方便你一眼看清结构。 代码里 L_letter、H_letter、HL_letter、HH_letter 是对象名,L_time/H_time/HL_time/HH_time 是对应柱的时间戳,L/H/HL/HH 是价格坐标。ObjectType 写 OBJ_TEXT 且子窗口填 0,意味着都锚定在主图。 字体设 15 并不是拍脑袋:太小在 4K 屏上容易看漏,太大又会挡掉相邻 K 线。你开 MT5 把这段粘进 EA 的 OnCalculate 末尾,编译后切到 XAUUSD 的 H1,大概率能看到绿字直接贴在近期摆动点上。外汇和贵金属波动剧烈,标注仅作结构参考,实际进出场仍需结合风控。

MQL5 / C++
class=class="str">"cmt">// Create and display text objects for Low, High, Higher Low, and Higher High on the chart
ObjectCreate(chart_id, L_letter, OBJ_TEXT, class="num">0, L_time, L);
ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L");
ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen);
ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, class="num">15);
ObjectCreate(chart_id, H_letter, OBJ_TEXT, class="num">0, H_time, H);
ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H");
ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen);
ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, class="num">15);
ObjectCreate(chart_id, HL_letter, OBJ_TEXT, class="num">0, HL_time, HL);
ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL");
ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen);
ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, class="num">15);
ObjectCreate(chart_id, HH_letter, OBJ_TEXT, class="num">0, HH_time, HH);
ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH");
ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen);

◍ 多头结构变量与折扣区预置

这段声明段把多头市场结构拆成了六个锚点:L 是上升趋势起点低点,H 是首次反弹高点,HL 必须高于 L,HH 必须高于 H;每个点都带独立时间标签和文字标记,方便在 MT5 图形上直接标注。 输入参数里 LookbackBars=10 控制摆动点前后回看/前瞻的 K 线数,bars_check=1000 决定从近期往历史扫多少根 BAR 找结构;show_bullish 开关打开才会画买点信号。外汇与贵金属杠杆高,结构识别只是概率辅助,不等于方向必现。 末尾新增的 premium-discount 区块用 pre_dis_box 存矩形对象名,lvl_50 专门算 HL 到 HH 之间 50% 回撤位——这就是后续画溢价/折扣区的价格中轴,能在 MT5 里据此框出均值回归可能的承接带。

MQL5 / C++
class="type">long chart_id = ChartID();
input class="type">int   LookbackBars = class="num">10;   class=class="str">"cmt">// Number of bars to look back/forward for swing points
input class="type">int   bars_check   = class="num">1000; class=class="str">"cmt">// Number of bars to check for swing points
input class="type">bool  show_bullish = true; class=class="str">"cmt">//Show Buy Signals
class=class="str">"cmt">// Variables for Bullish Market Structure
class="type">class="kw">double L;           class=class="str">"cmt">// Low: the starting low point in the up trend
class="type">class="kw">datetime L_time;    class=class="str">"cmt">// Time of the low
class="type">class="kw">string L_letter;    class=class="str">"cmt">// Label for the low point(e.g., "L")
class="type">class="kw">double H;           class=class="str">"cmt">// High: the first high after the low
class="type">class="kw">datetime H_time;    class=class="str">"cmt">// Time of the high
class="type">class="kw">string H_letter;    class=class="str">"cmt">// Label for the high point(e.g., "H")
class="type">class="kw">double HL;          class=class="str">"cmt">// Higher Low: the next low that is higher than the first low
class="type">class="kw">datetime HL_time;   class=class="str">"cmt">// Time of the higher low
class="type">class="kw">string HL_letter;   class=class="str">"cmt">// Label for the higher low point(e.g., "HL")
class="type">class="kw">double HH;          class=class="str">"cmt">// Higher High: the next high that is higher than the first high
class="type">class="kw">datetime HH_time;   class=class="str">"cmt">// Time of the higher high
class="type">class="kw">string HH_letter;   class=class="str">"cmt">// Label for the higher high point(e.g., "HH")
class=class="str">"cmt">// Variables for Premium and Discount
class="type">class="kw">string pre_dis_box; class=class="str">"cmt">// Name/ID for the premium-discount zone box(rectangle object on chart)
class="type">class="kw">double lvl_50;      class=class="str">"cmt">// The price level representing the class="num">50% retracement between Higher Low and Higher High

「摆动低点捕捉与对象清理的骨架」

这段代码片段给出的是指标初始化的空壳,以及退出时一次性删除图表对象的动作。OnDeinit 里直接调 ObjectsDeleteAll(chart_id),意味着切换周期或重载指标时,所有手动/自动画的线都会被清掉,写 EA 时若混用同图表对象得留神。 真正的扫描逻辑落在 OnCalculate:先用 show_bullish 开关控制是否只处理多头段,再用 rates_total >= bars_check 卡住最少 K 线数。循环从 rates_total - bars_check 跑到 rates_total - LookbackBars,步长是逐根 bar 倒推,避免未来函数。 判定摆动低点靠 IsSwingLow(low, i, LookbackBars),命中后把低价、时间、编号字符串(如 Low%d)存进 L / L_time / L_letter。随后内层再开一个从 i 起的 j 循环去找后面的摆动高点——这是典型 ZigZag 式高低点配对思路,LookbackBars 设 5 时,一段至少跨 10 根 bar 才可能成形。 外汇与贵金属波动跳空频繁,这类摆动识别在重大数据夜可能把影线误判为摆动点,实盘前建议在 MT5 用 EURUSD 的 M15 跑一遍看标记密度。

MQL5 / C++
class="type">class="kw">string lvl_50_line;     class=class="str">"cmt">// Name/ID for the horizontal line marking the class="num">50% level
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom indicator initialization function                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- indicator buffers mapping
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(const class="type">int reason)
  {
   ObjectsDeleteAll(chart_id);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom indicator iteration function                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
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 &tick_volume[],
                const class="type">long &volume[],
                const class="type">int &spread[])
  {
class=class="str">"cmt">//---
   if(show_bullish == true) class=class="str">"cmt">// Check if the bullish trend is to be displayed
     {
      if(rates_total >= bars_check) class=class="str">"cmt">// Ensure enough bars are available for analysis
        {
         class=class="str">"cmt">// Loop through the price data starting from a certain point based on bars_check and LookbackBars
         for(class="type">int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
           {
            class=class="str">"cmt">// Check if the current bar is a swing low
            if(IsSwingLow(low, i, LookbackBars))
              {
               class=class="str">"cmt">// Store the values for the swing low
               L = low[i];
               L_time = time[i];
               L_letter = StringFormat("Low%d", i);
               class=class="str">"cmt">// Loop through further to find a swing high after the low
               for(class="type">int j = i; j < rates_total - LookbackBars; j++)

嵌套循环锁定上涨结构

这段逻辑在已经抓到摆荡低点(L)之后,继续向右扫描,目标是把「更高的高点」完整序列认出来。它先用一层循环找摆荡高点 H,要求时间必须晚于 L_time,避免把前面的旧高点误认成新段起点。 找到 H 后不急着退出,再嵌一层循环从 j 开始找更高的低点 HL——条件是 IsSwingLow 成立且 time[k] > H_time。这一步把「回调不破前低」的行为量化成了可判定的时间先后关系。 最后一层循环从 j 扫到末尾,捕捉 HL 之后的更高高点 HH。三个价格变量 H、HL、HH 加上各自时间标签,构成了多头递进结构的最小闭包。外汇与贵金属杠杆高,这种结构只代表概率倾斜,实盘仍需结合风控。 直接把这层嵌套抄进 EA,把 LookbackBars 从默认 5 改成 3,在 EURUSD 的 M15 上回看,可能更容易抓到紧凑的波段,但假突破也会变多。

MQL5 / C++
{
   class=class="str">"cmt">// Check if the current bar is a swing high and occurs after the identified swing low
   if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time)
     {
      class=class="str">"cmt">// Store the values for the swing high
      H = high[j];
      H_time = time[j];
      H_letter = StringFormat("High%d", j);
      class=class="str">"cmt">// Loop further to find a higher low after the swing high
      for(class="type">int k = j; k < rates_total - LookbackBars; k++)
        {
         class=class="str">"cmt">// Check if the current bar is a swing low and occurs after the swing high
         if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time)
           {
            class=class="str">"cmt">// Store the values for the higher low
            HL = low[k];
            HL_time = time[k];
            HL_letter = StringFormat("Higher Low%d", j);
            class=class="str">"cmt">// Loop further to find a higher high after the higher low
            for(class="type">int l = j ; l < rates_total - LookbackBars; l++)
              {
               class=class="str">"cmt">// Check if the current bar is a swing high and occurs after the higher low
               if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time)
                 {
                  class=class="str">"cmt">// Store the values for the higher high
                  HH = high[l];
                  HH_time = time[l];
                  HH_letter = StringFormat("Higher High%d", l);

◍ 把多头结构画到图上

当 L < H 且 HL < H 且 HL > L 且 HH > H 同时成立,说明价格走出了低点抬升、高点也抬升的多头波段结构,这时才值得在图上标注。 判定通过后,代码在对应 K 线时间坐标上用 OBJ_TEXT 画出 L、H、HL、HH 四个字母,颜色统一 clrDarkGreen、字号 15,肉眼一眼能认出结构起点。 外汇与贵金属杠杆高,这类标注只帮你识别已发生的结构,后续延续概率不等于确定性,开 MT5 挂上这段就能验证自己的图表是否漏标了潜在多头段。

MQL5 / C++
class=class="str">"cmt">// Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High
if(L < H && HL < H && HL > L && HH > H)
  {
   class=class="str">"cmt">// Create and display text objects for Low, High, Higher Low, and Higher High on the chart
   ObjectCreate(chart_id, L_letter, OBJ_TEXT, class="num">0, L_time, L);
   ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L");
   ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen);
   ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, class="num">15);
   ObjectCreate(chart_id, H_letter, OBJ_TEXT, class="num">0, H_time, H);
   ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H");
   ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen);
   ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, class="num">15);
   ObjectCreate(chart_id, HL_letter, OBJ_TEXT, class="num">0, HL_time, HL);
   ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL");
   ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen);
   ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, class="num">15);
   ObjectCreate(chart_id, HH_letter, OBJ_TEXT, class="num">0, HH_time, HH);

常见问题

用循环逐个检查每根 K 线,比较相邻高低价找出局部极值,再用文字对象把 L、H 画到对应位置即可。
在每次重绘前遍历并删除上一轮创建的文字或箭头对象,只保留当前需要显示的结构标记。
可以,小布盯盘的 AIGC 已内置上升结构诊断,打开对应品种页就能直接看到 HL、HH 的文字标注,不用自己写循环。
三重循环逐根比对确实耗时,建议限制回溯根数或只在新 K 线出现时重算,避免每跳都跑全量扫描。
把多头结构判定阈值和折扣区比例提为输入参数,外部调一下数值就能改逻辑,不用动主体代码。