旗形形态——

旗形形态的基本特性,就如同它的名字所关联的,是一种明显的垂直方向价格变化("旗杆")伴着随后的水平方向的价格盘整所构成的水平旗形(图 1).


图 1. 旗形

在技术分析相关的书籍和网站中,旗形形态经常与三角旗形形态做横向比较,与旗形不同,三角旗形是三角形形状 (图 2), 这也是为什么在一些技术分析资源中,旗形形态与三角形态一起分析。


图 2. 三角旗形

可能看起来三角旗形和三角形只是相同形态的不同名称,在一些技术分析书籍中,例如 Tomas Bulkowski 的“图表形态百科全书”中, 这两种形态是分别独立描述的。而且书中还描述了楔形形态,它和水平绘制的三角形是类似的,它的狭窄部分在左边,而在右方价格变化的范围更大。


图 3. 楔形

除了楔形形态,还有扩张三角形,以及各种长方形形态都类似于旗形。所以,应该有明确的规则来区分三角旗形和三角形,区分楔形和扩张三角形,以及区分旗形和水平形态。这个问题将会在文章中首先做探讨,然后,我们将创建指标来搜索所有这些形态。 

三角旗形和三角形的差别

让我们探讨一下三角旗形和三角形的区别,以及其他上述形状类似的形态的区别:

  • 长方形形态和旗形;
  • 三角形和三角旗形;
  • 扩张三角形和楔形.

长方形形态,三角形和扩张三角形都属于一类,而旗形、三角旗形和楔形属于另一类,

第一类的模式都是由价格反转点构成的(图4),所以可以使用之字转向(ZigZag)指标来搜索它们。


图 4. 形态: a — 水平形态, b — 三角形, c — 扩张三角形.  
形态显示价格有上涨的期待(用于买入).

第二类的模式是由填充它们区域的柱构成的,当然,在图表上很难看到明显形成这样的图形,但是重点是有一个柱被邻近的柱大幅覆盖,而形成了形态。 


图 5. 形态: a — 旗形, b — 三角形, c — 楔形.
形态显示了价格可能会上涨 (用于买入).

现在我们已经确定了类别和它们的基本差别,让我们单独探讨每个形态。 

水平形态

让我们从第一类的形态开始讨论,确定这样形态的简便方法是使用之字转向指标,这类形态的第一个是水平形态, 它对应着第二种类的旗形形态。然而,在大多数技术分析相关资料中,水平形态也被称为旗形形态。

为了形成水平形态,价格应当有明显的垂直方向变化,然后再在相同的价格水平上形成至少双顶和双底。价格也可能形成三顶和三底(图 6), 或者更多。所以,指标的一个参数就是确定构成形态的顶部和底部的数量。


图 6. 水平形态: a — 由两个顶部/底部构成,b — 由三个顶部/底部构成.
形态显示出价格可能上涨 (适合买入) 

在形态中并不要求顶部和底部的边缘是水平的,它们必须是平行的,所以指标将有一个或者多个参数来选择形态的倾斜方向: 水平方向,向上倾斜,向下倾斜 (图 7).


图 7. a — 水平方向形态, b — 向下倾斜的形态, c — 向上倾斜的形态. 
形态显示价格可能上涨 (适合买入)

当然,这样向上或者向下倾斜的形态不能称为水平形态,但是它们在原则上与水平形态很接近,

当价格突破了顶部构成的水平时,形态就结束了。

 
图 8. 形态结束并建立一个
买入仓位: a — 对于水平形态,
b — 对于向下倾斜的形态 

向上倾斜形态进场的时刻将不考虑顶部的倾斜,而会使用最后一个顶部的水平价格 (图 9).


图 9. 确定向上倾斜形态的入场点 (买入)

在向下倾斜形态中也可以使用水平级别的简单变化,所以指标还将有个变量用于选择级别的类型而不论形态的类型。

水平形态的目标是由形态形成之前垂直方向价格变化的大小来决定的,价格的距离应当等于它形成之前的价格的距离(图10)。


