2016年4月8日金曜日

[MT4プログラミング]EAに組み込みやすいようにインジケータを作成する。

EAにインジケータを組み込もうと思うと、iCustomで呼び出さないのであれば、EA内部に実装する必要があります。

インジケータを作成する際、EAで実装しやすいようにするにはどうすればよいでしょうか?
例で示します。
このインジケータは高値安値終値始値の平均をとるものです。平滑化のために一本前の値と単純平均をとっています。

//------------------------------------------------------------------
// テストインジケータ
#property copyright "Copyright 2016,  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  "HARF"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrIndianRed
#property indicator_width1  2

// バッファー
double harf[];

//------------------------------------------------------------------
//初期化
int OnInit()
{
   string short_name = "Test";
   
   int count = 0;
   SetIndexBuffer(count++, harf);

   return INIT_SUCCEEDED;
}

//------------------------------------------------------------------
//終了処理
void OnDeinit(const int reason)
{

}

//------------------------------------------------------------------
//計算イベント
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 - 1 )
      {
         harf[i] = (open[i] + close[i] + high[i] + low[i]) / 4 ;
      }
      else
      {
         harf[i] = ((open[i] + close[i] + high[i] + low[i]) / 4 + harf[i + 1]) / 2;
      }
   }
   
   return rates_total - 1;
}

さて、表示用のバッファであるharfは、チャートのバーが更新される事に勝手に増えていきます。また関数の引数であるhigh low open closeも、EAの中では使えません。

そこで、このコードを次のように直すと、EAに組み込みやすくなります。

//------------------------------------------------------------------
// テストインジケータ
#property copyright "Copyright 2016,  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  "HARF"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrIndianRed
#property indicator_width1  2

// バッファー
double harf[];       // 表示用


double harfCalc[];   // 表示計算用
int prevCalculated;  // 前回計算値

//------------------------------------------------------------------
//初期化
int OnInit()
{
   string short_name = "Test";
   
   int count = 0;
   SetIndexBuffer(count++, harf);

   prevCalculated = 0;

   return INIT_SUCCEEDED;
}

//------------------------------------------------------------------
//終了処理
void OnDeinit(const int reason)
{

}

//------------------------------------------------------------------
//計算イベント
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 ratesTotal = iBars(Symbol(), Period());

   int currentArraySize = ArraySize(harfCalc);
   if( currentArraySize < ratesTotal )
   {
      ArraySetAsSeries(harfCalc, false);

      ArrayResize(harfCalc, ratesTotal);
      
      ArraySetAsSeries(harfCalc, true);
      
      for( int i = 0; i < ratesTotal - currentArraySize; i++ )
      {
         harfCalc[i] = EMPTY_VALUE;
      }
   }

   for( int i = ratesTotal - prevCalculated - 1; i >= 0 && !IsStopped(); i-- )
   {
      if( i >= ratesTotal - 1 )
      {
         harfCalc[i] = (iOpen(Symbol(), Period(), i) + iClose(Symbol(), Period(), i) + iHigh(Symbol(), Period(), i) + iLow(Symbol(), Period(), i)) / 4 ;
      }
      else
      {
         harfCalc[i] = ((iOpen(Symbol(), Period(), i) + iClose(Symbol(), Period(), i) + iHigh(Symbol(), Period(), i) + iLow(Symbol(), Period(), i)) / 4 + harfCalc[i + 1]) / 2;
      }
      
      // 表示用バッファに値を設定する。
      harf[i] = harfCalc[i];
   }
   
   prevCalculated = ratesTotal - 1;
   
   return rates_total - 1;
}

まず、open close high low引数は使用せず、iOpen iClose iHigh iLow関数を使用します。
同じ値が取得可能です。

さて、バーの更新毎に増えるバッファを自力で実装する必要があります。
   int currentArraySize = ArraySize(harfCalc);
   if( currentArraySize < ratesTotal )
   {
      ArraySetAsSeries(harfCalc, false);

      ArrayResize(harfCalc, ratesTotal);
      
      ArraySetAsSeries(harfCalc, true);
      
      for( int i = 0; i < ratesTotal - currentArraySize; i++ )
      {
         harfCalc[i] = EMPTY_VALUE;
      }
   }
の部分です。

これは、iBarsで取得したバーの本数と、配列の数が異なる場合、リサイズを行うという処理です。リサイズ時増える分には値は増える前までの値は保持されています。これを利用します。
初期値にEMPTY_VALUEを設定しています。

このように作成しておくと、EAにする際、簡単に移植が可能です。
EAにインジケータ部分を移植した例です。

//+------------------------------------------------------------------+
//|                                                       TestEA.mq4 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

double harfCalc[];   // 表示計算用
int prevCalculated;  // 前回計算値

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   prevCalculated = 0;
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //インジケータの計算
   int ratesTotal = iBars(Symbol(), Period());
   int currentArraySize = ArraySize(harfCalc);
   if( currentArraySize < ratesTotal )
   {
      ArraySetAsSeries(harfCalc, false);

      ArrayResize(harfCalc, ratesTotal);
      
      ArraySetAsSeries(harfCalc, true);
      
      for( int i = 0; i < ratesTotal - currentArraySize; i++ )
      {
         harfCalc[i] = EMPTY_VALUE;
      }
   }

   for( int i = ratesTotal - prevCalculated - 1; i >= 0 && !IsStopped(); i-- )
   {
      if( i >= ratesTotal - 1 )
      {
         harfCalc[i] = (iOpen(Symbol(), Period(), i) + iClose(Symbol(), Period(), i) + iHigh(Symbol(), Period(), i) + iLow(Symbol(), Period(), i)) / 4 ;
      }
      else
      {
         harfCalc[i] = ((iOpen(Symbol(), Period(), i) + iClose(Symbol(), Period(), i) + iHigh(Symbol(), Period(), i) + iLow(Symbol(), Period(), i)) / 4 + harfCalc[i + 1]) / 2;
      }
   }
   prevCalculated = ratesTotal - 1;   
  }
//+------------------------------------------------------------------+

このように、基本コピペだけで作成可能です。
関数化しておけば、さらに簡単です。

ということで、久しぶりにプログラムの話題でした。

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

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