如何利用 MQL5 检测趋势和图表形态·综合运用
📐

如何利用 MQL5 检测趋势和图表形态·综合运用

(3/3)· 把前两份基础铺垫收口,用一套 MQL5 程序把趋势与经典反转形态一次跑通

案例拆解 第 3/3 篇
很多人写完高低点检测就停手,以为趋势判断已经够用,结果双顶双底还得肉眼翻图。其实在同一套 MQL5 逻辑上续写条件分支,形态识别可以一气呵成。本篇把收尾的拼图补上,让你少切几次屏幕。

「双底识别的画线逻辑与高低点回溯」

在 MT5 脚本里标记双底,第一步是把两个低点连成趋势线。下面这段用 ObjectCreate 画了一条名为 bottomLine 的 OBJ_TREND,右向射线开启,线宽设 3、颜色绿,方便肉眼在图上直接看支撑区。 ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1)); ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen); ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3); ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true); 判定条件很直白:当 lowVal1>=lowVal2 且 highVal1<highVal2,视为潜在双底(Potential Double Bottom);若 highVal1 也突破前高 highVal2,则标为确认双底(Double Bottom)。这两个条件都用 Comment 把当前/前次高低点数值打印到图表左上角,省去肉眼比对。 高低点定位靠两个辅助函数。getNextMove 用 iHighest / iLowest 在指定 count 根 K 线内取极值偏移;getmove 则循环调用 getNextMove,直到返回的极值位置与当前柱一致,相当于在 ZigZag 式步长下锁定某一腿的拐点。外汇与贵金属波动大,双底失效概率不低,实盘前务必在策略测试器用历史数据验证触发频率。

MQL5 / C++
ObjectCreate(class="num">0,"bottomLine",OBJ_TREND,class="num">0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1));
ObjectSetInteger(class="num">0,"bottomLine",OBJPROP_COLOR,clrGreen);
ObjectSetInteger(class="num">0,"bottomLine",OBJPROP_WIDTH,class="num">3);
ObjectSetInteger(class="num">0,"bottomLine",OBJPROP_RAY_RIGHT,true);
if(lowVal1>=lowVal2&&highVal1<highVal2)
  {
    Comment("Potential Double Bottom",
            "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
            "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
  }
else
  if(lowVal1>=lowVal2&&highVal1>highVal2)
    {
     Comment("Double Bottom",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
    }
  else
    Comment(" ");
}
class="type">int getmove(class="type">int move, class="type">int count, class="type">int startPos)
  {
   if(move!=MODE_HIGH && move!=MODE_LOW)
      class="kw">return (-class="num">1);
   class="type">int currentBar=startPos;
   class="type">int moveReturned=getNextMove(move,count*class="num">2+class="num">1,currentBar-count);
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+class="num">1);
      moveReturned=getNextMove(move,count*class="num">2+class="num">1,currentBar-count);
     }
   class="kw">return(currentBar);
  }