图 10. 确定目标。L1 是在进入形态之前价格的距离,
它应当等于退出形态的距离L2。 

因为形态的上下边界是平行的,我们可以使用一个简单方法来确定目标: 我们可以根据价格和第一个形态顶部的距离来使用相同的距离设置最近的底部(图 11)。


图 11. 确定目标的简单方法。价格距离构成第一个顶部之间的距离L1
等于最后一个底部和目标的距离L2

收敛三角形

收敛三角形形态和水平形态有少许不同,唯一的不同是构成形态的之字转向片段必须是连续收敛的 (图 12)。


 图 12. 收敛三角形片段 3-4 必须比
片段 1-2 小, 而片段 5-6 必须比 3-4 小 

其余的条件与水平形态类似: 三角形的水平位置或者向上/向下倾斜,在突破了由最近两个顶部或者底部水平级别的阻力位时进场,目标水平的计算也类似。

扩张三角形

上面的收敛三角形原则可以应用于扩张三角形,唯一的区别是构成形态的之字转向片段在增大(图 13)。


 图 13. 扩张三角形. 片段 3-4 必须大于
片段 1-2, 而片段 5-6 必须大于 3-4 

根据这三种形态如此的类似,我们可以为搜索它们创建一个通用指标。  

用于搜索水平形态和三角形的通用指标

为了创建指标,我们将使用 iUniZigZagSW 指标,它来自通用的之字转向指标这篇文章。还需要以下另外的文件: CSorceData.mqh, CZZDirection.mqh 和 CZZDraw.mqh。这些文件以及iUniZigZagSW.mq5 文件可以从通用之字转向指标文章的附件中下载。下载文档,再解压并把 MQL5 文件夹复制到终端数据文件夹中,在复制之后, ZigZag 文件夹将会出现在 MQL5/Indicators 中,包含了几个文件 (包括 iUniZigZagSW.mq5), 还有另一个 ZigZag 文件夹,CSorceData.mqh, CZZDirection.mqh 和 CZZDraw.mqh 文件将出现在 MQL5/Includes 文件夹中。在复制文件之后,重新启动终端来编译指标或者在 MetaEditor 中逐个编译它们。在终端的图表中运行 iUniZigZagSW 指标已确保它能正常工作。

沃尔夫波形文章中,在创建指标的中间步骤中保存了iWolfeWaves_Step_1.mq5 文件,它实现了使用 iCustom() 函数访问 iUniZigZagSW.mq5 指标,另外,在它内部还有一个数组保存了所有之字转向的顶部和底部。下载‘沃尔夫波形’文章的附件, 把它解压缩, 复制iWolfeWaves_Step_1.mq5 到 MQL5/Indicators, 把它重命名为 "iHorizontalFormation" 并在 MetaEditor 中打开它,所有使用指标来侦测水平形态的工作都将在这个文件中进行。可能需要在这个文件中修改 iUniZigZagSW 指标的路径,为了检查这一点,要编译指标并尝试在图表上运行它。如果出现了 "载入指标出错" 的消息窗口, 就要在 OnInit() 中找到对 iCustom() 的调用, 并且把指标名称从"iUniZigZagSW"改为"ZigZags\\iUniZigZagSW"。在这样修改之后,再次编译指标,并确保它可以在图表上正确运行。在这个阶段,指标不会画出任何东西。

在这里讨论的整个搜索形态的过程可以分为几个独立任务:

  1. 确定在形成形态之前的价格变化的数值,
  2. 确定形态的形状,
  3. 确定形态的斜度,
  4. 形态生成结束: 在形态形成之后或者等到突破某一水平。
  5. 确定目标。

每个任务(除了第一个)都将提供集中方案选项,这将有助于我们创建一个通用指标来识别所有这三种形态。我们将能够在指标参数窗口中使用枚举的下拉列表在选项中作切换, 

选择形状(形态类型)的枚举:

enum EPatternType{
   PatternTapered,
   PatternRectangular,
   PatternExpanding
};

