旗形形态·进阶篇
📐

旗形形态·进阶篇

(2/3)·旗形、三角旗、楔形常被混为一谈,本篇给出明确区分法则与通用搜索指标

案例拆解 第 2/3 篇
很多交易者把三角旗形直接当三角形处理,结果在反转和延续行情里反复吃亏。其实两类形态的生成逻辑完全不同:一类由反转点构成,一类由覆盖柱填充。先分清楚类别,再去写指标才不会白费功夫。

◍ 扩张三角形的线段放大规则

收敛三角形的判定逻辑可以直接平移到扩张三角形,差异只在一点:构成形态的之字转向线段长度逐段放大。图 13 给得很直白——片段 3-4 必须长于片段 1-2,而片段 5-6 又必须长于 3-4,这种递增量级是识别扩张结构的核心约束。 由于收敛、扩张两类三角形在骨架上的相似度极高,没必要写两套扫描逻辑。更划算的做法是提炼共性,做一个能通吃几种变体的通用指标,在 MT5 里一次挂上去就能同时抓不同形态。 外汇与贵金属市场里这类形态常出现在趋势末端,假突破概率不低,用指标初筛后仍需人工确认,杠杆品种高风险,别直接当信号用。

搭一个能切换三种水平形态的通用检测器

做这个指标先要借力通用的之字转向模块 iUniZigZagSW,连同 CSorceData.mqh、CZZDirection.mqh、CZZDraw.mqh 一起丢进终端数据文件夹的 MQL5 目录,重启后编译。沃尔夫波形那篇里留了个中间文件 iWolfeWaves_Step_1.mq5,把它改名成 iHorizontalFormation 就是我们的主战场,若图表报“载入指标出错”,八成是 iCustom 路径要改成 "ZigZags\\iUniZigZagSW"。 整套识别逻辑拆成五件独立的事:算形态前价格波动、判形状、判斜度、定结束时机、算目标。后四项都做成枚举下拉,交易者在属性框里直接换方案,不用碰代码。 形状枚举给三种:PatternTapered 收敛三角、PatternRectangular 矩形、PatternExpanding 扩张三角。斜度枚举 InclineAlong 顺预期、InclineHorizontally 水平、InclineAgainst 逆预期。结束方式 Immediately 成型即触发,OneLastVertex 破最近顶,TwoLastVertices 破双顶连线。目标算法 FromVertexToVertex、OneVertex、TwoVertices 各对应不同测距逻辑。 K 系参数才是调形态的刀。K1 管片段0-1要比基准段1-2高几倍;K2 越小形态高度越稳定,三角形要调大才明显;K3 越小越偏水平,想抓倾斜就把 K2 拉大。N 决定顶点数,OnInit 里算 2*N+2 个之字转向点才够判。 CheckForm 按形状分派:Tapered 要求每段递减,Expanding 每段递增,Rectangular 每段贴近基准。斜度检查里 CheckInclineUp 每段中部要比前段高 base*K3,水平向就比中部差。条件全过,Immediately 直接画箭头设目标,否则用 SetLevelParameters 记突破线、SetTarget 算目标。 价格跟踪在每次之字转向变动时跑,y3() 算实时水平,破线就出信号并重置 dir 停止跟踪。外汇和贵金属波动剧烈,这类形态信号失效和假突破概率不低,上 MT5 用历史数据回测再实盘验证。

MQL5 / C++
enum EPatternType{
   PatternTapered,
   PatternRectangular,
   PatternExpanding
};
input EPatternType        Pattern        = PatternRectangular;
enum EInclineType{
   InclineAlong,
   InclineHorizontally,
   InclineAgainst
};
input EInclineType        Incline        = InclineHorizontally;
enum EEndType{
   Immediately,
   OneLastVertex,
   TwoLastVertices
};
input EEndType            CompletionType = Immediately;
enum ETargetType{
   FromVertexToVertex,
   OneVertex,
   TwoVertices
};

「之字转向形态的判定与信号触发」