class="type">int getNextMove(class="type">int move, class="type">int count, class="type">int startPos)
  {
   if(startPos<class="num">0)
     {
      count +=startPos;
      startPos =class="num">0;
     }
   class="kw">return((move==MODE_HIGH)?
          iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
          iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));
  }
  if(lowVal1>=lowVal2&&highVal1<highVal2)
    {
      Comment("Potential Double Bottom",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
    }
  else
    if(lowVal1>=lowVal2&&highVal1>highVal2)

◍ 把双底形态参数打到图表上

在 MT5 自定义指标或 EA 里,最直接验证双底识别逻辑的办法,就是把关键极值实时打印到图表左上角。下面这段代码利用 Comment 把当前与前置的高低点一并输出,省去你翻图表比对。

MQL5 / C++
{
 Comment("Double Bottom",
  "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
  "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
}
逐行拆解:外层花括号只是代码块作用域;Comment 是 MQL5 内置函数,把字符串与变量拼接后显示在图表,不写日志不弹窗。第一行写 "Double Bottom" 作标题,第二行输出当前高点 highVal1 与上一高点 highVal2,第三行输出当前低点 lowVal1 与上一低点 lowVal2,换行符 \n 控制排版。 你只要在脚本里先算好这四个变量(例如用 iHigh / iLow 取两根不同 K 线的极值),贴入这段输出,就能在 EURUSD 的 M15 上肉眼确认双底的两个谷是否接近。外汇与贵金属波动受杠杆放大,形态失效概率不低,仅作辅助参考。

MQL5 / C++
{
 Comment("Double Bottom",
  "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
  "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
}

把高低点检测扩成自己的形态库

前面几节把高点低点、趋势方向、双顶双底的检测逻辑都拆开了,核心思路是用 getmove 抓最近两段极值,再用价格关系判断形态。掌握这套骨架后,你可以继续往里加头肩、旗形、箱型等结构,不必每次都重写框架。 附带四个 mq5 文件可直接在 MT5 打开验证:moveFinder 约 2.24 KB、trendFinder 约 3.18 KB、DT_patternFinder 与 DB_patternFinder 各约 3.04 KB。跑起来后建议先改 checkBars 参数,从默认 20 根 K 线调到 50 观察信号密度变化。 下面这段是把趋势和双顶双底判断合并的片段,注意它用 NormaliceDouble 到 5 位小数,做外汇或贵金属时若品种报价小数位不同需同步调整,否则比较会偏。外汇贵金属杠杆高、回撤快,任何形态信号都只是概率倾向,实盘前请用策略测试器过一遍。 别把示例当终点 作者原版只覆盖了双顶双底,但 getmove 返回的是极值索引,你只要替换后面的判断分支就能挂自己的形态。比如头肩可定义为三高三低且中间高最高,写个独立函数比改原 if 更稳。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|TrendDTDB.mq5
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">int checkBars= class="num">20;
   class="type">int high1, high2, low1, low2;
   class="type">class="kw">double highVal1, highVal2, lowVal1, lowVal2;
class="type">void OnInit()
{
   PonEtiquetas();
}
class="type">void OnTick()
{
   high1=getmove(MODE_HIGH,checkBars,class="num">0);
   high2=getmove(MODE_HIGH,checkBars,high1+class="num">1);
   highVal1=NormalizeDouble(iHigh(_Symbol,_Period,high1),class="num">5);
   highVal2=NormalizeDouble(iHigh(_Symbol,_Period,high2),class="num">5);
   
   low1=getmove(MODE_LOW,checkBars,class="num">0);
   low2=getmove(MODE_LOW,checkBars,low1+class="num">1);
   lowVal1=NormalizeDouble(iLow(_Symbol,_Period,low1),class="num">5);
   lowVal2=NormalizeDouble(iLow(_Symbol,_Period,low2),class="num">5);
class=class="str">"cmt">// DOBLE TECHO
   if(highVal1<=highVal2&&lowVal1>lowVal2){ObjectSetString((class="type">int)_Symbol,"LS0",OBJPROP_TEXT,class="num">0,"Potencial DT-DOBLE TECHO"); DibujaArriba("DTP",clrGreen);}
   else if(highVal1<=highVal2&&lowVal1<lowVal2){ObjectSetString((class="type">int)_Symbol,"LS0",OBJPROP_TEXT,class="num">0,"DT-DOBLE TECHO");DibujaArriba("DT",clrGreen);}

「双顶双底与趋势状态的图形标注逻辑」

这段判定把相邻两段的高低点拿来比对,直接决定图表上标什么字、画什么箭头。若第一段低点不低于第二段且高点更低,倾向识别为潜在双底(DOBLE SUELO),在「LS1」标签写 Potencial DS,并调用 DibujaAbajo 用红色画下方标记;反之若高点更高则判双顶(DOBLE TECHO),绿色画上方。 趋势分支更直白:lowVal1>lowVal2 且 highVal1>highVal2 时标 UP 多头趋势并绿色画上;highVal1<highVal2 且 lowVal1<lowVal2 则标 DOWN 空头趋势红色画下;都不满足就标 SW 横盘并清掉趋势对象。外汇与贵金属波动受杠杆放大,这类形态只是概率倾向,实战须自验。 getmove 与 getNextMove 是底层取极值棒的函数。getNextMove 用 iHighest / iLowest 在指定 count 根内取最高或最低棒序号;getmove 通过循环把「当前棒」与「前推 count 根内的大级别极值棒」对齐,返回稳定拐点位置。把下面代码贴进 MT5 指标工程,改 lowVal1 等变量来源即可跑出自己的标注。

MQL5 / C++
else{ObjectSetString((class="type">int)_Symbol,"LS0",OBJPROP_TEXT,class="num">0,"."); ObjectsDeleteAll(class="num">0,"DT",class="num">0,-class="num">1);}
class=class="str">"cmt">//DOBLE SUELO
  if(lowVal1>=lowVal2&&highVal1<highVal2){ObjectSetString((class="type">int)_Symbol,"LS1",OBJPROP_TEXT,class="num">0,"Potencial DS-DOBLE SUELO"); DibujaAbajo("DSP",clrRed);}
  else if(lowVal1>=lowVal2&&highVal1>highVal2){ObjectSetString((class="type">int)_Symbol,"LS1",OBJPROP_TEXT,class="num">0,"DT-DOBLE TECHO");DibujaArriba("DS",clrGreen);}
        else{ObjectSetString((class="type">int)_Symbol,"LS1",OBJPROP_TEXT,class="num">0,"."); ObjectsDeleteAll(class="num">0,"DS",class="num">0,-class="num">1);}
class=class="str">"cmt">// 趋势发现者
  if(lowVal1>lowVal2&&highVal1>highVal2){ObjectSetString((class="type">int)_Symbol,"LS2",OBJPROP_TEXT,class="num">0,"UP-Tendencia ALCISTA");DibujaArriba("T",clrGreen);}
  else if(highVal1<highVal2&&lowVal1<lowVal2){ObjectSetString((class="type">int)_Symbol,"LS2",OBJPROP_TEXT,class="num">0,"DOWN-Tendencia BAJISTA");DibujaAbajo("T",clrRed);}
        else{ObjectSetString((class="type">int)_Symbol,"LS2",OBJPROP_TEXT,class="num">0,"SW-Tendencia LATERAL");  ObjectsDeleteAll(class="num">0,"T",class="num">0,-class="num">1);}
}
class="type">int getmove(class="type">int move, class="type">int count, class="type">int startPos)
{
   if(move!=MODE_HIGH && move!=MODE_LOW)
      class="kw">return (-class="num">1);
   class="type">int currentBar=startPos;
   class="type">int moveReturned=getNextMove(move,count*class="num">2+class="num">1,currentBar-count);
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+class="num">1);
      moveReturned=getNextMove(move,count*class="num">2+class="num">1,currentBar-count);
     }
   class="kw">return(currentBar);
}

class="type">int getNextMove(class="type">int move, class="type">int count, class="type">int startPos)
{
   if(startPos<class="num">0)
     {
      count +=startPos;
      startPos =class="num">0;
     }
   class="kw">return((move==MODE_HIGH)?
            iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
            iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));
}