属性窗口中对应的变量:

input EPatternType         Pattern        =  PatternRectangular;

这个参数可以用来选择形态的形状: PatternTapered — 收敛三角形, PatternRectangular - 长方形, PatternExpanding - 扩张三角形.

用于选择形态倾斜方向的枚举:

enum EInclineType{
   InclineAlong,
   InclineHorizontally,
   InclineAgainst
};

属性窗口中对应的变量:

input EInclineType         Incline        =  InclineHorizontally; 

这个参数可以选择形态的倾斜方向: InclineAlong — 在期望的方向上继续延伸 (对于买入就是上涨,对于卖出就是下跌), InclineHorizontally — 没有倾斜, InclineAgainst — 于期望的方向上相反。

用于选择形态结束方法的枚举:

enum EEndType{
   Immediately,
   OneLastVertex,
   TwoLastVertices
};

属性窗口中对应的变量:

input EEndType             CompletionType =  Immediately;

可以使用以下的选项: 在形态完成后立即, OneLastVertex — 在突破了形态最近的顶部水平后, TwoLastVertices — 在突破了形态中由两个顶部构成的水平之后.

用于选择目标计算选项的枚举:

enum ETargetType{
   FromVertexToVertex,
   OneVertex,
   TwoVertices
};

属性窗口中对应的变量:

input ETargetType          Target         =  OneVertex;

可以使用以下的选项: FromVertexToVertex (图 11), OneVertex (图 10), TwoVertices, 使用形态中的两个初始底部 (参见图 14).


图 14. 三顶的形态。目标确定选项是 TwoVertices,
形态完成方法是 OneLastVertex.

当形态完成方法设为 Immediately 的时候, Target 参数就是无效的,因为只能使用 FromVertexToVertex 。对于另外两种形态完成选项 (OneLastVertex 和 TwoLastVertices), 所有三种 CompletionType 选项的不同组合都是可能的。请注意下面的特性: 如果选择了 OneVertex 或者 TwoVertices 选项来确定目标, 一个或者两个第一底部(图14中的点2, 或者点2和点4)来用于确定目标, 而突破水平是根据最近一个或者两个顶部来确定的 (图14中的点5或者点3和点5). 如果使用了双顶的形态,将使用点3或者点1和点3。

为了解决任务1, 我们需要一个参数来确定形态之前价格变化的大小:

input double               K1             =  1.5;

片段1-2的高度(参见图14)用作形态的基础(它的参考大小), 而所有对大小的检查都是相对它的。K1 参数确定了片段0-1的高度应该比片段1-2高多少倍。

为了确定形态的形状,我们使用 K2 参数:

input double               K2             =  0.25;

参数值越小,形态的高度在它的长度中就越稳定。对于三角形形态 (扩张和收敛), 有一个增加参数用来搜索最明显的三角形。

为了确定形态的倾斜方向,我们使用 K3 参数:

input double               K3             =  0.25;

参数值越小,就意味着指标将搜索水平位置的形态,要搜索倾斜的形态,就应该在K2参数中指定较大的数值,它可以找到明显倾斜的形态。

最后,还有一个主要的参数:

input int                  N              =  2;

N 参数确定了形态定点的数量。 

结果,我们就有了以下的外部参数集 (还有 ZigZag 的参数):

input EPatternType         Pattern        =  PatternRectangular;
input EInclineType         Incline        =  InclineHorizontally;     
input double               K1             =  1.5;
input double               K2             =  0.25;
input double               K3             =  0.25;
input int                  N              =  2;
input EEndType             CompletionType =  Immediately;
input ETargetType          Target         =  OneVertex;

使用 N 参数,我们计算在确定形态时需要多少个之字转向点。首先,我们声明一个全局变量:

int RequiredCount;

在 OnInit() 函数中,我们计算它的值:

RequiredCount=N*2+2;

2*N 是构成形态的顶点的数量 (N 个顶部和 N 个底部). 另外一个顶点确定了之前价格的变化,而额外的一个点是新的之字转向片段(没有在计算中使用)的最后一个点。

