MQL5 简介(第 15 部分):构建自定义指标的初学者指南(四)·进阶篇
「用三重循环锁定上升结构」
识别一段上涨趋势中的 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 日线,能直接看到标错字母的假结构少了一截。
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,大概率能看到绿字直接贴在近期摆动点上。外汇和贵金属波动剧烈,标注仅作结构参考,实际进出场仍需结合风控。
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 里据此框出均值回归可能的承接带。
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 跑一遍看标记密度。
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 上回看,可能更容易抓到紧凑的波段,但假突破也会变多。
{
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 挂上这段就能验证自己的图表是否漏标了潜在多头段。
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);