クロスプラットフォームEA: タイムフィルタ

Enrico Lambino | 29 8月, 2017


目次

  1. イントロダクション
  2. 目的
  3. 基本クラス
  4. タイムフィルタのクラスと型
  5. タイムフィルタコンテナ
  6. サブフィルタ (CTimeFilter)
  7. 結論

イントロダクション

時間フィルタは、特定の期間が定義されている場合に使用し、EAは、指定した時間が当該期間内にあるかどうかを確認します。 満たされていない場合、特定の関数を無効にすることができます。 EAの特定の関数が常に動作するように設定されている場合 (定期的に、または例外で常時タスクする場合) に便利です。 以下に、時間フィルタリングを適用できる例をいくつか示します。

  1. 一定期間の回避 (例えば、レンジ相場の動きや高いボラティリティの期間)
  2. 相場のオーダーまたはポジションに「有効期限」を設定します (有効期限の時点で相場から抜ける)
  3. トレード週の終わりにトレードを閉じる。

他のバリエーションがありますが、トレーダーが使用する最も一般的な関数です。

目的

  • 時間フィルタリングで最も一般的なメソッドを適用します
  • EAが複数のタイムフィルタをシンプルに使用できるようにします
  • MQL4 と MQL5 との互換性を保ちます

基本クラス

CTime という名前のクラスは、EAについて説明した他のタイムフィルタオブジェクトの基本クラスです。 クラス CTimeBase (CTime の基になる場所) の定義を次のコードスニペットに示します。

class CTimeBase : public CObject
  {
protected:
   bool              m_active;
   bool              m_reverse;
   CSymbolManager   *m_symbol_man;
   CEventAggregator *m_event_man;
   CObject          *m_container;
public:
                     CTimeBase(void);
                    ~CTimeBase(void);
   virtual int       Type(void) const {return CLASS_TYPE_TIME;}
   //---初期化
   virtual bool      Init(CSymbolManager*,CEventAggregator*);
   virtual CObject *GetContainer(void);
   virtual void      SetContainer(CObject*);
   virtual bool      Validate(void);
   //---セッターとゲッター
   bool              Active(void) const;
   void              Active(const bool);
   bool              Reverse(void);
   void              Reverse(const bool);
   //---チェック
   virtual bool      Evaluate(datetime)=0;
  };

基本クラスには、プリミティブデータ型のメンバが3つあります。 m_active は、クラスオブジェクトを有効または無効にするために使用されます。 m_reverse は、クラスオブジェクトの出力を逆にするために使用されます (元の出力が false の場合は true を返し、元の出力が true の場合は false を返します)。 m_time_start は、クラスの インスタンスの作成を参照するために使用されます。

タイムフィルタのクラスと型

特定の日付範囲による時間フィルタリング

時間フィルタリングの最も簡単なメソッドです。 このメソッドを使用して時刻を確認するには、開始日と終了日が必要であり、時刻セットが日付の間にある場合は、出力が true になります。 それ以外の場合、出力は false です。

このメソッドは、CTimeRange として実装されます。 次のコードは、CTimeRange の基になる CTimeRangeBase の定義を示しています。

class CTimeRangeBase : public CTime
  {
protected:
   datetime          m_begin;
   datetime          m_end;
public:
                     CTimeRangeBase(void);
                     CTimeRangeBase(datetime,datetime);
                    ~CTimeRangeBase(void);
   //---初期化日時、datetime
   virtual bool      Set(datetime,datetime);
   virtual bool      Validate(void);
   //---セッターとゲッター
   datetime          Begin(void) const;
   void              Begin(const datetime);
   datetime          End(void) const;
   void              End(const datetime);
   //---処理
   virtual bool      Evaluate(datetime);
  };

クラスコンストラクターでは、開始時刻と終了時間を指定する必要があります。 2つの値に対して比較される実際の時間は、クラスメソッドの評価の呼び出し時に設定されます。 設定された時刻が解除またはゼロの場合、メソッドは呼び出し時に現在の時刻を使用します。

bool CTimeRangeBase::Evaluate(datetime current=0)
  {
   if(!Active())
      return true;
   if(current==0)
      current=TimeCurrent();
   bool result=current>=m_begin && current<m_end;
   return Reverse()?!result:result;
  }

曜日による時間フィルタリング

曜日によるフィルタリングは、最もシンプルで最も一般的な時間フィルタリングのメソッドの1つです。 この時間フィルタを使用して、特定の曜日にEAの関数を制限または許可することが一般的です。

ここでは、この特定のクラスをさまざまなメソッドで実装できます。 1つのメソッドは、MQL4 では使用できますが、MQL5 にはないTimeDayOfWeekのカスタム関数を提供することです。 もう1つのメソッドは、チェックする時間を構造体のMqlDateTimeに変換し、その day_of_week パラメータが以前に設定されたフラグに設定しているかどうかを確認します。 後者のメソッドは選択されており、基本クラス内で使用可能なすべてのクラスメソッドを配置できるため、推奨されています。

このメソッドは、CTimeDays としてEAに表されます。 次のコードは、CTimeDays の基になる CTimeDaysBase の定義を示しています。

