MQL実用プログラム

【MQL4】zigzagの天底の値を取得しよう。

1. zigzagとは?

zigzagとは、次の画像のようにチャートの波動に自動でラインを引いてくれるインジケーターです。一般的にトレンドやトレンド転換点を分析するのに使います。

自分で波形を引くのが苦手な人にとって、とても有用なインジケーターです。

それとzigzagはリペイント系インジケーターとして有名です。

2. リペイント(再描写)

リペイントとは、最新足が更新され、すでに表示されているインジケーターの値を新たに書き換えることです。下記の画像で詳しく説明します。

① 最新の高値を設定され、zigzagのラインが引かれた。

② その後の相場で安値を更新せずに高値を更新した。

③ すると、前回高値へのラインは削除され、新たな高値へとラインは書き換えられる。

3. zigzagの値を取得しよう。

zigzagの値を返す関数はMT4には用意されていません。そのため、zigzagの値を取得するためには、iCustom関数を使用します。

zigzagでのiCustom関数の引数は、

iCustom(通貨ペア,時間足,”ZigZag”,Depth,Deviation,Backstep,バッファー,シフト);

となります。なので今回は、

iCustom(NULL,0,”ZigZag”,12,5,3,0,0);

これでzigzagの値を取得出来ます。この値には価格が入っています。

しかし、現時点では小数点が大きいのでNormalizeDouble関数を使って、小数点を5桁にします。

double zig = NormalizeDouble(iCustom(NULL,0,"ZigZag",12,5,3,0,0),5);

zigzagの値の返し方

zigzagの値の返し方は特殊です。天底(山と谷)以外は「0」を返します。 画像を使って解説します。

zigzagの天底以外(上記画像では線だけの部分)では全て「0」が返ってきます。

価格が0なんてあり得ませんが、zigzagの値の返し方はそう決まっています。

天底(上記画像では丸で囲った部分)のみ「0でない値」が返ってきます。

4. OnInit()で配列に初期値を格納しよう。

セットした時に一度だけ処理をするOnInit()で配列に初期値を格納していきます。

まずはグローバル変数として、2つの変数と4つの配列を定義します。

int m=0;
int n=0;
double Top[5];
double Bottom[5];
datetime t_time[5];
datetime b_time[5];

次にzigzagの値を配列に格納するため、ローソク足0~100本分の繰り返し処理で配列に値を格納する。

  for(int i=0;i<=100;i++)
    {
         double zig = NormalizeDouble(iCustom(NULL,0,"ZigZag",12,5,3,0,i),5);
         if(zig!=0 && zig==NormalizeDouble(High[i],5))
         {
            Top[m]=zig;
            t_time[m]=Time[i];
            m++;
            if(m>=5)break;
         }
         if(zig!=0 && zig==NormalizeDouble(Low[i],5))
         {
            Bottom[n]=zig;
            b_time[n]=Time[i];
            n++;
            if(n>=5)break;
         }
   }

9行目 Top[5]、t_time[5]の全てに値が格納されたら処理を止める。

16行目 Bottom[5]、b_time[5]の全てに値が格納されたら処理を止める。

このプログラムをそのままOnTick()に書いても普通に機能します。

しかし、ティック毎に100本分の繰り返し処理を行うのはプログラムが重くなります。

だから、必要な時だけ必要な処理をしてくれるように、OnTick()では別の書き方をします。

5. OnTick()で取得した値を配列に格納しよう。

5-1. 高値の値を配列に格納

高値(上記画像の赤丸部分)でのみ処理が起こるよう記述します。やることは2つです。

  1. 高値かどうかチェックする。
  2. リペイントであるかそうでないかチェックする。

高値かどうかチェック

 if文で3つの条件式を満たすかどうか確認する。(3つとも”true”なら高値である)

if( zig!=0 &&
    zig==NormalizeDouble(High[0],5) &&
    zig==NormalizeDouble(iHigh(_Symbol,0,iHighest(_Symbol,0,MODE_HIGH,5,0)),5))

1行目 zigzagの値が0でない。(値が入っている)

2行目 zigzagの値が現在のローソク足の最高値と同じである。

3行目 zigzagの値がローソク足5本前までの中で最も高い価格と同じである。

上記の条件式が全て”true”なら、次の処理を実行する。

