【超入門】MQL5 EA講座 第109回「インジケータの値を簡単に取得できるクラスを作る」

MQL5でEA作ろう講座

前回インジケータを使ってトレードシグナルを生成する方法の前段階として、インジケータの値取得について解説をしました。

まず、そもそもインジケータとは何か?という部分を説明し、その上でインジケータにはシングルバッファインジケータマルチバッファインジケータがあることをお伝えしました。

※詳細は前回記事の、

「シングルバッファインジケータとは?」セクション

及び

「マルチバッファインジケータとは?」セクションをご覧ください。

シングルバッファインジケータの値取得の例として、単純移動平均(SMA)を取り上げ、そのコード記述例を順を追って解説を行いました。

※詳細はこれも前回の、

「シングルバッファインジケータの値を取得する記述」セクションをご覧ください。

また、マルチバッファインジケータの値取得例としてはストキャスティクスを取り上げ、これもまたコード記述例を紹介しました。

※詳細はこれも前回の、

「マルチバッファインジケータの値を取得する記述」セクションをご覧ください。

さて、今回からは前回までのインジケータに関する前提を踏まえて、インジケータの値取得を簡略化できるクラスを作っていきます。

前回の内容を振り返ってもらうとわかるように、インジケータの値取得には

という手順が必要になります。

薄々感じている方もいるかと思うのですが、EAを自作するたびに、メインプログラムに上記の記述を行うのは正直かなり煩わしいですよね。

その煩わしさを取り除いて、インジケータの値取得をメインプログラムでより簡単に行うためのクラスを作ってしまおう!というのが今回の趣旨です。

これまでの講座で作ってきたクラスについては、以下のようなものがある訳ですが・・・

第71回「トレード用のオリジナルクラスを作る」

第82回「ポジション情報管理クラスを作る-その1」

第83回「ポジション情報管理クラスを作る-その2」

第88回「待機注文情報取得用のクラスを追加する」

第96回「トレーリングストップクラスを作る1」

第97回「トレーリングストップクラスを作る2」

第102回「資金管理用のインクルードファイルを作る」

今回はそれにもう一つ新しいクラスが加わる事になり、できることがさらに増えます。

徐々にクラスというものが、

<なにやら複雑でとらえどころのないもの>

から

<プログラミングの拡張性を担う便利なもの>

と捉えることが段々できるようになってきたのではないでしょうか?

今回の講座記事を読み終わる頃には、その確信が強まるのではないか思いますので、是非今回も楽しんで学習して頂ければと思います

スポンサーリンク
スポンサーリンク

インジケ-タクラス作成のロードマップ

MT5に組み込まれているインジケータには構造的に共通しているいくつかの特徴があります。

上記の工程はMT5に組み込みインジケータの値取得にあたって必ず必要な手順になります。

従って、①まずは上記の工程を処理する親クラスを作ります。

そして、親クラスができあがった後は、

②各インジケータごとに異なった処理が必要な工程を担う、派生クラスを作ります。

今回の記事では親クラスを作る工程を解説し、派生クラスについては次回取り上げます。

派生クラスとは特定のクラス(=親クラス)の機能を引き継ぎつつ、さらに新しい機能をクラスに追加させたクラスの事です。詳細については下記の記事をご覧ください。

インジケ-タクラスを記述していくインクルードファイルの作成

まずはインジケータ クラスを記述していくインクルードファイル(mqhファイル)を作成します。

//+------------------------------------------------------------------+
//|                                           OriginalIndicators.mqh |
//|                                                         MQL5ssei |
//|                                    https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link      "https://mqlinvestmentlab.com/"

ファイル名を「OriginalIndicators.mqh」としました。

このファイルに、これから必要なクラス記述を追加していきます。

インクルードファイルについては↓の記事をご参照ください。

MQL5 EA講座 第56回「#include命令(#include directive)」

define命令により、インジケータ値を配列にコピーする定数を宣言する

続いて、propety命令記述の下のグローバル領域(関数の外の領域)に、define命令定数を1つ宣言します。

propety命令については↓の記事をご参照ください。

MQL5 EA講座 第4回「#プロパティ命令(#property directive)」

define命令及び定数については↓の記事をご参照ください。

MQL5 EA講座 第17回「定数(Constant)について」

//インジケータ値を配列にコピーす数を規定
#define MAX_COPY 100

定数名を「MAX_COPY」としました。