class CTimeDaysBase : public CTime
  {
protected:
   long              m_day_flags;
public:
                     CTimeDaysBase(void);
                     CTimeDaysBase(const bool sun=false,const bool mon=true,const bool tue=true,const bool wed=true,
                                   const bool thu=true,const bool fri=true,const bool sat=false);
                    ~CTimeDaysBase(void);
   //---初期化                    
   virtual bool      Validate(void);
   virtual bool      Evaluate(datetime);
   virtual void      Set(const bool,const bool,const bool,const bool,const bool,const bool,const bool);
   //---セッターとゲッター
   bool              Sunday(void) const;
   void              Sunday(const bool);
   bool              Monday(void) const;
   void              Monday(const bool);
   bool              Tuesday(void) const;
   void              Tuesday(const bool);
   bool              Wednesday(void) const;
   void              Wednesday(const bool);
   bool              Thursday(void) const;
   void              Thursday(const bool);
   bool              Friday(void) const;
   void              Friday(const bool);
   bool              Saturday(void) const;
   void              Saturday(const bool);
  };

定義に示すように、クラスには long 型のクラスメンバが1つだけあります。 評価時に true を返す必要がある曜日のフラグを設定するときに、クラスが使用するメンバーです (週の7日)。 ビットごとの操作を使用する予定なので、メンバーが7日間のそれぞれを表すカスタム列挙体も宣言する必要があります。

enum ENUM_TIME_DAY_FLAGS
  {
   TIME_DAY_FLAG_SUN=1<<0,
   TIME_DAY_FLAG_MON=1<<1,
   TIME_DAY_FLAG_TUE=1<<2,
   TIME_DAY_FLAG_WED=1<<3,
   TIME_DAY_FLAG_THU=1<<4,
   TIME_DAY_FLAG_FRI=1<<5,
   TIME_DAY_FLAG_SAT=1<<6
  };

曜日のフラグは set メソッドを使用して設定 (または解除) されます。 便宜上、このメソッドは、クラスコンストラクターの1つで、フラグを最初に設定せずにクラスインスタンスの偶発的な評価を防ぐための手段として呼び出されます。

void CTimeDaysBase::Set(const bool sun=false,const bool mon=true,const bool tue=true,const bool wed=true,
                        const bool thu=true,const bool fri=true,const bool sat=false)
  {
   Sunday(sun);
   Monday(mon);
   Tuesday(tue);
   Wednesday(wed);
   Thursday(thu);
   Friday(fri);
   Saturday(sat);
  }

フラグは、個別に設定することもできます。 すべての7日間 (状況によってはエラーが発生しやすい) のフラグを設定する Set 関数を呼び出すのではなく、1つのフラグを変更するだけの場合に便利です。 次のコードスニペットは、day フラグから2日目の設定/非設定に使用され、月曜日をコールするメソッドを示しています。 他の日のセッターとゲッターも同じメソッドでコーディングされています。

void CTimeDaysBase::Monday(const bool set)
  {
   if(set)
      m_day_flags|=TIME_DAY_FLAG_MON;
   else
      m_day_flags &=~TIME_DAY_FLAG_MON;
  }

フラグを設定するためのメソッドを使用して、次のメソッドは、フィルタの実際の評価すなわち、特定の時間が週の特定の日に該当するかどうかを決定します。

bool CTimeDaysBase::Evaluate(datetime current=0)
  {
   if(!Active())
      return true;
   bool result=false;
   MqlDateTime time;
   if(current==0)
      current=TimeCurrent();
   TimeToStruct(current,time);
   switch(time.day_of_week)
     {
      case 0: result=Sunday();      break;
      case 1: result=Monday();      break;
      case 2: result=Tuesday();     break;
      case 3: result=Wednesday();   break;
      case 4: result=Thursday();    break;
      case 5: result=Friday();      break;
      case 6: result=Saturday();    break;
     }
   return Reverse()?!result:result;
  }

以前少し説明したように、このメソッドは最初に datetime 型のパラメータを取得します。 メソッド呼び出しに引数がない場合、メソッドは現在の時刻を使用します。 次に、この時間を MqlDateTime 形式で変換し、その day_of_week メンバーを取得して、クラスの唯一のメンバー (m_day_flags) の現在の値に対して評価します。

このメソッドは、多くの場合、トレーダーがトレードに望ましくないと判断した特定の日にアクティブになっているときに、"金曜日にトレードがない"、あるいは "日曜日" のようなトレード業者の要件を満たすために使用されます。

タイマの使用

タイムフィルタのもう1つのメソッドは、タイマを使用することです。 タイマーでは、現在の時刻は、過去にある特定の時点と比較されます。 評価時に時間がまだ有効期限内にある場合は、true を返す必要があり、以外の場合は false を返します。 このメソッドは、CTimer クラスによって表されます。 次のスニペットは、CTimer の基になる CTimerBase の定義のコードを示しています。

