【MT5/MQL5】インジケータ取得処理をクラスでラッピング(ボリンジャーバンド)

この記事は約18分で読めます。

今回作成するプログラムの概要

今回は、ボリンジャーバンドのインジケータ値を取得する処理をクラスでラッピングするプログラムを作ろうと思います。

なぜこんな事が必要なのかというと、MT4からMT5になった時に、インジケータ取得関数の仕様が大幅に変わったため、使い勝手が悪くなってしまったからです。

MT5になってどのように仕様が変わったのかを踏まえながら、解説してきたいと思います。

インジケータ取得関数の仕様変更について(MT4 から MT5)

まずは、インジケータ取得関数の仕様変更について簡単に説明します。

例えば、MT4でボリンジャーバンドのアッパーバンド値を取得したい場合、以下のようにiBands()関数を呼び出す事によって、直接「アッパーバンド値」が取得できました。

【 MT4 】
double アッパーバンド値 = iBands(通貨ペア,・・・,アッパーバンドシフト,・・・)

ところがMT5になると、iBands()関数を呼び出すと「指標ハンドル」というint値が返却されるようになりました。この「指標ハンドル」を使ってCopyBuffer()関数を呼びだす事により、「アッパーバンド値」を得ることができます。

【 MT5 】
int 指標ハンドル = iBands(通貨ペア,・・・);
double buffer[];
CopyBuffer(指標ハンドル ,アッパーバンドの指標バッファ番号,・・,buffer);

おそらくインジケータ取得関数の処理速度向上を目的とした仕様変更だとは思うのですが、いちいち指標ハンドルを保持したり、指標バッファ番号を指定したり、専用のバッファ変数を定義したり、と色々面倒なので、クラスでラッピングしてインジケータを簡単に利用できるようにしていきます。

ボリンジャーバンドのインジケータをクラスでラッピング

サンプルコード

早速、サンプルコードを紹介します。今回は「ボリンジャーバンドのクラスファイル(CIndBands.mqh)」と、「インジケータ表示用ファイル(IndicatorBands.mq5)」の2ファイルあります。2ファイルともインジケータフォルダの同じ階層に配置してください。

ボリンジャーバンドのクラスファイル(CIndBands.mqh)

//+------------------------------------------------------------------+
//|                                                    CIndBands.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"

#define   BANDS_IDX_BASE_LINE     0
#define   BANDS_IDX_UPPER_BAND    1
#define   BANDS_IDX_LOWER_BAND    2

//+------------------------------------------------------------------+
//|
//| [インジケータラッピングクラス]
//| ボリンジャーバンド
//|
//+------------------------------------------------------------------+
class CIndBands
  {
private:

public:

   //|---------------------------------------------------------------+
   //| メンバ変数
   //|---------------------------------------------------------------+
   int               m_ind_handle;
   string            m_symbol;
   ENUM_TIMEFRAMES   m_timeframe;

   //|---------------------------------------------------------------+
   //| コンストラクタ、デストラクタ
   //|---------------------------------------------------------------+
                     CIndBands() {};
                    ~CIndBands() {};

   //|---------------------------------------------------------------+
   //| メンバ関数
   //|---------------------------------------------------------------+

   //| インジケータ生成関数
   void              createIndicator(string, ENUM_TIMEFRAMES, int, int, double, ENUM_APPLIED_PRICE);

   //| インジケータ値取得関数
   double            getValue(int, int);

   //| ボリンジャーバンドの基準線取得関数
   double            BaseLine(int iArgBar) { return this.getValue(BANDS_IDX_BASE_LINE,iArgBar); };

   //| ボリンジャーバンドのアッパーバンド取得関数
   double            UpperBand(int iArgBar) { return this.getValue(BANDS_IDX_UPPER_BAND,iArgBar); };

   //| ボリンジャーバンドのロワーバンド取得関数
   double            LowerBand(int iArgBar) { return this.getValue(BANDS_IDX_LOWER_BAND,iArgBar); };

  };