更多的操作将在 OnTick() 函数中进行,新的代码将会加在住指标循环的最后。如果之字转向点的数量足够并且只有在它方向改变时,才将检查形态形成的条件,价格和水平将在每个之字转向变化时作检查:

if(CurCount>=RequiredCount){
   if(CurDir!=PreDir){      
      // 检查条件

   }
   // 检查价格和水平级别

} 

首先,我们计算基础数值,也就是片段 1-2 的高度(参见图14),这个值将在检查所有形态形成条件的时候使用。然后,检查任务1的条件,也就是之前的价格变化

int li=CurCount-RequiredCount;                                            // 初始形态点在 PeackTrough 数组中的索引
double base=MathAbs(PeackTrough[li+1].Val-PeackTrough[li+2].Val);        // 基础数值
double l1=MathAbs(PeackTrough[li+1].Val-PeackTrough[li].Val);             // 片段 1-2 的高度
   if(l1>=base*K1){                                                       // 检查之前价格变化的大小
        // 其它的检查

   }

进一步的检查将依赖于之字转向的最后片段是指向上涨还是下跌。

if(CurDir==1){              // 最后的之字转向片段指向上涨
   // 检查上涨方向的条件  
             
}
else if(CurDir==-1){        // 最后的之字转向片段指向下跌
   // 检查下跌方向的条件

}

让我们讨论上涨方向条件的检查:

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

形态形成条件是使用两个函数作检查的: CheckForm() 来检查形态的形状,而 CheckInclineForBuy() 来检查倾斜度。如果成功检查了形状和倾斜,那么就根据形态完成的类型,在图表上画出箭头或者目标点,或者突破水平的参数。

CheckForm() 函数。形态中第一个点在 PeackTrough 数组中的索引基础数值 'base' 会传给函数,这里是函数的代码:

bool CheckForm(int li,double base){               
   switch(Pattern){
      case PatternTapered: 
         // 收敛
         return(CheckFormTapered(li,base));
      break;               
      case PatternRectangular: 
         // 长方形
         return(CheckFormRectangular(li,base));
      break;
      case PatternExpanding: 
         // 扩张
         return(CheckFormExpanding(li,base));
      break;
   }
   return(true);
}

在函数中,会根据 Pattern 参数的值调用适当的函数: 对于收敛三角形调用 CheckFormTapered(), 对于长方形形态调用 CheckFormRectangular(), 对于扩张三角形调用 CheckFormExpanding()。

CheckFormTapered() 函数:

bool CheckFormTapered(int li,double base){
   // 从1开始循环,不检查第一个片段,
   // 但是所有随后的片段都会相对它做检查 
   for(int i=1;i<N;i++){ 
      // 计算形态中下一个顶部点的索引
      int j=li+1+i*2;
      // 下一个片段的数值 
      double lv=MathAbs(PeackTrough[j].Val-PeackTrough[j+1].Val);
      // 前一个片段的数值
      double lp=MathAbs(PeackTrough[j-2].Val-PeackTrough[j-1].Val);
      // 前一个片段应该更大,否则
      // 函数返回 false   
      if(!(lp-lv>K2*base)){
         return(false);
      }
   } 
   return(true);
}

在函数中,构成形态的之字转向片段在一个循环中做检查,而每个后面的片段必须小于前一个。

CheckFormExpanding() 函数类似,只有一点区别:

if(!(lv-lp>K2*base)){
   return(false);
}

为了达成这个条件,每个后面的片段必须比前一个片段要大。

CheckFormRectangular() 函数:

bool CheckFormRectangular(int li,double base){   
   // 在除了第一个片段之外的所有片段做循环      
   for(int i=1;i<N;i++){
      // 计算下一个顶部的索引 
      int j=li+1+i*2; 
      // 计算下一个片段的大小
      double lv=MathAbs(PeackTrough[j].Val-PeackTrough[j+1].Val);
      // 片段应该和基础数值相差不大 
      if(MathAbs(lv-base)>K2*base){
         return(false); 
      }
   }
   return(true);
}