この定数は、CopyBuffer関数により、インジケータの値情報を配列にコピーする数を設定するのに使います。配列にコピーする数は、CopyBuffer関数の第4引数で指定するので、「MAX_COPY」はそこ(=CopyBuffer関数の第4引数)に記述される想定の定数となります。

インジケータに共通する処理を担う基礎クラスを宣言する

続いてインジケータに共通する処理を担う基礎クラスの宣言を行います。

//インジケータに共通する処理を担う基礎クラス
class CBaseIndicator
{
	
};

クラス名を「CBaseIndicator」としました。これをたたき台にして、必要なメンバを追加していきます。

クラスの作り方に関しては↓の記事をご参照ください。

MQL5 EA講座 第48回「クラスについて1-クラスとは?-」

「CBaseIndicator」クラスに必要なメンバを追加する

CBaseIndicator」クラス内に以下のメンバを追加します。

//インジケータに共通する処理を担う基礎クラス
class CBaseIndicator
{
	protected:
		int handle;//インジケータのハンドル値を格納する変数
		double main[];//インジケータの値を格納する配列
		
	public:
		CBaseIndicator(void);//コンストラクタ
    double Main(int parIndex=0);
		void Release();
		virtual int Init() { return(handle); }
};

アクセスレベルprotected変数handle」と、配列main」という2つのメンバを、

アクセスレベルpublic関数Main」、「Release」、「Init」という3つのメンバを宣言しました。

それぞれのメンバについて順を追って解説していきます。

アクセスレベルprotectedメンバは、その利用有効範囲が、宣言したクラスと、派生クラスに制限されます。一方、アクセスレベルpublicメンバクラスの外からでもアクセスできます。

private,protected,publicなどのアクセス指定子については以下の記事をご参照ください。

MQL5 EA講座 第50回「クラスについて3 -アクセス指定子-」

メンバ変数「handle」はインジケ-タのハンドル値を格納する

アクセスレベルprotectedに宣言した、int型メンバ変数handle」はインジケータハンドル値を格納する為に使います。

ここまで講座記事を読み続けてきた方であれば、

MQL5でのインジケータ値取得には、いくつかの工程を踏む」

という事にはだいぶ慣れてきたのではないかな、と思いますが、まだピンとこないという方は前回の講座記事↓

第108回「インジケータを使ってエントリーシグナルを生成する」

をご覧ください。

ハンドル値は各種インジケータ関数を使って取得します。

移動平均線だったら、iMA関数ストキャスティクスだったらiStochastic関数・・・といった具合ですね。各インジケータ 関数戻り値として返ってくるハンドル値を格納し、最終的にCopyBuffer関数の第1引数に記述されることを想定したメンバ変数となります。

メンバ変数配列「main」はインジケータの値を格納する

アクセスレベルprotectedに宣言した、

メンバ変数配列main」はインジケータの値を格納するのに使います。

CopyBuffer関数の第5引数に記述される想定です。

CBaseIndicator関数はインスタンス生成時に、配列の時系列をセットするコンストラクタ

アクセスレベルpublicに宣言したCBaseIndicator関数インスタンス生成時に、配列の時系列をセットするコンストラクタです。

コンストラクタは覚えていますでしょうか?

コンストラクタとはクラスインスタンスを生成した段階で、発動するクラスメンバ関数のことです。

コンストラクタの決まりとして、「関数名は同じである」という原則が有る為、クラス名と同じCBaseIndicator関数名になっています。

コンストラクタについては以下の記事をご参照ください。

MQL5 EA講座 第52回「クラスについて5 -コンストラクタ-」

それでは、インスタンスとは何だったでしょう?

インスタンスとは、構造体クラスなどを実際に利用する時に宣言する変数名のようなもの、と考えてください。

インスタンスについては、以下の記事をご参照ください。

MQL5 EA講座 第54回「インスタンスについて」

コンストラクタであるCBaseIndicator関数は、アクセスレベル=protected配列main」に時系列セットを施す処理を実装します。

グローバル領域(関数の外の領域)にCBaseIndicator関数の処理実装記述を行います。

コンストラクタ,CBaseIndicator関数の処理実装記述について

コンストラクタ,CBaseIndicator関数の処理実装記述は以下の通りです。

CBaseIndicator::CBaseIndicator(void)
{
	ArraySetAsSeries(main,true);
}

コンストラクタ戻り値を取らないので、データ型void型(=型なし)になります

