【超入門】MQL5 EA講座 第123回「トレードタイマークラスを実装する-その5-」【EAの作り方】

MQL5でEA作ろう講座

前回トレードタイマークラスに実装するメンバ関数の第4弾として、「複数の取引時間帯を設定する関数」=SegmentTimer関数を実装しました。

まずは、「複数の時間帯を設定する」という事の根本的な意味と、それによってEA開発にもたらされるメリットについて簡単に説明をしました。

※詳細は前回記事の「はじめに:「複数の取引時間帯を設定する関数」とは?」セクションをご覧ください。

そして、「 複数の取引時間帯を設定する関数」を実装するまでのロードマップ」に示した手順に従って、

OriginalTimer.mqh」ファイル内にTimerSegment構造体を作り、CTradeSessionクラスにはSegmentTimer関数メンバとして追加しました。

その後は、SegmentTimer関数引数設定~処理実装記述までを解説しました。

詳しくは前回記事の下記セクションをご確認ください。

取引時間帯に関する情報を格納する構造体を宣言する

「CTradeSession」クラス内に新しいメンバを加える

SegmentTimer関数に必要な仮引数を設定する

SegmentTimer関数に処理実装記述をおこなう

そして、最後に実装を終えたSegmentTimer関数をメインプログラムでどのように使っていくのか、という記述例の紹介をしました。詳しくは当記事の↓

完成したSegmentTimer関数をメインプログラムで使う」セクションをご覧ください。

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

はじめに:「開始時間と終了時間を確認する関数」とは?

講座記事第119回から取り扱ってきたトレードタイマークラスも今回でようやく最終回です。

今回は最後に2つメンバ関数を加えます。

今回追加するのは「開始時間を確認する関数」と「終了時間を確認する関数」です。

トレードタイマークラスであるCTradeSessionクラスにおいて、トレード開始時間が格納されているのはアクセスレベルprivateメンバであるSessionStartTimeです。

トレード終了時間が格納されているのは同じくアクセスレベルprivateメンバであるSessionEndTimeです。

※詳しい事はMQL5 EA講座 第120回「トレードタイマークラスを実装する-その2-」をご覧ください。

このSessionStartTimeSessionEndTimeを使って前回作った「複数の取引時間帯を設定する関数」であるSegmentTimer関数 も作られています。

今回やろうとしている事は、開始時間=SessionStartTime、終了時間=SessionEndTimeの値を取得するだけの関数を追加する、と言いう事になります。

ここまで書いてきたことで予想がつく方もいるかもしれませんが、今回追加するメンバ関数の実装は拍子抜けするほど簡単です。順を追って見ていきましょう。

「開始時間を確認する関数」と「終了時間を確認する関数」を実装する

前回までのCTradeSessionクラスメンバ構成は以下のようになっていました。

class CTradeSession
  {
private:
   //トレード開始時間と終了時間を格納する
   datetime          SessionStartTime, SessionEndTime;
   //トレードタイマーのオンオフ状態を格納
   bool timerStartedFlag ;
   //トレードタイマーのオンオフ状態をチャートとエキスパートログに表示する関数
   void              DisplayTimerStatus(bool parTimerState);

public:
   // 取引セッションがアクティブかどうかをチェックする関数
   bool              IsActive(datetime openTime, datetime closeTime, bool useLocalTime = false);
   //毎日のトレードタイマーを作成する関数
   bool              DailySessionTimer(int parStartHour, int parStartMinute, int parEndHour, int parEndMinute, bool parLocalTime = false);
   // 複数の取引時間帯を設定する関数
   bool              SegmentTimer(TimerSegment &parSegment[], bool parLocalTime=false);
  };

ここから、アクセスレベル=publicの領域に2つのメンバ関数を追加して以下のように変わります。

class CTradeSession
  {
private:
   //トレード開始時間と終了時間を格納する
   datetime          SessionStartTime, SessionEndTime;
   //トレードタイマーのオンオフ状態を格納
   bool timerStartedFlag ;
   //トレードタイマーのオンオフ状態をチャートとエキスパートログに表示する関数
   void              DisplayTimerStatus(bool parTimerState);

public:
   // 取引セッションがアクティブかどうかをチェックする関数
   bool              IsActive(datetime openTime, datetime closeTime, bool useLocalTime = false);
   //毎日のトレードタイマーを作成する関数
   bool              DailySessionTimer(int parStartHour, int parStartMinute, int parEndHour, int parEndMinute, bool parLocalTime = false);
   // 複数の取引時間帯を設定する関数
   bool              SegmentTimer(TimerSegment &parSegment[], bool parLocalTime=false);
   //取引開始時間を確認する関数
   datetime          GetSessionStartTime() {return(SessionStartTime);};
   //取引終了時間を確認する関数
   datetime          GetSessionEndTime() {return(SessionEndTime);};

  };