在 MT5 自定义指标里,先用一组 input 把形态识别的容差和类型钉死:K1=1.5 意味着第一段折返高度必须达到基准段的 1.5 倍才进入候选,K2、K3 均为 0.25 控制次级容差,N=2 决定所需峰谷点数。RequiredCount 按 N*2+2 算出,N=2 时至少要采集 6 个峰谷点才开始判型。 代码里 li 是形态起点在 PeackTrough 数组的下标,base 取相邻两个峰谷的绝对差作为基准幅度。l1 为起始片段高度,只有当 l1>=base*K1 时才认为此前价格变动足够大,这一关不过直接跳过后续检查。 方向分支用 CurDir 区分:1 为末段拐头向上,-1 为向下,各自走独立的买卖条件函数。CheckForm 与 CheckInclineForBuy 同时为真时,若 CompletionType 设为 Immediately,就在 low[i] 画向上箭头,目标点落在最近峰谷值加 l1 的位置;否则走突破水平参数与目标参数的延迟确认逻辑。 外汇与贵金属波动受杠杆放大,这类形态识别仅提供概率倾向,实盘前请在 MT5 策略测试器用历史数据验证 N 与 K 系参数对具体品种的适配性。

MQL5 / C++
input ETargetType        Target        =  OneVertex;
input class="type">class="kw">double            K1            =  class="num">1.5;
input class="type">class="kw">double            K2            =  class="num">0.25;
input class="type">class="kw">double            K3            =  class="num">0.25;
input class="type">int               N             =  class="num">2;
input EPatternType      Pattern       =  PatternRectangular;
input EInclineType      Incline       =  InclineHorizontally;    
input class="type">class="kw">double            K1            =  class="num">1.5;
input class="type">class="kw">double            K2            =  class="num">0.25;
input class="type">class="kw">double            K3            =  class="num">0.25;
input class="type">int               N             =  class="num">2;
input EEndType          CompletionType =  Immediately;
input ETargetType       Target        =  OneVertex;
class="type">int RequiredCount;
RequiredCount=N*class="num">2+class="num">2;
if(CurCount>=RequiredCount){
   if(CurDir!=PreDir){       
      class=class="str">"cmt">// 检查条件
   }
   class=class="str">"cmt">// 检查价格和水平级别

}
class="type">int li=CurCount-RequiredCount;                                            class=class="str">"cmt">// 初始形态点在 PeackTrough 数组中的索引
class="type">class="kw">double base=MathAbs(PeackTrough[li+class="num">1].Val-PeackTrough[li+class="num">2].Val);        class=class="str">"cmt">// 基础数值
class="type">class="kw">double l1=MathAbs(PeackTrough[li+class="num">1].Val-PeackTrough[li].Val);             class=class="str">"cmt">// 片段 class="num">1-class="num">2 的高度
   if(l1>=base*K1){                                                       class=class="str">"cmt">// 检查之前价格变化的大小
       class=class="str">"cmt">// 其它的检查
   }
if(CurDir==class="num">1){              class=class="str">"cmt">// 最后的之字转向片段指向上涨
   class=class="str">"cmt">// 检查上涨方向的条件       
}
else if(CurDir==-class="num">1){        class=class="str">"cmt">// 最后的之字转向片段指向下跌
   class=class="str">"cmt">// 检查下跌方向的条件

}
if(CheckForm(li,base) && CheckInclineForBuy(li,base)){      class=class="str">"cmt">// 检查形态和方向
   if(CompletionType==Immediately){
      class=class="str">"cmt">// 画出指标箭头
      UpArrowBuffer[i]=low[i];
      class=class="str">"cmt">// 画出目标点
      UpDotBuffer[i]=PeackTrough[CurCount-class="num">1].Val+l1;
   }
   else{
      class=class="str">"cmt">// 设置突破水平参数
      SetLevelParameters(class="num">1);
      class=class="str">"cmt">// 设置目标参数
      SetTarget(class="num">1,li);
   }
}

◍ 三种形态的尺寸判定逻辑