配列main」に時系列セットを施すには当然ArraySetAsSeries関数を使います。

配列main」なんてどこにも宣言されていないじゃないか!』

という勘違いはしないよう注意してください。

アクセスレベルprotectedの領域で既に宣言済みです。コンストラクタであるCBaseIndicator関数CBaseIndicatorクラスメンバ関数なので、配列main」に対しては当然アクセス権を持っています。

コンストラクタにこの処理を施しておくことによって、メインプログラムでこのクラスインスタンスを宣言した時点で、配列が時系列セットされるので、今後メインプログラムでArraySetAsSeries関数を呼び出す必要がなくなります。(今の時点ではピンとこなくても、大丈夫です。次回の講座記事で、CBaseIndicatorクラス派生クラスを作り終わった後には腑に落ちるように解説していきます)

メンバ関数「Main」はインジケータの値を取得して戻り値として返す

メンバ関数Main」はインジケータの値を取得して戻り値として返す関数です。

メインプログラムで呼び出す想定なので、アクセスレベルpublicとなります。

インジケータ値は、小数点を含むことが多々ありますから、戻り値データ型double型となります。

Main関数の処理実装記述について

Main関数の処理実装記述は以下のようになっています。

double CBaseIndicator::Main(int parIndex=0)
{
	CopyBuffer(handle,0,0,MAX_COPY,main);
	double value = NormalizeDouble(main[parIndex],_Digits);
	return(value);
}

引数「parIndex」には、インジケータの値を取得したい時系列のインデックス番号を指定します。

引数「parIndex」の初期値は「0」にしてあります。従って、メインプログラムで引数の記述を省略した場合、現在足のインジケータ値を取得する事になります。

関数初期値については↓の記事をご参照ください。

MQL5 EA講座 第46回「関数の初期値について」

{}内の処理実装記述についてですが、CopyBuffer関数を使い、アクセスレベル=protected配列main」にインジケータ値をコピーします。CopyBuffer関数の第1引数には、これもアクセスレベル=protected変数handle」を記述します。

配列main」の[]内に引数[parIndex]を記述します。これでメインプログラムからMain関数を呼び出せば、parIndexに記述したインデックス(通し番号)のインジケータ値が取得できるようになります。

配列main」に格納されている値は、NormalizeDouble関数を使って正規化します。

正規化とは、プログラムが取り扱うルールに則って値を整える作業

のことを指します。

NormalizeDouble関数の第1引数には正規化する対象、すなわちmain[parIndex]を記述します。

第2引数には定義済み変数_Digits」を記述します。

定義済み変数については↓の記事をご参照ください。

MQL5 EA講座 第29回「定義済み変数について」

正規化して、値を整えたmain[parIndex]を、double型ローカル変数「value」 に代入します。

最後に変数「value」をreturn演算子戻り値として返して処理実装記述は終了です。

メンバ関数「Release」はインジケータの値をメモリから解放する時に使う。

メンバ関数Release」はインジケータの値をメモリから解放する時に使います。

インジケータの値がもはや必要なくなった局面や、条件分岐で使います。

Release関数の処理実装記述について

Release関数の処理実装記述は以下の通りです。

void CBaseIndicator::Release(void)
{
	IndicatorRelease(handle);
}

Release関数の処理実装記述は極めてシンプルです。戻り値はないのでvoid型とします。

{}内にはメモリからインジケータの値を開放する処理を担うIndicatorRelease関数を呼び出し、その引数protected変数handle」をあてがえばそれで終了です。

IndicatorRelease関数について

IndicatorRelease関数は指標ハンドルを削除し、インジケータの値をメモリから解放する処理を担う関数です。IndicatorRelease関数引数戻り値は以下の通りです。

MT5には「ストラテジーテスター」という、EAの性能を過去相場で多角的に検証できる機能が実装されているのですが、ストラテジーテスターでの作業においてはIndicatorRelease関数は実行されません。

bool  IndicatorRelease( 
   int      indicator_handle    // 指標ハンドル 
  );戻り値は処理が成功した場合→true 失敗した場合はfalseを返す

MQL5リファレンスIndicatorRelease関数説明ページにはサンプルコードがあります。↓

MQL5のドキュメンテーション: 時系列と指標へのアクセス / IndicatorRelease
この関数は、指標ハンドルを削除し、誰によっても使用されていない場合は指標の計算ブロックを解放します。 戻り値 成功の場合は true、それ以外の場合は false 注意事項 この関数は不要になった指標ハンドルを削除しメモリを節約します...