次の処理は、入った高値がリペイントであるか、そうでないかを確かめます。

リペイントであるか、そうでないかをチェック

zigzagは「高値」→「安値」→「高値」→「安値」と交互に値が入る必要があります。しかし、第2章で言ったように、リペイントすると「高値」→「高値」と連続で高値が入ってしまいます。

なので、リペイントされたのであれば「前回高値」「今回高値」で上書きしなければなりません。

よって、入った値が、リペイントであるか、そうでないかで処理を分岐させる必要があります。

  {
    if( t_time[0]>b_time[0] )
   {
        Top[0]=zig;
        t_time[0]=Time[0];
   }else {
        Top[3]=Top[2];
        Top[2]=Top[1];
        Top[1]=Top[0];
        Top[0]=zig;
        t_time[3]=t_time[2];
        t_time[2]=t_time[1];
        t_time[1]=t_time[0];
        t_time[0]=Time[0];
      }
   }

2行目 条件式:「”t_time[0]”が”b_time[0]”より未来にある。」

この条件式を噛み砕くと、「”前回高値”が”前回安値”より未来にある。」

もっと噛み砕くと・・・

「ひとつ前に入ったzigzagの値は”安値”でなく、”高値”である。」

ということは(trueなら)・・・

今回「高値」が入ると、「高値」が連続になるので、それはリペイントである。

この条件式が、”true”の場合は最新の配列だけを入れ替え、”false”の場合は配列を総入れ替え。

trueの場合(リペイントである)

4行目 zigzagの値”zig“を”Top[0]“に上書きする。

5行目 現在時間”Time[0]“を”t_time[0]“に上書きする。

false時の場合(リペイントでない)

7~9行目 Top[]“に入ってる配列を1つずつ、ずらす。

10行目 zigzagの値”zig“を”Top[0]“に代入する。

11~13行目 t_time[]“に入ってる配列を1つずつ、ずらす。

14行目 現在時間”Time[0]“を”t_time[0]“に代入する。

5-2. 安値の値を配列に格納

安値(下記画像の青丸部分)でのみ処理が起こるよう記述します。やることは2つです。

  1. 安値かどうかチェックする。
  2. リペイントであるかそうでないかチェックする。

安値かどうかチェック

if文で3つの条件式を満たすかどうか確認する。(3つとも”true”なら安値である)

if( zig!=0 &&
 zig==NormalizeDouble(Low[0],5) &&
 zig==NormalizeDouble(iLow(_Symbol,0,iLowest(_Symbol,0,MODE_LOW,5,0)),5))

1行目 zigzagの値が0でない。(値が入っている)

2行目 zigzagの値が現在のローソク足の最安値と同じである。

3行目 zigzagの値がローソク足0~5本前までの中で最も安い価格と同じである。

上記の条件式が全て”true”なら、次の処理を実行する。

次の処理は、入った安値がリペイントであるか、そうでないかを確かめます。

リペイントかどうかチェック

zigzagは「高値」→「安値」→「高値」→「安値」と交互に値が入る必要があります。しかし、第2章で言ったように、リペイントすると「安値」→「安値」と連続で安値が入ってしまいます。

なので、リペイントされたのであれば「前回安値」「今回安値」で上書きしなければなりません。

よって、入った値が、リペイントであるか、そうでないかで処理を分岐させる必要があります。

  {
    if( b_time[0]>t_time[0] )
  {
        Bottom[0]=zig;
        b_time[0]=Time[0];
    }else{
        Bottom[3]=Bottom[2];
        Bottom[2]=Bottom[1];
        Bottom[1]=Bottom[0];
        Bottom[0]=zig;
        b_time[3]=b_time[2];
        b_time[2]=b_time[1];
        b_time[1]=b_time[0];
        b_time[0]=Time[0];
     }
   }

2行目   条件式:「”b_time[0]”が”t_time[0]”より未来にある。」

この条件式を噛み砕くと、「”前回安値”が”前回高値”より未来にある。」

もっと噛み砕くと・・・

「ひとつ前に入ったzigzagの値は”高値”でなく、”安値”である。」

ということは(trueなら)・・・

今回「安値」が入ると、「安値」が連続になるので、それはリペイントである。

この条件式が、”true”の場合は最新の配列だけを入れ替え、”false”の場合は配列を総入れ替え。

