前回は「特定の日時を簡単に生成できる関数」=GenerateTimestamp関数を実装する手順について解説しました。
※GenerateTimestamp関数の実装手順に関しては、前回の下記セクション↓
・「OriginalTrade.mqh」ファイルの中に関数の宣言をする
の内部リンクから復習したいところに飛んで確認をして頂ければと思います。
そして完成したGenerateTimestamp関数をメインプログラムの中でどのように使うのか、という点についても解説を行い、その過程で「OriginalTimer.mqh」ファイルに、「1分」「1時間」「1日」「1週間」に相当する経過秒数を定数にして追加しました。
詳細については前回記事の
「完成したGenerateTimestamp関数を使ってみる」セクション及び
「「1分」「1時間」「1日」「1週間」に相当する経過秒数をdefine命令により定数として設定する」セクションをご参照ください。
今回からは、これまで数回の講座記事で解説してきた知識を利用して、EAにトレードタイマーを実装できるクラスを作成していきます
トレードタイマーとは?
トレードタイマークラスを実装する前に、そもそもトレードタイマーとは何か?という事なんですが、トレードタイマーとは
スケジュールされたタイミングで特定の指令を実行する機能のことを指します。
トレードタイマーの仕組みも突き詰めていけば、色んな考え方ができると思うのですが、
例えば2つの時刻情報(仮に午前9時と午後3時としましょう)を設定し、現在時刻が2つの値の間(午前9時から午後3時)にあるかどうかを判断します。
↓
現在時刻が2つの値の間(午前9時~午後3時)にあればトレードを実行し、現在時刻が2つの値(午前9時~午後3時)から外れていたらトレードは実行しない・・・といった機能を盛り込む事を言います。
トレードタイマー関数を実装する過程を解説する中で、定められた時間ごとに特定の動作を行うためのタイマーを設定する手順を学んでいきます。
これらのクラス実装が終わる頃には、EAの動作を時間に基づいてコントロールする方法や、定期的なイベントを自動で実行する仕組みについての理解が多いに深まっている事でしょう。
今回の記事では、トレードタイマークラスの第1弾として「トレード時間かどうかをチェックする」メンバ関数をクラスに実装する過程を解説していきたいと思います。
是非今回も楽しみながら読んでいただければと思います。
※クラスについての概要は以下のリンクから確認・復習をお願いします。
- MQL5 EA講座 第48回「クラスについて1-クラスとは?-」
- MQL5 EA講座 第49回「クラスについて2 -クラスの使い方-」
- MQL5 EA講座 第50回「クラスについて3 -アクセス指定子-」
- MQL5 EA講座 第51回「クラスについて4 -派生クラス-」
- MQL5 EA講座 第52回「クラスについて5 -コンストラクタ-」
- MQL5 EA講座 第53回「クラスについて6 -仮想関数-」
「トレード時間かどうかをチェックする」メンバ関数実装へのロードマップ
「トレード時間かどうかをチェックする」メンバ関数は以下の手順を踏んで実装していきます。
・「OriginalTimer.mqh」ファイルに「トレードタイマークラス」を宣言する
順を追って見ていきましょう
「OriginalTimer.mqh」ファイルに「トレードタイマークラス」を宣言する
まずは「トレードタイマークラス」を宣言する所から始めます
宣言は第115回「チャート上に新しいバーが生成される瞬間にのみ注文できるクラスを作る」で作成した「OriginalTimer.mqh」インクルードファイル内に行います。
宣言する場所は「OriginalTimer.mqh」ファイルのグローバル領域であればどこでも構いません
class CTradeSession
{
};
クラス名を「CTradeSession」としました。
アクセスレベル=publicのメンバを追加する
※private,protected,publicなどクラスのアクセスレベルについては↓
・MQL5 EA講座 第50回「クラスについて3 -アクセス指定子-」
をご覧ください。
class CTradeSession
{
public:
// 取引セッションがアクティブかどうかをチェックする関数
bool IsActive(datetime openTime, datetime closeTime, bool useLocalTime = false);
};
今回追加するメンバ関数「IsActive」は取引セッションがアクティブかどうかをチェックする関数です。
「取引セッションがアクティブかどうかをチェックする」とは、具体的に言えば仮引数にトレード開始時間とトレード終了時間という2つの日時情報を設定し、現在時刻がその間にあるかどうかを確認する、という事になります。
後程処理実装記述の所でも詳しく解説しますが、トレード開始時間とトレード終了時間の間に現在時刻があればトレード許可フラグとしてtrueを返し、トレード開始時間とトレード終了時間の範囲外に現在時刻があればfalseとなるように記述をしていきます。
メンバ関数「IsActive」に必要な仮引数を設定する
続いてメンバ関数「IsActive」に必要な仮引数を設定していきます
IsActive関数には3つの仮引数を設定します。
/ 取引セッションがアクティブかどうかをチェックする関数の実装
bool CTradeSession::IsActive(datetime openTime, datetime closeTime, bool useLocalTime = false)
第1仮引数「openTime」にはトレード開始時間が格納される想定
第1仮引数の「openTime」には、メインプログラムにてトレード開始時間が格納される想定となっています。日時情報なのでデータ型はdateTime型です。
第2仮引数「closeTime」にはトレード終了時間が格納される想定
第2仮引数の「openTime」には、メインプログラムにてトレード終了時間が格納される想定となっています。日時情報なのでデータ型はdateTime型です。
第3仮引数「useLocalTime」にはローカル時間を基準として使うかが格納される想定
第3仮引数「useLocalTime」には、メインプログラムにてローカル時間を基準として使うかが格納される想定となっています。
trueの場合はローカル時間を基準としてトレード時間のチェックを行い、falseの場合はサーバー時間を基準としてトレード時間のチェックを行います。
第3仮引数には初期値=falseを設定しておきます。すなわち、なにも記述をしなければ、サーバー時間を基準としてトレード時間のチェックを行う、という事になります。
※初期値については↓の記事をご参照ください
サーバー時間とは?
サーバー時間は、取引サーバーが基準とする時間です。サーバー時間は通常、FXブローカーが位置するタイムゾーンか、国際的な金融市場が多く使用するGMT/UTCに合わせられています。
サーバー時間の取得にはTimeCurrent関数を使います。TimeCurrent関数の詳細は↓の記事をご参照ください。
※タイムゾーンというのは地球上の「時間の地図」のようなもので、それぞれの地域がどの時間を使うかを決めるために設けられています。
またグリニッジ標準時(GMT)というのは、イギリスのグリニッジ天文台を通る経度における時間の事です。
タイムゾーンはこのGMTを基準にして、地球を東西に24の帯に分け、それぞれで1時間ずつ時間がずれるようにしています。
ローカル時間とは?
ローカル時間は、皆さんのコンピュータやデバイスが設定されている地域の時間です。
プログラムが市場データにアクセスする際や取引を行う際にはサーバー時間が使われることが多いですが、プログラミングの記述によって今回実装する IsActive関数のようにローカル時間を使うかどうか選択制にすることも可能です。
ローカル時間の取得にはTimeLocal関数を使います。
※TimeLocal関数についての詳細は↓の記事をご参照ください
メンバ関数「IsActive」に処理実装記述をおこなう
仮引数の設定が終わったので、今度はIsActive関数に処理実装記述をほどこしていきましょう
処理実装記述の全体は以下のようになっています↓
// 取引セッションがアクティブかどうかをチェックする関数の実装
bool CTradeSession::IsActive(datetime openTime, datetime closeTime, bool useLocalTime = false)
{
// 開始時間と終了時間が正常に設定されているかどうかを確認
if (openTime >= closeTime)
{
// 開始時間と終了時間が不正である場合、警告メッセージを出力して処理を中止
Alert("エラー:開始時間または終了時間が不正です");
return false; // 不正な時間設定のため、falseを返す
}
// 現在時刻を取得する
datetime currentTime;
//ローカル時間を使うかどうかのチェック
if(useLocalTime == true) currentTime = TimeLocal();
else currentTime = TimeCurrent();
// 現在時刻が開始時間と終了時間の範囲内かどうかをチェック
bool timerOn = false; // タイマーがオンかどうかのフラグを初期化
if(currentTime >= openTime && currentTime < closeTime)
{
// 現在時刻が指定された時間範囲内にある場合、タイマーをオンにする
timerOn = true;
}
return timerOn; // タイマーの状態を返す
}
細かく区切って1つ1つ見ていきましょう
処理実装記述1:開始時間と終了時間が正常に設定されているかどうかを確認する
// 開始時間と終了時間が正常に設定されているかどうかを確認
if (openTime >= closeTime)
{
// 開始時間と終了時間が不正である場合、警告メッセージを出力して処理を中止
Alert("エラー:開始時間または終了時間が不正です");
return false; // 不正な時間設定のため、falseを返す
}
冒頭の
if (openTime >= closeTime)
↑という条件を満たしている場合というのは、開始日時が終了日時より時系列的に後になってしまっている状態(例えばopenTimeの値が「2024/03/25/22:00」で、closeTimeの値が「2024/03/25/08:00」といった具合)です。
この場合は、Alert関数を使って警告メッセージを出力して処理を中止します。
処理実装記述2:現在時刻の取得~ローカル時間を使うかどうかのチェック
続いて現在時刻の取得を行うのですが、その際にローカル時間を使うかどうかのチェックを行います↓
// 現在時刻を取得する
datetime currentTime;
//ローカル時間を使うかどうかのチェック
if(useLocalTime == true) currentTime = TimeLocal();
else currentTime = TimeCurrent();
まずはdateTime型のローカル変数「currentTime」を宣言します。
その次の記述である↓についてですが、
if(useLocalTime == true)
「useLocalTime」は IsActive関数の第3仮引数であり、メインプログラムでローカル時間を使うかどうかを設定するのに使います。※当記事の「第3仮引数「useLocalTime」にはローカル時間を基準として使うかが格納される想定」セクション参照
従ってuseLocalTime == trueを満たしている場合には、ローカル時間を基準にトレード時間の判定をするとして、ローカル変数「currentTime」にはTimeLocal関数の戻り値を代入します。
逆にuseLocalTime == trueを満たしていない場合には、サーバー時間を基準にトレード時間の判定をするとして、ローカル変数「currentTime」にはTimeCurrent関数の戻り値を代入します。
処理実装記述3:現在時刻が開始時間と終了時間の範囲内かどうかをチェック
最後に現在時刻が開始時間と終了時間の範囲内かどうかをチェック
そして最終的に、現在時刻が現在時刻が開始時間と終了時間の範囲内かどうかの判定結果を戻り値として返します。
// 現在時刻が開始時間と終了時間の範囲内かどうかをチェック
bool timerOn = false; // タイマーがオンかどうかのフラグを初期化
if(currentTime >= openTime && currentTime < closeTime)
{
// 現在時刻が指定された時間範囲内にある場合、タイマーをオンにする
timerOn = true;
}
return timerOn; // タイマーの状態を返す
この「timerOn」に格納された値を戻り値として返します。初期値はfalseと設定しておきます。
その下の、
if(currentTime >= openTime && currentTime < closeTime)
は日本語訳すれば「現在時刻がトレード開始時間よりも時系列的に後であり、かつ、現在時刻がトレード終了時間よりも時系列的に前である」と読み解けます。
日本語訳すると、何とも当たり前すぎる感じですが、この条件を満たすときがトレードが許可されている時間という事になります。
trueあるいはfalsseの値を格納した「timerOn」の値をreturnで返して終了です。
IsActive関数の処理実装記述も以上となります。
完成したIsActive関数を使ってみる
それでは、完成したIsActive関数をメインプログラムで使い、トレード時間のチェックができるか見ていきましょう。
使い方の1例となるサンプルコードは以下の通りです。
//+------------------------------------------------------------------+
//| IsActive_Test.mq5 |
//| |
//| |
//+------------------------------------------------------------------+
// OriginalTimer.mqhファイルをインクルード
#include <OriginalTimer.mqh>
// CTradeSessionクラスのインスタンスを生成
CTradeSession TradeTimer;
// 取引開始時間を設定
input datetime TradeStart=D'2024.03.15 08:00';
// 取引終了時間を設定
input datetime TradeEnd=D'2024.03.19 20:00';
// 新しいティックデータが来たときに実行される関数
void OnTick()
{
// 現在の時刻が取引時間内かどうかをチェック
bool timerOn=TradeTimer.IsActive(TradeStart,TradeEnd);
// 取引時間内であれば
if(timerOn==true)
{
// 注文を発注する処理をここに書く
}
}
OriginalTimer.mqhファイルをインクルードする
まずはメインプログラムで今回作ったクラスを使えるようにするためにinclude命令でOriginalTimer.mqhファイルを読み込みます。
// OriginalTimer.mqhファイルをインクルード
#include <OriginalTimer.mqh>
※include命令については↓の記事をご参照ください
MQL5 EA講座 第56回「#include命令(#include directive)」
CTradeSessionクラスのインスタンスを宣言
今回作ったCTradeSessionクラスのインスタンスを宣言します。
インスタンス名は「TradeTimer」としました。
// CTradeSessionクラスのインスタンスを宣言する
CTradeSession TradeTimer;
※インスタンスについての詳細は↓の記事をご参照ください
取引時間を設定するためのinput変数を宣言
続いてグローバル領域に取引時間を設定するためのinput変数を宣言します
// 取引開始時間を設定
input datetime TradeStart=D'2024.03.15 08:00';
// 取引終了時間を設定
input datetime TradeEnd=D'2024.03.19 20:00';
取引開始時間を設定するinput変数「TradeStart」と取引終了時間を設定する「TradeEnd」、2つのinput変数を宣言しました。日時情報なのでデータ型はdateTime型となります。
※input変数については↓の記事をご参照ください
※dateTime型については↓の記事群をご参照ください
・MQL5 EA講座 第16回「データ型その6 datetime型」
・MQL5 EA講座 第116回「DateTime型を使って、日時データを操作する時の基本的な考え方」
OnTick関数内の記述をおこなう
グローバル領域での記述が終わったので、今度はOnTick関数内の記述をおこないます
// 新しいティックデータが来たときに実行される関数
void OnTick()
{
// 現在の時刻が取引時間内かどうかをチェック
bool timerOn=TradeTimer.IsActive(TradeStart,TradeEnd);
// 取引時間内であれば
if(timerOn==true)
{
// 注文を発注する処理をここに書く
}
}
まずbool型のローカル変数「 timerOn」を宣言します。
そしてCTradeSessionクラスのインスタンス「TradeTimer」からメンバ関数であるIsActive関数を呼び出します。
IsActive関数の第1引数にはトレード開始時間情報が格納される想定なので、input変数「TradeStart」を記述します。
第2引数にはトレード終了時間情報が格納される想定なので、input変数「TradeEnd」を記述します。
ローカル変数「 timerOn」はIsActive関数の戻り値を受け取る形となります。
if(timerOn==true)
の条件を満たしている場合、現在時刻が事前に設定したトレード時間内にある事を意味しているので{}内に発注記述を書いてトレード開始となります。
まとめ
今回はトレードタイマークラスに実装するメンバ関数の第1弾として、「トレード時間かどうかをチェックする」メンバ関数=IsActive関数を実装する様子を解説しました。
まずは、そもそもトレードタイマーとは何か?という事を解説し、IsActive関数を実装するまでの時系列的な手順について説明しました。詳しくは
「トレードタイマーとは?」セクション及び
「「トレード時間かどうかをチェックする」メンバ関数実装へのロードマップ」セクションをご確認ください。
そして、ロードマップに示した手順に従い、「OriginalTimer.mqh」ファイルへトレードタイマークラス(=CTradeSessionクラス)を宣言~IsActive関数の仮引数設定~処理実装記述までを解説していきました。
詳しくは当記事の下記セクションをご確認ください↓
・「OriginalTimer.mqh」ファイルに「トレードタイマークラス」を宣言する
そして、最後に実装を終えたIsActive関数をメインプログラムでどのように使っていくのか、という記述例の紹介をしました。詳しくは当記事の↓
「完成したIsActive関数を使ってみる」セクションをご覧ください
現時点での「OriginalTimer.mqh」ファイルに記述した内容は以下の通りです↓
//+------------------------------------------------------------------+
//| OriginalTimer.mqh |
//| |
//| |
//+------------------------------------------------------------------+
#define MINUTES_TO_SECONDS 60 // 1分を秒に変換する定数(60秒)
#define HOURS_TO_SECONDS 3600 // 1時間を秒に変換する定数(3600秒)
#define DAYS_TO_SECONDS 86400 // 1日を秒に変換する定数(86400秒)
#define WEEKS_TO_SECONDS 604800 // 1週間を秒に変換する定数(604800秒)
class OriginalCNewBar
{
private:
datetime TimeArray[], //現在の時間を格納
LastTimeVar;//直近にチェックした時間
public:
void OriginalCNewBar();//配列「TimeArray」に時系列セットを施すコンストラクタ
//新しいバーがオープンした瞬間かどうかをチェックする
bool CheckNewBar(string parSymbol, ENUM_TIMEFRAMES parTimeframe);
};
//+------------------------------------------------------------------+
//コンストラクタOriginalCNewBarの処理実装記述
void OriginalCNewBar::OriginalCNewBar(void)
{
ArraySetAsSeries(TimeArray,true);//配列「TimeArray」を時系列にセットする
}
// 新しいバーがオープンしたかどうかをチェックするメンバ関数の実装
bool OriginalCNewBar::CheckNewBar(string parSymbol, ENUM_TIMEFRAMES parTimeframe)
{
// 初回実行かどうかを判定するフラグ
bool firstExe = false;
// 新しいバーがオープンしたかどうかを示すフラグ
bool newBarOpen = false;
// 指定されたシンボルと時間枠の現在と一つ前のバーの時間をTimeArrayにコピー
CopyTime(parSymbol, parTimeframe, 0, 2, TimeArray);
// LastTimeVarが0の場合、これが初回実行と判断
if(LastTimeVar == 0) firstExe = true;
// TimeArrayの最新の時間がLastTimeVarより大きい場合、新しいバーと判断
if(TimeArray[0] > LastTimeVar)
{
// 初回実行ではない場合、newBarフラグをtrueに設定
if(firstExe == false) newBarOpen = true;
// 最後にチェックしたバーのオープン時間を更新
LastTimeVar = TimeArray[0];
}
// 新しいバーの有無を返す
return(newBarOpen);
}
// 日付時間値を生成する関数
datetime GenerateTimestamp(int newHour = 0, int newMinute = 0)
{
MqlDateTime dateTimeDetails; // MqlDateTime構造体を使用して日時データを保存するための変数
TimeToStruct(TimeCurrent(), dateTimeDetails); // 現在のサーバーの時間を取得し、dateTimeDetails構造体に代入
dateTimeDetails.hour = newHour; // 時間を新しい値に設定
dateTimeDetails.min = newMinute; // 分を新しい値に設定
datetime constructedTime = StructToTime(dateTimeDetails); // MqlDateTime構造体値をdatetime値に変換
return(constructedTime); // 変換したdatetime値を返す
}
class CTradeSession
{
public:
// 取引セッションがアクティブかどうかをチェックする関数
bool IsActive(datetime openTime, datetime closeTime, bool useLocalTime = false);
};
// 取引セッションがアクティブかどうかをチェックする関数の実装
bool CTradeSession::IsActive(datetime openTime, datetime closeTime, bool useLocalTime = false)
{
// 開始時間が終了時間より後であるかどうかを確認
if (openTime >= closeTime)
{
// 開始時間と終了時間が不正である場合、警告メッセージを出力して処理を中止
Alert("エラー:開始時間または終了時間が不正です");
return false; // 不正な時間設定のため、falseを返す
}
// 現在時刻を取得する
datetime currentTime;
//ローカル時間を使うかどうかのチェック
if(useLocalTime == true) currentTime = TimeLocal();
else currentTime = TimeCurrent();
// 現在時刻が開始時間と終了時間の範囲内かどうかをチェック
bool timerOn = false; // タイマーがオンかどうかのフラグを初期化
if(currentTime >= openTime && currentTime < closeTime)
{
// 現在時刻が指定された時間範囲内にある場合、タイマーをオンにする
timerOn = true;
}
return timerOn; // タイマーの状態を返す
}
CTradeSessionクラスには、次回以降もメンバを追加していきます。
日時情報の管理・操作がより柔軟かつ簡単にできるようになっていきますので、是非次回以降も楽しみにお待ちください。
今回は以上とさせていただきます。
最後までお読みいただきありがとうございました。
<参照>
TimeCurrent関数/TimeToString関数/UNIXタイム/タイムスタンプ/datetime型/iTime関数/StringToTime関数/ENUM_TIMEFRAMES/TimeToStruct関数/GetTickCount関数/MqlDateTime構造体/タイムゾーン/TimeDaylightSavings関数/TimeGMT関数/TimeGMTOffset関数/TimeLocal関数/夏時間