高値・安値を表示するインジケーターを作る(その2)

プログラミング

前回に引き続き、リペイントしない高値・安値を表示するインジケーターの作成方法について書いていきます。今回はプログラミングまで入りたいと思います。前回の記事はこちら↓

高値・安値を表示するインジケーターを作る(その1)
リペイントしないMT4用の高値・安値インジケーターを作成します

前回の補足事項

前回、移動平均の傾きの符号が変わったら、その範囲に新たな高値(安値)があるものとして、高値(安値)を探そう、と書きました。実際のチャートで見ると以下のようになります。

これは10期間の単純移動平均線を表示しており、オシレーターに移動平均線の傾きを表示しています。特にもみ合ってるあたりだと傾きの符号が頻繁に変わってしまうことがわかります。

左の図は、オシレーターを「移動平均線の傾き」ではなく、「『移動平均線の傾き』の移動平均」に変えたものです。先程と比べて、オシレーターがかなり滑らかになっており、移動平均線の傾きの符号が変わる頻度が下がっていることがわかるかと思います。今回はこちらを採用します。

プログラム作成の方針

それでは実際のプログラミングに入っていきましょう。今回作成するインジケーターの入力項目や必要なバッファーについて考えましょう。

入力項目

今回のインジケーターの入力項目は

  • Zigzagをさかのぼって表示するバーの本数
  • 基準にする移動平均線の期間/種類/適用価格
  • 傾きを平滑化するための移動平均期間
  • Zigzagの線の色/線の太さ

が考えられます。

必要なバッファー

今回のインジケーターに必要なバッファーは、

  • Zigzag出力用バッファー
  • 基準となる移動平均線用のバッファー
  • 移動平均線の傾き用のバッファー
  • 移動平均線の傾きの移動平均線用のバッファー

の4つです。(このうち、表示するのはZigzagのみ)

高値/安値を探す方法

ここでは、詳細に高値・安値を検出する方法について考えましょう。

  • インジケーター初期化直後は、前回の高値(安値)がわからないので、計算するバー本数の直前に安値(高値)があったものと考える
  • 初期化直後は、現在探しているのが高値なのか安値なのかわからないので、移動平均線の傾きが正なら高値を探している/負なら安値を探していると見なす
  • 高値/安値はローソク足確定後に走査する
  • 高値を探しているときに、移動平均線の傾き(平滑化したもの)が負になれば、前回の安値から、現在調査しているローソク足までの範囲に高値があるとして、最も高値を付けた足の高値を「高値とする」
  • 安値を探しているときに、移動平均線の傾き(平滑化したもの)が正になれば、前回の高値から、現在調査しているローソク足までの範囲に安値があるとして、最も低い値を付けた足の安値を「安値とする」

プログラミング

それでは、実際のプログラミングをしていきましょう。

変数宣言および初期化は以下のようになります。

#property strict
#property indicator_chart_window
#property indicator_buffers 1

input int inpBarNum = 1000; //計算バー本数
input int period = 10;   //ベース移動平均線の期間
input ENUM_MA_METHOD method   = MODE_SMMA;    //ベース移動平均の種類
input ENUM_APPLIED_PRICE app   = PRICE_TYPICAL;    //適用価格
input int gradPeriod = 5;          //傾きの平均期間(短期)
input color zigzagLineColor = clrWhiteSmoke;  //線の色
input int zigzagWidth = 2;    //線の太さ

//--- 指標バッファ
double ExtZigzagBuffer[];  //Zigzag出力用バッファー
double ExtMaBuffer[];   //移動平均線用バッファー
double ExtGradientBuffer[];   //移動平均線の傾き用のバッファー
double ExtGradientMaBuffer[]; //移動平均線の傾きの移動平均線用のバッファー
int seek = 0;  //現在探してるのが高値か安値か判別するための変数
int barNum;    //実際に計算するローソク足本数
datetime prevDT = 0; //前回高値(安値)をつけたローソク足の開始時刻
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   IndicatorBuffers(4);
   
   IndicatorDigits(Digits);
   SetIndexBuffer(0,ExtZigzagBuffer);
   SetIndexStyle(0,DRAW_SECTION, EMPTY, zigzagWidth, zigzagLineColor);
   SetIndexBuffer(1,ExtMaBuffer);
   SetIndexBuffer(2,ExtGradientBuffer);
   SetIndexBuffer(3,ExtGradientMaBuffer);   
