2017年11月24日金曜日

[MT4プログラム]単純移動平均はループ不要

EAとかで単純移動平均を利用していてなるべく早く計算したい場合などがあります。
まぁMT4標準関数のiMAOnArrayがアホほど遅いというのもありますが・・。

単純移動平均は N期間のデータの合計をNで割った値です。
単純に実装するとループでN個分合計を求めて、最後にNで割ると行ったコードです。

         double total = 0.0;
         for( int j = i + 1; j < i + Period1 + 1; j++)
         {
            total += close[j];
         }
         sma1[i + 1] = total / Period1;

こんな感じですね。
ところが、すでに前のバーの値を計算済みの場合、こんなループは不要です。

移動平均の算出計算式を書き出すとこんな感じです。
sma[i] = (value[i] + value[i + 1] ..... + value[i + N - 1]) / N

これを分解すると次の形です。
sma[i] = value[i]/ N + value[i + 1]/ N ..... + value[i + N - 1]/ N

次のsma[i + 1]の値は、このようになります。
sma[i + 1] = value[i + 1]/ N + value[i + 2]/ N .....+ value[i + N - 1]/ N + value[i + N]/ N

さて、sma[i]とsma[i+1]の差分は次の通りです。
sma[i] - sma[i + 1] = value[ i ]/N - value[i + N]/N

両辺にsma[i + 1]を足せば、次の式となります。
sma[i] = sma[i + 1] + value[ i ]/N - value[i + N]/N

つまり、sma[i] は sma[i + 1]の値がわかっていれば、足し算と引き算と割り算で求めることが可能です。

で、終値の単純移動平均を出すプログラムはこんな感じなります。
ふと、どの記事か忘れましたが、移動平均ってループしなくても求められるんだ〜という返事を見た覚えがあったため、案外知られてないのかな?と思い記事にしてみました。


//------------------------------------------------------------------
// 単純移動平均
#property copyright "Copyright 2017,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

//バッファーを指定する。
#property indicator_buffers 1

//プロット数を指定する。
#property indicator_plots   1

#property indicator_label1  "SMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrAqua
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

input int Period1 = 21; // SMA1期間

double sma1[];

//------------------------------------------------------------------
//初期化
int OnInit()
{
   //インジケーターバッファを初期化する。
   int count = 0 ;
   SetIndexBuffer(count++, sma1);
   string short_name = "SMA";
   IndicatorShortName(short_name);

   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[])            //スプレット
{
   for( int i = rates_total - prev_calculated - 1; i >= 0  && !IsStopped(); i-- )
   {
      //計算する長さを算出
      if( i > rates_total - Period1 - 1)
      {
         continue;
      }
      
      if( MathIsValidNumber(sma1[i + 1]) )
      {
         //最初の一つ目はふつうにループして計算する。
         double total = 0.0;
         for( int j = i + 1; j < i + Period1 + 1; j++)
         {
            total += close[j];
         }
         sma1[i + 1] = total / Period1;
      }
      else
      {
         sma1[i] = sma1[i + 1] - close[i + Period1] / Period1 + close[i] / Period1;  
      }
   }
   
   return(rates_total - 1);
}
「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program
Trading View プロフィール

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ FX テクニカルトレード派へ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。