前回は、主に現在値情報へのアクセス方法についての解説を行いました。
前回の記事で解説した方法は以下の通りです。
・取得方法3:SymbolInfoTick関数とMqlTick構造体を使って取得する
MQL4のようにAskやBidといった定義済み変数のないMQL5においては、現在値へのアクセス方法に関する基本を押さえておくことがEA(自動売買プログラム)開発において重要です。
さて、今回は、バー情報の取得方法に関する記述について解説を行います。
今回の記事を読む事によって、バー情報へのアクセス記述が理解できるようになり、EA(自動売買プログラム)開発における肝となる売買条件に迷う事がなくなります。
バー情報の取得方法について
これも前回お伝えした事ではありますが、MQL5では各バーの始値、終値、高値、安値、オープン時間などを格納している定義済み配列は事前に用意されていません。
これらの定義済み配列情報にアクセスするには自分でその配列を用意する記述が必要です。
バー情報の取得にはMqlRates構造体とCopyRates関数を使います。
※MqlRates構造体及びCopyRates関数についてはすでに講座記事第92回で解説をしておりますので、
それぞれのアンカーテキストから参照をして頂ければと思います。
MqlRates構造体とMqlTick構造体の違いについて
価格情報やオープン時間帯を格納する構造体で言うと、前回解説したMqlTick構造体があります。
それぞれの構造体のメンバ構成を見ると、どちらも価格や時間を格納するメンバが連なっており、最初は混乱するかもしれません。
MqlRates構造体は、「指定された期間内における情報」を格納するのに対し、MqlTick構造体は「最新の価格関連情報」を格納するのに使う、という違いがあります。
MqlTick構造体が格納する情報は、SymbolInfoTick関数で取得しました。
※MqlTick構造体についてはコチラのリンクをご覧ください。
MqlRates構造体が格納する情報については、CopyRates関数を使って取得します。
※CopyRates関数についてはコチラのリンクをご覧ください。
↓のような形で取得します
//バー情報を格納する配列を用意
MqlRates bar[];
//配列を時系列にセット
ArraySetAsSeries(bar,true);
//現在足から100本分のバー情報を取得し、コピーする
copyrates(_Symbol,_Period,0,100,bar);
//1本前の終値を代入
double close = bar[1].close;
//1本前の始値を代入
double open = bar[1].open;
最初にMqlRates構造体のインスタンス「bar」を宣言します。一定期間過去のデータを格納したいので、配列として宣言します。
※配列については 第18回「配列(Array)について」をご覧ください。
次にArraySetAsSeries関数を使い、配列を時系列にセットします。
ArraySetAsSeries関数は、第1引数に指定した動的配列に 時系列設定を付与することができる関数です。
第1引数には、時系列設定を付与する配列を記述し、第2引数には、時系列の設定フラグをtrueかfalseで記述します。時系列の設定フラグというのは、簡単に言えば
最新の時間→過去の時間 の順番で要素を配列に格納していくのか?
それとも
過去の時間→最新の時間 の順番で要素を配列に格納していくのか?
この第2引数をtrueに設定する事によって、配列には直近の時間のデータから、配列に格納されていきます。配列[0]には、取得したデータの中で最新の時間のデータが格納される、ということになります。
詳細は↓のArraySetAsSeries関数に関する記事をご覧ください。
配列の時系列セットが完了したら、CopyRates関数を使ってバーデータをコピーします。
CopyRates関数の第4引数には、コピーするデータ数を記述します。
今回は100本分のバーを取る、
という前提にしました。
第3引数はコピー開始位置を示していますので、「0」を記述すると現在足から100本分という事になります。
これで、晴れてインスタンス「bar」に、バーデータが格納されアクセスできるようになりました。
例えば、現在足から1本前の終値にアクセスしたい時は、 bar[1]の後にドット(.)を記述し、メンバ変数「close」を記述します。
bar[1].close;
↑のような形ですね。
同じように、現在足から1本前の始値にアクセスしたい時は、 bar[1]の後にドット(.)を記述し、メンバ変数「open 」を記述します。
bar[1].open;
↑のような形です。
この基本形を念頭に置いていただいた上で、次は、このバーデータ情報取得を簡略化できるオリジナルクラスを作っていきます。
※構造体の使い方については以下の記事をご参照ください。
バーデータを取得する為のクラスを作る
バーデータを取得するクラスは、前回作った「OriginalPrice.mqh」ファイル内に作っていきます。
class OriginalCBars
{
};
クラス名を「OriginalCBars」としました。これをたたき台にメンバを追加していきます。
class OriginalCBars
{
public:
MqlRates bar[];
OriginalCBars(void);
void Update(string parSymbol, ENUM_TIMEFRAMES parPeriod);
};
とりあえず、3つのメンバを宣言しました。
OriginalCBarsクラスでは、全てアクセスレベルはpublicとします。入れ子構造にするような情報を扱う予定がないので、private protectedのアクセス指定子は用いません。
※private,protected,publicなどのアクセス指定子については↓の
MQL5 EA講座 第50回「クラスについて3 -アクセス指定子-」
をご覧ください。
MqlRates構造体のインスタンス bar[]配列
MqlRates構造体のインスタンス bar[]配列は、バー情報にアクセスするための配列です。
「バー情報の取得方法について」セクションでも登場しましたが、この時の解説と同じ使い方をする為に用意しています。
コンストラクタである、OriginalCBars関数
OriginalCBars(void)はコンストラクタです。コンストラクタは、覚えていますでしょうか?
コンストラクタとはクラスのインスタンスを生成した段階で、発動するクラスのメンバ関数のことです。
普通のメンバ関数は、生成したインスタンスから呼び出す記述をしないと発動しないのですが、コンストラクタは呼び出す記述をしなくても、インスタンス生成しただけで自動的に実装した処理を行います。なぜ今回、OriginalCBarsというコンストラクタを宣言したのかは、後程解説します。
※コンストラクタの詳細については↓の記事をご覧ください。
OriginalCBars関数の処理実装記述
OriginalCBars関数の処理実装記述は以下の通りです。
CBars::OriginalCBars(void)
{//MqlRates型のpublic変数配列barを時系列順にセット
ArraySetAsSeries(bar,true);
}
↑を見てもらえばわかる通り、{}内で行っている処理はArraySetAsSeries関数にて、MqlRates型の「bar[]配列」を時系列にセットしているだけです。
自前で配列を用意→配列を時系列にセット、という処理は価格情報・バー情報を取得する上で必ず行われる処理になります。
従って、インスタンス生成後、呼び出さないと機能しないメンバ関数としてではなく、インスタンスを生成した時点で処理を実行してくれるコンストラクタとして宣言した方が都合が良いのです。
時系列セットされた配列「bar[]」は、この後解説するUpdate関数で、バーデータをコピーする先として使われます。
バーデータをコピーするメンバ関数、Update関数
Update関数はバーデータをコピーする役割を担うOriginalCBarsクラスのメンバ関数です。
Update関数の戻り値と仮引数
void Update(string parSymbol, ENUM_TIMEFRAMES parPeriod);
特に処理結果を返す設計にはしないので、戻り値はvoid型です。
第1仮引数「parSymbol」は取引銘柄が記述される想定です。データ型はstring型となります。
第2仮引数「parPeriod」はチャートの時間軸が記述される想定です。データ型はENUM_TIMEFRAMESとなります。
ENUM_TIMEFRAMESはチャートの時間軸を格納するenum列挙型です。
※enum列挙型については↓の記事をご覧ください。
Update関数の処理実装記述
Update関数の処理実装記述は以下の通りです。
void OriginalCBars::Update(string parSymbol,ENUM_TIMEFRAMES parPeriod)
{
CopyRates(parSymbol,parPeriod,0,BARS_MAX,bar);//barはMqlRates型のpublic変数配列
}
CopyRates関数を使って、配列「bar[]」にバーデータをコピーします。
CopyRates関数の第3仮引数はコピー開始位置を示しており、0の場合は現在足からコピーを開始します。
CopyRates関数の第4仮引数にはコピーするバーの数を記述する訳ですが、ここには定数値「BARS_MAX」を記述します。定数値「BARS_MAX」は「OriginalPrice.mqh」ファイル内の冒頭、グローバル領域に定数の宣言を行います。
コピーするバーの数を設定する定数値「BARS_MAX」
「OriginalPrice.mqh」ファイル内の冒頭、グローバル領域に#define命令により、定数値「BARS_MAX」を宣言します。
//+------------------------------------------------------------------+
//| OriginalPrice.mqh |
//| MQL5ssei |
//| https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link "https://mqlinvestmentlab.com/"
#define BARS_MAX 100 //コピーするバー情報の数
↑こんな感じです。
※#define命令については第17回の「#define命令(define directive)を使う」セクションをご覧ください。
これで、今後、Update関数を呼び出せば、現在足+過去99本分のバーデータを配列「bar[]」にコピーする事ができるようになりました。
メインプログラムでUpdate関数と併せて配列「bar[]」を呼び出せば、「bar[]」はMqlRates型の配列なので、各種メンバ変数を使ってバーデータにもアクセスできるようになった訳です。
ただ、このままだとまだ若干使い勝手が悪いです。
例えば、終値へのアクセスをするのであれば、メインプログラムにて、OriginalCBarsクラスのインスタンスを宣言した後、メンバである「bar[]」を呼び出し、さらに「bar[]」のメンバである「close」を呼び出さなくてはいけない事になります。
その為、もうちょっと直感的に使えるメンバ関数を追加します。
追加する関数は以下の通りです。
class OriginalCBars
{//このクラスにはプライベートやプロテクテッドセグメントがない
public:
OriginalCBars(void);//コンストラクタ MqlRates型のpublic変数配列barを時系列順にセット
MqlRates bar[];
//バーデータをbar[]にコピーする
void Update(string parSymbol, ENUM_TIMEFRAMES parPeriod);
//終値にアクセスする
double Close(int parShift);
//高値にアクセスする
double High(int parShift);
//安値アクセスする
double Low(int parShift);
//始値にアクセスする
double Open(int parShift);
//オープン時間にアクセスする
datetime Time(int parShift);
//実際の取引数量にアクセスする
long TickVolume(int parShift);
//取引数量にアクセスする
long Volume(int parShift);
};
Close関数、High関数、Low関数、Open関数、Time関数、TickVolume関数、Volume関数
という7つのメンバ関数を追加しました。
処理実装記述は非常に簡単ですが、一応1つ1つ見ていきます
※構造体の使い方は第22回「構造体」を、クラスの使い方を復習されたい方は下記の記事群をご参照ください。
- MQL5 EA講座 第48回「クラスについて1-クラスとは?-」
- MQL5 EA講座 第49回「クラスについて2 -クラスの使い方-」
- MQL5 EA講座 第50回「クラスについて3 -アクセス指定子-」
- MQL5 EA講座 第51回「クラスについて4 -派生クラス-」
- MQL5 EA講座 第52回「クラスについて5 -コンストラクタ-」
- MQL5 EA講座 第53回「クラスについて6 -仮想関数-」
終値情報にアクセスするClose関数
戻り値はdouble型、仮引数「parShift」は情報を取得するインデックス(通し番号)になります。0であれば、現在足の、1であれば現在足から一本前の情報にアクセスする事になります。初期値は0に設定しています。
処理実装記述は以下の通りです。
//終値情報にアクセスする
double OriginalCBars::Close(int parShift=0)
{
return(bar[parShift].close);
}
配列「bar[]」のメンバである「close」を呼び出して、仮引数「parShift」で指定したインデックスの終値情報を取得します。
高値情報にアクセスするHigh関数
戻り値はdouble型、仮引数「parShift」は情報を取得するインデックス(通し番号)になります。0であれば、現在足の、1であれば現在足から一本前の情報にアクセスする事になります。初期値は0に設定しています。
処理実装記述は以下の通りです。
//高値情報にアクセスする
double OriginalCBars::High(int parShift=0)
{
return(bar[parShift].high);
}
配列「bar[]」のメンバである「high」を呼び出して、仮引数「parShift」で指定したインデックスの高値情報を取得します。
安値情報にアクセスするLow関数
戻り値はdouble型、仮引数「parShift」は情報を取得するインデックス(通し番号)になります。0であれば、現在足の、1であれば現在足から一本前の情報にアクセスする事になります。初期値は0に設定しています。
処理実装記述は以下の通りです。
//安値情報にアクセスする
double OriginalCBars::Low(int parShift=0)
{
return(bar[parShift].low);
}
配列「bar[]」のメンバである「low」を呼び出して、仮引数「parShift」で指定したインデックスの安値情報を取得します。
始値情報にアクセスするOpen関数
戻り値はdouble型、仮引数「parShift」は情報を取得するインデックス(通し番号)になります。0であれば、現在足の、1であれば現在足から一本前の情報にアクセスする事になります。初期値は0に設定しています。
処理実装記述は以下の通りです。
//始値情報にアクセスする
double OriginalCBars::Open(int parShift=0)
{
return(bar[parShift].open);
}
配列「bar[]」のメンバである「open」を呼び出して、仮引数「parShift」で指定したインデックスの始値情報を取得します。
ティック数情報にアクセスするTickVolume関数
TickVolume関数はティック数情報にアクセスするメンバ関数です。
戻り値はlong型、仮引数「parShift」は情報を取得するインデックス(通し番号)になります。0であれば、現在足の、1であれば現在足から一本前の情報にアクセスする事になります。初期値は0に設定しています。
処理実装記述は以下の通りです。
//ティック数情報にアクセスする
long OriginalCBars::TickVolume(int parShift=0)
{
return(bar[parShift].tick_volume);
}
配列「bar[]」のメンバである「tick_volume」を呼び出して、仮引数「parShift」で指定したインデックスのティック数情報を取得します。
取得したティック数をreturnで戻り値として返して終了です。
オープン時間情報にアクセスするTime関数
Time関数はオープン時間情報にアクセスするメンバ関数です。
戻り値はdatetime型、仮引数「parShift」は情報を取得するインデックス(通し番号)になります。0であれば、現在足の、1であれば現在足から一本前の情報にアクセスする事になります初期値は0に設定しています。
処理実装記述は以下の通りです。
//オープン時間情報にアクセスする
datetime OriginalCBars::Time(int parShift=0)
{
return(bar[parShift].time);
}
配列「bar[]」のメンバである「time」を呼び出して、仮引数「parShift」で指定したインデックスのオープン時間情報を取得します。
取得したオープン時間をreturnで戻り値として返して終了です
※datetime型については↓の記事をご参照ください。
MQL5 EA講座 第16回「データ型その6 datetime型」
取引量情報にアクセスするVolume関数
戻り値はlong型、仮引数「parShift」は情報を取得するインデックス(通し番号)になります。0であれば、現在足の、1であれば現在足から一本前の情報にアクセスする事になります。初期値は0に設定しています。
処理実装記述は以下の通りです。
//取引量情報にアクセスする
long OriginalCBars::Volume(int parShift=0)
{
return(bar[parShift].real_volume);
}
配列「bar[]」のメンバである「real_volume」を呼び出して、仮引数「parShift」で指定したインデックスの取引量情報を取得します。
OriginalCBarsクラスを使って、実際にバー情報にアクセスしてみる
一通りOriginalCBarsクラスにメンバ関数を追加し終わったところで、メインプログラムにおけるOriginalCBarsクラスを使ったバー情報へのアクセス記述について見ていきましょう。
以下は、「現在足から一本前の終値データにアクセスする」という前提のサンプルコードです。
//「OriginalPrice.mqh」ファイルをインクルードする
#include <OriginalPrice.mqh>
//インスタンスの生成
OriginalCBars Price;
void OnTick()
{
//バーデータをコピーする
Price.Update(_Symbol,_Period);
//一本前の終値情報にアクセスし、変数に格納する
double close=Price.Close(1);
}
「OriginalPrice.mqh」ファイルをインクルードする
まずはグローバル領域にて、「OriginalPrice.mqh」ファイルをインクルードし、OriginalCBarsクラスをメインプログラム内で使えるようにします。
#include命令については↓の記事をご覧ください。
MQL5 EA講座 第56回「#include命令(#include directive)」
インスタンスの生成を行う
続いてOriginalCBarsクラスのインスタンスを生成します。インスタンス名を「Price」としました。
※インスタンスは構造体、クラスなどを実際に利用する時に宣言する変数名のようなものです。
詳しくは↓の記事をご参照ください。
OnTick関数内でUpdate関数を呼び出す
続いてはイベントハンドラーであるOnTick関数内の記述です。
OriginalCBarsクラスのメンバ関数であるUpdate関数を呼び出します。
呼び出すときはインスタンス名「Price」の後にドット(.)→メンバ関数名であるUpdate関数を記述します。
(なお、このインスタンス「Price」の宣言により、コンストラクタである、OriginalCBars関数も発動しています。)
このUpdate関数呼び出しにより、配列「bar[]」にバーデータがコピーされます。
※配列「bars」については、今回の記事の
MqlRates構造体のインスタンス bar[]配列 セクションをご参照ください。
Close関数を呼び出す
最後に、OriginalCBarsクラスのメンバ関数であるClose関数を呼び出します。
今回は終値へのアクセスを例に挙げましたが、他のバーデータにアクセスしたい時は対応するメンバ関数を呼び出せば良いだけです。
始値であればOpen関数、高値であればHigh関数といった具合です。
まとめ
今回は、バーデータへのアクセスを容易にするために、「OriginalPrice.mqh」ファイル内に「OriginalCBarsクラス」というクラスを作りました。
「OriginalCBarsクラス」内に作ったメンバは以下の通りです。(アクセスレベルは全てpublicになります)
・bar[]配列(バー情報にアクセスするためMqlRates構造体型のインスタンス)
・OriginalCBars関数(「bar[]」を時系列にセットする為のコンストラクタ)
・Update関数(バーデータをbar[]配列にコピーする役割を担うメンバ関数)
・TickVolume関数(ティック数情報にアクセスするメンバ関数)
上記のメンバに加え、前回第103回では、Ask関数とBid関数という独立関数も「OriginalPrice.mqh」ファイル内には作っています。
前回と今回の内容を踏まえた「OriginalPrice.mqh」ファイルの全体記述は以下の通りです。
//+------------------------------------------------------------------+
//| OriginalPrice.mqh |
//| MQL5ssei |
//| https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link "https://mqlinvestmentlab.com/"
#define BARS_MAX 100 //コピーするバー情報の数
class OriginalCBars
{//このクラスにはプライベートやプロテクテッドセグメントがない
public:
OriginalCBars(void);//コンストラクタ MqlRates型のpublic変数配列barを時系列順にセット
MqlRates bar[];
//バーデータをbar[]にコピーする
void Update(string parSymbol, ENUM_TIMEFRAMES parPeriod);
//終値にアクセスする
double Close(int parShift);
//高値にアクセスする
double High(int parShift);
//安値アクセスする
double Low(int parShift);
//始値にアクセスする
double Open(int parShift);
//オープン時間にアクセスする
datetime Time(int parShift);
//ティック量にアクセスする
long TickVolume(int parShift);
//取引数量にアクセスする
long Volume(int parShift);
};
OriginalCBars::OriginalCBars(void)
{//MqlRates型のpublic変数配列barを時系列順にセット
ArraySetAsSeries(bar,true);
}
void OriginalCBars::Update(string parSymbol,ENUM_TIMEFRAMES parPeriod)
{
CopyRates(parSymbol,parPeriod,0,BARS_MAX,bar);//barはMqlRates型のpublic変数配列
}
//終値情報にアクセスする
double OriginalCBars::Close(int parShift=0)
{
return(bar[parShift].close);
}
//高値情報にアクセスする
double OriginalCBars::High(int parShift=0)
{
return(bar[parShift].high);
}
//安値情報にアクセスする
double OriginalCBars::Low(int parShift=0)
{
return(bar[parShift].low);
}
//始値情報にアクセスする
double OriginalCBars::Open(int parShift=0)
{
return(bar[parShift].open);
}
//ティック数情報にアクセスする
long OriginalCBars::TickVolume(int parShift=0)
{
return(bar[parShift].tick_volume);
}
//オープン時間情報にアクセスする
datetime OriginalCBars::Time(int parShift=0)
{
return(bar[parShift].time);
}
//取引量情報にアクセスする
long OriginalCBars::Volume(int parShift=0)
{
return(bar[parShift].real_volume);
}
//Ask値を取得する関数
double Ask(string parSymbol=NULL)
{
if(parSymbol == NULL) parSymbol = _Symbol;
return(SymbolInfoDouble(parSymbol,SYMBOL_ASK));
}
//Bid値を取得する関数
double Bid(string parSymbol=NULL)
{
if(parSymbol == NULL) parSymbol = _Symbol;
return(SymbolInfoDouble(parSymbol,SYMBOL_BID));
}
今回は以上とさせていただきます。
最後までお読みいただきありがとうございました<m(__)m>
コメント