2016年4月11日月曜日

[MT4プログラミング]クラスを使うとEAもインジケータも同じコードを利用できる。


先週金曜日の記事で、EAとインジケータで移植しやすい形にするための注意点を書きました。

・[MT4プログラミング]EAに組み込みやすいようにインジケータを作成する
http://mt4program.blogspot.com/2016/04/mt4ea.html

さらに、EA化する際、インジケータ値に不具合があって修正するという作業が発生します。そのとき、インジケータ側、EA側と二つ修正しているのでは手間がかかります。そこでこれを共通的に扱うためにあとひと手間かけてみます。

前回記事のインジケータをクラスとして移植します。
基本はコピペです。クラスの形に従って初期化位置などを書いています。OnCalculateの戻り値として古い未計算値を返すようにしています。これは、インジケータで表示する場合、更新必要な本数を取得するためです。

//------------------------------------------------------------------
// テストインジケータ クラス
#property copyright "Copyright 2016,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict

//------------------------------------------------------------------
// テストインジケータ クラス
class CHarfIndicator
{
private:
   //対象シンボル
   string m_symbol;
   
   //タイムフレーム
   ENUM_TIMEFRAMES m_timeframe;

   // バッファ
   double m_harfCalc[];   

   // 前回計算値
   int m_prevCalculated;  
public:
   //------------------------------------------------------------------
   // コンストラクタ
   CHarfIndicator(
      string symbol              //シンボル
   ,  ENUM_TIMEFRAMES timeframe  //タイムフレーム
      );
   
   //------------------------------------------------------------------
   // デストラクタ
   ~CHarfIndicator();
  
   //------------------------------------------------------------------
   // バッファの値を取得する。
   // return バッファの値
   double GetHarf(int i)
   {
//       安全性を高めるなら範囲チェックがあったほうがいいが、速度を考える。呼び出し側で考慮する   
//      if( i < 0 || i >= ArraySize(i) ) return 0;
      return m_harfCalc[i];
   }
   
   //------------------------------------------------------------------
   // Tick更新時の計算
   // return 未計算数
   int OnCalculate();
};

//------------------------------------------------------------------
// コンストラクタ
CHarfIndicator::CHarfIndicator(
   string symbol              //シンボル
,  ENUM_TIMEFRAMES timeframe  //タイムフレーム
   ):
   m_symbol(symbol)
,  m_timeframe(timeframe)
,  m_prevCalculated(0)
{
}

//------------------------------------------------------------------
// デストラクタ
CHarfIndicator::~CHarfIndicator()
{
   ArrayFree(m_harfCalc);
}

//------------------------------------------------------------------
// Tick更新時の計算
// return 未計算数
int CHarfIndicator::OnCalculate()
{
   //インジケータの計算
   int ratesTotal = iBars(m_symbol, m_timeframe);
   int currentArraySize = ArraySize(m_harfCalc);
   if( currentArraySize < ratesTotal )
   {
      ArraySetAsSeries(m_harfCalc, false);

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

   for( int i = ratesTotal - m_prevCalculated - 1; i >= 0 && !IsStopped(); i-- )
   {
      if( i >= ratesTotal - 1 )
      {
         m_harfCalc[i] = (iOpen(m_symbol, m_timeframe, i) + iClose(m_symbol, m_timeframe, i) + iHigh(m_symbol, m_timeframe, i) + iLow(m_symbol, m_timeframe, i)) / 4 ;
      }
      else
      {
         m_harfCalc[i] = ((iOpen(m_symbol, m_timeframe, i) + iClose(m_symbol, m_timeframe, i) + iHigh(m_symbol, m_timeframe, i) + iLow(m_symbol, m_timeframe, i)) / 4 + m_harfCalc[i + 1]) / 2;
      }
   }
   int oldPrevCalculated = m_prevCalculated;
   m_prevCalculated = ratesTotal - 1;
   return oldPrevCalculated;
}

これをインジケータで表示するには次のような形になります。

//------------------------------------------------------------------
// テストインジケータ
#property copyright "Copyright 2016,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

#include <harfindicator.mqh>

//バッファーを指定する。
#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[];

CHarfIndicator *harfIndicator;

//------------------------------------------------------------------
//初期化
int OnInit()
{
   string short_name = "Test";
   
   int count = 0;
   SetIndexBuffer(count++, harf);
   
   harfIndicator = new CHarfIndicator(Symbol(), (ENUM_TIMEFRAMES)Period());

   return INIT_SUCCEEDED;
}

//------------------------------------------------------------------
//終了処理
void OnDeinit(const int reason)
{
   delete harfIndicator;
}

//------------------------------------------------------------------
//計算イベント
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 prevCalculated = harfIndicator.OnCalculate();

   for( int i = rates_total - prevCalculated - 1; i >= 0 && !IsStopped(); i-- )
   {
      harf[i] = harfIndicator.GetHarf(i);
   }
   
   return rates_total - 1;
}

もうほとんど何もないインジケータのコードとなりました
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

#include <harfindicator.mqh>
CHarfIndicator *harfIndicator;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   harfIndicator = new CHarfIndicator(Symbol(), (ENUM_TIMEFRAMES)Period());
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   delete harfIndicator;
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   harfIndicator.OnCalculate();
   
   if( harfIndicator.GetHarf(1) < harfIndicator.GetHarf(0) )
   {
      //売ったり買ったり
   }
}

こちらも前回と見比べてください。かなりすっきりしましたね!
インジケータもEAも同じCHarfIndicatorクラスを利用しています。そのため、indicator値に計算違いなどの不具合があった場合はCHarfIndicatorクラスのコードを直すだけで、EAとインジケータが同時に修正されます。
EAを開発する際、最終的にはバックテストのポジションが想定通りの箇所で売買されているかを確認していく作業となります。その際チャート上にインジケータ値が表示されている必要がありますので、同時にインジケータも開発することになります。

このようにEA化しやすいインジケータコードと、クラスを利用した共用化によってEAに向けての実装コストを大幅に削減可能です。

ちなみに、今回作成したインジケータを表示するとこんな感じになります。バーの中央値を0.5本遅れて表示するといった形ですね。中央値に対して何か仕掛ける場合の基準値になるかも?
■今回のテストインジケータ


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

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

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

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