class CTimerBase : public CTime
  {
protected:
   uint              m_years;
   uint              m_months;
   uint              m_days;
   uint              m_hours;
   uint              m_minutes;
   uint              m_seconds;
   int               m_total;
   int               m_elapsed;
   datetime          m_time_start;
public:
                     CTimerBase(const int);
                     CTimerBase(const uint,const uint,const uint,const uint,const uint,const uint);                     
                    ~CTimerBase(void);
   //---初期化
   virtual bool      Set(const uint,const uint,const uint,const uint,const uint,const uint);
   virtual bool      Validate(void);
   //---ゲッターとセッター
   uint              Year(void) const;
   void              Year(const uint);
   uint              Month(void) const;
   void              Month(const uint);
   uint              Days(void) const;
   void              Days(const uint);
   uint              Hours(void) const;
   void              Hours(const uint);
   uint              Minutes(void) const;
   void              Minutes(const uint);
   uint              Seconds(void) const;
   void              Seconds(const uint);
   bool              Total(void) const;
   datetime          TimeStart(void) const;
   void              TimeStart(const datetime);
   //---処理   
   virtual bool      Elapsed(void) const;
   virtual bool      Evaluate(datetime);
   virtual void      RecalculateTotal(void);
  };

コンストラクターの引数は、クラスメンバー m_total に格納されている開始時刻からの経過時間または有効期限の合計を構築するために使用されます。 便宜上、一定期間の秒数に基づいて定数を宣言します (1 年から1分まで)。

# defineYEAR_SECONDS 31536000
# defineMONTH_SECONDS 2419200
# defineDAY_SECONDS 86400
# defineHOUR_SECONDS 3600
# defineMINUTE_SECONDS 60

クラスのコンストラクターには、年から秒までのタイマーの有効期限が必要です。

CTimerBase::CTimerBase(const uint years,const uint months,const uint days,const uint hours,const uint minutes,const uint seconds) : m_years(0),
                                                                                                                                    m_months(0),
                                                                                                                                    m_days(0),
                                                                                                                                    m_hours(0),
                                                                                                                                    m_minutes(0),
                                                                                                                                    m_seconds(0),
                                                                                                                                    m_total(0),
                                                                                                                                    m_time_start(0)
  {
   Set(years,months,days,hours,minutes,seconds);
  }

また、唯一の引数として m_total の優先値を使用してクラスのインスタンスを構築することもできます。

CTimerBase:: CTimerBase (const int total_time): m_years (0),
                                               m_months(0),
                                               m_days(0),
                                               m_hours(0),
                                               m_minutes(0),
                                               m_seconds(0),
                                               m_total(0),
                                               m_time_start(0)
  {
   m_total=total_time;
  }

クラスをインスタンス化した直後にクラスの評価メソッドを呼び出すと、m_total を UNIX 時間の開始と比較し、datetime データ型の基になります。 したがって、評価メソッドを呼び出す前に、希望の開始時刻を設定する必要があります (希望する開始時刻が1970年1月1日 (UTC/gmt) の場合を除く)。 オーバーロードされた TimeStart メソッドを使用して、m_time_start クラスメンバーのセッターメソッドとゲッターメソッドを次に示します。

datetime CTimerBase::TimeStart(void) const
  {
   return m_time_start;
  }

void CTimerBase::TimeStart(const datetime time_start)
  {
   m_time_start=time_start;
  }

このクラスの評価メソッドは非常にシンプルです: 開始時刻とメソッド引数として渡された時間 (通常は現在の時刻) の差を取得します。 これは経過時間であり、経過時間が許容される合計時間 (m_total) を超えた場合、メソッドは false を返します。

bool CTimerBase::Evaluate(datetime current=0)
  {
   if(!Active())
      return true;
   bool result=true;
   if(current==0)
      current= TimeCurrent();
   m_elapsed=(int)(current-m_time_start);
   if(m_elapsed>=m_total) result=false;
   return Reverse()?!result:result;
  }

この時間フィルタリングのメソッドは、EAの特定の関数に対して最大期間 (有効期限) を設定し、相場のオーダーまたはポジション (バイナリーオプショントレードに類似) の "有効期限" を設定するなど、さまざまなメソッドで使用されます。 この時間フィルタは、タイマイベント (MQL4 と MQL5 の両方とも互換性がある) を使用するのとほぼ同じですが、このイベント関数を使用して設定できるタイマイベントは1つだけなので、EAが追加のタイマを実際に必要とする場合に限り、CTimer が必要になります。

日中のタイムスケジュールを使用したフィルタリング

日中のタイムスケジュールを使用してフィルタリングは、多くのトレーダーによって使用されています。 このフィルタは、24時間のスケジュールを使用し、EAが (通常、エントリーシグナルに基づいてトレード) 操作を実行できるようにする特定のスケジュール (パラメータを通じて) を選択します。 このフィルター処理のメソッドは、CTimeFilter クラスによって表されます。 次のコードは、CTimeFilter の基になる CTimeFilterBase の定義を示しています。

class CTimeFilterBase : public CTime
  {
protected:
   MqlDateTime       m_filter_start;
   MqlDateTime       m_filter_end;
   CArrayObj         m_time_filters;
public:
                     CTimeFilterBase(void);
                     CTimeFilterBase(const int,const int,const int,const int,const int,const int,const int);
                    ~CTimeFilterBase(void);
   virtual bool      Init(CSymbolManager*,CEventAggregator*);
   virtual bool      Validate(void);
   virtual bool      Evaluate(datetime);
   virtual bool      Set(const int,const int,const int,const int,const int,const int,const int);
   virtual bool      AddFilter(CTimeFilterBase*);
  };

