前回はMqlDateTime構造体に関して解説をしました。
MqlDateTime構造体が日付と時刻に関連する情報を格納する構造体である、という事をまず説明し、MqlDateTime構造体の各メンバがどのような役割をもっているのかを説明しました。
詳細は前回の
「MqlDateTime構造体とは?」セクション及び、
「MqlDateTime構造体のメンバ構成について」セクションをご覧ください。
そして具体的にMqlDateTime構造体をどうやって使っていくのか、という部分をサンプルコードを見ながら解説を加えました。
その過程で、TimeCurrent関数、TimeToStruct関数、StructToTime関数といった初出の関数についても紹介しました
※詳細は前回の「MqlDateTime構造体の使い方」セクションをご覧ください。
そして、MqlDateTime構造体の各メンバに、無効な値を代入した場合、どのような結果が生じるのか、という点について補足させて頂きました。
※詳細は前回の「MqlDateTime構造体に値を代入しなかった場合、または無効な値を代入した場合」セクションをご覧ください。
はじめに:「特定の日時を簡単に生成できる関数」について
今回は、「特定の日時を簡単に生成できる関数」を実装する手順を解説していきます。
講座記事第115回「チャート上に新しいバーが生成される瞬間にのみ注文できるクラスを作る」
にて「OriginalTimer.mqh」ファイルというインクルードファイルを生成しました。
この「OriginalTimer.mqh」ファイルの中に「OriginalCNewBarクラス」という、チャート上に新しいバーが生成される瞬間にのみ注文できるクラスを実装するプロセスを解説してきたのが、第115回の講義内容だったわけですが、今回の「特定の日時を簡単に生成できる関数」もこの「OriginalTimer.mqh」ファイルの中に追加していく形をとります。
「特定の日時を簡単に生成できる関数」の実装が完成すれば、メインプログラムのEAで任意の日時や時間帯だけ「トレードをする」あるいは「トレードをしない」といった機能も簡単に追加できるようになります。
これまでの講座で、チャートの価格関係や、インジケータ値を使ってトレード条件を考え出す方法はお伝えしてきましたが、今回の講座内容はトレード条件に「時間の概念」も落とし込めるようになる事を意味します。
この事は、EA開発において大きな武器になる事は間違いありません。
是非今回も楽しんで読んでいただければと思います。
「特定の日時を簡単に生成できる関数」実装へのロードマップ
「特定の日時を簡単に生成できる関数」は以下の手順を踏んで実装していきます。
・「OriginalTimer.mqh」ファイルの中に関数の宣言をする
一つ一つ順を追って見ていきましょう
「OriginalTimer.mqh」ファイルの中に関数の宣言をする
まずは第115回で作った「OriginalTimer.mqh」ファイルのグローバル領域(関数の外の領域)に、今回作る関数を宣言します。
// 日付時間値を生成する関数
datetime GenerateTimestamp(int newHour = 0, int newMinute = 0)
{
}
今回はクラスのメンバとしてではなく、単体で使える独立関数として宣言します。
関数名を「GenerateTimestamp関数」としました。
関数の仮引数と戻り値の設定を行う
続いてGenerateTimestamp関数の仮引数と戻り値の設定を行います
// 日付時間値を生成する関数
datetime GenerateTimestamp(int newHour = 0, int newMinute = 0)
{
}
GenerateTimestamp関数は、特定の時間値を生成する事を目的とした関数なので、戻り値は必然的にdateTime型となります。
仮引数はint型のものを2つ用意しました。第1仮引数名を「newHour」、第2仮引数名を「newMinute」としました。どちらも初期値を0にしてあります。
どのような使われ方をするのか、という点に関してはこの後の「関数の処理実装記述を行う」セクションで説明しますが、第1仮引数「newHour」はMqlDateTime構造体型インスタンスのメンバである「hour」に値を代入する事を想定しています。
第2仮引数「newMinute」は同じくMqlDateTime構造体型インスタンスのメンバである「min」に値を代入する事を想定しています。
関数の処理実装記述を行う
GenerateTimestamp関数の処理実装記述は以下のようになっています
// 日付時間値を生成する関数
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値を返す
}
1つ1つ順を追って見ていきましょう
MqlDateTime型のインスタンスを宣言する
まずはMqlDateTime型のインスタンスを宣言します
MqlDateTime dateTimeDetails;
// MqlDateTime構造体を使用して日時データを保存するための変数
インスタンス名を「dateTimeDetails」としました。
MqlDateTime構造体は、日時情報を分解した形で、各メンバに格納できる構造体です。
詳しくは前回の講座記事をご参照ください↓
※インスタンスについては↓の記事をご参照ください
※構造体については↓の記事をご参照ください
TimeToStruct関数を利用して、MqlDateTime構造体型のインスタンスに日時情報を格納する
続いてTimeToStruct関数を利用して、MqlDateTime構造体型のインスタンスに日時情報を格納します
TimeToStruct(TimeCurrent(), dateTimeDetails);
// 現在のサーバーの時間を取得し、dateTimeDetails構造体に代入
TimeToStruct関数は第1引数に記述したdateTime型の日時情報データを、第2引数に記述したMqlDateTime構造体型インスタンスに変換・格納する関数です。
第1引数にはTimeCurrent関数を記述します。
TimeCurrent関数は現在のサーバー時間を取得してくれる関数です。
第2引数には先程作ったMqlDateTime構造体型インスタンスである「dateTimeDetails」を記述します。
これで、サーバーの現在時間がインスタンス「dateTimeDetails」の各メンバに分解して受け渡されたことになります。
※TimeToStruct関数についての詳細は↓の記事をご参照ください
※TimeCurrent関数についての詳細は↓の記事をご参照ください
MqlDateTime型インスタンスのメンバ「hour」「min」に新しい値を代入する
続いて、MqlDateTime型インスタンスのメンバ「hour」「min」に新しい値を代入します。
dateTimeDetails.hour = newHour; // 時間を新しい値に設定
dateTimeDetails.min = newMinute; // 分を新しい値に設定
インスタンス「dateTimeDetails」のメンバ「hour」には、仮引数「newHour」を、メンバ「min」には仮引数「newMinute」をそれぞれ代入します。
仮引数「newHour」「newMinute」は、「関数の仮引数と戻り値の設定を行う」セクションで作ったもので、メインプログラムでEAを作る時に具体的な値を記述されることを想定したものです。
先程「TimeToStruct関数を利用して、MqlDateTime構造体型のインスタンスに日時情報を格納する」セクションで解説した通り、メンバ「hour」とメンバ「min」には、TimeCurrent関数によって取得した「時間」情報と「分」情報がそれぞれint型の整数値で本来格納されています。
(例えばサーバーの現在時間が「2024.01.01 12:30:00」だった場合、メンバ「hour」には「12」、メンバ「min」には「30」が格納されています)
ただ、GenerateTimestamp関数では「時間」情報と「分」情報は、自由に指定できる挙動にしたいので、順次進行の原則に従って、仮引数「newHour」「newMinute」で上書きした形です。
StructToTime関数を使って、MqlDateTime型の情報をdatetime型に戻す
最後はStructToTime関数を使って、MqlDateTime型の情報をdateTime型に戻します。
datetime constructedTime = StructToTime(dateTimeDetails); // MqlDateTime構造体値をdatetime値に変換
return(constructedTime); // 変換したdatetime値を返す
インスタンス「dateTimeDetails」には、
サーバーの現在時間+メンバ「hour」とメンバ「min」には任意に指定した値
が入っているので、これをdateTime型に戻します。
StructToTime関数は引数に指定したMqlDateTime型データ値をdateTime型に変換してくれる関数です。
※StructToTime関数の詳細についての詳細は↓の記事をご参照ください
StructToTime関数を使って変換した値をdateTime型の 変数「constructedTime」に代入します。
最後に変数「constructedTime」を戻り値としてreturnで返し、GenerateTimestamp関数の処理実装記述は終了です。
完成したGenerateTimestamp関数を使ってみる
GenerateTimestamp関数が完成し、これで日時情報を「ある程度」自在に加工・操作できるようになりました。「ある程度」と書いたのは、現時点ではまだ不十分な点があるからですが、それはひとまず置いておいて実際にこのGenerateTimestamp関数をメインプログラムでどうやって使っていけばよいか?という点を今度はお話していきます。
トレードの開始時間と終了時間を設定する。
例えば「22時~翌8時までの間だけトレードする」という取引戦略を立てたとします。
言い換えれば「8時~22時の間はトレードをしない」という事になる訳ですが、これを実現させるにはどうしたらよいでしょうか?
早速先程作ったGenerateTimestamp関数を使って、開始時間と終了時間の日時データを作ってみます
// 開始時間を22:00に設定
datetime startTime = GenerateTimestamp(22,0);
// 終了時間を08:00に設定
datetime endTime = GenerateTimestamp(8,0);
「startTime」と「endTime」という2つのdateTime型の変数をつくり、GenerateTimestamp関数の戻り値を代入させています。
「startTime」には現在日時+22時「endTime」には現在日時+8時という日時情報が格納されています。
勘の良い方なら既にお気づきかもしれませんが、このままだと問題があります。
代入した「startTime」と「endTime」の値をログ出力させてみましょう
#include <OriginalTimer.mqh>
void OnStart()
{
// 開始時間を22:00に設定
datetime startTime = GenerateTimestamp(22,0);
// 終了時間を08:00に設定
datetime endTime = GenerateTimestamp(8,0);
// 開始時間をログに出力
Print("開始時間: ", TimeToString(startTime, TIME_DATE|TIME_MINUTES));
// 終了時間をログに出力
Print("終了時間: ", TimeToString(endTime, TIME_DATE|TIME_MINUTES));
}
出力結果は今のようになります
//上記サンプルコードの出力結果
開始時間: 2024.03.20 22:00
終了時間: 2024.03.20 08:00
今回やりたかったことは「22時にトレードを開始して翌朝の8時にトレードを終了させる」という戦略を実現させる為に、その値を取得する事でした。
開始時間が2024.03.20 22:00であるのならば、終了時間は2024.03.21 08:00になっているべきですが、そうはなっていません。
結果が開始時間よりも終了時間の方が時系列的に早い、という矛盾が生じてしまっており、このままEA開発を進めても想定通りの挙動を得られません。
じゃあ、どうすればよいのでしょうか?
ここで講座記事第116回で解説した知識を用います
※なおPrint関数内に記述されているTimeToString関数は、時刻を表すdatetime型の数字情報(=UNIXタイム)を、私たちが普段使う日時の形式の文字列に変換する関数です。
※TimeToString関数についての詳細は↓の記事をご参照ください
「1分」「1時間」「1日」「1週間」に相当する経過秒数をdefine命令により定数として設定する
前提の確認をしておきましょう。
トレードを開始したい時間が「03.20 22:00」だとすると、トレードを終了したい時間は翌日の8時→「03.21 08:00」という時間を取得したい訳です
講座記事第116回では、1日→24時間は86400秒に相当する事をお伝えしました。
つまり、変数「endTime」に「86400」という値を足すことによって、「03.21 08:00」という値は簡単に求まる事になります。
しかし、24時間=「86400秒」という値を覚えておく、というのもいかにもスマートではありませんし、汎用性もありません。
そこで「OriginalTimer.mqh」ファイルの冒頭に、define命令によって、「1分」「1時間」「1日」「1週間」に相当する経過秒数を定数として定義してしまいましょう。
#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秒)
「OriginalTimer.mqh」ファイルに上記の定数を加える事によって、「OriginalTimer.mqh」ファイルをインクルードしたメインプログラムでは、いきなりこれらの定数が使えるようになります。
例えば先程使ったサンプルコードの変数「endTime」に以下のような形で・・・
endTime+ =DAYS_TO_SECONDS;
とすることで、本来求めたかった「03.21 08:00」という日時情報を取得する事ができるようになりました。
#include <OriginalTimer.mqh>
void OnStart()
{
// 開始時間を22:00に設定
datetime startTime = GenerateTimestamp(22,0);
// 終了時間を08:00に設定
datetime endTime = GenerateTimestamp(8,0);
// 1日を秒に変換する定数(86400秒)を足す
endTime+=DAYS_TO_SECONDS;
// 開始時間をログに出力
Print("開始時間: ", TimeToString(startTime, TIME_DATE|TIME_MINUTES));
// 終了時間をログに出力
Print("終了時間: ", TimeToString(endTime, TIME_DATE|TIME_MINUTES));
}
まとめ
今回は「特定の日時を簡単に生成できる関数」=GenerateTimestamp関数を実装する手順について解説しました。
※GenerateTimestamp関数の実装手順に関しては、当記事の下記セクション↓
・「OriginalTimer.mqh」ファイルの中に関数の宣言をする
の内部リンクから復習したいところに飛んで確認をして頂ければと思います。
そして完成したGenerateTimestamp関数をメインプログラムの中でどのように使うのか、という点についても解説を行い、その過程で「OriginalTimer.mqh」ファイルに、「1分」「1時間」「1日」「1週間」に相当する経過秒数を定数にして追加しました。
詳細については当記事の
「完成したGenerateTimestamp関数を使ってみる」セクション及び
「「1分」「1時間」「1日」「1週間」に相当する経過秒数をdefine命令により定数として設定する」セクションをご参照ください。
現時点での「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値を返す
}
今回は以上とさせていただきます。
次回はさらに、日時情報を細かく、かつ簡単に扱えるようにするためにトレードタイマークラスを作成していきます。
最後までお読みいただきありがとうございました。
MQL5 EA講座 第117回「MqlDateTime構造体について」←
→MQL5 EA講座 第119回「トレードタイマークラスを実装する-その1-」
<参照>
TimeToString関数/StringToTime関数/UNIXタイム/タイムスタンプ/datetime型/iTime関数/ENUM_TIMEFRAMES/TimeToStruct関数/StructToTime関数/構造体/MqlDateTime構造体