「取引開始時間を確認する関数」の関数名を「GetSessionStartTime」とし、「終了時間を確認する関数」の関数名を「GetSessionEndTime」としました。

GetSessionStartTime関数について

 //取引開始時間を確認する関数
   datetime          GetSessionStartTime() {return(SessionStartTime);};

GetSessionStartTime関数戻り値は、「開始時間」という日時情報を返すので必然的にdateTime型になります。引数は設定しません。

普段であれば処理実装記述をグローバル領域に別途記述していくのですが、今回の場合「開始時間」情報というのは既にアクセスレベル=privateメンバであるSessionStartTimeに格納済みなので、SessionStartTimeの値をreturnで返すだけです。

従って、処理実装記述はメンバ関数宣言と同時に行ってしまいます。

メンバ関数宣言と同時に処理実装記述も行うような記述を、インライン定義と言います。

インライン定義についての詳細は↓の記事リンクをご参照ください

GetSessionEndTime関数について

//取引終了時間を確認する関数
   datetime          GetSessionEndTime() {return(SessionEndTime);};

GetSessionEndTime関数戻り値も、「終了時間」という日時情報を返すのでデータ型dateTime型になります。引数は設定しません。

「終了時間」情報は既にアクセスレベル=privateメンバであるSessionEndTimeに格納済みなので、SessionEndTimeの値をreturnで返すだけです。

GetSessionEndTime関数の処理実装記述もメンバ関数宣言と同時にインライン定義で済ませて終了です。

これまでに「OriginalTimer.mqh」ファイルに組み込んできたクラス、関数についてのまとめ

今回でトレードタイマークラスCTradeSessionクラスの実装については終了です。

皆様お疲れさまでした。

CTradeSessionクラスを含めた、日時情報を取り扱うその他のクラス関数構造体定数をまとめた「OriginalTimer.mqh」ファイルの作成は講座記事第115回から取り扱ってきたものになります。

これまでの講座を通して「OriginalTimer.mqh」ファイルにどんなクラス関数・その他の要素を盛り込んできたのか、忘れてしまったり混乱している方もいらっしゃるかと思うので、まとめてみました。

※ファイルの上から順番に説明していきます。

「1分」「1時間」「1日」「1週間」に相当する定数群

ファイルの冒頭に、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秒)

※詳細は講座記事第118回

「1分」「1時間」「1日」「1週間」に相当する経過秒数をdefine命令により定数として設定する

セクションをご覧ください。

TimerSegment構造体

TimerSegment構造体は複数の取引時間帯を設定する為の情報を格納する構造体です

//タイマーセグメントを管理するための構造体
struct TimerSegment
  {
   bool              enabled; // タイマーセグメントが有効かどうかを示すフラグ
   int               start_day; // タイマー開始日(0=日曜、1=月曜、...、6=土曜)
   int               start_hour; // タイマー開始時(0~23の間で指定)
   int               start_min; // タイマー開始分(0~59の間で指定)
   int               end_day; // タイマー終了日(0=日曜、1=月曜、...、6=土曜)
   int               end_hour; // タイマー終了時(0~23の間で指定)
   int               end_min; // タイマー終了分(0~59の間で指定)
  };

※詳細は講座記事第122回の、

取引時間帯に関する情報を格納する構造体を宣言する」セクションをご覧ください。

OriginalCNewBarクラス

class OriginalCNewBar
  {
private:
   datetime TimeArray[], //現在の時間を格納
            LastTimeVar;//直近にチェックした時間

public:

   void              OriginalCNewBar();//配列「TimeArray」に時系列セットを施すコンストラクタ
   //新しいバーがオープンした瞬間かどうかをチェックする
   bool              CheckNewBar(string parSymbol, ENUM_TIMEFRAMES parTimeframe);
  };

OriginalCNewBarクラス「チャート上に新しいバーが生成される瞬間にのみ処理を行える」

事を目的としたクラスです。

OriginalCNewBarクラスについては第115回「チャート上に新しいバーが生成される瞬間にのみ注文できるクラスを作る」をご参照ください。

現在の時間を格納する配列TimeArray[]、及び直近にチェックした時間を格納する変数LastTimeVarについては同115回の「アクセスレベル=privateのメンバを追加する」セクションをご覧ください。

OriginalCNewBarクラスコンストラクタであるOriginalCNewBar関数については同115回の「OriginalCNewBar関数について」セクションをご覧ください。