このクラスには、MqlDateTime 型のメンバが2つ、 CArrayObjのメンバが1つあります。 2つの構造体は、24時間以内に範囲を格納するために使用されますが、オブジェクトメンバーはサブフィルターを格納するために使用されます。

クラスオブジェクトコンストラクタを使用して、開始時刻と決済時間を時間、分、および秒で取得します。 これらの値は、クラスの Set メソッドを通じて、最終的にクラスメンバー m_filter_start と m_filter_end に格納されます。 gmt パラメータは、ブローカの GMT オフセットを考慮するために使用されます。

bool CTimeFilterBase::Set(const int gmt,const int starthour,const int endhour,const int startminute=0,const int endminute=0,
                          const int startseconds=0,const int endseconds=0)
  {
   m_filter_start.hour=starthour+gmt;
   m_filter_start.min=startminute;
   m_filter_start.sec=startseconds;
   m_filter_end.hour=endhour+gmt;
   m_filter_end.min=endminute;
   m_filter_end.sec=endseconds;
   return true;
  }

その後、クラスの評価メソッドに進みます。 初期化では、2つの MqlDateTime パラメータのデータは、24時間以内に時間、分、および秒の条件でのみ表現されます。 年や月などの他のデータは含まれていません。 開始時刻と終了時刻を、指定された時間 (または現在の時刻、メソッドの引数がデフォルト値の場合) と比較するためです。 これを行うには、少なくとも2つのメソッドが必要です。

  1. 指定された時間を時間、分、および秒で表現し、を struct パラメータと比較します。
  2. 現在の時刻を使用して、構造体を UNIX 時間 (datetime 型) に変換し、指定された時刻と比較して、不足している構造体パラメータを更新します。

2番目のメソッドは、次のように評価メソッドで実装するように選択されました。

bool CTimeFilterBase::Evaluate(datetime current=0)
  {
   if(!Active())
      return true;
   bool result=true;
   MqlDateTime time;
   if(current==0)
      current=TimeCurrent();
   TimeToStruct(current,time);
   m_filter_start.year= time.year;
   m_filter_start.mon = time.mon;
   m_filter_start.day = time.day;
   m_filter_start.day_of_week = time.day_of_week;
   m_filter_start.day_of_year = time.day_of_year;
   m_filter_end.year= time.year;
   m_filter_end.mon = time.mon;
   m_filter_end.day = time.day;
   m_filter_end.day_of_week = time.day_of_week;
   m_filter_end.day_of_year = time.day_of_year;
   /*
     その他のタスク
   */
  }

比較は終了時刻を除きます。 つまり、このクラスを使用して、EAが08:00 と17:00 の間でのみ取引されるようにすると、EAは、08:00 キャンドルの開始時から13:59 まで取引を開始することができますが、その決済のトレードは17:00 まで許可されています。

構造体には時間よりも大きいデータが含まれていないため、欠落しているデータは現在の時刻 (または指定した時刻) から取得する必要があります。 ただし、開始時刻と決済時間が24時間以内の場合は、一部の調整が必要ですが、同じ日には属していません。 上記の例では、08:00 は8:00 ですが17:00 は 5:00 PM です。この場合、両方の時間は同日内に見つけることができます。 しかし、 5:00 PM に開始時刻と8:00 で終了時刻で、2つを切り替えると仮定します。開始時間が決済時間より大きい場合は、時間範囲が次の日に延びていることを意味します。 したがって、終了時刻は開始時刻と同じ日ではありません。 その後の状況は、2つのいずれかです:
  1. 開始時刻は現在の日 (または indiated の時刻からの日) から、終了時刻は次の日です。
  2. 開始時刻は、今日の前日 (指定された時刻の前日) から、終了時刻は現在の日 (または指定された時刻からの日) です。

必要な調整は、現在の時刻または指定された時間によって異なります。 5:01 PM (17:01) の指定された時間 (または現在の時刻) があるとします。 この場合、開始時刻は指定した時刻と同じ日になります。 ここでは、終了時刻が次の日に属することは確かです。 一方、指定された時間として01:00 または1:00 の場合、指定された時間は、終了時刻と同じ日に、開始時刻が属している。 したがって、前に計算した MqlDateTime 構造体は、次のように調整する必要があります。

  1. 開始時刻が指定された時刻と同じ日に該当する場合は、終了時刻に1日を追加します。
  2. 終了時刻が指定された時刻と同じ日に該当する場合は、開始時刻から1日を減算します。

開始時刻と終了時刻が同じ日に属していない場合にのみ適用され、開始時間がフィルタの決済時間よりも大きい場合です。 調整は、次のように評価メソッド内で実装されます。

if(m_filter_start.hour>=m_filter_end.hour)
  {
   if(time.hour>=m_filter_start.hour)
     {
      m_filter_end.day++;
      m_filter_end.day_of_week++;
      m_filter_end.day_of_year++;
     }
   else if(time.hour<=m_filter_end.hour)
     {
      m_filter_start.day--;
      m_filter_start.day_of_week--;
      m_filter_start.day_of_year--;
     }
  }