class="type">void DibujaArriba(class="type">class="kw">string Nombre, class="type">int mColor)
{
   ObjectDelete(class="num">0,Nombre);

◍ 画趋势线与角标标签的落地代码

在 MT5 里用 EA 自动标双顶双底,光算出了 high1/high2、low1/low2 还不够,得把线画出来并且给出视觉提示。下面这段把上沿趋势线、下沿趋势线以及两个角标文字一次性建好,复制进你的脚本就能直接看到图形输出。 DibujaArriba 和 DibujaAbajo 两个函数先用 ObjectDelete 清掉同名旧对象,再用 ObjectCreate 以 OBJ_TREND 类型连线:上端取 high2 与 high1 两根 K 线的时间与最高价,下端取 low2 与 low1 的最低价,右射线 OBJPROP_RAY_RIGHT 设为 true 让线向右延伸。线宽固定为 1,颜色由传入的 mColor 控制。 PonEtiquetas 则在图表右下角(OBJPROP_CORNER=4)堆两个 OBJ_LABEL:LS0 距右边 800 像素、距底 50 像素,绿色 Arial 25 号字;LS1 同样右距 800、底距 90,红色同字号。初始文字都是 "-",实际跑的时候你可以在外面把 OBJPROP_TEXT 改成 "双顶"/"双底" 之类。外汇和贵金属波动大,这类标注只作形态参考,信号失效的概率不低。 别把标签位置当摆设 XDISTANCE 写死 800 在 4K 屏上可能贴边,跑之前先按自己图表宽度改这个值,否则标签容易被推出可视区。

MQL5 / C++
  ObjectCreate(class="num">0,Nombre,OBJ_TREND,class="num">0,iTime(Symbol(),Period(),high2),iHigh(Symbol(),Period(),high2),iTime(Symbol(),Period(),high1),iHigh(Symbol(),Period(),high1));
  ObjectSetInteger(class="num">0,Nombre,OBJPROP_COLOR,mColor);
  ObjectSetInteger(class="num">0,Nombre,OBJPROP_WIDTH,class="num">1);
  ObjectSetInteger(class="num">0,Nombre,OBJPROP_RAY_RIGHT,true);
}
class="type">void DibujaAbajo(class="type">class="kw">string Nombre, class="type">int mColor)
{
  ObjectDelete(class="num">0,Nombre);
  ObjectCreate(class="num">0,Nombre,OBJ_TREND,class="num">0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1));
  ObjectSetInteger(class="num">0,Nombre,OBJPROP_COLOR,mColor);
  ObjectSetInteger(class="num">0,Nombre,OBJPROP_WIDTH,class="num">1);
  ObjectSetInteger(class="num">0,Nombre,OBJPROP_RAY_RIGHT,true);
}
class="type">void PonEtiquetas()
{
  ObjectCreate((class="type">int)_Symbol,"LS0", OBJ_LABEL, class="num">0, class="num">0, class="num">0);
  ObjectSetString(class="num">0,"LS0",OBJPROP_FONT,"Arial");
  ObjectSetInteger(class="num">0,"LS0",OBJPROP_FONTSIZE,class="num">25);
  ObjectSetInteger(class="num">0,"LS0",OBJPROP_COLOR,clrGreen);    
  ObjectSetInteger(class="num">0,"LS0", OBJPROP_CORNER, class="num">4);  
  ObjectSetInteger(class="num">0,"LS0", OBJPROP_XDISTANCE, class="num">800);
  ObjectSetInteger(class="num">0,"LS0", OBJPROP_YDISTANCE, class="num">50);
  ObjectSetString((class="type">int)_Symbol,"LS0",OBJPROP_TEXT,class="num">0,"-");  
  