//---
   return(INIT_SUCCEEDED);
  }

入力項目と計算用バッファーの宣言をおこなっています。

次に、メインとなるTickごとの処理を作成します。

int OnCalculate(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[])
  {
//---
   int i,j,p,pos;
   if(prev_calculated>1){
      pos=rates_total - prev_calculated+1;
   }else{
      pos=barNum;
   }
   
   //基準となる移動平均線の値を計算
   for(i = pos + gradPeriod + 1; i > 0; i--){
      ExtMaBuffer[i] = iMA(_Symbol, PERIOD_CURRENT, period, 0, method, app, i);
   }
   //基準となる移動平均線の傾きを計算
   for(i = pos + gradPeriod; i > 0; i--){
      ExtGradientBuffer[i] = ExtMaBuffer[i] - ExtMaBuffer[i + 1];
   }
   //移動平均線の傾きを平滑化
   for(i = pos; i > 0; i--){
      ExtGradientMaBuffer[i] = iMAOnArray(ExtGradientBuffer, 0, gradPeriod, 0, MODE_SMA, i);
   }
   
   for(i = pos; i > 0; i--){
      //seekが-1,1,0で場合分け
      switch(seek){
         case 0:  //インジケーター初期化直後。どちらの極値を探しているかわからない
            prevDT = iTime(_Symbol, PERIOD_CURRENT, i + 1);   //前回高値(安値)の時刻を計算バーの直前に設定
            if(ExtGradientMaBuffer[i] <= 0) seek = -1;        //傾きが負なら捜査値を「安値」に設定
            else seek = 1;                    //傾きが正なら捜査値を「高値」に設定
            break;
         case 1:  //高値を探しているとき
            if(ExtGradientMaBuffer[i] < 0){    //傾きが負なら高値がある区間が決まる
               p = iBarShift(_Symbol, PERIOD_CURRENT, prevDT, false);      //前回安値の位置を特定
               j = iHighest(_Symbol, PERIOD_CURRENT, MODE_HIGH, p - i, i); //高値の位置を決定
               ExtZigzagBuffer[j] = iHigh(_Symbol, PERIOD_CURRENT, j);     //高値を表示用バッファーに設定
               prevDT = iTime(_Symbol, PERIOD_CURRENT, j);                 //前回時刻を更新
               seek = -1;                                                  //安値走査中に更新
            }
            break;
         case -1: //looking for lawn
            if(ExtGradientMaBuffer[i] > 0){
               p = iBarShift(_Symbol, PERIOD_CURRENT, prevDT, false);     //前回高値の位置を特定
               j = iLowest(_Symbol, PERIOD_CURRENT, MODE_LOW, p - i, i);  //安値の位置を決定
               ExtZigzagBuffer[j] = iLow(_Symbol, PERIOD_CURRENT, j);     //安値を表示用バッファーに設定
               prevDT = iTime(_Symbol, PERIOD_CURRENT, j);                //前回時刻を更新
               seek = 1;                                                  //高値走査中に更新
            }
            break;
      }
   }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

インジケーター画面

実際に出来上がったインジケーターと、移動平均線の傾き(平滑化)を表示して画面がこちらです。

これで完成です!

まとめ

今回は移動平均線の傾きを使って、高値・安値を検出・表示するインジケーターのプログラムを行いました。リペイントしないZigzagインジケーターが欲しいという方はぜひ作ってみてください。

不明な点や疑問があればコメントでお知らせください。本記事が何かの参考になれば幸いです。

コメント

タイトルとURLをコピーしました