新しいバーがオープンした瞬間かどうかをチェックするメンバ関数であるCheckNewBar関数については同115回の「CheckNewBar関数について」セクションをご覧ください。

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値を返す
  }

GenerateTimestamp関数「特定の日時を生成する」事を目的に作られた独立関数です。

GenerateTimestamp関数については第118回「特定の日時を簡単に生成できる関数を実装する」をご参照ください

※また、GenerateTimestamp関数内で使われているMqlDateTime構造体については第117回「MqlDateTime構造体について」をご参照ください。

CTradeSessionクラス

class CTradeSession
  {
private:
   //トレード開始時間と終了時間を格納する
   datetime          SessionStartTime, SessionEndTime;
   //トレードタイマーのオンオフ状態を格納
   bool timerStartedFlag ;
   //トレードタイマーのオンオフ状態をチャートとエキスパートログに表示する関数
   void              DisplayTimerStatus(bool parTimerState);

public:
   // 取引セッションがアクティブかどうかをチェックする関数
   bool              IsActive(datetime openTime, datetime closeTime, bool useLocalTime = false);
   //毎日のトレードタイマーを作成する関数
   bool              DailySessionTimer(int parStartHour, int parStartMinute, int parEndHour, int parEndMinute, bool parLocalTime = false);
   // 複数の取引時間帯を設定する関数
   bool              SegmentTimer(TimerSegment &parSegment[], bool parLocalTime=false);
   //取引開始時間を確認する関数
   datetime          GetSessionStartTime() {return(SessionStartTime);};
   //取引終了時間を確認する関数
   datetime          GetSessionEndTime() {return(SessionEndTime);};

  };

CTradeSessionクラスは、日時情報を管理するためのトレードタイマー機能をまとめたクラスになります。

CTradeSessionクラスについては今回も含め、

の5回に分けて、実装の様子をお届けしました。

トレード開始時間を格納するSessionStartTimeに関しては第120回の「メンバ「SessionStartTime」について」セクションを、トレード終了時間を格納するSessionEndTimeに関しては「メンバ「SessionEndTime」について」セクションをそれぞれご確認ください。

トレードタイマーのオンオフ状態を格納するメンバ変数」であるtimerStartedFlagについては第121回の「メンバ「timerStartedFlag」について」セクションをご覧ください。

取引セッションがアクティブかどうかをチェックするメンバ関数」であるIsActive関数については第119回の「アクセスレベル=publicのメンバを追加する」セクションをご覧ください。

毎日のトレードタイマーを作成するメンバ関数」であるDailySessionTimer関数については第120回の「DailySessionTimer関数について」セクションをご覧ください。

複数の取引時間帯を設定するメンバ関数」であるSegmentTimer関数については第122回全体にわたって解説しているのでご確認頂ければと思います。

そして、GetSessionStartTime関数GetSessionEndTime関数については、今回の記事を改めてご確認をお願いします。

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秒)

//タイマーセグメントを管理するための構造体
struct TimerSegment
  {
   bool              enabled; // タイマーセグメントが有効かどうかを示すフラグ
   int               start_day; // タイマー開始日(0=日曜、1=月曜、...、6=土曜)
   int               start_hour; // タイマー開始時(0~23の間で指定)
   int               start_min; // タイマー開始分(0~59の間で指定)
   int               end_day; // タイマー終了日(0=日曜、1=月曜、...、6=土曜)
   int               end_hour; // タイマー終了時(0~23の間で指定)
   int               end_min; // タイマー終了分(0~59の間で指定)
  };




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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
  {
private:
   //トレード開始時間と終了時間を格納する
   datetime          SessionStartTime, SessionEndTime;
   //トレードタイマーのオンオフ状態を格納
   bool timerStartedFlag ;
   //トレードタイマーのオンオフ状態をチャートとエキスパートログに表示する関数
   void              DisplayTimerStatus(bool parTimerState);

public:
   // 取引セッションがアクティブかどうかをチェックする関数
   bool              IsActive(datetime openTime, datetime closeTime, bool useLocalTime = false);
   //毎日のトレードタイマーを作成する関数
   bool              DailySessionTimer(int parStartHour, int parStartMinute, int parEndHour, int parEndMinute, bool parLocalTime = false);
   // 複数の取引時間帯を設定する関数
   bool              SegmentTimer(TimerSegment &parSegment[], bool parLocalTime=false);
   //取引開始時間を確認する関数
   datetime          GetSessionStartTime() {return(SessionStartTime);};
   //取引終了時間を確認する関数
   datetime          GetSessionEndTime() {return(SessionEndTime);};

  };


