インジケーター作成03

単純移動平均を計算して、移動平均線を表示させよう!

ここでは、最も有名なインジケーター単純移動平均線の作成方法を解説します。

前回記事をまだ読んでない方は、まずはそちらから読み進めると良いでしょう。
前回記事 インジケーター作成2【過去部分の描画方法】

前回記事のおさらい

前回記事では、過去の部分が描画された状態で始まるようにしました。

Dr.EADr.EA

前回記事 インジケーター作成2【過去部分の描画方法】で完成したファイルのコードがこちら

#property strict
#property indicator_chart_window
#property indicator_buffers 1          // インジケーターバッファ 1つ
#property indicator_color1 clrRed      // 1つ目の色 赤色
#property indicator_width1 2           // 1つ目の太さ 2

double buf[];                          // ライン用の配列を宣言

//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, buf);             // 配列 buf[]をインジケーター用に割り当てる
   SetIndexStyle(0, DRAW_LINE);        // 描画タイプをラインと定義

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
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;
   int limit;
   if (prev_calculated == 0)              // 最初
      limit = rates_total - 1;               // 左端のローソク足から全部
   else                                   // それ以外
      limit = rates_total - prev_calculated; // ローソク足増加時「1」、通常「0」
   
   for (i=limit; i>=0; i--)               // limit本前から現在のローソク足を処理
   {
      buf[i] = Close[i];
   }
   
//--- return value of prev_calculated for next call
   return(rates_total);
}

前回完成ファイル【Sample_Indicator_02.mq4】をダウンロード

インジケーター用配列の範囲に注意

まず前回完成ファイルを、期間 3 の単純移動平均線を表示させるインジケーターにカスタマイズしてみます。
期間 3 の終値の単純移動平均は、次の計算式で算出されます。

単純平均(期間3) = (0本前終値 + 1本前終値 + 2本前終値) / 3

それを算出してライン表示させるため、OnCalculate()関数内のfor()文内のコードを変更します。

失敗例

にゃんたにゃんた

もとのコードのClose[i]にその1本前と2本前のClose[]を足してから3で割ればいいにゃ!

for (i=limit; i>=0; i--)  // limit本前から現在のローソク足を処理
{
   buf[i] = (Close[i] + Close[i+1] + Close[i+2]) / 3;
}

コンパイルして表示させようとしてもされません。ターミナルのエキスパートタブ(テスターの場合操作履歴タブ)を見ると、「array out of range in ・・・」というエラーが出て停止したようです。

このエラーは配列の要素数の範囲を超えている場合に出力されます。
表示開始時のi=limit(= rates_total – 1; 左端のローソク足のシフト数)を処理するときに、Close[i+1]でさらに左(過去)のローソク足の終値を取得しようとしているのが原因です。

にゃんたにゃんた

ガ~ン…

失敗例の対策例1 変数「limit」の値をコントロールする

for()文の前に次の条件文を追加します。

if (limit > Bars - 1 - 2) limit = Bars - 3;
もし、limitが左端のローソク足の2つ右のシフト数より大きい場合は、limitにそのシフト数の値を代入。

チャート全体のローソク足の本数を返す「Bars」は、変数「rates_total」としても良いです。
※ちなみに、EA作成では「Bars」を使います。

失敗例の対策例2 for()文内で処理をコントロールする

for()文を次のように変更します。

for (i=limit; i>=0; i--)  // limit本前から現在のローソク足を処理
{
   if (i+2 <= Bars - 1)   // 「i+2」が左端のシフト数以下ならば
      buf[i] = (Close[i] + Close[i+1] + Close[i+2]) / 3;
}

この方法は、期間が違う複数のラインをそれぞれ別々に描画開始させる場合に有効です。

単純移動平均線を表示させる

Dr.EADr.EA

ここからは、移動平均の期間をパラメーターで指定できるようにしていくぞい

にゃんたにゃんた

は、はい!

平均の期間をパラメーターにする

変数宣言の左側にexternを付けることで、インジケーターを使用する際に変更可能なパラメーターにすることができます。

配列 buf[]の宣言部分の上に、次のコードを追加します。

extern int  SMA_Period  = 10;          // SMAの期間

期間のパラメーターを使ったコードに変更する

次のコードは先ほどの失敗例の対策例2のfor()文内のコードです。

   if (i+2 <= Bars - 1)   // 「i+2」が左端のシフト数以下ならば
      buf[i] = (Close[i] + Close[i+1] + Close[i+2]) / 3;

この部分を期間 3 の代わりにパラメーター変数「SMA_Period」を使って変形すると、次のようになります。

   if (i + SMA_Period - 1 <= Bars - 1)
      buf[i] = (Close[i] ~ Close[i+SMA_Period-1] の総和) / SMA_Period;

この(Close[i] ~ Close[i+SMA_Period-1] の総和)は、次のコードのようにfor()文を使って算出することができます。
※整数変数「j」を予め宣言しておきます。

   if (i + SMA_Period - 1 <= Bars - 1)
   {
      // buf[i]を0に初期化して
      buf[i] = 0;

      // Close[i+0] ~ Close[i+SMA_Period]の一つ手前までを足していき
      for (j=0; j<SMA_Period; j++) buf[i] += Close[i+j];

      // SMA_Periodで割る
      buf[i] /= SMA_Period;
   }
Dr.EADr.EA

ようやく完成したわい。

#property strict
#property indicator_chart_window
#property indicator_buffers 1          // インジケーターバッファ 1つ
#property indicator_color1 clrRed      // 1つ目の色 赤色
#property indicator_width1 2           // 1つ目の太さ 2

extern int  SMA_Period  = 10;          // SMAの期間

double buf[];                          // ライン用の配列を宣言

//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, buf);             // 配列 buf[]をインジケーター用に割り当てる
   SetIndexStyle(0, DRAW_LINE);        // 描画タイプをラインと定義

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
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;
   int limit;
   if (prev_calculated == 0)              // 最初
      limit = rates_total - 1;               // 左端のローソク足から全部
   else                                   // それ以外
      limit = rates_total - prev_calculated; // ローソク足増加時「1」、通常「0」
   
   for (i=limit; i>=0; i--)               // limit本前から現在のローソク足を処理
   {
      if (i + SMA_Period - 1 <= Bars - 1)
      {
         // buf[i]を0に初期化して
         buf[i] = 0;
         // Close[i+0] ~ Close[i+SMA_Period]の一つ手前までを足していき
         for (j=0; j<SMA_Period; j++) buf[i] += Close[i+j];
         // SMA_Periodで割る
         buf[i] /= SMA_Period;
      }
   }
//--- return value of prev_calculated for next call
   return(rates_total);
}

今回完成ファイル【Sample_Indicator_03.mq4】をダウンロード

あとがき

単純移動平均線は正常に表示されましたか?疑問などがありましたらコメントで聞いてくださいね!

ではまた、次回をお楽しみに!