戻り値の変数は最初に true に設定されているため、タイムフィルタの実際のチェックは、指定された時間が開始時刻と終了時刻の間に収まるかどうかによって異なります。 評価メソッドの計算によって、開始時刻が常に終了時刻以下であることが保証されます。 開始時刻が終了時刻 (時間、分、および秒の単位で等しい) に等しい場合、メソッドは true を返します。 たとえば、開始時刻が05:00 で、終了時刻が05:00 である場合、フィルターはこの2回が同じ日に該当しない場合、フィルターは24時間全体を包含します。

タイムフィルタコンテナ

このシリーズで説明している他のクラスオブジェクトと同様に、タイムフィルタにはポインタが格納されるコンテナもあります。 このコンテナーの評価メソッドを呼び出すことによって評価を実行できるようになります。 すべての時間フィルタの評価メソッドが true を返す場合 (時間フィルタリングに関する限りでは異議はありません)、このコンテナは true を返す必要があります。 CTimes クラスによって実装されます。 次のコードは、CTimes が基づいている CTimesBase の定義を示しています。

class CTimesBase : public CArrayObj
  {
protected:
   bool              m_active;
   int               m_selected;
   CEventAggregator *m_event_man;
   CObject          *m_container;
public:
                     CTimesBase(void);
                    ~CTimesBase(void);
   virtual int       Type(void) const {return CLASS_TYPE_TIMES;}
   //-- initialization
   virtual bool      Init(CSymbolManager*,CEventAggregator*);
   virtual CObject *GetContainer(void);
   virtual void      SetContainer(CObject*);
   virtual bool      Validate(void) const;
   //--- activation and deactivation
   bool              Active(void) const;
   void              Active(const bool);
   int               Selected(void);
   //---チェック
   virtual bool      Evaluate(datetime) const;
   //--- recovery
   virtual bool      CreateElement(const int);
  };

サブフィルタ (CTimeFilter)

時間フィルターコンテナーの評価メソッドでは、true を返すためには、すべてのプライマリメンバーが true を返す必要があります。 ほとんどの時間フィルターオブジェクトは同じEAで複数のインスタンスを必要としませんが、対する例外は CTimeFilter であり、最も使用される時間フィルターです。 次のコードを検討します。

CTimes times = new CTimes();
CTimeFilter time1 = new CTimeFilter(gmt,8,17);
times.Add(GetPointer(time1));

タイムフィルタは、トレード (エントリ) に使用されていると仮定しましょう。 この場合、[タイムフィルタ] コンテナには、ダイナミックポインタ配列に1つのポインタのみが含まれます。 この設定では、時間の評価メソッドが呼び出されると、指定された時刻が 08:00 ~ 17:00 の間に収まる場合、最終的な結果が異なります。

今度は、8:00 AM から 5:00 PM までのトレードよりもむしろ、EAが時間をとばすように構成された場合を考慮します。 つまり、8:00 AM ~ 12:00 pm の間、1:00 pm と 5:00 pm の間でトレードされます。これで、タイムラインは連続しなくなりましたが、代わりに2つに分割されます。 コーダーは、CTimeFilter の2つのインスタンスを使用するだけではなく、最初のコードを変更されることがあります:

CTimes times = new CTimes();
CTimeFilter time1 = new CTimeFilter(gmt,8,12);
CTimeFilter time2 = new CTimeFilter(gmt,13,17);
times.Add(GetPointer(time1));
times.Add(GetPointer(time2));

上記のコードは正しく機能しません。 時間フィルターコンテナーは、すべてのプライマリタイムフィルターインスタンスが true を返す必要があるため、常に false を返します。 上記の設定では、1つが true を返すと、もう一方は false を返し、その逆も同様になります。 2つ以上のタイムフィルタが含まれている場合、状況はさらに複雑になります。 この設定を使用すると、1つのフィルタがアクティブになり、残りが非アクティブになるのは、正しく動作する唯一のメソッドです。

このソリューションでは、time フィルターコンテナーが CTimeFilter に1つのポインターだけを格納するように常に確認する必要があります。 CTimeFilter の複数のインスタンスが必要な場合は、CTimeFilter の別のインスタンスのサブフィルタとして追加する必要があります。 サブフィルタへのポインタは、CTimeFilter のクラスメンバ m_time_filters の1つの中に格納され、ポインタは AddFilter メソッドを通じて追加されます。 サブフィルタを評価するためのコードは、クラスの評価メソッド内にあり、次のようになります。

if(!result)
  {
   for(int i=0;i<m_time_filters.Total();i++)
     {
      CTimeFilter *filter=m_time_filters.At(i);
      if(filter.Evaluate(current))
      {
         return true;
      }   
     }
  }

このコードは、main フィルタが、最初の評価に例外があるかどうかを確認する最後の手段として false を返す場合にのみ実行されます。 少なくとも1つのサブフィルタが true を返す場合、メインフィルタは常に true を返します。 前のコード例を次のように変更します。