//+------------------------------------------------------------------+
//|
//| [メンバ関数]
//| インジケータ生成関数
//|
//+------------------------------------------------------------------+
void   CIndBands::createIndicator(
   string              sArgSymbol,
   ENUM_TIMEFRAMES     enArgTimeframe,
   int                 iArgPeriod,
   int                 iArgShift,
   double              dArgDeviation,
   ENUM_APPLIED_PRICE  enArgAppliedPrice)
  {

   this.m_symbol      = sArgSymbol;
   this.m_timeframe   = enArgTimeframe;
   this.m_ind_handle  = iBands(
                          sArgSymbol,
                          enArgTimeframe,
                          iArgPeriod,
                          iArgShift,
                          dArgDeviation,
                          enArgAppliedPrice);

  }

//+------------------------------------------------------------------+
//|
//| [メンバ関数]
//| インジケータ値取得関数
//|
//+------------------------------------------------------------------+
double CIndBands::getValue(int iArgBufIndex,          // インデックス番号
                           int iArgBar)               // Bar位置
  {
   int iRet;

   double buf[];
   iRet=CopyBuffer(this.m_ind_handle,iArgBufIndex,iArgBar,1,buf);
   return buf[0];
  }
Expand

インジケータ表示用ファイル(IndicatorBands.mq5)

//+------------------------------------------------------------------+
//|                                               IndicatorBands.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

#include "CIndBands.mqh"

#property indicator_buffers  3
#property indicator_plots    3

#property indicator_type1    DRAW_LINE
#property indicator_style1   STYLE_SOLID
#property indicator_color1   clrBlue
#property indicator_width1   1
double    bufUpper[];

#property indicator_type2    DRAW_LINE
#property indicator_style2   STYLE_SOLID
#property indicator_color2   clrGreen
#property indicator_width2   1
double    bufBase[];

#property indicator_type3    DRAW_LINE
#property indicator_style3   STYLE_SOLID
#property indicator_color3   clrRed
#property indicator_width3   1
double    bufLower[];

//+------------------------------------------------------------------+
//| インジケータ取得用クラス(ポインタ)
//+------------------------------------------------------------------+
CIndBands *cIndBands;

//+------------------------------------------------------------------+
//| 【初期化関数】
//|  ・チャートの初期表示時、または時間足変更等のチャート初期化が必要な
//|    タイミングで呼び出される。
//+------------------------------------------------------------------+
int OnInit()
  {

// アッパーバンド
   ArraySetAsSeries(bufUpper, true);
   ArrayInitialize(bufUpper,  0);
   SetIndexBuffer(0,          bufUpper,          INDICATOR_DATA);
   PlotIndexSetDouble(0,      PLOT_EMPTY_VALUE,  0.0);

// 基準線
   ArraySetAsSeries(bufBase,  true);
   ArrayInitialize(bufBase,   0);
   SetIndexBuffer(1,          bufBase,           INDICATOR_DATA);
   PlotIndexSetDouble(1,      PLOT_EMPTY_VALUE,  0.0);

// ロワーバンド
   ArraySetAsSeries(bufLower, true);
   ArrayInitialize(bufLower,  0);
   SetIndexBuffer(2,          bufLower,          INDICATOR_DATA);
   PlotIndexSetDouble(2,      PLOT_EMPTY_VALUE,  0.0);

// ボリンジャーバンド取得インスタンス生成
   cIndBands = new CIndBands();
   cIndBands.createIndicator(Symbol(),PERIOD_CURRENT,20,0,2.0,PRICE_CLOSE);

   return(INIT_SUCCEEDED);

  }

//+------------------------------------------------------------------+
//| 【後処理関数】
//|   ・チャートからインジケータが削除されたタイミングで呼び出される関数
//|   ・通常のコードテンプレートでは当関数は定義されないため、個別で実装する必要がある
//|   ・クラスインスタンスは動的に生成しているので、明示的にクリアする必要がある
//|     (当処理を実装しない場合はメモリリークが発生してしまう)
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   delete(cIndBands);
  }