MQL5リファレンスの見方については第47回「MQL5公式リファレンスの見方」をご覧ください。

上記リンクにもう少し丁寧なコメントアウト解説を追加したものが以下のサンプルコードになります。


//--- 入力パラメータ 
input int               MA_Period=15; 
input int               MA_shift=0; 
input ENUM_MA_METHOD     MA_smooth=MODE_SMA; 
input ENUM_APPLIED_PRICE price=PRICE_CLOSE; 
//--- 指標ハンドルを格納する 
int MA_handle=INVALID_HANDLE; 
//+------------------------------------------------------------------+ 
//| エキスパート初期化に使用される関数                                        | 
//+------------------------------------------------------------------+ 
int OnInit() 
  { 
//--- 指標ハンドルを作成する 
  MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,PRICE_CLOSE); 
//--- グローバル変数を削除する 
  if(GlobalVariableCheck("MA_value")) //"MA_value"を確認できたら、"MA_value"を削除する
     GlobalVariableDel("MA_value"); 
//--- 
   return(INIT_SUCCEEDED); 
  } 
//+------------------------------------------------------------------+ 
//| エキスパートティック関数                                                 | 
//+------------------------------------------------------------------+ 
void OnTick() 
  { 
//--- グローバル変数"MA_value"の値が存在しない場合 、"MA_value"を作成する
  if(!GlobalVariableCheck("MA_value")) 
     { 
     //--- 最後の 2 つのバーの指標値を取得する 
    if(MA_handle!=INVALID_HANDLE) //OnInit関数内で無事にハンドルを取得出来ている場合、
        { 
         //--- 指標値を格納する動的配列 の宣言
        double values[]; 
        //配列に2要素分の情報をコピーする
         if(CopyBuffer(MA_handle,0,0,2,values)==2 && values[0]!=EMPTY_VALUE) //取得要素が2で、インデックス「0」の値が空でない時
         
           { 
           //--- 最後から2 番目のバーをグローバル変数の値に保存する 
          if(GlobalVariableSet("MA_value",values[0])) //GlobalVariableSet関数の戻り値は最終更新時刻→無事にグローバル変数を設定出来た時
              { 
               //--- 指標ハンドルを解放する 
              if(!IndicatorRelease(MA_handle)) //開放作業が失敗した場合、エラーをログ出力する
                 Print("IndicatorRelease() failed. Error ",GetLastError()); 
               else MA_handle=INVALID_HANDLE; //解放作業が成功した場合、ハンドルにINVALID_HANDLEを代入する
              } //if(CopyBuffer(MA_handle,0,0,2,values)==2 && values[0]!=EMPTY_VALUE) //取得要素が2で、インデックス「0」の値が空でない時
           else 
               Print("GlobalVariableSet failed. Error ",GetLastError()); 
           } //if(CopyBuffer(MA_handle,0,0,2,values)==2 && values[0]!=EMPTY_VALUE) //取得要素が2で、インデックス「0」の値が空でない時
        } //if(CopyBuffer(MA_handle,0,0,2,values)==2 && values[0]!=EMPTY_VALUE) //取得要素が2で、インデックス「0」の値が空でない時
     } 
//--- 
  }

<参照>

INVALID_HANDLE

EMPTY_VALUE

このサンプルコードの実行結果は以下のようになります。

サンプルコード内にはGlobalVariableCheck関数GlobalVariableSet関数など見慣れない関数が混じっていますが、これらはGlobal変数というものに関する関数群になります。

Global変数というのは、MT5をシャットダウン後も、一定期間値の記憶を保つことができる変数の事を言います。

Global変数についてはこの後の講座記事にて、1つの単元として取り上げる予定なので今は細かい意味はわからなくても大丈夫です。

※先にGlobal変数について詳しく知りたいという方は↓の記事をご参照ください

上記のサンプルコードを実行した動画はGlobal変数インジケータの値を保存した後は、インジケータ値のメモリ保存は不要、としてIndicatorRelease関数を使いメモリを開放しているという挙動になります。

※注意:Global変数MQL5 EA講座 第27回「グローバル変数について」で取り上げたグローバル変数とは別物になります。