  ObjectCreate((class="type">int)_Symbol,"LS1", OBJ_LABEL, class="num">0, class="num">0, class="num">0);
  ObjectSetString(class="num">0,"LS1",OBJPROP_FONT,"Arial");
  ObjectSetInteger(class="num">0,"LS1",OBJPROP_FONTSIZE,class="num">25);
  ObjectSetInteger(class="num">0,"LS1",OBJPROP_COLOR,clrRed);    
  ObjectSetInteger(class="num">0,"LS1", OBJPROP_CORNER, class="num">4);  
  ObjectSetInteger(class="num">0,"LS1", OBJPROP_XDISTANCE, class="num">800);
  ObjectSetInteger(class="num">0,"LS1", OBJPROP_YDISTANCE, class="num">90);

记住这一条就够了

上面这段把两个 OBJ_LABEL 控件挂到了图表上:LS1 先清文本,LS2 用 Arial 25 号字、WhiteSmoke 色,锚定在右下角(CORNER=4),X 偏移 800 像素、Y 偏移 130 像素,最后文本置为“-”。 在 MT5 里跑这段,你能直接看到右上/右下角出现两块预留标签位——它们本身不画信号,只是给后续动态文字(比如小布盯盘的状态播报)留坑。 外汇和贵金属波动剧烈,这类界面元素只解决“信息往哪放”,不替代任何风控;真要接策略,先把标签坐标在你的屏幕分辨率下测一遍,避免被价格窗遮住。

MQL5 / C++
ObjectSetString((class="type">int)_Symbol,"LS1",OBJPROP_TEXT,class="num">0,"-");

ObjectCreate((class="type">int)_Symbol,"LS2", OBJ_LABEL, class="num">0, class="num">0, class="num">0);
ObjectSetString(class="num">0,"LS2",OBJPROP_FONT,"Arial");
ObjectSetInteger(class="num">0,"LS2",OBJPROP_FONTSIZE,class="num">25);
ObjectSetInteger(class="num">0,"LS2",OBJPROP_COLOR,clrWhiteSmoke);
ObjectSetInteger(class="num">0,"LS2", OBJPROP_CORNER, class="num">4);
ObjectSetInteger(class="num">0,"LS2", OBJPROP_XDISTANCE, class="num">800);
ObjectSetInteger(class="num">0,"LS2", OBJPROP_YDISTANCE, class="num">130);
ObjectSetString((class="type">int)_Symbol,"LS2",OBJPROP_TEXT,class="num">0,"-");
把形态扫描交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到双顶双底与趋势状态的自动标注,你只需在 MQL5 里复刻逻辑做二次校验,专注决策而非盯线。

常见问题

可用摆动点数窗口或 ZigZag 缓冲做预筛选,再对局部极值做买卖力道比较,样本里给的是逐根比对的轻量写法,便于改参数。
能,小布盯盘在品种页内置了形态扫描,会给出候选双底及置信区间,但外汇贵金属波动剧烈,仍建议用本文代码自行回测确认。
纯价格等价容易在震荡里误报,加入右侧回落幅度阈值和前期高点时间距离,可滤掉大部分假突破形态。
若每 tick 重算全图会吃资源,按时间轴缓存上次极值、仅在新柱触发重判,实测对五分钟以上周期几乎无感。