不変なジグザグ

Dmitry Fedoseev | 9 1月, 2017


コンテンツ

イントロダクション

ジグザグ (図 1) は、メタト レーダー 5のユーザーの間でも人気の高いインジケーターの一つです。日に日に、さまざまなジグザグの亜種が開発されました。しかし、そのいくつかは遅すぎて、EAを作成するのには不向きです。常にエラーが発生し、目視であっても使用が困難なものもあります。高速かつエラーなしで動作するインジケーターのために、EA または別のインジケーターを開発します。ジグザグ データを解釈することは簡単ではないので、こういう事態が発生します。


図1。ジグザグ インジケーター

別のメソッドとして、ジグザグをプロットするための要件を検討し、統合したアルゴリズムを得ます。このアルゴリズムは、プロパティ ウィンドウでジグザグの種類を選択することができ、不変なインジケーターを作成するための基礎として使用します。

オブジェクト指向プログラミングを、インジケータの開発に使用します。ジグザグのプロットのさまざまな段階で、複数の基本クラスが作成され、複数の子クラスがステージごとに作成されます。異なる新しいジグザグ パターンの作成を可能な限りシンプルになるように実施します。

インジケーターを使用して、他のインジケーターやEAを開発する点についても焦点を当てます。ジグザグからデータを取得し、複雑で時間がかかることを確認するためです。

ジグザグ インジケーターの関数

ジグザグ (図 1) インジケーターは、ローカル価格の高値と安値を接続するポリラインです。初心者はすぐに「天井と底を指し示す素晴らしいものだ!」と判断してしまいます。もちろん、とても魅力的に見えます。悲しいかな、ジグザグはヒストリーの場合、とても魅力的に見えます。しかし、実際には、状況が違います。実際に形成しつつある場合、新しい上部または下部が明らかになります。インジケーターの最後のセグメントでは、その形成 (変更) 価格を逆転し、(上) に反対の方向でストップしたときの状況を図 2 に示します。

 
図 2。ジグザグは、下方を指示するが、価格は上向きに反転

ただし、いくつか足価格(図 3)、その後、ジグザグの最後のセグメントは、下方へストレッチ続けています。

  
図3。価格は下に移動し続け、ジグザグの最後のセグメントでその形成を再開

この間インジケーターが最小に達することを、いくつか足 (図 4) の後で確認できます。

 
図4。新しい上向きのセグメントをジグザグが描画するまでに10本のロウソク足を必要としました。


ジグザグの特性は、図5 の通りです。色のドットは、以前の形成足を示します。このインジケーターは、「ブルーのドットと足の新しい上昇セグメント」と「赤いドットと足の新しい下方セグメント」のプロットを始めていました。


図5赤と青のドットを示すジグザグ反転   

この特異性にもかかわらず、ジグザグはその人気と魅力を失うことはありません。少なくとも、グラフの視覚的分析が容易にし、ノイズをフィルタ リングし、価格の動きの主な軌跡を検出するのに役立ちます。実用的なアプリケーションとして、 サポート/レジスタンスのレベルを検出するため、パターンの識別インジケーターとして使用する可能性もあります。テクニカル分析、フィボナッチ レベル、ファン等の他のグラフィック ツールと同じように、近似曲線を描画するための基礎として使用することができます。ジグザグを使用すると、洗練されたトレーダーのような一覧表示をすることが可能です。

ジグザグの亜種のプロット

ジグザグに2つの状態があることは明らかです。:つまり、上向きか下向きか。ラインは上向きに指示されるとき、新たしい高値が出現し、下向きを表しているとき安値が出現します。この方向の変化を示す条件の監視が必要です。よって、ジグザグをプロットするために次の必要があります。

  1. ソースデータを取得します。 
  2. ラインの方向を変更するための条件を策定します。  
  3. 新高値と安値の発生を監視します。

ソース データは、1 つの列 (たとえば、足の終値) または (たとえば、高と低の価格足) 2 つのシリーズをとることができます。1つのデータ系列が使用されている場合、終値のみならず、あらゆるインジケーターを使うことができます。インジケーターのデータに基づいてジグザグを描写するときも、2つのデータ系列を使用することが可能です。つまり、高値と別に安値に基づくインジケーターです。 

方向の変更の条件は、ジグザグの種類を定義する最も重要なポイントです。これらの条件は大きく異なります。たとえば、現在の足に n 足より先で高値/安値の形成があるかもしれません。つまり、現在の足のソースの系列の値は、最後の n 個の足を最大値または最小値の方向によって定義されます。この原則は、古典的なジグザグ インジケーターで使用されています。別のメソッド-固定の最大値または最小値からロールバックのサイズに基づいたものもあります。ロールバック サイズはポイント単位で指定することができます。使用可能なメソッドは、 2 つに限定されません。任意のインジケーターを使用して、方向を決定することが可能です。ストキャスティクスが50 を超える場合は上向きになり、50 を下回る場合は、ジグザグは上向きに指します。ADXに基づいて方向を決定する方法もあります。: PDI 線 MDI 線の上の場合上向き、PDIがMDIの下は場合は下向き。 