trueの場合(リペイントである)

4行目 zigzagの値”zig“を”Bottom[0]“に上書きする。

5行目 現在時間”Time[0]“を”b_time[0]“に上書きする。

falseの場合(リペイントでない)

7~9行目 Bottom[]“に入ってる配列を1つずつ、ずらす。

10行目 zigzagの値”zig“を”Bottom[0]“に代入する。

11~13行目 b_time[]“に入ってる配列を1つずつ、ずらす。

14行目 現在時間”Time[0]“を”b_time[0]“に代入する。

6. まとめ(プログラム)

最後に、ここまでの記事の内容をまとめておきます。

OnInit()とOnTick()で書き方が違うため、少し難しく感じたかもしれませんが、してることはシンプルです。「リペイント」を理解することが出来れば、さほど難しくありません。

    要点まとめ
  • zigzagの値をiCustom関数で取得する。
  • OnInit()内で配列に初期値を格納しておく。
  • 取得した値がリペイントであるか、そうでないかで処理を分岐し、配列に格納する。

まとめプログラムです。変数名や関数の値は自由に変更してお使い下さい。

int m=0;
int n=0;
double Top[5];
double Bottom[5];
datetime t_time[5];
datetime b_time[5];
  
int OnInit()
  {
    for(int i=0;i<=100;i++)
    {
         double zig = NormalizeDouble(iCustom(NULL,0,"ZigZag",12,5,3,0,i),5);
         if(zig!=0 && zig==NormalizeDouble(High[i],5))
         {
            Top[m]=zig;
            t_time[m]=Time[i];
            m++;
            if(m>=5)break;
         }
         if(zig!=0 && zig==NormalizeDouble(Low[i],5))
         {
            Bottom[n]=zig;
            b_time[n]=Time[i];
            n++;
            if(n>=5)break;
         }
   }
   Comment("Top_0= ",Top[0],"|",t_time[0],"\n",
           "Top_1=",Top[1],"|",t_time[1],"\n",
           "Top_2=",Top[2],"|",t_time[2],"\n",
           "Bottom_0=",Bottom[0],"|",b_time[0],"\n",
           "Bottom_1=",Bottom[1],"|",b_time[1],"\n",
           "Bottom_2=",Bottom[2],"|",b_time[2],"\n");
   
   return(INIT_SUCCEEDED);
  }

void OnTick()
  {
    double zig = NormalizeDouble(iCustom(NULL,0,"ZigZag",12,5,3,0,0),5);  //zigzagの値取得
    if( zig!=0 &&
        zig==NormalizeDouble(High[0],5) &&
        zig==NormalizeDouble(iHigh(_Symbol,0,iHighest(_Symbol,0,MODE_HIGH,5,0)),5))
    {
    if( t_time[0]>b_time[0] ){
        Top[0]=zig;
        t_time[0]=Time[0];
    }else {
        Top[3]=Top[2];
        Top[2]=Top[1];
        Top[1]=Top[0];
        Top[0]=zig;
        t_time[3]=t_time[2];
        t_time[2]=t_time[1];
        t_time[1]=t_time[0];
        t_time[0]=Time[0];
     }
     }
     if( zig!=0 &&
         zig==NormalizeDouble(Low[0],5) &&
         zig==NormalizeDouble(iLow(_Symbol,0,iLowest(_Symbol,0,MODE_LOW,5,0)),5))
     {
     if( b_time[0]>t_time[0] ){
         Bottom[0]=zig;
         b_time[0]=Time[0];
     }else{
        Bottom[3]=Bottom[2];
        Bottom[2]=Bottom[1];
        Bottom[1]=Bottom[0];
        Bottom[0]=zig;
        b_time[3]=b_time[2];
        b_time[2]=b_time[1];
        b_time[1]=b_time[0];
        b_time[0]=Time[0];
     }
     }
   Comment("Top_0= ",Top[0],"|",t_time[0],"\n",
           "Top_1=",Top[1],"|",t_time[1],"\n",
           "Top_2=",Top[2],"|",t_time[2],"\n",
           "Bottom_0=",Bottom[0],"|",b_time[0],"\n",
           "Bottom_1=",Bottom[1],"|",b_time[1],"\n",
           "Bottom_2=",Bottom[2],"|",b_time[2],"\n");
  }