当サイトではMT5をシャットダウン後も、一定期間値の記憶を保つことができる変数をアルファベット表記のGlobal変数関数の外(=グローバル領域)に作られた(=宣言された)変数のことをカタカナ表記のグローバル変数として区別したいと思います。

メンバ関数「Init」は戻り値だけを定めた仮想関数

メンバ関数Init」は戻り値だけを定めた仮想関数となります。

仮想関数とは、「大まかな処理内容だけ元のクラスで決めて、細かい処理内容は、各派生クラスで決める」為の関数になります。

仮想関数については以下の記事をご参照ください。

MQL5 EA講座 第53回「クラスについて6 -仮想関数-」

virtual int Init() { return(handle); }

仮想関数につき、関数名とデータ型の前に virtualキーワードがついています。

処理実装記述は現時点ではインライン定義

{ return(handle); }

と記載しているのみです。

インライン定義とは、本来グローバル領域で行うメンバ関数の処理実装記述を、クラスの{}内にて、メンバ関数の設定と同時に行ってしまう事です。

アクセスレベル=protectedメンバ変数handle」はインジケータハンドル値を格納する変数ですが、インジケータの種類だけ、ハンドル値を取得するインジケータ系の関数は存在し、それらインジケータ関数引数インジケータごとに異なります。

従って、具体的な処理実装記述は次回の講座記事で解説する予定の、各インジケータ派生クラスで記述していきます。

現時点では、戻り値を返すだけのインライン定義で済ませているのはその為です。

次回講座記事の

・『仮想関数「Init」の仮引数と処理実装記述をCDerivediMAクラスで再定義する』セクション

及び

・『仮想関数「Init」の仮引数と処理実装記述をCDerivediStochasticクラスで再定義する』セクション箇所に特に注意を払ってお読みいただければ幸いです。

まとめ

今回は、インジケータの値取得を簡略化できるクラスを作る解説の第1回目でした。

MT5に組み込まれているインジケータには構造的に共通している処理を担る基礎クラスと、各インジケータごとに異なった処理が必要な工程を担う、派生クラスを作っていくというロードマップをまずは示し、今回はまず基礎クラスの作成に取り掛かりました。

※詳細は今回の記事の「インジケ-タクラス作成のロードマップ」セクションをご覧ください。

今回作成した基礎クラスCBaseIndicatorクラスには以下のメンバを追加しました。

メンバ変数「handle」インジケータハンドル値を格納する、アクセスレベル=protected変数

メンバ変数配列「main」インジケータ値を格納する、アクセスレベル=protected配列

CBaseIndicator関数インスタンス生成時に、配列の時系列をセットする、アクセスレベル=publicコンストラクタ

メンバ関数「Main」インジケータの値を取得して戻り値として返す、アクセスレベル=publicメンバ関数

メンバ関数「Release」インジケータの値をメモリから解放する時に使う、アクセスレベル=publicメンバ関数

メンバ関数「Init」戻り値だけを定めた、アクセスレベル=public仮想関数。細かい処理内容は、各派生クラスで定義する。

現時点での「OriginalIndicators.mqh」ファイルの記述内容全体は以下のようになっています。

//+------------------------------------------------------------------+
//|                                           OriginalIndicators.mqh |
//|                                                         MQL5ssei |
//|                                    https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link      "https://mqlinvestmentlab.com/"

//インジケータ値を配列にコピーす数を規定
#define MAX_COPY 100

//インジケータに共通する処理を担う基礎クラス
class CBaseIndicator
{
	protected:
		int handle;//インジケータのハンドル値を格納する変数
		double main[];//インジケータの値を格納する配列
		
	public:
		CBaseIndicator(void);//コンストラクタ
		double Main(int parIndex=0);
		void Release();
		virtual int Init() { return(handle); }
};


CBaseIndicator::CBaseIndicator(void)
{
	ArraySetAsSeries(main,true);
}

double CBaseIndicator::Main(int parIndex=0)
{
	CopyBuffer(handle,0,0,MAX_COPY,main);
	double value = NormalizeDouble(main[parIndex],_Digits);
	return(value);
}

void CBaseIndicator::Release(void)
{
	IndicatorRelease(handle);
}

今回は以上とさせていただきます。

最後までお読みいただきありがとうございました。

MQL5 EA講座 第108回「インジケータを使ってエントリーシグナルを生成する」

MQL5 EA講座 第110回「インジケータ取得クラスに派生クラスを追加する」

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