// SegmentTimer
bool CTradeSession::SegmentTimer(TimerSegment &parSegment[], bool parLocalTime=false)
  {
   MqlDateTime today; // 現在の日時を格納するための変数
   bool timerOn = false; // タイマーがオンかどうかを保持するフラグ
   int timerCount = ArraySize(parSegment); // 渡されたタイマーセグメントの配列サイズを取得

   for(int i = 0; i < timerCount; i++) // 各タイマーセグメントについてループ
     {
      if(parSegment[i].enabled == false)
         continue; // タイマーセグメントが有効でなければ、次のループへ

      SessionStartTime = GenerateTimestamp(parSegment[i].start_hour, parSegment[i].start_min); // 開始時間を設定
      SessionEndTime = GenerateTimestamp(parSegment[i].end_hour, parSegment[i].end_min); // 終了時間を設定

      TimeToStruct(SessionStartTime,today); // 開始時間を構造体に変換
      int dayShift = parSegment[i].start_day - today.day_of_week;
      // 開始曜日と現在の曜日との差を計算
      if(dayShift != 0)
         SessionStartTime += DAYS_TO_SECONDS * dayShift;
      // 開始曜日が今日と異なれば、適切な曜日に調整

      TimeToStruct(SessionEndTime,today); // 終了時間を構造体に変換
      dayShift = parSegment[i].end_day - today.day_of_week;
      // 終了曜日と現在の曜日との差を計算
      if(dayShift != 0)
         SessionEndTime +=DAYS_TO_SECONDS * dayShift;
      // 終了曜日が今日と異なれば、適切な曜日に調整

      timerOn = IsActive(SessionStartTime,SessionEndTime,parLocalTime); // 設定された時間内かどうかをチェック
      if(timerOn == true)
         break; // 一つでも条件を満たすタイマーセグメントがあればループを抜ける
     }

   DisplayTimerStatus(timerOn); // タイマーの状態に応じてメッセージを出力

   return(timerOn); // タイマーがオンかどうかの結果を返す
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTradeSession::DisplayTimerStatus(bool parTimerState)
  {
// parTimerStateがtrueで、かつtimerStartedFlagがfalseの場合、タイマーが開始されたことを示す。
   if(parTimerState == true && timerStartedFlag == false)
     {
      string message = "タイマー開始"; // "タイマー開始"を代入
      Print(message); // メッセージをエキスパートログに出力
      Comment(message); // メッセージをチャートに表示
      timerStartedFlag = true; // timerStartedFlagをtrueに設定して、タイマーが開始されたことを記録
     }
   else
      if(parTimerState == false && timerStartedFlag == true) // parTimerStateがfalseで、かつtimerStartedFlagがtrueの場合、タイマーが停止したことを示す。
        {
         string message = "タイマー停止"; // "タイマー停止"を代入
         Print(message); // メッセージをエキスパートログに出力
         Comment(message); // メッセージをチャートに表示
         timerStartedFlag = false; // timerStartedFlagを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; // タイマーの状態を返す
  }

// Daily timer
bool CTradeSession::DailySessionTimer(int parStartHour, int parStartMinute, int parEndHour, int parEndMinute, bool parLocalTime=false)
  {
// 現在の時間を取得
   datetime currentTime;
   if(parLocalTime == true)
      currentTime = TimeLocal(); // ローカル時間を使用
   else
      currentTime = TimeCurrent(); // サーバー時間を使用

// セッションの開始時間を設定
   SessionStartTime = GenerateTimestamp(parStartHour,parStartMinute);

// セッションの終了時間を設定
   SessionEndTime = GenerateTimestamp(parEndHour,parEndMinute);

// セッションの終了時間が開始時間より前に設定されている場合、終了時間を調整する
   if(SessionEndTime <= SessionStartTime)
     {
      SessionStartTime -= DAYS_TO_SECONDS; // 開始時間を1日分マイナスする

      // 現在時刻が終了時間を過ぎていた場合、開始時間と終了時間を次の日にずらす
      if(currentTime > SessionEndTime)
        {
         SessionStartTime += DAYS_TO_SECONDS;
         SessionEndTime += DAYS_TO_SECONDS;
        }
     }

// セッションがアクティブかどうかを判断
   bool timerOn = IsActive(SessionStartTime, SessionEndTime, parLocalTime);

// タイマーの状態を出力
   DisplayTimerStatus(timerOn);

// タイマーがオンかオフかの状態を返す
   return(timerOn);
  }

//+------------------------------------------------------------------+

今回は以上とさせて頂きます。

次回はイベントハンドラーの1つであるOnTimer関数について解説予定です。

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

MQL5 EA講座 第122回「トレードタイマークラスを実装する-その4-」

                  →MQL5 EA講座 第124回「OnTimer関数について」

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