CTimes times = new CTimes();
CTimeFilter time1 = new CTimeFilter(gmt,8,12);
CTimeFilter time2 = new CTimeFilter(gmt,13,17);
CTimeFilter time0 = new CTimeFilter(gmt,0,0);
time0.Reverse();
time0.AddFilter(GetPointer(time1));
time0.AddFilter(GetPointer(time2));
times.Add(GetPointer(time0));
この変更によって、time フィルタオブジェクトには、配列内の1つのポインターのみが含まれ、time0 が行われます。 一方、time0 は、もともとタイムフィルタコンテナの下にあった2つのサブフィルタ、日時と time2 があります。 time0 は、開始時刻と決済時間に同じパラメータを持ち、したがって、常に true を返します。 time0 が常に false を返すようにリバースメソッドを呼び出して、初期評価に例外があるかどうかをチェックします (サブフィルターを使用します)。 図的に表現すると、次のようにタイムテーブルが表示されます。

メインタイムフィルタとサブフィルタのグラフィック表示

time0 へのポインタは、タイムフィルタコンテナ内にあります。 上記の図を考えると、最初に評価されます。 time0 は常に false を返すため、サブフィルタをチェックします。 最初に、時間が8:00 と12:00 の間にあるかどうかを確認します。 これがされていない場合は、指定された時間が13:00 と17:00 の間にあるかどうかを確認します。 このいずれかが true を返した場合、time0 は true (以外の場合は false) も返します。 したがって、指定された時間が 8:00 ~ 12:00、または13:00 と17:00 の間にある場合、最終タイムラインは true を返します。 2つ以上のサブフィルタが可能であり、まだ上記のように同じルールに従います。 ただし、サブフィルタのサブフィルタは、日中のタイムフィルタの組み合わせが2つのレベルでのみ表されるため、ほとんど必要ありません。

例として、前の記事からEAの例を変更します。 このEAには、この記事で説明したすべての時間フィルタが含まれます。 CTimesBase のヘッダーファイルを含めることによって開始, EAの十分なすべての時間フィルタのクラスを含むようにします。

# include「MQLx\Base\OrderManager\OrderManagerBase.mqh」
# include「MQLx\Base\Signal\SignalsBase.mqh」
#include "MQLx\Base\Time\TimesBase.mqh" //追加の行を含める ]
# include<Indicators\Custom.mqh>

次に、タイムフィルタコンテナへのグローバルポインタを宣言します。

CTimes *time_filters;

CTimes の下に、次のように、このポインタに格納されているインスタンスを作成します。

time_filters = new CTimes();

このEAに、決済ではなく、新しいトレードのエントリにのみ、時間のフィルタリングを適用します。 これをするためには、EAは、トレードをエントリーする前に、その時点でトレードをエントリーする許可されているかどうかを確認されます:

if(signals.CheckOpenLong())
  {
   close_last();
   if (time_filters.Evaluate(TimeCurrent()))
   {
      //Print("Entering buy trade..");            
      money_manager.Selected(0);
      order_manager.TradeOpen(Symbol(),ORDER_TYPE_BUY,symbol_info.Ask());
   }
  }
else if(signals.CheckOpenShort())
  {
   close_last();
   if (time_filters.Evaluate(TimeCurrent()))
   {
      //Print("Entering sell trade..");
      money_manager.Selected(1);
      order_manager.TradeOpen(Symbol(),ORDER_TYPE_SELL,symbol_info.Bid());
   }
  }

上記のコードに示されているように、最後のポジションの決済は時間の点検および新しいトレードの前に呼ばれ、従って時間フィルターは既存のトレードの決済ではなくエントリーにだけ適用します。

日付範囲を使用した時間フィルタリングでは、開始日と終了日のエントリーパラメータを指定します。 パラメータは datetime 型です。 また、ユーザーがこの関数をオンまたはオフにできるようにするパラメータもあります。

input bool time_range_enabled = true;
input datetime time_range_start = 0;
input datetime time_range_end = 0;

両方のパラメータのデフォルト値の0は、 UNIX の時刻の先頭を参照していることを意味します。 デフォルト値を使用して偶発的なエラーが発生しないようにするには、終了時刻が0より大きく、終了時刻が開始時刻より大きい場合にのみ、時間フィルターが作成されるように、追加のメジャーを設定します。 これはEA の関数の中でエンコードされます:

if (time_range_enabled && time_range_end>0 && time_range_end>time_range_start)
 {
     CTimeRange *timerange = new CTimeRange(time_range_start,time_range_end);
     time_filters.Add(GetPointer(timerange));
 }    

また、上記のコードでは、時間フィルターコンテナ上のオブジェクトのポインタの追加が見つかりました。

トレードする日によって時間フィルターに、週の内の特定の日を表す7つの異なった変数を入れます。 また、この関数をオンまたはオフにするパラメータも付けます。

input bool time_days_enabled = true;
input bool sunday_enabled = false;
input bool monday_enabled = true;
input bool tuesday_enabled = true;
input bool wednesday_enabled = true;
input bool thursday_enabled = true;
input bool friday_enabled = false;
input bool saturday_enabled = false;

関数が有効になっている場合は、この関数の下で、タイムフィルタの新しいインスタンスを作成し、コンテナにも追加します。

if (time_days_enabled)
 {
    CTimeDays *timedays = new CTimeDays(sunday_enabled,monday_enabled,tuesday_enabled,wednesday_enabled,thursday_enabled,friday_enabled,saturday_enabled);
    time_filters.Add(GetPointer(timedays));
 }