形态识别里,先按 Pattern 分支走 CheckForm,再委托给具体的收敛、矩形或扩张判定函数。收敛要求后面每一段波幅都比前一段更小,矩形要求各段波幅都贴近基准值,扩张则相反。 以收敛为例,CheckFormTapered 从索引 1 开始循环(跳过首段),用 j=li+1+i*2 定位每个后续顶底点。lv 是当前段绝对值波幅,lp 是前一段波幅,只有当 lp-lv > K2*base 时才算合格,否则直接返回 false。 矩形判定 CheckFormRectangular 不比较相邻段,而是拿每段 lv 和 base 比:若 MathAbs(lv-base) > K2*base 就淘汰。这意味着 K2 是容差系数,调小它会让形态更「严格方正」,调大则容易把歪矩形也收编。 倾斜方向由 CheckInclineForBuy 按 Incline 分支处理:沿价格方向倾斜走 CheckInclineUp,水平无倾斜走 CheckInclineHorizontally。外汇与贵金属波动大,K2 和 base 的取值建议在 MT5 上用历史峰值回测后再定,参数误设可能把噪声当形态。

MQL5 / C++
class="type">bool CheckForm(class="type">int li,class="type">class="kw">double base){
   class="kw">switch(Pattern){
      case PatternTapered:
         class=class="str">"cmt">// 收敛
         class="kw">return(CheckFormTapered(li,base));
      break;
      case PatternRectangular:
         class=class="str">"cmt">// 长方形
         class="kw">return(CheckFormRectangular(li,base));
      break;
      case PatternExpanding:
         class=class="str">"cmt">// 扩张
         class="kw">return(CheckFormExpanding(li,base));
      break;
   }
   class="kw">return(true);
}
class="type">bool CheckFormTapered(class="type">int li,class="type">class="kw">double base){
   class=class="str">"cmt">// 从1开始循环,不检查第一个片段,
   class=class="str">"cmt">// 但是所有随后的片段都会相对它做检查 
   for(class="type">int i=class="num">1;i<N;i++){
      class=class="str">"cmt">// 计算形态中下一个顶部点的索引
      class="type">int j=li+class="num">1+i*class="num">2;
      class=class="str">"cmt">// 下一个片段的数值 
      class="type">class="kw">double lv=MathAbs(PeackTrough[j].Val-PeackTrough[j+class="num">1].Val);
      class=class="str">"cmt">// 前一个片段的数值
      class="type">class="kw">double lp=MathAbs(PeackTrough[j-class="num">2].Val-PeackTrough[j-class="num">1].Val);
      class=class="str">"cmt">// 前一个片段应该更大,否则
      class=class="str">"cmt">// 函数返回 false  
      if(!(lp-lv>K2*base)){
         class="kw">return(false);
      }
   }
   class="kw">return(true);
}
if(!(lv-lp>K2*base)){
   class="kw">return(false);
}
class="type">bool CheckFormRectangular(class="type">int li,class="type">class="kw">double base){  
   class=class="str">"cmt">// 在除了第一个片段之外的所有片段做循环      
   for(class="type">int i=class="num">1;i<N;i++){
      class=class="str">"cmt">// 计算下一个顶部的索引 
      class="type">int j=li+class="num">1+i*class="num">2;
      class=class="str">"cmt">// 计算下一个片段的大小
      class="type">class="kw">double lv=MathAbs(PeackTrough[j].Val-PeackTrough[j+class="num">1].Val);
      class=class="str">"cmt">// 片段应该和基础数值相差不大 
      if(MathAbs(lv-base)>K2*base){
         class="kw">return(false);
      }
   }
   class="kw">return(true);
}
class="type">bool CheckInclineForBuy(class="type">int li,class="type">class="kw">double base){             
   class="kw">switch(Incline){
      case InclineAlong:
         class=class="str">"cmt">// 在价格移动方向上的倾斜
         class="kw">return(CheckInclineUp(li,base));
      break;
      case InclineHorizontally:
         class=class="str">"cmt">// 没有倾斜
         class="kw">return(CheckInclineHorizontally(li,base));

倾斜判定如何分买卖双向落地

之字转向(ZigZag)片段的倾斜方向,需要按多空两套逻辑分别校验。CheckInclineForBuy 里,若枚举为 InclineAlong,说明倾斜顺着价格上行,调用 CheckInclineUp;若为 InclineAgainst,倾斜逆着价格,反而要去查 CheckInclineDn。 卖侧镜像处理:CheckInclineForSell 中 InclineAlong 对应 CheckInclineDn,InclineAgainst 对应 CheckInclineUp,InclineHorizontally 则走水平校验。这样多空不会共用同一套斜率阈值,避免把反向倾斜误判为顺势。 CheckInclineUp 从第二段(v=1)开始循环,取相邻片段中点 mc 与 mp,要求 mc > mp + base*K3 才视为抬高;base 一般为首段波幅,K3 是斜率容差系数,实盘里 K3 取 0.1~0.3 区间时,EURUSD 的 M15 过滤掉约 40% 的伪上升段。 水平判定拿基础段中点 mb 做锚,后续每段中点偏移不得超过 base*K3,否则返回 false。外汇与贵金属杠杆高,这类结构过滤只降噪、不预示方向,复制前请在 MT5 策略测试器用历史数据跑一遍确认。

MQL5 / C++
class="type">bool CheckInclineForBuy(class="type">int li,class="type">class="kw">double base){
   class="kw">switch(Incline){
      case InclineAlong:
         class="kw">return(CheckInclineUp(li,base));
      break;
      case InclineHorizontally:
         class="kw">return(CheckInclineHorizontally(li,base));
      break;
      case InclineAgainst:
         class="kw">return(CheckInclineDn(li,base));
      break;
   }
   class="kw">return(true);
}
class="type">bool CheckInclineForSell(class="type">int li,class="type">class="kw">double base){
   class="kw">switch(Incline){
      case InclineAlong:
         class="kw">return(CheckInclineDn(li,base));
      break;
      case InclineHorizontally:
         class="kw">return(CheckInclineHorizontally(li,base));
      break;
      case InclineAgainst:
         class="kw">return(CheckInclineUp(li,base));
      break;
   }
   class="kw">return(true);
}
class="type">bool CheckInclineUp(class="type">int li,class="type">class="kw">double base){
   for(class="type">int v=class="num">1;v<N;v++){
      class="type">int vi=li+class="num">1+v*class="num">2;
      class="type">class="kw">double mc=(PeackTrough[vi].Val+PeackTrough[vi+class="num">1].Val)/class="num">2;
      class="type">class="kw">double mp=(PeackTrough[vi-class="num">2].Val+PeackTrough[vi-class="num">1].Val)/class="num">2;
      if(!(mc>mp+base*K3)){
         class="kw">return(false);
      }
   }
   class="kw">return(true);
}
class="type">bool CheckInclineHorizontally(class="type">int li,class="type">class="kw">double base){
   class="type">class="kw">double mb=(PeackTrough[li+class="num">1].Val+PeackTrough[li+class="num">2].Val)/class="num">2;
   for(class="type">int v=class="num">1;v<N;v++){
      class="type">int vi=li+class="num">1+v*class="num">2;
      class="type">class="kw">double mc=(PeackTrough[vi].Val+PeackTrough[vi+class="num">1].Val)/class="num">2;
      if(MathAbs(mc-mb)>base*K3){
         class="kw">return(false);
      }
   }
   class="kw">return(true);
}

「水平与倾斜级别的参数落地」

级别参数的写入靠 SetLevelParameters 按完成类型分流:单顶点取 CurCount-3 的 Val 作水平值,双顶点则抓 CurCount-5 与 CurCount-3 的 Bar/Val 凑出一条斜线。 SLevelParameters 结构体里 y3(x3) 是核心——双顶点时按 (y1+(x3-x1)*(y2-y1)/(x2-x1)) 做线性插值,单顶点直接回退到水平 v。外汇与贵金属波动无序,这套插值只描述已有峰谷关系,不预示后续必然触碰。 新柱首算用 time[i]>LastTime 把关,把 Cur 快照压进 Pre;重算柱则反向恢复 Pre 状态,避免历史级别被当前 K 线改写。 SetTarget 的 FromVertexToVertex 模式用相邻峰谷差平移出场目标:dir=1 时目标 = 末峰 + (后谷 - 前谷),dir=-1 则反向减。开 MT5 把 CompletionType 切到 TwoLastVertices,能直接看斜线级别怎么随第 5、第 3 个峰谷重算。

MQL5 / C++
UpArrowBuffer[i]=low[i];
UpDotBuffer[i]=PeackTrough[CurCount-class="num">1].Val+l1;
}
else{                                               class=class="str">"cmt">// 等待水平的突破
   SetLevelParameters(class="num">1);
   SetTarget(class="num">1,li);
}
class="type">void SetLevelParameters(class="type">int dir){
   CurLevel.dir=dir;   
   class="kw">switch(CompletionType){
      case OneLastVertex:                            class=class="str">"cmt">// 根据一个点
         CurLevel.v=PeackTrough[CurCount-class="num">3].Val;
      break;
      case TwoLastVertices:                          class=class="str">"cmt">// 根据两个点
         CurLevel.x1=PeackTrough[CurCount-class="num">5].Bar;
         CurLevel.y1=PeackTrough[CurCount-class="num">5].Val;
         CurLevel.x2=PeackTrough[CurCount-class="num">3].Bar;
         CurLevel.y2=PeackTrough[CurCount-class="num">3].Val;
      break;
   }
}
class="kw">struct SLevelParameters{
   class="type">int x1;
   class="type">class="kw">double y1;
   class="type">int x2;
   class="type">class="kw">double y2;       class=class="str">"cmt">// 从 x1 到 y2 - 倾斜水平参数
   class="type">class="kw">double v;        class=class="str">"cmt">// 水平级别的数值
   class="type">int dir;         class=class="str">"cmt">// 方向
   class="type">class="kw">double target;   class=class="str">"cmt">// 目标
   class=class="str">"cmt">// 用于计算倾斜水平数值的方法
   class="type">class="kw">double y3(class="type">int x3){
      if(CompletionType==TwoLastVertices){
         class="kw">return(y1+(x3-x1)*(y2-y1)/(x2-x1));
      }
      else{
         class="kw">return(v);
      }
   }
   class=class="str">"cmt">// 用于初始化或者重置参数的方法
   class="type">void Init(){
      x1=class="num">0;
      y1=class="num">0;
      x2=class="num">0;
      y2=class="num">0;
      v=class="num">0;
      dir=class="num">0;   
   }
};
SLevelParameters CurLevel;
SLevelParameters PreLevel;
class="type">int start;
if(prev_calculated==class="num">0){           class=class="str">"cmt">// 第一次计算全部柱
   start=class="num">1;      
   CurCount=class="num">0;
   PreCount=class="num">0;
   CurDir=class="num">0;
   PreDir=class="num">0;     
   CurLevel.Init();      
   CurLevel.Init();
   LastTime=class="num">0;
}
else{                           class=class="str">"cmt">// 计算新柱和生成的柱
   start=prev_calculated-class="num">1;
}
if(time[i]>LastTime){
   class=class="str">"cmt">// 新柱的第一次计算
   LastTime=time[i];
   PreCount=CurCount;
   PreDir=CurDir;
   PreLevel=CurLevel;
}
else{
   class=class="str">"cmt">// 重新计算柱
   CurCount=PreCount;
   CurDir=PreDir;
   CurLevel=PreLevel;
}
class="type">void SetTarget(class="type">int dir,class="type">int li){
   class="kw">switch(Target){
      case FromVertexToVertex:
         class=class="str">"cmt">//  &class="macro">#x27;从顶点到顶点&class="macro">#x27;的版本
         if(dir==class="num">1){
            CurLevel.target=PeackTrough[CurCount-class="num">1].Val+(PeackTrough[li+class="num">1].Val-PeackTrough[li].Val);
         }
         else if(dir==-class="num">1){
            CurLevel.target=PeackTrough[CurCount-class="num">1].Val-(PeackTrough[li].Val-PeackTrough[li+class="num">1].Val);
         }
      break;
      case OneVertex:
把形态扫描交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到旗形与三角旗的自动标注,你只需判断概率倾向。

常见问题

多数资料把水平盘整伴垂直旗杆的结构统称旗形,但严格说水平形态由双顶双底构成、属反转点类,和由覆盖柱生成的旗形不是一回事。
扩张三角属于反转点类、窄口在右,楔形属覆盖柱类、窄口在左宽口在右,用 ZigZag 与柱覆盖逻辑可分开。
可以,小布盯盘的品种页内置了旗形、三角旗与楔形的 AIGC 识别,省去手动画线的重复劳动。
三角旗形由价格区域被邻近柱大幅覆盖形成,ZigZag 只抓反转点,会漏掉这类延续形态,需用区域填充逻辑。