在这个函数中,每个片段都和基础数值作比较,如果差别很大,函数就返回 false。

如果形状检查成功,就检查它的倾斜度。CheckInclineForBuy() 函数:

bool CheckInclineForBuy(int li,double base){                 
   switch(Incline){
      case InclineAlong:
         // 在价格移动方向上的倾斜
         return(CheckInclineUp(li,base));
      break;
      case InclineHorizontally:
         // 没有倾斜
         return(CheckInclineHorizontally(li,base));
      break;                     
      case InclineAgainst:
         // 倾斜方向与价格移动方向相反
         return(CheckInclineDn(li,base));
      break;
   } 
   return(true);
}  


用于卖出的倾斜检查函数只有两行不同:

bool CheckInclineForSell(int li,double base){                 
   switch(Incline){
      case InclineAlong:
         // 在价格移动方向上的倾斜
         return(CheckInclineDn(li,base));
      break;
      case InclineHorizontally:
         // 没有倾斜
         return(CheckInclineHorizontally(li,base));
      break;                     
      case InclineAgainst:
         // 倾斜方向与价格移动方向相反
         return(CheckInclineUp(li,base));
      break;
   } 
   return(true);
} 

如果 Incline 等于 InclineAlong (与移动方向相同), 买入就调用 CheckInclineUp() 而卖出则调用 CheckInclineDn()。如果 Incline = InclineAgainst 就相反。

CheckInclineUp(), 该函数用于检查形态的向上倾斜:

bool CheckInclineUp(int li,double base){   
   // 在除了第一个片段之外的所有片段做循环      
   for(int v=1;v<N;v++){
      // 计算下一个顶部的索引
      int vi=li+1+v*2;
      // 计算下一个之字转向片段的中部
      double mc=(PeackTrough[vi].Val+PeackTrough[vi+1].Val)/2;
      // 计算前一个之字转向片段的中部
      double mp=(PeackTrough[vi-2].Val+PeackTrough[vi-1].Val)/2;
      // 下一个片段应该比前一个更高
      if(!(mc>mp+base*K3)){
         return(false);
      }
   }
   return(true);
} 

所有的之字转向片段都在函数中作检查: 计算每个片段的中部,并且与前一个片段的中部作比较,每个片段应该高于它前一个片段 base*K3 的数值。

CheckInclineDn() 函数有一点差别:

if(!(mc<mp-base*K3)){
   return(false);
}

为了满足这个条件,每个后续片段必须比前一个要小。

CheckInclineHorizontally() 函数:

bool CheckInclineHorizontally(int li,double base){ 
   // 基础片段的中点
   double mb=(PeackTrough[li+1].Val+PeackTrough[li+2].Val)/2;        
   for(int v=1;v<N;v++){
      // 下一个顶部的索引
      int vi=li+1+v*2;
      // 下一个片段的中点
      double mc=(PeackTrough[vi].Val+PeackTrough[vi+1].Val)/2;
      // 下一个片段的中点 
      // 和基础片段的中点不能偏移太多
      if(MathAbs(mc-mb)>base*K3){
         return(false);
      }
   }                  
   return(true);
}

如果检查形状和倾斜成功,就执行以下的代码部分:

if(CompletionType==Immediately){                    // 立即入场
   UpArrowBuffer[i]=low[i];
   UpDotBuffer[i]=PeackTrough[CurCount-1].Val+l1;
}
else{                                               // 等待水平的突破
   SetLevelParameters(1);
   SetTarget(1,li);
}

如果形态结束设为 Immediately, 指标就画出箭头并设置目标点,在其它情况下就使用 SetLevelParameters() 函数来设置突破水平,而使用 SetTarget() 函数来设置目标。

SetLevelParameters() 函数:

void SetLevelParameters(int dir){
   CurLevel.dir=dir;   
   switch(CompletionType){
      case OneLastVertex:                            // 根据一个点
          CurLevel.v=PeackTrough[CurCount-3].Val;
      break;
      case TwoLastVertices:                          // 根据两个点
         CurLevel.x1=PeackTrough[CurCount-5].Bar;
         CurLevel.y1=PeackTrough[CurCount-5].Val;
         CurLevel.x2=PeackTrough[CurCount-3].Bar;
         CurLevel.y2=PeackTrough[CurCount-3].Val;
      break;
   }
} 

在 SetLevelParameters() 函数中,SLevelParameters 结构用于保存水平参数:

struct SLevelParameters{
   int x1;
   double y1;
   int x2;
   double y2;       // 从 x1 到 y2 - 倾斜水平参数
   double v;        // 水平级别的数值
   int dir;         // 方向
   double target;   // 目标
   // 用于计算倾斜水平数值的方法
   double y3(int x3){
      if(CompletionType==TwoLastVertices){
            return(y1+(x3-x1)*(y2-y1)/(x2-x1));
      }
      else{
         return(v);
      }
   }
   // 用于初始化或者重置参数的方法
   void Init(){
      x1=0;
      y1=0;
      x2=0;
      y2=0;
      v=0;
      dir=0;   
   }
};

该结构包含了线形参数的栏位: x1, y1, x2, y2; 栏位 'v' 用于水平级别数值; 'd' 是形态的方向; 'target' 是目标。目标可以设为价格水平 (当使用 FromVertexToVertex 时) 或者是突破水平的数值 (当使用 OneVertex 或者 TwoVertices 时)。y3() 方法用于计算倾斜水平的数值,Init() 方法用于初始化或者重置数值,

如果所有的形态生成条件都满足,就调用 SetLevelParameter() 函数。根据所选水平的类型 (水平或者倾斜),倾斜水平参数(栏位 x1, y1, x2, y2) 或者一个水平级别数值 'v' 会在这个函数中设置。在 y3() 方法中, 水平数值是使用 x1, y1, x2, y2 栏位或者返回的 'v' 栏位数值计算的。

在指标中声明了两个 SLevelParameters 类型的变量:

SLevelParameters CurLevel;
SLevelParameters PreLevel;

这对变量的使用和变量 CurCount-PreCount 以及 CurDir-PreDir 类似, 变量的数值在指标初始计算时会重置 (代码部分位于 OnTick() 函数的开头):

int start;

if(prev_calculated==0){           // 第一次计算全部柱
   start=1;      
   CurCount=0;
   PreCount=0;
   CurDir=0;
   PreDir=0;  
   CurLevel.Init();    
   CurLevel.Init();
   LastTime=0;
}
else{                           // 计算新柱和生成的柱
   start=prev_calculated-1;
}


在每个柱的计算中,这些变量的数值会有变化(代码位于指标循环的开始):

if(time[i]>LastTime){
   // 新柱的第一次计算
   LastTime=time[i];
   PreCount=CurCount;
   PreDir=CurDir;
   PreLevel=CurLevel;
}
else{
   // 重新计算柱
   CurCount=PreCount;
   CurDir=PreDir;
   CurLevel=PreLevel;
}

目标参数是通过 SetTarget() 函数调用的:

void SetTarget(int dir,int li){
   switch(Target){
      case FromVertexToVertex:
         //  '从顶点到顶点'的版本
         if(dir==1){
            CurLevel.target=PeackTrough[CurCount-1].Val+(PeackTrough[li+1].Val-PeackTrough[li].Val);
         }
         else if(dir==-1){
            CurLevel.target=PeackTrough[CurCount-1].Val-(PeackTrough[li].Val-PeackTrough[li+1].Val);
         }
      break;
      case OneVertex:
         // 使用一个顶点
         CurLevel.target=MathAbs(PeackTrough[li].Val-PeackTrough[li+2].Val);
      break;
      case TwoVertices:
         // 使用两个顶点
         SetTwoVerticesTarget(dir,li);
      break;
   }
}