タイマでは、関数のオン/オフを設定するパラメータに加えて、有効期限前のフィルタの合計時間であるパラメータを1つだけ宣言します。

input bool timer_enabled= true;
input int timer_minutes = 10080;

前に説明した以前のフィルターと同様に、CTimer の新しいインスタンスを作成し、関数が有効になっている場合はそのコンテナへのポインタを追加します。

if(timer_enabled)
  {
   CTimer *timer=new CTimer(timer_minutes*60);
   timer.TimeStart(TimeCurrent());
   time_filters.Add(GetPointer(timer));
  }

次のシナリオに基づいて時間フィルタリングで EA の容量を実証したいので、日中の時間のフィルタリングは、もう少し複雑です:

  1. 開始時刻と終了時刻が同じ日に収まるときのフィルター処理
  2. 開始時刻および終了時刻が同日内にない場合のタイムフィルタリング
  3. CTimeFilter の複数のインスタンス

シナリオ # 1 & # 2 は、同じパラメータセットを使用して示すことができます。 開始時間が決済時間 (シナリオ # 1) より小さい場合は、2つの値を切り替えるだけで、シナリオ # 2 を取得します。 ただし、# 3 では、2つ以上のインスタンス (好ましくは異なる値を持つ) が必要となるため、24時間以内に少なくとも2つのセットの開始と終了時間が必要になります。 これを実現するには、まず、シナリオ # 1/# 2、およびシナリオ3の3つの設定を使用してカスタム列挙体を宣言します。

enum ENUM_INTRADAY_SET 
  {
   INTRADAY_SET_NONE=0,
   INTRADAY_SET_1,
   INTRADAY_SET_2
  };

次に、パラメータを次のように宣言します。

input ENUM_INTRADAY_SET time_intraday_set=INTRADAY_SET_1;
input int time_intraday_gmt=0;
//第1セット
input int intraday1_hour_start=8;
input int intraday1_minute_start=0;
input int intraday1_hour_end=17;
input int intraday1_minute_end=0;
//第2セット
input int intraday2_hour1_start=8;
input int intraday2_minute1_start=0;
input int intraday2_hour1_end=12;
input int intraday2_minute1_end=0;
//第3セット
input int intraday2_hour2_start=13;
input int intraday2_minute2_start=0;
input int intraday2_hour2_end=17;
input int intraday2_minute2_end=0;
この時間フィルタを初期化するには、switch ステートメントを使用します。 ime_intraday_set が INTRADAY_SET_1 に設定されている場合は、最初のパラメータセットを使用して CTimeFilter の単一インスタンスを初期化します。 一方、設定が INTRADAY_SET_2 の場合、2番目と3つ目のパラメータを使用して CTimeFilter の異なるインスタンスを作成します。
switch(time_intraday_set)
  {
   case INTRADAY_SET_1:
     {
      CTimeFilter *timefilter=new CTimeFilter(time_intraday_gmt,intraday1_hour_start,intraday1_hour_end,intraday1_minute_start,intraday1_minute_end);
      time_filters.Add(timefilter);
      break;
     }
   case INTRADAY_SET_2:
     {
      CTimeFilter *timefilter=new CTimeFilter(0,0,0);
      timefilter.Reverse(true);
      CTimeFilter *sub1 = new CTimeFilter(time_intraday_gmt,intraday2_hour1_start,intraday2_hour1_end,intraday2_minute1_start,intraday2_minute1_end);
      CTimeFilter *sub2 = new CTimeFilter(time_intraday_gmt,intraday2_hour2_start,intraday2_hour2_end,intraday2_minute2_start,intraday2_minute2_end);
      timefilter.AddFilter(sub1);
      timefilter.AddFilter(sub2);
      time_filters.Add(timefilter);
      break;
     }
   default: break;
  }

時間フィルタクラスのインスタンス化のすべてのコードの後、タイムフィルタコンテナ、CTimes を初期化します。 まず、シンボルマネージャへのポインタを割り当てます (この例では必要ありませんが、タイムフィルタを拡張する必要がある場合に必要になる場合があります)、次にその設定を確認します。

time_filters.Init(GetPointer(symbol_manager));
if(!time_filters.Validate())
  {
   Print("one or more time filters failed validation");
   return INIT_FAILED;
  }

ここで、EAのテスト結果に進みましょう。 テストは2017年1月全体でストラテジーテスターを使用しました。

時間範囲によるフィルタリングを有効にしたEAを実行するため、テスト結果はこの記事 (tester_time_range の html) の下部にあります。 このテストでは、時間範囲は2017年の最初に開始され、月の最初の金曜日、1月6日に終了します。 このため、EAが終了日以降にトレードに入らなくなったときに、フィルタが機能することがわかります。 最後のトレードのスクリーンショットを以下に示します。

時間日最後のトレード

テストの最後のトレードは、EAに設定された制限内にある 01/06 15:00 にエントリーされました。 そのトレードは、次の週まで開いたままであることに注意してください。時間フィルタは、エントリにのみ適用されます。 点線の縦線は、1週間の最後のろうそくを表します。