//+------------------------------------------------------------------+
//| 【計算イベント関数】
//|  ・ローソク足に変化が発生する毎に呼び出される。
//+------------------------------------------------------------------+
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[])
  {

//+------------------------------------------------------------------+
//| 1. 新規チャートのインジケータ値がまだ計算されていない場合
//|   → 過去チャートのインジケータ値を計算する。
//+------------------------------------------------------------------+
   if(prev_calculated == 0)
     {
      for(int i=rates_total-21; i>=0; i--)
        {
         setBufferValue(i,false);
        }
     }

//+------------------------------------------------------------------+
//| 2. 過去チャートのインジケータ値は計算済で、
//|    前回計算済Bar数 < チャートの最大Bar数 の場合 (Liveで新規Barが発生した) 
//|    → 新規Barのインジケータ値を計算する。
//+------------------------------------------------------------------+
   else
     {
      if(prev_calculated < rates_total)
        {
         setBufferValue(0,true);
        }
     }

//+------------------------------------------------------------------+
//| 3. 同Bar内でのティック変更によって呼び出された場合
//|    → インジケータ値は計算しない
//+------------------------------------------------------------------+

// NOP

//+------------------------------------------------------------------+
//| 今回の処理でインジケータ値を計算したBar数を返却する。
//|   → このreturn値は、次回OnCalculate関数が呼び出された際、
//|     引数のprev_calculatedに設定される。
//+------------------------------------------------------------------+

   return(rates_total);

  }

//+------------------------------------------------------------------+
//| 【インデックスバッファ設定関数】
//|  ・指定されたBar位置のインジケータ値を計算する。
//+------------------------------------------------------------------+
void setBufferValue(int iCurrentBar, bool bIsLiveNewBar)
  {

   bufUpper[iCurrentBar] = cIndBands.UpperBand(iCurrentBar);
   bufBase[iCurrentBar]  = cIndBands.BaseLine(iCurrentBar);
   bufLower[iCurrentBar] = cIndBands.LowerBand(iCurrentBar);

  }

//+------------------------------------------------------------------+
Expand

コード解説

ボリンジャーバンドのクラスファイル(CIndBands.mqh)

74~82行目

インジケータ生成関数に渡された引数のうち、必要なものはメンバ変数に保持しておき、iBand()関数を呼び出します。iBand()関数呼び出しによって得られた指標ハンドルも、メンバ変数に保持しておきます。

97~99行目

メンバ変数に保持している指標ハンドルを利用して、引数で指定された「インデックス番号」「Bar位置」のインジケータ値を取得してリターンします。

48~55行目

上記で定義したgetValue()関数を使用して、ボリンジャーバンドの各指標値を取得します。これらの関数は定義しなくても大丈夫ですが、getValue()関数を使う場合、インジケータのインデックス番号を指定する必要があるので、各指標データ毎に個別のアクセス関数を準備しておいたほうが便利です。

インジケータ表示用ファイル(IndicatorBands.mq5)

11行目

ボリンジャーバンドのクラスファイルを取り込んでいます。インジケータファイルと同階層なので、””で括っていますが、クラスファイルをIncludeフォルダに配置した場合は、<>で括ってください。詳しくは、MQL5の公式サイトのこちらを参照してください。

37,66,67行目
クラスファイルのポインタを作成し、インスタンス生成、及びインジケータ初期化を実施しています。

85~141行目

新規ローソク足発生時のみ計算する処理になっていますが、解説はこちらの記事を参照してください。

150~152行目

インジケータバッファを設定しています。インジケータ値設定処理は、この3行だけです!クラスファイルに、それぞれの指標用のメンバ関数を準備しているので、Bar位置だけ引数に渡してあげれば簡単に指標値を取得する事ができます。

MQL5の公式サイトにiBandを利用したサンプルプログラムが載っていますが、そのコードと比較しても、だいぶすっきりとしています。

実行結果

IndicatorBandsインジケータの実行結果は以下のようになります。何の変哲もない、普通のボリンジャーバンドが表示されます。

まとめ

今回は、ボリンジャーバンドのインジケータ値を取得する処理をクラスでラッピングしてみました。

サンプルプログラムでは、クラスファイル(CIndBands.mqh)をインジケータと同じ場所(Indicatorフォルダ)に配置しましたが、Includeフォルダに配置すれば、EAからでもクラスファイルにアクセスできるようになります。

インジケータとして表示するだけであれば、普通にプリインストールされているボリンジャーバンドを利用すれば良いだけですが、EAに組み込んだり、クラスに拡張メンバ関数を作成する際に便利です。また、クラス化することによって、コーディング時にメンバ関数の自動補完機能が有効になるので、コーディングの効率も向上します。

今後は、他のインジケータもクラス化していければと思います。

コメント

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