为 FromVertexToVertex 要计算一个价格值。对于 OneVertex, 价格变化的数值是从突破水平到目标,赋值到 'target' 栏位。在 SetTwoVerticesTarget() 函数中计算 SetTwoVerticesTarget :

void SetTwoVerticesTarget(int dir,int li){
   // 形态中初始线的坐标
   //  - 从底部到顶部  
   double x11=PeackTrough[li].Bar;
   double y11=PeackTrough[li].Val;
   double x12=PeackTrough[li+1].Bar;
   double y12=PeackTrough[li+1].Val;
   // 通过两个底部画线用于买入的坐标
   // 或者通过两个顶部画线用于卖出
   double x21=PeackTrough[li+2].Bar;
   double y21=PeackTrough[li+2].Val;
   double x22=PeackTrough[li+4].Bar;
   double y22=PeackTrough[li+4].Val;
   // 线的交叉点的数值
   double t=TwoLinesCrossY(x11,y11,x12,y12,x21,y21,x22,y22);
   // 根据方向设置目标数值
   if(dir==1){
      CurLevel.target=t-PeackTrough[li].Val;
   }
   else if(dir==-1){
      CurLevel.target=PeackTrough[li].Val-t;         
   }
}

对于 SetTwoVerticesTarget 版本, 'target' 栏位的赋值为从突破水平到目标的价格移动,与 OneVertex 类似。

让我们探讨价格和水平跟踪是如何进行的 (CompletionType 不等于 Immediately):

// 使用水平级别
if(CompletionType!=Immediately){
   // 之字转向有变化
   if(PeackTrough[CurCount-1].Bar==i){
      if(CurLevel.dir==1){                // 等待向上突破
         // 把水平数值赋予 cl 变量
         double cl=CurLevel.y3(i); 
         // 之字转向突破了水平级别
         if(PeackTrough[CurCount-1].Val>cl){
            // 设置向上箭头
            UpArrowBuffer[i]=low[i];
            // 设置目标点
            if(Target==FromVertexToVertex){
               // 'target' 栏位中的价格
               UpDotBuffer[i]=CurLevel.target;                        
            }
            else{
               // 与 'target' 栏位中水平的距离
               UpDotBuffer[i]=cl+CurLevel.target;
            }
            // 清零 'dir' 栏位以停止跟踪水平
            CurLevel.dir=0;
         }
      }
      else if(CurLevel.dir==-1){         // 等待向下突破
         // 把水平数值赋予 cl 变量
         double cl=CurLevel.y3(i);
         // 之字转向突破了水平级别
         if(PeackTrough[CurCount-1].Val<cl){
            // 设置向下箭头
            DnArrowBuffer[i]=low[i];
            // 设置目标点
            if(Target==FromVertexToVertex){
               // 'target' 栏位中的价格
               DnDotBuffer[i]=CurLevel.target;
            }
            else{                     
               // 与 'target' 栏位中水平的距离
               DnDotBuffer[i]=cl-CurLevel.target;
            }
            // 清零 'dir' 栏位以停止跟踪水平
            CurLevel.dir=0;
         }         
      }         
   }
} 

这个检查是在每次之字转向有变化的时候进行的,所有的之字转向峰值都保存在 PeackTrough 数组中,改变是由最新之字转向点对当前柱索引变化来确定的:

if(PeackTrough[CurCount-1].Bar==i){

当前的水平数值使用 y3() 方法来计算:

double cl=CurLevel.y3(i); 

要检查最新的之字转向片段是否突破了这一水平:

if(PeackTrough[CurCount-1].Val>cl){

如果水平被突破,指标就画出箭头并加上目标点,'target' 栏位可能包含目标的价格数值,在这种情况下直接使用这个数值。它也可能包含和目标的距离,在这种情况下目标是根据当前价格水平数值来计算的: 

if(Target==FromVertexToVertex){
   UpDotBuffer[i]=CurLevel.target;                        
}
else{
   UpDotBuffer[i]=cl+CurLevel.target;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值