日ごとのフィルター処理では、[日ごとのフィルター処理] パラメータが true に設定されます。 また、日付範囲によるフィルタ処理は有効のままですが、金曜日パラメータは無効になっています。 前回のテストからの最後のトレードは、最後のトレードが 01/06 (金曜日) に入っていたことを示しています。 したがって、このトレードは、もはやエントリーされたことを見れば、この特定の時間フィルタが動作することを確認することができます。 テスト結果は、この記事の下部にもあります (tester_time_days. html)。 最後のトレードのスクリーンショットを以下に示します。

時間日最後のトレード

スクリーンショットに示されているように、前回のトレードは、以前の構成で01/06 のトレードに対して、01/05 で生成されました。 この新しい構成の下で、2番目のトレードはこのテストの最終的なトレードです。 EA は常に1つのポジション (買いまたは売り) を維持するので、その決済は、前のテストの最後のトレードのエントリポイントでもある2番目と一致します。

タイムフィルタについては、前述のように、1つの引数のみを受け付ける CTimer の代替コンストラクタを使用しました。 有効期限が切れる前にフィルターが true を返すクラスメンバー m_total に格納されます。 秒単位で表されるため、値が秒単位で格納されるように、エントリーパラメータを60で乗算する必要があります。 10080は、エキスパートのデフォルトの分数で、1週間に相当します。 したがって、最初のフィルターをこのフィルターと組み合わせた場合、最初のフィルターを使用したテストの結果は、このフィルターの結果と同一である必要があります。 テスト結果は、最初のものと全く同じであり、この記事の最後に提供されます (tester_timer. html)。

CTimeFilter である最終的な時間フィルターに、3つの異なった場合を有する、各々をまたテストする必要があります。 EAは常にいつものポジションを保持しているので、以前のトレードを閉じて、新しいポジションを開いて、再びを閉じて、新しいポジションを開くことを余儀なくされます。 この例外は、1つまたは複数のタイムフィルタが false を返す場合です。 したがって、トレードは日中の期間内に行方不明になった場合、EA はトレードに入るから時間フィルターによって阻止されています。 時間フィルターのない完全なテスト resullt はこの記事 (tester_full の html) の終わりにあります。

この記事に記載されている日中のタイムフィルタのシナリオ # 1 では、開始時刻を08:00、終了時刻を17:00 に設定しています。 テストでは、最初のトレードは最初の時間 (00:00) の、テストの初めに入った。 これは最初のシナリオで設定されている範囲外です。 これらの設定を考えると、EA は、そのトレードを取ることではなく、期待されているフィルタを適用した最初のトレードとして時間フィルタ内に該当する次のトレードを取ります。 この設定によって得られたテスト結果は、この記事の最後 (tester_time_hour1. html) にあり、最初のトレードのスクリーンショットは以下のようになります。

時間のトレード3

予想通り、EAはテストの冒頭でトレードをしませんでした。 日中の範囲に達するまで待ちました。 点線の縦線はテストの開始を表し、完全なテストの最初のトレード (フィルタなし) が検出されます。

2番目のシナリオでは、フィルターの開始時刻と終了時刻を切り替えるだけで、開始時間は17:00 で、終了時間は08:00 になります。 完全なテスト (フィルタなし) で、 01/03 10:00 の日中の範囲内に該当しない最初のトレードを見つけることができます。 10:00 は08:00 より大きく、17:00 より前であり、このトレードが日中の範囲を越えることは確かです。 この設定によって得られたテスト結果は、この記事の最後 (tester_time_hour2. html) にあり、そのトレードのスクリーンショットは以下の通りです。

2時間帯 トレード

スクリーンショットからご覧の通り、このEA は、以前のオーダーを閉じましたが、新しいオーダーを開いていませんでした。 点線の縦線は、新しいセッションの開始を表します。 EA はそのセッションの開始後、日中セッションの3ロウソク足の最初にトレードを開きました。

3番目のシナリオでは、ランチタイムを除いて、最初のシナリオの設定を使用するように EA を構成しました。 従って、12:00 のロウソクで、トレードするべきではないし、EA は13:00 の開始でトレードを再開することになります。 完全なテスト (フィルタなし) で、 01/09 12:00 でトレードを開いたインスタンスを見つけることができます。 このトレードは、このシナリオの設定を考えると、12:00 に落ちるので、EAによってエントリーされていないトレードを期待しています。 この設定によって得られたテスト結果は、この記事の最後 (tester_time_hour3. html) にあり、そのトレードのスクリーンショットは以下の通りです。

3時間帯 トレード

スクリーンショットに示すように、12:00 ロウソクの上で、EA は、既存の買いトレードを閉じたが、別のトレードをエントリーしていません。 EA は予想通り1時間の中断をし、エントリーしました。 16:00、午後のセッションの3時間後に別のポジション (買い) をエントリーし、セッションの終了前に、 次のシグナルの反転に基づいてトレードをエントリーすることができる時間です。

結論

この記事では、クロスプラットフォームEAにおけるさまざまな時間のフィルタリングメソッドの実装について説明しました。 この記事では、特定の関数が特定の時間の設定に応じてEAで有効または無効にすることができるように、タイムフィルタコンテナを介して組み合わせることができるメソッドについてだけでなく、様々な時間フィルタについて扱いました。