したがって、ポイント1 とポイント2 の別の亜種を組み合わせれば、多様なジグザグが得られます。結局のところ、RSI はデータを使用して、確率などに基づいて方向を決定します。ポイント3は、ジグザグのようにインジケーター必要で、プロット オプションは非常に異なります。 

アルゴリズムをできるだけ慎重に 2 つの部分に分割する必要があり、ここでは不変な指標を取得します。個々のパーツは、ソースデータとインジケーターバッファーをエントリーに使います。(ジグザグ線の方向を決定する) 別のバッファーは、1 または-1 の値が設定されます。3つのバッファーは、プロット自体インジケーターを使用して、共通の部分に渡されます。

最初に動作する別のインジケーターを作成するには、足とその方向に基づいて変更します。

高値/安値に基づいたシンプルなジグザグ

メタエディターで新しいインジケーターを作成(メイン メニュー-ファイル-新規または Ctrl + N キーを押します)。インジケーター作成ウィザードでは、"iHighLowZigZag"という名前を入力します。1 つ外部パラメーター"period"(int 型、12 の値を持つ) を作成し、OnCalculate(...,open,high,low,close) イベント ハンドラーを選択します。「ジグザグ」(セクション タイプ、赤色) と「方向」、"LastHighBar"、"LastLowBar"名を持つ 3 つ多くバッファー名前を持つ 1 つバッファーを作成 (線の色)します。.

「ジグザグ」バッファーがジグザグを表示するため使用する、残りのバッファーは補助です。すべての補助バッファーのOnInit()関数内のSetIndexBuffer()関数の呼び出しでINDICATOR_DATA型をINDICATOR_CALCULATIONSに変更します。ファイルの先頭のindicator_plotsプロパティの値を変更: 値を 1 に設定します。その後、インジケーターを一つの「ジグザグ」バッファー描画のみ使い、余分な行は描画されません。同時に、追加のバッファーはiCustom()関数によって処理可能になります。 

まず、インジケーターを起動する場合にのみ、すべてのバーで計算が実行されるよう、新しいバーでさらに計算が実行されるように、(変数を ' 開始') インデックスはOnCalculate()関数で決定されます。さらに、バッファーの要素を初期化します。

  int start; //計算を開始する足のインデックスの変数
  if(prev_calculated==0)
    { //起動時
     //バッファーの要素を初期化
     DirectionBuffer[0]=0;
     LastHighBarBuffer[0]=0;
     LastLowBarBuffer[0]=0;
     start=1; //初期化されたものを次の要素から始まめて計算
    }
  else
    { //操作中
     start=prev_calculated-1;
    }
}

主要インジケーターサイクル:

for(int i=start;i<rates_total;i++)
{

上記の通り、不変性を達成するために必要なジグザグとそのプロットの計算にコードを分割します。この原則が今も支持されるでしょう。最初の方向を決定するコードを記述します。方向を決定するには、 ArrayMaximum()ArrayMinimum()関数を使用します。高値または安値で計算される場合、方向配列の要素が 1 または-1 の値を割り当てられます。方向を計算する前に各足では、ジグザグの現在の方向についての情報を持っているために、バッファーの前の要素から値を取得し、現在の要素に割り当てます。

//以前に決定されたの値を取得する
//前の要素からの方向
   DirectionBuffer[i]=DirectionBuffer[i-1];

//計算の最初の足
//ArrayMaximum() と ArrayMinimum() の関数
   int ps=i-period+1;
//高値と安値の足を決定する
//'period' 足の範囲
   int hb=ArrayMaximum(high,ps,period);
   int lb=ArrayMinimum(low,ps,period);

//高値または安値が識別される場合
   if(hb==i && lb!=i)
     { //高値
      DirectionBuffer[i]=1;
}
   else if(lb==i && hb!=i)
     { //安値
      DirectionBuffer[i]=-1;
     }

コードの最後の部分に注意してください。時々、非常に長い足があり、両方の方向になる場合があります。この場合、方向のバッファーは、以前に決定された方向になります。

一般的に言えば、メタト レーダー5ターミナル内で、同じ足に2つの変更を表示することが可能なジグザグ垂直セグメントを描画するを作成することが可能です。ただし、このようなジグザグ型はこの記事では考慮しません。 

メインループ内のコードの記述を続けます。 次のフラグメントは、ジグザグ線を描画を担当します。他の2つのバッファーになるバッファーと同様にします。

LastHighBarBuffer[i]=LastHighBarBuffer[i-1];
LastLowBarBuffer[i]=LastLowBarBuffer[i-1];  

2つのバッファーは、最新の高値または安値のジグザグと足のインデックスのデータが格納されます。インジケーターの描画に直接必要である足のインデックスに加えて、EAから、ジグザグを呼び出します。最新のトップを求めてループで足を反復処理する必要がありません。

ジグザグ バッファーのクリアを確認します。

ZigZagBuffer[i]=EMPTY_VALUE;  

ヒストリーがダウンロードされるときにインジケーターの完全な計算が実行される必要があります。バッファーのインディケータ線を歪める古いデータがあります。  

図面を読み進めてください。ここでは、アルゴリズムは4つに分割されます: 新しい上向きの動き、新しい下方の始まり、上方への動きの継続、下向きの動きの継続の始まり。方向をチェックするには、足の計算結果とスイッチ演算子を使用します。

switch((int)DirectionBuffer[i])
  {
   case 1:
      switch((int)DirectionBuffer[i-1])
        {
         case 1:
            //上方への動きの継続
            ...
            break;
         case -1:
            //新しい上向きの動きの初め
            ...
            break;
        }
      break;
   case -1:
      switch((int)DirectionBuffer[i-1])
        {
         case -1:
            //下方への移動の継続
            ...
            break;
         case 1:
            //新しい下方への移動の初め
            ...
            break;
        }
      break;

4 つのコードの記述が残っています。新しい上向きの動きと上向きの動きの継続の始まりとみなされます。新しい上向きの動きの初めは、-1 からバッファー内の値が変更されたときに行われます。そのとき、アルゴリズムはジグザグの新しいポイントを描画し、新しい方向が始まりまった足のインデックスを格納します。

ZigZagBuffer[i]=high[i];
LastHighBarBuffer[i]=i;

運動の継続はもう少し複雑です。このアルゴリズムは、現在の足がジグザグの以前の高値よりも大きいか確認します。大きい場合は、最後のセグメントの終点を移動させ、つまり、以前に描いたポイントを削除し、新しいものを配置します。ここでは、新しいポイントが描かれている情報を格納します。

//上方への動きの継続
   if(high[i]>high[(int)LastHighBarBuffer[i]])
     { //新しい高値
      //古いジグザグ ポイントを削除
      ZigZagBuffer[(int)LastHighBarBuffer[i]]=EMPTY_VALUE;
      //新しい場所
      ZigZagBuffer[i]=high[i];
      //新しいトップと足のインデックス
      LastHighBarBuffer[i]=i;
     }

これで完了です。右かっこでループを終了することを忘れないでください。戦略テスターのビジュアル モードでインジケーターをテストします。"iHighLowZigZag"インジケーターは、添付ファイルにあります。

終値に基づいたシンプルなジグザグ

新しく作成したインジケーターは終値に基づいています。ゼロからスタートする必要はありません。名の下の"iHighLowZigZag"インジケーター"iCloseZigZag"'終値' 配列の呼び出しを '高値'、'安値' 配列への呼び出しに置き換えます。一見、これで作業が完了しますが、テスト インジケーター (図 6) は不正な操作を示しています

 
図6。高値/安値に基づいてインジケーターから変換された「close」に基づくジグザグの不正な操作

なぜこれが起こるかを見てみましょう。現在の足の高値形成の足の特定の範囲内のトップなら、この足が残り方に関係ありません。天井が終値によって形成される場合、終値は足の形成中に変化するので、より高値が存在かもしれません。古いポイントを削除する条件が満たされるのも、問題があります。新しい高値が取り消されるか、新しいポイントが削除され、同様に削除されます。よって、古いポイントのポジションを復元する必要があります。最新の極値の情報をバッファーに含める: LastHighBarBuffer、LastLowBarBuffer。2つの最新のポイントから復元されます。スイッチの演算子の前にインジケーターのメイン ループに 2 行のコードを追加します。

ZigZagBuffer[(int)LastHighBarBuffer[i]]=close[(int)LastHighBarBuffer[i]];
ZigZagBuffer[(int)LastLowBarBuffer[i]]=close[(int)LastLowBarBuffer[i]];  

この改正後、インジケーターが正常に動作する必要があります。結果として得られる"iCloseZigZag"インジケーターは、この記事の添付ファイルにあります。。 

<

不変なジグザグの作成

ジグザグは 3 つのタスクを別々 に解くことによって達成されます。
  1. ソース データをバッファーを充填します。2 つのバッファーが使用されます。高値と安値でできるようにする必要があります。終値または他のインジケーターに基づいてジグザグを得るためには、同じ値を持つ両方のバッファーを使う必要があります。 
  2. ソース データの解析に基づくバッファーを使います。
  3. ジグザグのプロット。
各タスクが解決される別の基本クラスと追加の子クラスによって、様々なオプションとインジケーターのプロパティ ウィンドウでその組み合わせを選択することができます。

ソースデータのクラス

CSorceData クラスを追加する"CSorceData.mqh"ファイルが含まれます。親クラスになります。1つの仮想 '計算' メソッドで、特定の変更が、インジケーターの OnCalculate() 関数に含まれます。2つの追加配列がメソッドに渡されます: BufferHigh と BufferLow。バッファーは、さらにジグザグの計算に使用するデータに満ちています。価格だけでなく、他のインジケーターの値は、ソースデータとして役立つかもしれないので、読み込みプロセス インジケーターを制御します。これを行うには、CheckHandle() (bool 型) の仮想メソッドを追加します。

class CSorceData
  {
private:
public:
   virtual int Calculate(const int rates_total,
                         const int prev_calculated,
                         const datetime &time[],
                         const double &open[],
                         const double &high[],
                         const double &low[],
                         const double &close[],
                         const long &tick_volume[],
                         const long &volume[],
                         const int &spread[],
                         double &BufferHigh[],
                         double &BufferLow[])
     {
      return(0);
     }
   virtual bool CheckHandle()
     {
      return(true);
     }

  };

複数の子クラスを作成します。1つは高値/安値用:

class CHighLow:public CSorceData
  {
private:
public:
   int Calculate(const int rates_total,
                 const int prev_calculated,
                 const datetime &time[],
                 const double &open[],
                 const double &high[],
                 const double &low[],
                 const double &close[],
                 const long &tick_volume[],
                 const long &volume[],
                 const int &spread[],
                 double &BufferHigh[],
                 double &BufferLow[])
     {
      int start=0;
      if(prev_calculated!=0)
        {
         start=prev_calculated-1;
        }
      for(int i=start;i<rates_total;i++)
        {
         BufferHigh[i]=high[i];
         BufferLow[i]=low[i];
        }
      return(rates_total);
}
  };

 もう一つは、終値用。ループ内のコードでのみ異なります。

for(int i=start;i<rates_total;i++)
  {
   BufferHigh[i]=close[i];
   BufferLow[i]=close[i];
  }

このクラスの名前は、"CClose:public CSorceData"です。CheckHandle() メソッドは、今のところは使用されません。

また、幾つかのインジケーターからデータを取得するためのクラスを作成します。パラメータと異なる (価格チャート上または別のサブウィンドウで) 数が異なるインジケーターを選択-RSI と移動平均。クラスを書いてみよう。

RSI のクラスを作成、"CRSI:public CSorceData"という名前を付けます。インジケーター ハンドルの変数を private セクションに追加します。

   private:
      int m_handle;

コンス トラクタを追加: RSI パラメータは、インジケーター中にロードされます。

void CRSI(int period,ENUM_APPLIED_PRICE price)
  {
   m_handle=iRSI(Symbol(),Period(),period,price);
  }

CheckHandle() メソッド:

boolCheckHandle()
  {
   return(m_handle!=INVALID_HANDLE);
  }

Calculate メソッドは、バッファーの簡単なコピーです。

int to_copy;
   if(prev_calculated==0)
     {
      to_copy=rates_total;
     }
   else
     {
      to_copy=rates_total-prev_calculated;
      to_copy++;
     }

   if(CopyBuffer(m_handle,0,0,to_copy,BufferHigh)<=0)
     {
      return(0);
     }

   if(CopyBuffer(m_handle,0,0,to_copy,BufferLow)<=0)
     {
      return(0);
     }
   return(rates_total);

コピー ( CopyBuffer()関数の呼び出し) に失敗した場合0、正常なコピーの場合はrates_totalを返すことに注意してください。失敗した場合はインジケーターの後、再計算を許可します。 

同様に、移動平均の"CMA:public CSorceData"という名前のクラスを作成します。コンス トラクタで違いがあります。

void CMA(int period,int shift,ENUM_MA_METHOD method,ENUM_APPLIED_PRICE price)
   {
    m_handle=iMA(Symbol(),Period(),period,shift,method,price);
   }

この場合、Calculate() メソッドが完全に同一であることが判明します。"CSorceData.mqh"ファイルは、添付ファイルにあります。動作のみ暫定的に注意してください。さらに他のインジケーターの新しい子メソッドの追加による拡大をします。

方向のクラス

このクラスは、基本クラスの名前"CZZDirection.mqh"ファイルに配置されます-"CZZDirection"。Calculate() メソッドが含まれます。ソース データでバッファーおよび方向のバッファーの足を判断できるようにパラメータを渡します (rates_total、prev_calculated 変数)。インジケーターを使用し、ジグザグの方向を決定することができます。前に述べたように、インジケーターを使用する関数も追加されます。仮想の CheckHandle() メソッドを追加します。

class CZZDirection
  {
private:
public:
   virtual int Calculate(const int rates_total,
                         const int prev_calculated,
                         double &BufferHigh[],
                         double &BufferLow[],
                         double &BufferDirection[])
     {
      return(0);
     }
   virtual bool CheckHandle()
     {
      return(true);
}
  };

"IHighLowZigZag"インジケーターと同様に、方向性を決める子クラスを記述します。このメソッドを使用して方向を決定する「period」パラメータが必要になります。したがって、m_period および期間のパラメータを持つコンストラクターは、プライベート セクションに追加されます。

class CNBars:public CZZDirection
  {
private:
   int               m_period;
public:
   void CNBars(int period)
     {
      m_period=period;
     }
   int Calculate(const int rates_total,
                 const int prev_calculated,
                 double &BufferHigh[],
                 double &BufferLow[],
                 double &BufferDirection[]
                 )
     {
      int start;

      if(prev_calculated==0)
        {
         BufferDirection[0]=0;
         start=1;
        }
      else
        {
         start=prev_calculated-1;
        }

      for(int i=start;i<rates_total;i++)
        {

         BufferDirection[i]=BufferDirection[i-1];

         int ps=i-m_period+1;
         int hb=ArrayMaximum(BufferHigh,ps,m_period);
         int lb=ArrayMinimum(BufferLow,ps,m_period);

         if(hb==i && lb!=i)
           { //高値の識別
            BufferDirection[i]=1;
           }
         else if(lb==i && hb!=i)
           { //安値の識別
            BufferDirection[i]=-1;
           }

        }
      return(rates_total);
     }

CCI インジケーターに基づいて方向を決定するもう一つの子クラスを作成します。ゼロの上のCCIの位置は、ジグザグの方向に対応します。

class CCCIDir:public CZZDirection
   {
private:
    int               m_handle;
public:
    void CCCIDir(int period,ENUM_APPLIED_PRICE price)
      {
       m_handle=iCCI(Symbol(),Period(),period,price);
      }
    bool CheckHandle()
      {
       return(m_handle!=INVALID_HANDLE);
      }
    int Calculate(const int rates_total,
                  const int prev_calculated,
                  double &BufferHigh[],
                  double &BufferLow[],
                  double &BufferDirection[]
                  )
      {
       int start;
       if(prev_calculated==0)
         {
          BufferDirection[0]=0;
          start=1;
         }
       else
         {
          start=prev_calculated-1;
         }

       for(int i=start;i<rates_total;i++)
         {

          BufferDirection[i]=BufferDirection[i-1];

          double buf[1];
          if(CopyBuffer(m_handle,0,rates_total-i-1,1,buf)<=0)return(0);

          if(buf[0]>0)
            {
             BufferDirection[i]=1;
            }
          else if(buf[0]<0)
            {
             BufferDirection[i]=-1;
            }
         }
       return(rates_total);
      }
   };

CCIパラメータは、クラスのコンストラクターに渡され、読み込まれます。CheckHandle() メソッドを使用することで、オブジェクトを作成した後、呼び出す必要があります。メインループは、CCI をチェックし、BufferDirection バッファーに格納します。

"CZZDirection.mqh"ファイルは、添付ファイルにあります。 

プロットのクラス

ジグザグのプロットにはさまざまなオプションがあります。単一行で描画することができ、色を指定でき、頂点のドットを持つことができます。この記事で1つだけのプロットメソッドを検討しますが、将来のために開発のベースと子クラスを作成します。"CZZDraw.mqh"ファイル、クラスの名前-「CZZDraw」。方向のクラスと同じパラメータを持つ仮想 Calculate() メソッドが含まれます。さらに、ジグザグから 3 つの配列を渡されます。 (インデックスは最新の高値) の BufferLastHighBar (インデックスは最新の安値) の BufferLastLowBar BufferZigZag (ジグザグ自体). 

class CZZDraw
  {
private:
public:
   virtual int Calculate(const int rates_total,
                         const int prev_calculated,
                         double &BufferHigh[],
                         double &BufferLow[],
                         double &BufferDirection[],
                         double &BufferLastHighBar[],
                         double &BufferLastLowBar[],
                         double &BufferZigZag[]
                         )
     {
      return(0);
     }
  };
子クラス: 
class CSimpleDraw:public CZZDraw
   {
private:
public:
    virtual int Calculate(const int rates_total,
                          const int prev_calculated,
                          double &BufferHigh[],
                          double &BufferLow[],
                          double &BufferDirection[],
                          double &BufferLastHighBar[],
                          double &BufferLastLowBar[],
                          double &BufferZigZag[]
                          )
      {
       int start;
       if(prev_calculated==0)
         {
          BufferLastHighBar[0]=0;
          BufferLastLowBar[0]=0;
          start=1;
         }
       else
         {
          start=prev_calculated-1;
         }

       for(int i=start;i<rates_total;i++)
         {
          BufferLastHighBar[i]=BufferLastHighBar[i-1];
          BufferLastLowBar[i]=BufferLastLowBar[i-1];

          BufferZigZag[i]=EMPTY_VALUE;

          BufferZigZag[(int)BufferLastHighBar[i]]=BufferHigh[(int)BufferLastHighBar[i]];
          BufferZigZag[(int)BufferLastLowBar[i]]=BufferLow[(int)BufferLastLowBar[i]];

          switch((int)BufferDirection[i])
            {
             case 1:
                switch((int)BufferDirection[i-1])
                  {
                   case 1:
                      if(BufferHigh[i]>BufferHigh[(int)BufferLastHighBar[i]])
                        {
                         BufferZigZag[(int)BufferLastHighBar[i]]=EMPTY_VALUE;
                         BufferZigZag[i]=BufferHigh[i];
                         BufferLastHighBar[i]=i;
                        }
                      break;
                   case -1:
                      BufferZigZag[i]=BufferHigh[i];
                      BufferLastHighBar[i]=i;
                      break;
                  }
                break;
             case -1:
                switch((int)BufferDirection[i-1])
                  {
                   case -1:
                      if(BufferLow[i]<BufferLow[(int)BufferLastLowBar[i]])
                        {
                         BufferZigZag[(int)BufferLastLowBar[i]]=EMPTY_VALUE;
                         BufferZigZag[i]=BufferLow[i];
                         BufferLastLowBar[i]=i;
                        }
                      break;
                   case 1:
                      BufferZigZag[i]=BufferLow[i];
                      BufferLastLowBar[i]=i;
                      break;
                  }
                break;
            }
         }
       return(rates_total);
      }
   };
すでにセクション「高値/安値に基づいてシンプルなジグザグ」および「終値に基づくシンプルなジグザグ」セクションに記載されているように、このクラスは詳しく取り上げませんCZZDraw.mqh"ファイルは、添付ファイルにあります。

3つのクラスをマージ

最後に、上記で作成した 3 つのクラスを使用して、インジケーターを作成する必要があります。価格とRSIインジケーターは、サブウィンドウで動作する通常のデータを使用する関数を提供します。価格データは、サブウィンドウに表示できますが、価格チャートで RSI を表示できません。よって、サブウィンドウのインジケーターを作成します。 

MetaEditor で新しいインジケーターの作成 (メイン メニュー-ファイル-新規または Ctrl + N キーを押します)。インジケーター作成ウィザードでは、"iUniZigZagSW"という名前にし、1つの外部パラメータ「period」(int 型、値 12) を作成し、OnCalculate(...,open,high,low,close) イベント ハンドラーを選択、次のバッファーを作成します: 

NameStyleColor
HighLineGreen
LowLineGreen
ZigZagSectionRed
DirectionLinenone
LastHighBarLine none 
LastLowBarLinenone
新しいインジケーターを作成すると、クラスに 3 つのファイルが含まれます。 

#include <CSorceData.mqh>
#include <CZZDirection.mqh>
#include <CZZDraw.mqh>

インジケーターには、ソースデータの種類と方向を決定するの種類を選択するためのパラメータが必要です。この目的のため、2つの列挙を作成します。

enum ESorce
  {
   Src_HighLow=0,
   Src_Close=1,
   Src_RSI=2,
   Src_MA=3
  };
enum EDirection
  {
   Dir_NBars=0,
   Dir_CCI=1
  };

2つの外部パラメータを作成します。 

input ESorce      SrcSelect=Src_HighLow;
input EDirection  DirSelect=Dir_NBars;

RSI と MA データも必要です。追加。

input int                  RSIPeriod   =  14;
input ENUM_APPLIED_PRICE   RSIPrice    =  PRICE_CLOSE;
input int                  MAPeriod    =  14;
input int                  MAShift     =  0;
input ENUM_MA_METHOD       MAMethod    =  MODE_SMA;
input ENUM_APPLIED_PRICE   MAPrice     =  PRICE_CLOSE;
input int                  CCIPeriod   =  14;
input ENUM_APPLIED_PRICE   CCIPrice    =  PRICE_TYPICAL;

N本足上の方向を決定するための追加のパラメータも必要です。

input int                  ZZPperiod   =  14;

基本クラス (外部パラメータ) の種類に応じた3 つのポインター。

CSorceData * src;
CZZDirection * dir;
CZZDraw * zz;

SrcSelect と DirSelect の変数に従って、OnInit 関数の対応する子クラスをロードします。まず第一に、SrcSelect:

switch(SrcSelect)
  {
   case Src_HighLow:
      src=new CHighLow();
      break;
   case Src_Close:
      src=new CClose();
      break;
   case Src_RSI:
      src=new CRSI(RSIPeriod,RSIPrice);
      break;
   case Src_MA:
      src=new CMA(MAPeriod,MAShift,MAMethod,MAPrice);
      break;
  }

ロードした後、ハンドルを確認します。

if(!src.CheckHandle())
  {
   Alert("Error loading the indicator");
   return(INIT_FAILED);
  }

次に、DirSelect:

switch(DirSelect)
  {
   case Dir_NBars:
      dir=new CNBars(ZZPeriod);
      break;
   case Dir_CCI:
      dir=new CCCIDir(CCIPeriod,CCIPrice);
      break;
  }

ハンドルをチェックします。

if(!dir.CheckHandle())
  {
   Alert("Error loading the indicator 2");
   return(INIT_FAILED);
  }

3番目のクラス:

zz = new CSimpleDraw();

OnDeinit()関数内でオブジェクトを削除します。

void OnDeinit(const int reason)
  {
   if(CheckPointer(src)==POINTER_DYNAMIC)
     {
      delete(src);
     }
   if(CheckPointer(dir)==POINTER_DYNAMIC)
     {
      delete(dir);
     }
   if(CheckPointer(zz)==POINTER_DYNAMIC)
     {
      delete(zz);
     }
  }

最後の仕上げは-OnCalculate() 関数です。CSorceData および CZZDirection のクラスの Calculate() メソッドは 0 を返します。エラーの場合 (0を取得)、完全な再計算が行われます。

int rv;

rv=src.Calculate(rates_total,
                 prev_calculated,
                 time,
                 open,
                 high,
                 low,
                 close,
                 tick_volume,
                 volume,
                 spread,
                 HighBuffer,
                 LowBuffer);

if(rv==0)return(0);

rv=dir.Calculate(rates_total,
                 prev_calculated,
                 HighBuffer,
                 LowBuffer,
                 DirectionBuffer);

if(rv==0)return(0);

zz.Calculate(rates_total,
             prev_calculated,
             HighBuffer,
             LowBuffer,
             DirectionBuffer,
             LastHighBarBuffer,
             LastLowBarBuffer,
             ZigZagBuffer);

return(rates_total);

"IUniZigZagSW"インジケーターは、添付ファイルにあります。

価格チャートのバリエーション

ステータスインジケーターに以前に作成したすべてのバリエーション、価格のグラフに対応するソース データの両方が含まれています。ただし、価格のグラフでジグザグを見るのもいいででしょう。この場合、rsi はデータ ソースを犠牲にする必要があります。"IUniZigZag"というインジケーターのコピーを作成し、indicator_separate_window プロパティを indicator_chart_window に変更します。ESorce 列挙体から Src_RSI のバリアントを削除し、OnInit() 関数から RSI のバリアントを削除し、価格チャートのバリアントを取得します。完成した"iUniZigZag"インジケーターは、添付ファイルにあります。   

価格のバリアント

厳密に定義されたソース データではなく、他のインジケーターに基づき作品がチャート上にポジションするターミナル、メタト レーダーのインジケーターを作成することが可能です。そのようなインジケーターをグラフやサブウィンドウにアタッチするときは、「前のデータ」や最初のインジケーターのデータ"に"適用する"パラメータを設定します。"iUniZigZagSW"インジケーターを"置く"ことができる""別のインジケーターにします。"IUniZigZagPriceSW"という名の下でインジケーターを保存し、CSorceData クラスに関連するすべてを削除し、OnCalculate 関数と関数の開始の種類を変更し、価格配列の値と HighBuffer と LowBuffer のバッファーを作成するためのループを記述します。

intOnCalculate(const int rates_total
                const int prev_calculated,
                const int begin,
                const double &price[]
                )
  {
   int start;
   if(prev_calculated==0)
     {
      start=0;
     }
   else
     {
      start=prev_calculated-1;
     }

   for(int i=start;i<rates_total;i++)
     {
      HighBuffer[i]=price[i];
      LowBuffer[i]=price[i];
     }
   int rv;
   rv=dir.Calculate(rates_total,
                    prev_calculated,
                    HighBuffer,
                    LowBuffer,
                    DirectionBuffer);

   if(rv==0)return(0);
   zz.Calculate(rates_total,
                prev_calculated,
                HighBuffer,
                LowBuffer,
                DirectionBuffer,
                LastHighBarBuffer,
                LastLowBarBuffer,
                ZigZagBuffer);
   return(rates_total);
  }

同様に、価格配列に基づいて価格のグラフに、さまざまなバリエーションを作成することが可能です。これを行うには、"iUniZigZagPriceSW"インジケーターで indicator_chart_window に indicator_separate_window プロパティを変更します。"IUniZigZagPriceSW"インジケーターは、添付ファイルにあります。IUniZigZagPrice インジケーターもありますが、価格配列に基づく価格グラフのバリエーションです。 

EAからの呼び出し

通常、EAから呼び出すと、ジグザグサイクルの最後の上部または下部の検索足を反復処理し、ジグザグのプロット用のバッファーの値をチェックします。これは一緒に非常に遅いです。この記事で開発したジグザグは追加のバッファーは、すべての必要なデータをすぐに取得できるようにします。DirectionBufferバッファーにジグザグの最後のセグメントの方向上のデータがあります。LastHighBarBuffer と LastLowBarBuffer のバッファーを含む最新の天井と底のインデックス。足を数えるために、(インジケーターは左から右 と 右から左に関数をカウント CopyBuffer にカウントしている) 側から数えてのバーのインデックスを計算することが可能です。足のインデックス、この足にジグザグの値を取得することが可能です。

次のコードを使用して、インジケーターからデータを取得できます。"IUniZigZagSW"インジケーターで実験しましょう。OnInit() 関数で、インジケーターを読み込みます。

handle=iCustom(Symbol(),Period(),"iUniZigZagSW",SrcSelect,
               DirSelect,
               RSIPeriod,
               RSIPrice,
               MAPeriod,
               MAShift,
               MAMethod,
               MAPrice,
               CCIPeriod,
               CCIPrice,
               ZZPeriod);
方向を取得し、 OnTick()関数のグラフのコメントに出力します。
   string cs="";
//方向
   double dir[1];
   if(CopyBuffer(handle,3,0,1,dir)<=0)
     {
      Print("Error obtaining data from the ZigZag");
      return;
     }
   if(dir[0]==1)
     {
      cs=cs+"Direction Up";
     }
   if(dir[0]==-1)
     {
      cs=cs+"Direction Down";
     }
   Comment(cs,"\n",GetTickCount());

いくつか最新の天井/底の値を取得します。インジケータラインが上向きに指示している場合、LastHighBarBuffer バッファーから最新の天井と足のインデックスを得ます。右から左にカウントする場合は、足のインデックスを計算するのに使用します。このインデックスを使用して、ZigZagBuffer バッファーの値を取得することができます。LastLowBarBuffer バッファーからジグザグ値を得た足の値を取得します。前の底の足のインデックスです。交互に LastHighBarBuffer と LastLowBarBuffer のバッファーへの呼び出しのインディケータ線のすべての天井/底のデータを収集することができます。次は、上向きに指示されたとき、ジグザグの 2 つの最新のポイントを取得するためのコードの例です。 

if(dir[0]==1)
  {
   //0 の左側にカウントする場合は、最新のトップと足のインデックス
   if(CopyBuffer(handle,4,0,1,lhb)<=0)
     {
      Print("Error obtaining data from the ZigZag 2");
      return;
     }
   //右から数えたときの足のインデックス
   ind=bars-(int)lhb[0]-1;

   //ジグザグの値
   if(CopyBuffer(handle,2,ind,1,zz)<=0)
     {
      Print("Error obtaining data from the ZigZag 3");
      return;
     }
   //= = =
   //この天井の前の底のインデックス
   if(CopyBuffer(handle,5,ind,1,llb)<=0)
{
      Print("Error obtaining data from the ZigZag 4");
      return;
     }
   //右から数えたときに足のインデックス
   ind=bars-(int)llb[0]-1;

   //ジグザグの値
   if(CopyBuffer(handle,2,ind,1,zz1)<=0)
     {
      Print("Error obtaining data from the ZigZag 5");
      return;
     }

   cs=cs+"\n"+(string)zz1[0]+" "+(string)zz[0];
  }
else if(dir[0]==-1)
  {

  }

完全な例は、添付"eUniZigZagSW"のEAにあります。このEA は、ジグザグの方向についてのメッセージとグラフのコメントだけでなく、(図 7) 2 行目の 2 つの最新ジグザグ ポイントの値を持つ 2 つの数値を出力します。3行目は、 GetTickCount()関数を明確にするため、返されます。

 
図 7。左上にEAによって出力されるメッセージが含まれています

もちろん、インジケーターの最新のポイントのデータは、その値を0番目または最初のバーで LastHighBarBuffer と LastLowBarBuffer のバッファーから取得できますが、この例のポイントは連続してジグザグ ポイントの任意の数のデータを抽出します。

別のインジケーターからの呼び出し

あるインジケーターが別のジグザグに基づいて作られる必要がある場合、iCustom() を使用してインジケーターを呼ばず、そのコピーを作成し、変更すれば簡単です。このアプローチは(再利用と柔軟性) (変更速度とシンプルさ) の面で正当化されるかもしれない。この記事で作成したインジケーターは、他のインジケーターを開発するとき、iCustom 関数を介してアクセスできます。

ヒストリーのジグザグはもはや実際にそのヒストリーの形成の間に起こったものです。ただし、ジグザグの中間の状態に、データを格納する LastHighBarBuffer と LastLowBarBuffer のバッファーがあります。これをわかりやすくするため、矢印を描画するインジケーター線の方向 (DirectionBuffer バッファーの値の変更) が変わり、ジグザグの新高値/安値が登録されていた足にドットを配置します。(値の変更、 LastHighBarBuffer と LastLowBarBuffer のバッファー)。このインジケーターのコードは詳細に説明しませんが、添付ファイル"iUniZigZagSWEvents"にあります。インジケーターの外観を図 8 に示します。

 
図8。IUniZigZagSWEventsインジケーター

結論

この記事では、完全なソリューションは提供しません。すべてのインジケーターは最低限ソース データの選択と方向を決定する選択肢があります。ただし、この記事を理解した後、すべての必要な子クラスに自分のクラスを作成することができるので、インジケーターを作成するプロセスは、非常に詳細に記載されています。さらに、完全に不変なインジケーターにしようとすると、形成段階ではかなり難しい問題になります。データソースとして、または方向を決定するために様々なインジケーターを追加します。インジケーターのパラメータを [プロパティ] ウィンドウに追加する必要があります。最後に、パラメータの数があまりにも大きくなり、そのようなインジケーターを使用するよりも非常に便利です。この記事で得られた不変なクラスを使用して別のインジケーターを作成すると良いでしょう。       

添付

  • iHighLowZigZag.mq5-高値/安値に基づいたシンプルなジグザグ。
  • iCloseZigZag.mq5-終値に基づいたシンプルなジグザグ。
  • CSorceData.mqh-元のデータを選択するためのクラス。
  • CZZDirection.mqh-ジグザグの方向を決定するクラス。
  • CZZDraw.mqh ジグザグのプロットのクラスです。
  • iUniZigZagSW.mq5-サブウィンドウの不変なジグザグ。
  • iUniZigZag.mq5-価格グラフの不変なジグザグ。
  • iUniZigZagPriceSW.mq5-サブウィンドウの価格配列に基づいた不変なジグザグ。
  • iUniZigZagPrice.mq5-価格チャートの価格配列に基づいた不変なジグザグ。 
  • eUniZigZagSW-iCustom() 関数を使用してEAから"iUniZigZagSW"インジケーターを呼び出す例。
  • iUniZigZagSWEvents-iCustom() 関数を使用して"iUniZigZagSW"インジケーターへの呼び出しで別のインジケーターを作成する例。