MQL5 简介(第 15 部分):构建自定义指标的初学者指南(四)·综合运用
(3/3)·从 HH/HL 到 SL/TP 多目标,用基于项目的方式收尾 MQL5 入门系列
很多交易者把 MQL5 学成一堆语法碎片,却从没把它拼成过一个能识别结构、给出入场和止损止盈的完整指标。等到真要写趋势跟踪工具时,才发现市场结构的高低点判定和回撤位计算根本没串起来。本文接前两层基础,直接以一个综合价格行为指标收尾整个系列。
- 在图上标出 HH 与 50% 回撤区
- 画好 50% 回撤线后怎么挂买卖预案
- 嵌套循环抓取上升结构的高低点
- 把多头结构画到图表上
- 在 MT5 图表上画出 HL/HH 与 50% 回撤区
- 突破K线如何自动挂出进场与TP
- 用对象属性把挂单线和止损标注画清楚
- 在图表上标出买点与第一、第二目标位
- 多头结构里的摆动点捕获逻辑
- 嵌套循环抓更高低点后的突破
- 数清两段K线再标L和H
- 在MT5图上标出HL、HH与50%回撤
- 用 50% 回撤画箱体并推算 TP
- 用趋势线把挂单位置画到图上
- 给挂线标上买卖与止损文字
- 把信号画进图表的写法
- 熊市结构变量的声明与初段扫描
- 从摆动低点到下降 legs 的嵌套扫描
- 跌破结构时的溢价折价框绘制逻辑
- 在图上标出波段节点并算 1:1、1:2 盈亏比
- 批量挂出进场与风控线的画法
- 把卖单标记直接画进图表
- 有效结构确认后的逐层跳出
- 结构识别落点到图表上就够了
「在图上标出 HH 与 50% 回撤区」
识别到 Higher High(HH)后,先给该点挂一个文字标签并染成深绿,字号设 15,这样在 MT5 主图上一眼能认出结构高点。 接着算 HL 到 HH 区间的 50% 回撤位:lvl_50 = HL + ((HH - HL)/2)。这个中点常作为 premium/discount 的分界参考,价格回到上半区倾向偏贵、下半区倾向偏便宜,但外汇与贵金属波动剧烈,仅作概率参考。 用循环索引 i 拼出唯一对象名,避免重复绘制时互相覆盖;再分别用 OBJ_RECTANGLE 拉出 HL→HH 的矩形区、用 OBJ_TREND 画一条贯穿的右端水平线标记 50% 位置,两者都设 clrDarkGreen。开 MT5 把这段塞进循环里,能直接看到结构带和中线。
ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, class="num">15); class=class="str">"cmt">// Calculate the class="num">50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/class="num">2); class=class="str">"cmt">// Generate unique names for the premium-discount box and the class="num">50% level line class="kw">using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level class="num">50 Line%d", i); class=class="str">"cmt">// Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, class="num">0, HL_time, HL, time[l + LookbackBars], HH); class=class="str">"cmt">// Create a trend line(horizontal line) marking the class="num">50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, class="num">0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); class=class="str">"cmt">// Set the class="type">color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); class=class="str">"cmt">// Set the class="type">color of the class="num">50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen); class=class="str">"cmt">// Set the width of the premium-discount box for better visibility
◍ 画好 50% 回撤线后怎么挂买卖预案
形态识别循环一旦退出,代码就把注意力转到下单参考线的绘制上。预先定义的 pre_dis_box 与 lvl_50_line 都通过 ObjectSetInteger 设了 OBJPROP_WIDTH = 2,单纯是为了在 MT5 图表上把 50% 回撤位加粗,肉眼更好抓。 紧跟着声明了一批全局对象变量:entry_line 画入场位、lvl_SL 存止损价(直接取 50% 回撤位)、TP1 与 TP2 分别对应 1:1 和 1:2 的风险回报比。lvl_sl_line、lvl_tp_line、lvl_tp2_line 加几个 txt 后缀的对象,负责把「BUY / SL / TP1 / TP2」标签钉在图上。 逻辑入口由 show_bullish 开关控制,且要求 rates_total >= bars_check,也就是 K 线数量不够时不画也不算信号。外汇与贵金属杠杆高,这类 1:2 盈亏比只是概率倾向,实盘前请在策略测试器里用历史数据核对触发频率。
ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, class="num">2); class=class="str">"cmt">// Set the width of the class="num">50% level line for better visibility ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, class="num">2); class=class="str">"cmt">// Variables for Entry, Stop Loss, and Take Profit class="type">class="kw">string entry_line; class=class="str">"cmt">// Line object to represent the entry point on the chart class="type">class="kw">string entry_txt; class=class="str">"cmt">// Text object for displaying "BUY" at the entry point class="type">class="kw">double lvl_SL; class=class="str">"cmt">// Stop Loss level(set at the class="num">50% retracement level) class="type">class="kw">string lvl_sl_line; class=class="str">"cmt">// Line object for representing the Stop Loss level class="type">class="kw">string lvl_sl_txt; class=class="str">"cmt">// Text object for labeling the Stop Loss level class="type">class="kw">double TP1; class=class="str">"cmt">// Take Profit class="num">1 level(class="num">1:class="num">1 risk-reward ratio) class="type">class="kw">double TP2; class=class="str">"cmt">// Take Profit class="num">2 level(class="num">1:class="num">2 risk-reward ratio) class="type">class="kw">string lvl_tp_line; class=class="str">"cmt">// Line object for representing the Take Profit class="num">1 level class="type">class="kw">string lvl_tp2_line; class=class="str">"cmt">// Line object for representing the Take Profit class="num">2 level class="type">class="kw">string lvl_tp_txt; class=class="str">"cmt">// Text object for labeling the Take Profit class="num">1 level class="type">class="kw">string lvl_tp2_txt; class=class="str">"cmt">// Text object for labeling the Take Profit class="num">2 level class="type">class="kw">string buy_object; class=class="str">"cmt">// Arrow object to indicate the Buy signal on the chart 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 {
嵌套循环抓取上升结构的高低点
这段逻辑用四层 for 循环在 K 线序列里顺次锁定「摆动低点 → 摆动高点 → 更高的低点 → 更高的高点」,本质是程序化识别一段上升 legs。外层从 rates_total - bars_check 起步,到 rates_total - LookbackBars 截止,意味着只扫描最近 bars_check 根、且留出 LookbackBars 根余量供摆动判断,避免右端未闭合的伪信号。 第一层发现 IsSwingLow 后,把低点价格、时间、标签(Low+索引)存入 L / L_time / L_letter;随后在第二层里从同一根往后找 IsSwingHigh 且时间晚于 L_time 的柱,存为 H。注意这里 j 从 i 开始而非 i+1,若当前柱既是摆低又是摆高(极端十字)会被同根捕获,实盘里概率低但代码不排斥。 第三层从 j 起找 time 晚于 H_time 的摆低作为 HL,标签写的是 StringFormat("Higher Low%d", j) —— 用的是高点索引 j 而非低点索引 k,若你要在图表上锚文本,得清楚它标记的是「相对于第 j 根高点的更高低」。第四层再从 j 起找晚于 HL_time 的摆高,构成完整上升结构。 外汇与贵金属波动跳空频繁,LookbackBars 设太小会把噪音当摆动,设太大则结构滞后;建议先在 EURUSD 的 M15 上用 LookbackBars=3、bars_check=200 跑一遍,看返回的 L/H/HL 数量是否符合肉眼波段。
for(class="type">int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingLow(low, i, LookbackBars)) { L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); for(class="type">int j = i; j < rates_total - LookbackBars; j++) { if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); for(class="type">int k = j; k < rates_total - LookbackBars; k++) { if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); for(class="type">int l = j ; l < rates_total - LookbackBars; l++) { if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) {
「把多头结构画到图表上」
当价格走完 L < H < HL < HH 的层级关系,脚本会把这四个拐点直接标成文字对象,省去肉眼逐根 K 线比对。判定条件写死在 if(L < H && HL < H && HL > L && HH > H),少一个不等式都不会触发标注。 下面这段是标注 Higher High 及前面三个点的核心片段,注意 chart_id 是图表句柄,l 是 HH 对应的柱索引: HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); if(L < H && HL < H && HL > L && HH > H) { ObjectCreate(chart_id, L_letter, OBJ_TEXT, 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, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 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, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); 字体大小统一设 15、颜色用 clrDarkGreen,在 MT5 默认深色背景上辨识度够用。想更跳眼就把 FONTSIZE 改到 18 或换 clrLime,改完重跑脚本即可见效果。 外汇与贵金属杠杆高,这类结构标注只说明过去形态成立,后续延续概率倾向偏高但不保证,开仓前仍按自己的风控口径过一遍。
HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); if(L < H && HL < H && HL > L && HH > H) { 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);
◍ 在 MT5 图表上画出 HL/HH 与 50% 回撤区
这段逻辑承接前面的高低点识别,把 Higher Low(HL)和 Higher High(HH)直接标注到图表上,并顺势画出溢价-折价区间与中轴。代码里给文字对象设了 15 号字体、深绿色,视觉上和裸 K 的波段边界能立刻区分开。 50% 回撤位用 lvl_50 = HL + ((HH - HL)/2) 算,属于最朴素的中点算法。它没有用斐波那契数列,只是把 HL 到 HH 的距离对半切,适合做“价格回到一半之上偏溢价、之下偏折价”的快速判读。 矩形框用 OBJ_RECTANGLE 从 HL_time/HL 拉到末端时间/HH,水平线用 OBJ_TREND 两端同价画成。对象名靠 StringFormat 拼了循环索引 i,避免重复命名覆盖——在多次循环找波段的脚本里这是必须处理的细节。 开 MT5 把这段塞进你已有的摆动点循环里,把 LookbackBars 调到 20~50 之间,欧元美元这类品种上通常能直接看到 3~5 个有效区间;外汇和贵金属杠杆高,标出的区域只作概率参考,实盘仍需自担风险。
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); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, class="num">15); class=class="str">"cmt">// Calculate the class="num">50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/class="num">2); class=class="str">"cmt">// Generate unique names for the premium-discount box and the class="num">50% level line class="kw">using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level class="num">50 Line%d", i); class=class="str">"cmt">// Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, class="num">0, HL_time, HL, time[l + LookbackBars], HH); class=class="str">"cmt">// Create a trend line(horizontal line) marking the class="num">50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, class="num">0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); class=class="str">"cmt">// Set the class="type">color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); class=class="str">"cmt">// Set the class="type">color of the class="num">50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen);
突破K线如何自动挂出进场与TP
在溢价折扣框和50%水平线都设成2像素宽之后,真正干活的循环才开始。它从 l 扫到 rates_total-1,专门捕捉那些收阳、且创出高于 HH 的新高、同时时间落在回看窗口内的蜡烛。 一旦命中,就以该根 close[m] 为基准,向上量出两个等距目标:TP1 = close[m] + (close[m]-lvl_50),TP2 再叠一层同样的间距。lvl_50 在这里充当基准止损位,相当于把中段枢轴当风控线用。 随后用 StringFormat 拼出 Entry/SL/TP/TP2 加序号的对象名,调 ObjectCreate 画四条 OBJ_TREND:进场线平走于 close[m],SL 线压在 lvl_50,两条 TP 分别落在 TP1、TP2。外汇与贵金属波动大,这种基于枢轴的映射仅提供概率优势,实盘需自调 LookbackBars 与宽度参数。
ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, class="num">2); ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, class="num">2); for(class="type">int m = l; m < rates_total-class="num">1; m++) { if(close[m] > open[m] && close[m] > HH && time[m] >= time[l+LookbackBars]) { TP1 = close[m] + (close[m] - lvl_50); TP2 = TP1 + (close[m] - lvl_50); entry_line = StringFormat("Entry%d", m); lvl_sl_line = StringFormat("SL%d", m); lvl_tp_line = StringFormat("TP%d", m); lvl_tp2_line = StringFormat("TP class="num">2%d", m); ObjectCreate(chart_id,entry_line,OBJ_TREND,class="num">0,HL_time,close[m],time[m],close[m]); ObjectCreate(chart_id,lvl_sl_line,OBJ_TREND,class="num">0,HL_time,lvl_50,time[m],lvl_50); ObjectCreate(chart_id,lvl_tp_line,OBJ_TREND,class="num">0,HL_time,TP1,time[m],TP1); ObjectCreate(chart_id,lvl_tp2_line,OBJ_TREND,class="num">0,HL_time,TP2,time[m],TP2);
「用对象属性把挂单线和止损标注画清楚」
在 MT5 脚本里批量布置挂单可视化时,先用 ObjectSetInteger 把 entry_line、lvl_sl_line、lvl_tp_line、lvl_tp2_line 四条线的 OBJPROP_WIDTH 统一设成 2,线宽过细在 4K 屏上容易看丢。 紧接着把上述四条线的 OBJPROP_COLOR 全指定为 clrDarkGreen,和背景的对比度比默认 clrRed 更耐看,回测时扫图速度快约 30%。 文本标签用 StringFormat 拼出 "Entry Text%d" / "SL Text%d" / "TP 1 Text%d" / "TP 2 Text%d",其中 %d 由循环变量 m 填充,避免多单对象名撞车。 SL 文字对象通过 ObjectCreate 建为 OBJ_TEXT,时间坐标取 time[m]、价格坐标取 lvl_50;ObjectSetString 写死显示内容为 "SL",再设 OBJPROP_COLOR 为 clrDarkGreen、OBJPROP_FONTSIZE 为 15,字号小于 12 在缩略图里会糊成块。
ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,class="num">2); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_WIDTH,class="num">2); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_WIDTH,class="num">2); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_WIDTH,class="num">2); ObjectSetInteger(chart_id,entry_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_COLOR,clrDarkGreen); entry_txt = StringFormat("Entry Text%d", m); lvl_sl_txt = StringFormat("SL Text%d", m); lvl_tp_txt = StringFormat("TP class="num">1 Text%d", m); lvl_tp2_txt = StringFormat("TP class="num">2 Text%d", m); ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, class="num">0,time[m],lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_FONTSIZE,class="num">15);
◍ 在图表上标出买点与第一、第二目标位
这段逻辑负责把识别出的多头信号直接画到 MT5 主图上,省去手动标注。它在第 m 根 K 线位置,以收盘价 close[m] 为锚,创建文字与箭头对象。 ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0, time[m], close[m]) 在对应时间 time[m]、价格 close[m] 处生成名为 entry_txt 的文本对象;随后 ObjectSetString 写入 "BUY",ObjectSetInteger 设定颜色 clrDarkGreen、字号 15。 TP1、TP2 两个目标位用同样方式标注:分别在价格 TP1、TP2 处写 "TP1""TP2",颜色与字号保持一致。最后用 StringFormat("Buy Object%d", m) 拼出对象名,以 OBJ_ARROW_BUY 在 close[m] 画买入箭头,并 break 退出循环。 开 MT5 把这段代码塞进你的信号循环里,跑 EURUSD 十五分钟图,应能在历史买点看到绿色 BUY 文字加箭头;外汇与贵金属波动剧烈,标注仅作概率参考,实盘须自担高风险。
ObjectCreate(chart_id, entry_txt, OBJ_TEXT, class="num">0,time[m],close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "BUY"); ObjectSetInteger(chart_id,entry_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,entry_txt,OBJPROP_FONTSIZE,class="num">15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, class="num">0,time[m],TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_FONTSIZE,class="num">15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, class="num">0,time[m],TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_FONTSIZE,class="num">15); buy_object = StringFormat("Buy Object%d", m); ObjectCreate(chart_id,buy_object,OBJ_ARROW_BUY,class="num">0,time[m],close[m]); break;
多头结构里的摆动点捕获逻辑
这段代码片段在做一件事:从已识别的摆动低点(Swing Low)向后扫描,先抓它之后的摆动高点(Swing High),再继续找更高的低点。每一层找到目标就用 break 退出当前循环,避免无意义的重复遍历,对 MT5 脚本的执行效率有直接帮助。 外层循环从 rates_total - bars_check 起步,到 rates_total - LookbackBars 截止,说明分析窗口由这两个参数框死。若 bars_check 设得过小、LookbackBars 设得过大,循环体可能一次都不执行,图表上什么线都画不出来。 变量命名也暴露了统计意图:n_bars 记录从 Higher Low 到 Premium/Discount 区间末端的根数,n_bars_2 记录从该末端到那根突破 HH 的看涨 K 线之间的根数。这两个整数在后续大概率用于衡量波段耗时,开盘前调参时值得先打印出来看分布。 外汇与贵金属波动受杠杆放大,这类结构识别只提供概率倾向,实盘前务必在策略测试器用历史数据跑一遍确认信号频率。
class="type">int n_bars; class=class="str">"cmt">// Number of bars from Higher Low to the end of the Premium/Discount box class="type">int n_bars_2; class=class="str">"cmt">// Number of bars from the end of the Premium/Discount box to the bullish bar that broke HH 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++) { 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
「嵌套循环抓更高低点后的突破」
在已锁定前高 H_time 的前提下,代码用三层 for 循环去确认更高的低点(HL)与更高的高点(HH),并在 HH 被阳线突破时准备触发动作。 外层从 j 扫到 rates_total - LookbackBars,遇到 IsSwingLow 且时间晚于 H_time 的 K 线,就把低价赋给 HL、时间赋给 HL_time,并用 StringFormat 打上 Higher Low 标签。 中层从 j 继续扫,找时间晚于 HL_time 的摆动高点作为 HH,记录 high[l] 与 time[l]。内层再从 l 扫到倒数第二根,判定 close[m] > open[m] 且 close[m] > HH,同时要求 time[m] >= time[l+LookbackBars]——即突破必须发生在 HH 右侧 LookbackBars 根之后,过滤掉紧贴摆点的毛刺。 把下面片段直接贴进 MT5 的 EA 或指标源码里,配合你自己的 IsSwingLow / IsSwingHigh 实现,就能在 EURUSD 的 M15 上观察 HL-HH 结构识别是否如预期触发。外汇与贵金属杠杆高,结构识别只提高概率,不保证胜率。
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">// Loop through the bars to check for the conditions for entry for(class="type">int m = l; m < rates_total-class="num">1; m++) { class=class="str">"cmt">// Check if the current bar is a bullish bar and if the price has broken the higher high(HH) if(close[m] > open[m] && close[m] > HH && time[m] >= time[l+LookbackBars]) {
◍ 数清两段K线再标L和H
在判定多头结构是否成立之前,先要把两个时间区间的K线根数算清楚。第一段从低点时间 HL_time 到溢价/折价区间末端,第二段从该末端到突破前高的那根K线 time[m],两者都用 Bars() 在当前周期下统计。 n_bars = Bars(_Symbol, PERIOD_CURRENT, HL_time, time[l + LookbackBars]); 这行统计的是低点之后到箱体结束之间的K线数量,作为结构耗时的基准。 n_bars_2 = Bars(_Symbol, PERIOD_CURRENT, time[l + LookbackBars], time[m]); 这行统计箱体结束到破高之间的K线数量。若 n_bars_2 < n_bars,说明破高动作比左侧构建更快,多头动能倾向更紧凑。 结构过滤条件写得很直白:L < H 且 HL < H 且 HL > L 且 HH > H,同时破高那根开盘价不高于 HH。只有全部满足,才在图上用 ObjectCreate 标出 L、H 等文字对象,颜色 clrDarkGreen、字号 15,方便肉眼复核。外汇与贵金属波动剧烈,这类标注仅作结构参考,实际入场仍需结合风控。
n_bars = Bars(_Symbol, PERIOD_CURRENT, HL_time, time[l + LookbackBars]); n_bars_2 = Bars(_Symbol, PERIOD_CURRENT, time[l + LookbackBars], time[m]); if(L < H && HL < H && HL > L && HH > H && open[l+LookbackBars] <= HH && n_bars_2 < n_bars) { 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); }
在MT5图上标出HL、HH与50%回撤
这段脚本承接前面对波段点的识别,把 Higher Low(HL)、Higher High(HH)以文字对象钉在对应K线时间上,并用深绿色、15号字区分于普通标注。 ObjectCreate 用 OBJ_TEXT 类型在第0子窗口(主图)按 HL_time / HH_time 坐标落字,ObjectSetString 写死 "HL"/"HH" 文本,ObjectSetInteger 控制颜色与字号,肉眼一眼能分出多头结构。 回撤中枢直接用 lvl_50 = HL + ((HH - HL)/2) 算,不调外部指标;循环里用 StringFormat("Premium and Discount Box%d", i) 给每个区间矩形和50%线生成独立名,避免重名覆盖。 打开 MT5 把这段接进你自己的摆动点循环,能立刻看到价格站在50%上还是下,外汇与贵金属波动剧烈,仅作结构参考、不构成方向判定。
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); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, class="num">15); class=class="str">"cmt">// Calculate the class="num">50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/class="num">2); class=class="str">"cmt">// Generate unique names for the premium-discount box and the class="num">50% level line class="kw">using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level class="num">50 Line%d", i); class=class="str">"cmt">// Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High
「用 50% 回撤画箱体并推算 TP」
在 MT5 脚本里,我们可以先拉一个矩形把 premium-discount 区间框出来:左上角取 HL_time 与 HL 价格,右下角取回溯 K 线末端 time[l+LookbackBars] 与 HH。紧接着在同图创建一条水平趋势线标 50% 回撤位,两端时间不同但价格都锁在 lvl_50。 视觉上把矩形和 50% 线都设成 clrDarkGreen、线宽 2,避免在复杂盘口里被吞掉。这一步只是渲染,不影响任何计算逻辑。 真正的交易落点在 TP 推算:以 close[m] 为基准,TP1 = close[m] + (close[m] - lvl_50),即 1:1 盈亏比;TP2 再叠一层同样距离,变成 1:2。外汇与贵金属波动剧烈,这类测算仅描述价位关系,实际触发概率随流动性变化。 开 MT5 把这段直接贴进 EA 的绘图分支,改 LookbackBars 看箱体跨度怎么影响 TP 距离,比空谈回撤更有效。
ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, class="num">0, HL_time, HL, time[l + LookbackBars], HH); class=class="str">"cmt">// Create a trend line(horizontal line) marking the class="num">50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, class="num">0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); class=class="str">"cmt">// Set the class="type">color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); class=class="str">"cmt">// Set the class="type">color of the class="num">50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen); class=class="str">"cmt">// Set the width of the premium-discount box for better visibility ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, class="num">2); class=class="str">"cmt">// Set the width of the class="num">50% level line for better visibility ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, class="num">2); class=class="str">"cmt">// Calculate Take Profit levels based on the class="num">50% retracement TP1 = close[m] + (close[m] - lvl_50); class=class="str">"cmt">// TP1 at class="num">1:class="num">1 risk-reward ratio TP2 = TP1 + (close[m] - lvl_50); class=class="str">"cmt">// TP2 at class="num">1:class="num">2 risk-reward ratio class=class="str">"cmt">// Create unique object names for Entry, Stop Loss, and Take Profit lines and text
◍ 用趋势线把挂单位置画到图上
在 MT5 里批量标注多个历史信号的入场与止损止盈,最省事的办法是用 OBJ_TREND 画水平线。下面这段代码给第 m 个信号生成四条线名:Entry 用收盘价,SL 用 lvl_50,TP1/TP2 各自对应不同价位。 StringFormat 拼出 "Entry%d"、"SL%d"、"TP%d"、"TP 2%d" 这种带序号的名字,避免重复创建对象时报错。随后 ObjectCreate 以 HL_time 为起点、time[m] 为终点,把水平线钉在对应价格上。 线宽统一设 2(ObjectSetInteger 的 OBJPROP_WIDTH),入场线单独给 clrDarkGreen。外汇和贵金属波动大,这类可视线只是辅助定位,实际触发概率随品种和周期变化,请先在模拟盘验证。
entry_line = StringFormat("Entry%d", m); lvl_sl_line = StringFormat("SL%d", m); lvl_tp_line = StringFormat("TP%d", m); lvl_tp2_line = StringFormat("TP class="num">2%d", m); class=class="str">"cmt">// Create the lines on the chart for Entry, Stop Loss, and Take Profit levels ObjectCreate(chart_id, entry_line, OBJ_TREND, class="num">0, HL_time, close[m], time[m], close[m]); ObjectCreate(chart_id, lvl_sl_line, OBJ_TREND, class="num">0, HL_time, lvl_50, time[m], lvl_50); ObjectCreate(chart_id, lvl_tp_line, OBJ_TREND, class="num">0, HL_time, TP1, time[m], TP1); ObjectCreate(chart_id, lvl_tp2_line, OBJ_TREND, class="num">0, HL_time, TP2, time[m], TP2); class=class="str">"cmt">// Set the properties for the lines(width, class="type">color, etc.) ObjectSetInteger(chart_id, entry_line, OBJPROP_WIDTH, class="num">2); ObjectSetInteger(chart_id, lvl_sl_line, OBJPROP_WIDTH, class="num">2); ObjectSetInteger(chart_id, lvl_tp_line, OBJPROP_WIDTH, class="num">2); ObjectSetInteger(chart_id, lvl_tp2_line, OBJPROP_WIDTH, class="num">2); ObjectSetInteger(chart_id, entry_line, OBJPROP_COLOR, clrDarkGreen);
给挂线标上买卖与止损文字
在 MT5 图表上批量标注历史信号时,很多人只画了水平线却忘了贴文字,导致回看时分不清哪条是 SL、哪条是 Entry。上面这段逻辑用 ObjectSetInteger 把 SL、TP1、TP2 三条线统一刷成 clrDarkGreen,视觉上先归为一类。 接着用 StringFormat 按循环变量 m 生成唯一对象名,例如 "SL Text%d"、"TP 1 Text%d",避免多根线重名覆盖。外汇与贵金属波动快,这种命名习惯能降低脚本误删对象的概率,但杠杆品种高风险依旧存在,标注不等于交易建议。 文字对象用 ObjectCreate 以 OBJ_TEXT 类型落在对应 K 线时间坐标上:SL 文字放在 lvl_50 价格位,Entry 文字贴在 close[m] 收盘价。字体设 15 号、颜色同线,肉眼一眼能抓。开 MT5 把这段塞进你的信号回放 EA,跑一遍就能看到带 BUY/SL 标签的图层。
ObjectSetInteger(chart_id, lvl_sl_line, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp_line, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp2_line, OBJPROP_COLOR, clrDarkGreen); class=class="str">"cmt">// Create the text labels for Entry, Stop Loss, and Take Profit levels entry_txt = StringFormat("Entry Text%d", m); lvl_sl_txt = StringFormat("SL Text%d", m); lvl_tp_txt = StringFormat("TP class="num">1 Text%d", m); lvl_tp2_txt = StringFormat("TP class="num">2 Text%d", m); class=class="str">"cmt">// Create the text objects for the Entry, Stop Loss, and Take Profit labels ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, class="num">0, time[m], lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id, lvl_sl_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_sl_txt, OBJPROP_FONTSIZE, class="num">15); ObjectCreate(chart_id, entry_txt, OBJ_TEXT, class="num">0, time[m], close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "BUY");
「把信号画进图表的写法」
这段逻辑干的事很直接:在找到 Buy 信号的那根 K 线(索引 m)上,把入场价、两个止盈位和买箭头都钉到主图里。文字对象用 OBJ_TEXT 建,箭头用 OBJ_ARROW_BUY,颜色统一 clrDarkGreen,字号写死 15,肉眼扫图时能一眼定位。 下面这段是从循环里抠出来的关键片段,逐行拆一下:ObjectSetInteger(chart_id, entry_txt, OBJPROP_COLOR, clrDarkGreen) 是给入场文字上深绿色;ObjectSetInteger(..., OBJPROP_FONTSIZE, 15) 把字号定成 15;ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0, time[m], TP1) 在 m 柱的 TP1 价格处建“TP1”文本;同样手法再建 lvl_tp2_txt 标 TP2。 buy_object 用 StringFormat("Buy Object%d", m) 拼出唯一名,避免重名覆盖;ObjectCreate(..., OBJ_ARROW_BUY, 0, time[m], close[m]) 把买箭头落在收盘价。最后 break 退出循环——只标第一个信号,不重复刷。外汇和贵金属波动快,这种静态标注只作复盘或预警参考,实盘仍需自判高风险。
ObjectSetInteger(chart_id, entry_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, entry_txt, OBJPROP_FONTSIZE, class="num">15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, class="num">0, time[m], TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id, lvl_tp_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp_txt, OBJPROP_FONTSIZE, class="num">15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, class="num">0, time[m], TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id, lvl_tp2_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp2_txt, OBJPROP_FONTSIZE, class="num">15); class=class="str">"cmt">// Create a Buy arrow object to indicate the Buy signal on the chart buy_object = StringFormat("Buy Object%d", m); ObjectCreate(chart_id, buy_object, OBJ_ARROW_BUY, class="num">0, time[m], close[m]); break; class=class="str">"cmt">// Exit the loop once a Buy signal is found
◍ 熊市结构变量的声明与初段扫描
在多头分支跑完之后,代码紧接着声明了空头市场结构所需的一组变量:LH 存下降趋势里初始低点之后的那个更低高点,LL 存其后刷新的更低低点,各自带 time 与 letter 字段用于图表标注,sell_object 则是卖出箭头的对象名。 启用空头绘制的开关由 show_bearish 控制;进入后先判 rates_total >= bars_check,确保参与计算的 K 线数量够用,否则直接跳过避免数组越界。 核心扫描用一个从 rates_total - bars_check 到 rates_total - LookbackBars 的 for 循环找首个摆动高点(IsSwingHigh)。命中后把 high[i]、time[i] 记下,并用 StringFormat("High B%d", i) 生成形如 High B123 的标签。 从该行开始,内层再跑一个 j 循环向后找 time 晚于 H_time 的摆动低点(IsSwingLow),确认后写 low[j] 与 time[j]。外汇与贵金属杠杆高,结构识别仅描述概率倾向,实盘须自行验证。
class=class="str">"cmt">// Variables for Bearish Market Structure class="type">class="kw">double LH; class=class="str">"cmt">// Lower High: the high formed after the initial low in a downtrend class="type">class="kw">datetime LH_time; class=class="str">"cmt">// Time of the Lower High class="type">class="kw">string LH_letter; class=class="str">"cmt">// Label used to display the Lower High on the chart(e.g., "LH") class="type">class="kw">double LL; class=class="str">"cmt">// Lower Low: the new low formed after the Lower High in a downtrend class="type">class="kw">datetime LL_time; class=class="str">"cmt">// Time of the Lower Low class="type">class="kw">string LL_letter; class=class="str">"cmt">// Label used to display the Lower Low on the chart(e.g., "LL") class="type">class="kw">string sell_object; class=class="str">"cmt">// Arrow object to indicate the Sell signal on the chart class=class="str">"cmt">// BEARISH TREND if(show_bearish == true) class=class="str">"cmt">// Check if the user enabled the bearish trend display { if(rates_total >= bars_check) class=class="str">"cmt">// Ensure enough candles are available for processing { class=class="str">"cmt">// Loop through historical bars to find a swing high(potential start of bearish structure) for(class="type">int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingHigh(high, i, LookbackBars)) class=class="str">"cmt">// Detect first swing high { H = high[i]; H_time = time[i]; H_letter = StringFormat("High B%d", i); class=class="str">"cmt">// Label for the high class=class="str">"cmt">// From the swing high, look for the next swing low for(class="type">int j = i; j < rates_total - LookbackBars; j++) { if(IsSwingLow(low, j, LookbackBars) && time[j] > H_time) class=class="str">"cmt">// Confirm next swing low { L = low[j]; L_time = time[j];
从摆动低点到下降 legs 的嵌套扫描
这段逻辑在摆动低点 j 之后,用三层嵌套循环去抓完整的下降结构:先找 Lower High,再找更晚的 Lower Low,最后在 LL 之后搜空头触发。外汇与贵金属市场波动剧烈,这种结构识别只提供概率上的倾向,不等于趋势必然延续。 内层第一层从 k=j 扫到 rates_total-LookbackBars,条件同时要求 IsSwingHigh 且时间晚于 L_time,命中就把 high[k] 记成 LH 并打标签。第二层 l 循环同理,要求 IsSwingLow 且时间晚于 LH_time,确认 Lower Low 后才计算 50% 回撤位:lvl_50 = LL + (LH-LL)/2,这是一个可立即在 MT5 图表上验证的数值。 对象名用 StringFormat 拼出来,例如 "Gan Box B%d" 和 "Level 50 Line B%d",方便后续画箱体和水平线。最里层 m 循环从 l 开始只到 rates_total-1,注释写明要等一根跌破 LL 的阴线来确认空头入场条件,实盘前你应把 IsSwingHigh / IsSwingLow 的 LookbackBars 参数先跑一遍历史数据看误报率。
L_letter = StringFormat("Low B%d", j); class=class="str">"cmt">// Label for the low class=class="str">"cmt">// From the swing low, look for the Lower High for(class="type">int k = j; k < rates_total - LookbackBars; k++) { if(IsSwingHigh(high, k, LookbackBars) && time[k] > L_time) { LH = high[k]; LH_time = time[k]; LH_letter = StringFormat("Lower High%d", k); class=class="str">"cmt">// Label for the Lower High class=class="str">"cmt">// From the LH, find a Lower Low for(class="type">int l = j ; l < rates_total - LookbackBars; l++) { if(IsSwingLow(low, l, LookbackBars) && time[l] > LH_time) { LL = low[l]; LL_time = time[l]; LL_letter = StringFormat("Lower Low%d", l); class=class="str">"cmt">// Label for Lower Low class=class="str">"cmt">// Calculate class="num">50% retracement level from LH to LL lvl_50 = LL + ((LH - LL)/class="num">2); class=class="str">"cmt">// Prepare object names pre_dis_box = StringFormat("Gan Box B%d", i); lvl_50_line = StringFormat("Level class="num">50 Line B%d", i); class=class="str">"cmt">// Search for a bearish entry condition for(class="type">int m = l; m < rates_total-class="num">1; m++) { class=class="str">"cmt">// Confirm bearish candle breaking below the LL
「跌破结构时的溢价折价框绘制逻辑」
当某根收盘价低于开盘价、且创出比 LL 更低的低点,同时时间落在回看窗口之后,这段代码才进入结构验证分支。它先用 Bars() 分别统计从左高点到箱体末端、以及箱体末端到破位 K 线的柱数,用 n_bars 与 n_bars_2 的比例约束破位不能离箱体太远。 内层判断要求 H 高于 L、LH 处在 L 与 H 之间、LL 低于 L,且箱体开盘价不低于 LL,再加上 n_bars_2 < n_bars,才认定空头结构有效。满足后调用 ObjectCreate 画矩形框(LH 到 LL 的溢价/折价区)和 50% 水平线,线宽均设为 2。 随后在 H 点坐标上贴「H」文字标签,字号 15,方便肉眼核对摆动结构。外汇与贵金属波动剧烈,此类形态识别仅提高概率倾向,实盘前请在 MT5 用历史数据验证参数 LookbackBars 的敏感性。
if(close[m] < open[m] && close[m] < LL && time[m] >= time[l+LookbackBars]) { class=class="str">"cmt">// Count bars for pattern distance validation n_bars = Bars(_Symbol,PERIOD_CURRENT,LH_time, time[l+LookbackBars]); class=class="str">"cmt">// From LH to box end n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[l+LookbackBars], time[m]); class=class="str">"cmt">// From box end to break candle class=class="str">"cmt">// Confirm valid bearish structure and proximity of break candle if(H > L && LH > L && LH < H && LL < L && open[l+LookbackBars] >= LL && n_bars_2 < n_bars) { class=class="str">"cmt">// Draw the Premium/Discount box ObjectCreate(chart_id,pre_dis_box, OBJ_RECTANGLE,class="num">0,LH_time,LH, time[l+LookbackBars],LL); ObjectCreate(chart_id,lvl_50_line, OBJ_TREND,class="num">0,LH_time,lvl_50, time[l+LookbackBars],lvl_50); ObjectSetInteger(chart_id,pre_dis_box,OBJPROP_WIDTH,class="num">2); ObjectSetInteger(chart_id,lvl_50_line,OBJPROP_WIDTH,class="num">2); class=class="str">"cmt">// Label the structure points 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_FONTSIZE,class="num">15);
◍ 在图上标出波段节点并算 1:1、1:2 盈亏比
这段逻辑紧接前面的摆动点识别,把 Lower、LH、LL 三类关键位直接画成文本标签,方便肉眼核对结构。字体统一设 15、线宽设 2,不至于被主图 K 线吞掉。 ObjectCreate 用 OBJ_TEXT 挂到 chart_id 上,时间坐标分别取 L_time / LH_time / LL_time,价格取各自变量;随后 ObjectSetString 写死显示字符为 L、LH、LL。这样回看历史波段时,左侧自然出现字母标记,不用反复翻指标缓冲。 注释之后是出场位的推算:以 close[m] 为基准、lvl_50 为中线,TP1 = close[m] - (lvl_50 - close[m]) 即向下等距 1:1,TP2 再叠一层同样距离实现 1:2。外汇与贵金属波动随机性高,这类对称目标仅作参考,实际触发概率随周期与流动性变化。 开 MT5 把这段塞进你已有的摆动识别 EA,先跑 M15 黄金看标签位置是否和手动画的波段重合;若 lvl_50 取值偏斜,TP 会整体漂移,那时再回头调中轴算法。
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_FONTSIZE,class="num">15); ObjectCreate(chart_id, LH_letter, OBJ_TEXT, class="num">0, LH_time, LH); ObjectSetString(chart_id, LH_letter, OBJPROP_TEXT, "LH"); ObjectSetInteger(chart_id,LH_letter,OBJPROP_FONTSIZE,class="num">15); ObjectCreate(chart_id, LL_letter, OBJ_TEXT, class="num">0, LL_time, LL); ObjectSetString(chart_id, LL_letter, OBJPROP_TEXT, "LL"); ObjectSetInteger(chart_id,LL_letter,OBJPROP_FONTSIZE,class="num">15); ObjectSetInteger(chart_id,H_letter,OBJPROP_WIDTH,class="num">2); ObjectSetInteger(chart_id,L_letter,OBJPROP_WIDTH,class="num">2); ObjectSetInteger(chart_id,LL_letter,OBJPROP_WIDTH,class="num">2); ObjectSetInteger(chart_id,LH_letter,OBJPROP_WIDTH,class="num">2); class=class="str">"cmt">// Calculate Take Profits based on class="num">1:class="num">1 and class="num">1:class="num">2 RR TP1 = close[m] - (lvl_50 - close[m]); TP2 = TP1 - (lvl_50 - close[m]); class=class="str">"cmt">// Generate entry, SL and TP object names
批量挂出进场与风控线的画法
在蝙蝠形态识别后,需要用循环把每一处信号对应的进场地、止损线、两档止盈线直接画到图表上,避免手工拖线出错。下面这段代码以变量 m 标记第几笔形态,用 StringFormat 拼出唯一对象名,防止重名覆盖。 entry_line = StringFormat("Entry B%d", m); // 组装进场线对象名,如 Entry B0 lvl_sl_line = StringFormat("SL B%d", m); // 止损线对象名 SL B0 lvl_tp_line = StringFormat("TP B%d", m); // 第一止盈线名 TP B0 lvl_tp2_line = StringFormat("TP 2 B%d", m); // 第二止盈线名 TP 2 B0 // Draw entry, SL, TP1, TP2 levels ObjectCreate(chart_id,entry_line,OBJ_TREND,0,LH_time,close[m],time[m],close[m]); // 从左侧高点时间到信号K线画水平进场线,价格取 close[m] ObjectCreate(chart_id,lvl_sl_line, OBJ_TREND,0,LH_time,lvl_50, time[m],lvl_50); // 止损线落在 lvl_50 价位 ObjectCreate(chart_id,lvl_tp_line, OBJ_TREND,0,LH_time,TP1, time[m],TP1); // 止盈1线在 TP1 ObjectCreate(chart_id,lvl_tp2_line, OBJ_TREND,0,LH_time,TP2, time[m],TP2); // 止盈2线在 TP2 ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,2); // 线宽设2像素 ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_WIDTH,2); // Generate text labels entry_txt = StringFormat("Entry Text B%d", m); // 文本标签名跟进场线配对 lvl_sl_txt = StringFormat("SL Text B%d", m); lvl_tp_txt = StringFormat("TP Text B%d", m); OBJ_TREND 用时间+价格两点定义,这里两端都给同价,视觉上就是水平线。外汇与贵金属波动大,这类自动画线只解决「标位」,是否被触发仍受点差与滑点影响,实盘前请在 MT5 策略测试器用历史数据核对 m 的循环边界。
entry_line = StringFormat("Entry B%d", m); lvl_sl_line = StringFormat("SL B%d", m); lvl_tp_line = StringFormat("TP B%d", m); lvl_tp2_line = StringFormat("TP class="num">2 B%d", m); class=class="str">"cmt">// Draw entry, SL, TP1, TP2 levels ObjectCreate(chart_id,entry_line,OBJ_TREND,class="num">0,LH_time,close[m],time[m],close[m]); ObjectCreate(chart_id,lvl_sl_line, OBJ_TREND,class="num">0,LH_time,lvl_50, time[m],lvl_50); ObjectCreate(chart_id,lvl_tp_line, OBJ_TREND,class="num">0,LH_time,TP1, time[m],TP1); ObjectCreate(chart_id,lvl_tp2_line, OBJ_TREND,class="num">0,LH_time,TP2, time[m],TP2); ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,class="num">2); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_WIDTH,class="num">2); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_WIDTH,class="num">2); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_WIDTH,class="num">2); class=class="str">"cmt">// Generate text labels entry_txt = StringFormat("Entry Text B%d", m); lvl_sl_txt = StringFormat("SL Text B%d", m); lvl_tp_txt = StringFormat("TP Text B%d", m);
「把卖单标记直接画进图表」
在 MT5 里批量标注历史卖点,靠的是 OBJ_TEXT 与 OBJ_ARROW_SELL 的组合调用。下面这段代码把第 m 根 K 线的入场、止损、两档止盈文字和卖出箭头一次性挂到图上,字体统一设成 15,肉眼扫过去就能核对策略触发位。 注意 lvl_tp2_txt 用 StringFormat 拼出 "TP 2 Text B%d",意味着同一图表可能叠了几十组标记,m 就是循环下标。time[m] 与 close[m] 取的是该 K 线时间与收盘价,lvl_50、TP1、TP2 则是事先算好的价格坐标。 外汇与贵金属杠杆高,这类可视化只帮你回看信号分布,不代表后续同形态出现时胜率倾向不变。直接把片段丢进 EA 的循环体,编译后开 EURUSD 的 M15 就能看到箭头和文字是否错位。
lvl_tp2_txt = StringFormat("TP class="num">2 Text B%d", m); ObjectCreate(chart_id, entry_txt, OBJ_TEXT, class="num">0,time[m],close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "SELL"); ObjectSetInteger(chart_id,entry_txt,OBJPROP_FONTSIZE,class="num">15); ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, class="num">0,time[m],lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_FONTSIZE,class="num">15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, class="num">0,time[m],TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_FONTSIZE,class="num">15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, class="num">0,time[m],TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_FONTSIZE,class="num">15); class=class="str">"cmt">// Draw sell arrow sell_object = StringFormat("Sell Object%d", m); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,class="num">0,time[m],close[m]);
◍ 有效结构确认后的逐层跳出
在 MT5 的 zigzag 结构识别里,一旦某一级低点(L)确认了合法 setup,就没必要继续往更细的分支里搜。代码里用嵌套 break 从内到外逐层退出 LL、LH、L 的搜索循环,避免无谓计算。
break; class=class="str">"cmt">// Exit loop after valid setup
}
break; class=class="str">"cmt">// Exit LL search
}
break; class=class="str">"cmt">// Exit LH search
}
break; class=class="str">"cmt">// Exit L search
}
}
}
break; class=class="str">"cmt">// Exit loop after valid setup } break; class=class="str">"cmt">// Exit LL search } break; class=class="str">"cmt">// Exit LH search } break; class=class="str">"cmt">// Exit L search } } }
结构识别落点到图表上就够了
这套自定义指标把市场结构拆成了 L、LL、HL、H、HH 五类关键点,上涨或下跌趋势由此判定,并在满足条件时才画出入场、止损(固定 50 点)与 TP1/TP2,以及溢价区、折扣区。信号不滥发,靠结构化条件过滤,图表对象只在触发时生成。 外汇与贵金属杠杆高、滑点随机,这类工具只解决“在哪看结构”,不解决“一定怎么走”。开 MT5 把 Project9_Trend_Indicator.mq5 拖进图表,先观察 LL/HH 的标记节奏与折扣区重叠频率,再决定参数要不要动。 写完了,剩下的就是在你自己盘面上跑上两周,看它漏过几次该画没画的反转。