クロスプラットフォームEA: CExpertAdvisor と CExpertAdvisors クラス

13 11月 2017, 09:06
Enrico Lambino
0
483

目次

  1. イントロダクション
  2. EAクラス
  3. 初期化
  4. 新しい足の検出
  5. OnTick ハンドラ
  6. EAコンテナ
  7. データの永続性
  8. 最終リマーク
  9. 結論

イントロダクション

このトピックに関連する以前の記事では、EAのメインヘッダーファイルにコンポーネントが散在しています。 この記事は、クロスプラットフォームEAの様々なコンポーネント間で、より調和のとれた相互作用を作成することを目的とするクラス CExpertAdvisor と CExpertsAdvisors を使います。 また、ボラティリティデータの読み込みと保存、新しい足検出など、EAで通常発生する一般的な問題にも対処します。

EAクラス

CExpertAdvisorBase クラスは、次のコードスニペットに示されています。 この時点で、MQL4 と MQL5 の違いは、前の記事で説明した他のクラス・オブジェクトによって処理されます。

class CExpertAdvisorBase : public CObject
  {
protected:
   //---トレードパラメータ
   bool              m_active;
   string            m_name;
   int               m_distance;
   double            m_distance_factor_long;
   double            m_distance_factor_short;
   bool              m_on_tick_process;
   //---シグナルパラメータ
   bool              m_every_tick;
   bool              m_one_trade_per_candle;
   datetime          m_last_trade_time;
   string            m_symbol_name;
   int               m_period;
   bool              m_position_reverse;
   //---シグナルオブジェクト
   CSignals         *m_signals;
   //--- トレードオブジェクト
   CAccountInfo      m_account;
   CSymbolManager    m_symbol_man;
   COrderManager     m_order_man;
   //---トレード時間オブジェクト
   CTimes           *m_times;
   //---ロウソク足
   CCandleManager    m_candle_man;
   //---イベント
   CEventAggregator *m_event_man;
   //---コンテナ
   CObject          *m_container;
public:
                     CExpertAdvisorBase(void);
                    ~CExpertAdvisorBase(void);
   virtual int       Type(void) const {return CLASS_TYPE_EXPERT;}
   //---初期化
   bool              AddEventAggregator(CEventAggregator*);
   bool              AddMoneys(CMoneys*);
   bool              AddSignal(CSignals*);
   bool              AddStops(CStops*);
   bool              AddSymbol(const string);
   bool              AddTimes(CTimes*);
   virtual bool      Init(const string,const int,const int,const bool,const bool,const bool);
   virtual bool      InitAccount(void);
   virtual bool      InitCandleManager(void);
   virtual bool      InitEventAggregator(void);
   virtual bool      InitComponents(void);
   virtual bool      InitSignals(void);
   virtual bool      InitTimes(void);
   virtual bool      InitOrderManager(void);
   virtual bool      Validate(void) const;
   //---コンテナ
   void              SetContainer(CObject*);
   CObject          *GetContainer(void);
   //---アクティベーションと非アクティブ化
   bool              Active(void) const;
   void              Active(const bool);
   //---セッターとゲッター       
   string            Name(void) const;
   void              Name(const string);
   int               Distance(void) const;
   void              Distance(const int);
   double            DistanceFactorLong(void) const;
   void              DistanceFactorLong(const double);
   double            DistanceFactorShort(void) const;
   void              DistanceFactorShort(const double);
   string            SymbolName(void) const;
   void              SymbolName(const string);
   //---オブジェクトポインタ
   CAccountInfo     *AccountInfo(void);
   CStop            *MainStop(void);
   CMoneys          *Moneys(void);
   COrders          *Orders(void);
   COrders          *OrdersHistory(void);
   CStops           *Stops(void);
   CSignals         *Signals(void);
   CTimes           *Times(void);
   //---オーダーマネージャ
   string            Comment(void) const;
   void              Comment(const string);
   bool              EnableTrade(void) const;
   void              EnableTrade(bool);
   bool              EnableLong(void) const;
   void              EnableLong(bool);
   bool              EnableShort(void) const;
   void              EnableShort(bool);
   int               Expiration(void) const;
   void              Expiration(const int);
   double            LotSize(void) const;
   void              LotSize(const double);
   int               MaxOrdersHistory(void) const;
   void              MaxOrdersHistory(const int);
   int               Magic(void) const;
   void              Magic(const int);
   uint              MaxTrades(void) const;
   void              MaxTrades(const int);
   int               MaxOrders(void) const;
   void              MaxOrders(const int);
   int               OrdersTotal(void) const;
   int               OrdersHistoryTotal(void) const;
   int               TradesTotal(void) const;
   //---シグナルマネージャ   
   int               Period(void) const;
   void              Period(const int);
   bool              EveryTick(void) const;
   void              EveryTick(const bool);
   bool              OneTradePerCandle(void) const;
   void              OneTradePerCandle(const bool);
   bool              PositionReverse(void) const;
   void              PositionReverse(const bool);
   //---追加のロウソク足
   void              AddCandle(const string,const int);
   //---新しい足の検出
   void              DetectNewBars(void);
   //--イベント
   virtual bool      OnTick(void);
   virtual void      OnChartEvent(const int,const long&,const double&,const string&);
   virtual void      OnTimer(void);
   virtual void      OnTrade(void);
   virtual void      OnDeinit(const int,const int);
   //---リカバリ
   virtual bool      Save(const int);
   virtual bool      Load(const int);

protected:
   //---ロウソク足マネージャー   
   virtual bool      IsNewBar(const string,const int);
   //---オーダーマネージャ
   virtual void      ManageOrders(void);
   virtual void      ManageOrdersHistory(void);
   virtual void      OnTradeTransaction(COrder*) {}
   virtual datetime  Time(const int);
   virtual bool      TradeOpen(const string,const ENUM_ORDER_TYPE,double,bool);
   //---シンボルマネージャ
   virtual bool      RefreshRates(void);
   //---初期化
   void              DeinitAccount(void);
   void              DeinitCandle(void);
   void              DeinitSignals(void);
   void              DeinitSymbol(void);
   void              DeinitTimes(void);
  };

このクラス内で宣言されたクラスメソッドのほとんどは、そのコンポーネントのメソッドのラッパとして機能します。 このクラスの主要なメソッドについては、後のセクションで説明します。

初期化

EAの初期化フェーズでは、トレード戦略に必要なオブジェクト (資金管理、シグナルなど) をインスタンス化し、CExpertAdvisor のインスタンスと統合することが主な目標です。 OnInitに作成されます。 この目的によって、EA内でイベント関数のいずれかがトリガされ、CExpertAdvisor インスタンスの適切なハンドラまたはメソッドを呼び出すコードの1行だけが必要になります。 MQL5標準ライブラリ CExpert のメソッドとよく似ています。

CExpertAdvisor のインスタンスを作成した後、次に呼び出すメソッドは Init メソッドです。 このメソッドのコードを以下に示します。

bool CExpertAdvisorBase::Init(string symbol,int period,int magic,bool every_tick=true,bool one_trade_per_candle=true,bool position_reverse=true)
  {
   m_symbol_name=symbol;
   CSymbolInfo *instrument;
   if((instrument=new CSymbolInfo)==NULL)
      return false;
   if(symbol==NULL) symbol=Symbol();
   if(!instrument.Name(symbol))
      return false;
   instrument.Refresh();
   m_symbol_man.Add(instrument);
   m_symbol_man.SetPrimary(m_symbol_name);
   m_period=(ENUM_TIMEFRAMES)period;
   m_every_tick=every_tick;
   m_order_man.Magic(magic);
   m_position_reverse=position_reverse;
   m_one_trade_per_candle=one_trade_per_candle;
   CCandle *candle=new CCandle();
   candle.Init(instrument,m_period);
   m_candle_man.Add(candle);
   Magic(magic);
   return false;
  }

ここでは、トレード戦略でよく見られるほとんどのコンポーネントのインスタンスを作成します。 これは、使用するシンボルまたはツール (オブジェクトの種類に変換する)、およびデフォルトの期間または時間枠が含まれます。 また、すべての新しいティックで、もしくは各ロウソクの最初のティックで、そのコアタスクを操作する必要があるかどうか規則が含まれているかどうか、ロウソクごとに1つのトレードの最大値を制限する必要があります。

CExpertAdvisor のインスタンスは、InitComponents メソッドの呼び出しを行う必要があります。 次のコードは、CExpertBase というメソッドを示しています。

bool CExpertAdvisorBase::InitComponents (void)
  {
   if(!InitSignals())
     {
      Print(__FUNCTION__+": error in signal initialization");
      return false;
     }
   if(!InitTimes())
     {
      Print(__FUNCTION__+": error in time initialization");
      return false;
     }
   if(!InitOrderManager())
     {
      Print(__FUNCTION__+": error in order manager initialization");
      return false;
     }
   if(!InitCandleManager())
     {
      Print(__FUNCTION__+": error in candle manager initialization");
      return false;
     }
   if(!InitEventAggregator())
     {
      Print(__FUNCTION__+": error in event aggregator initialization");
      return false;
     }
   return true;
  }

このメソッドでは、EAインスタンスの各コンポーネントの Init メソッドが呼び出されます。 また、このメソッドを使用して、各コンポーネントの Validate メソッドを呼び出して、設定が検証に合格するかどうかを確認します。

新しい足の検出

トレード戦略は、新しいロウソクの最初のティックで動作するために必要です。 この関数を実装するには、さまざまな方法があります。 1つは CCandle のクラスで実行されるメソッドである前の状態へ現在のロウソクの開いた時間そして開いた価格の比較です。 次のコードは、CCandle の基になる CCandleBase の宣言を示しています。

class CCandleBase : public CObject
  {
protected:
   bool              m_new;
   bool              m_wait_for_new;
   bool              m_trade_processed;
   int               m_period;
   bool              m_active;
   MqlRates          m_last;
   CSymbolInfo      *m_symbol;
   CEventAggregator *m_event_man;
   CObject          *m_container;
public:
                     CCandleBase(void);
                    ~CCandleBase(void);
   virtual int       Type(void) const {return(CLASS_TYPE_CANDLE);}
   virtual bool      Init(CSymbolInfo*,const int);
   virtual bool      Init(CEventAggregator*);
   CObject          *GetContainer(void);
   void              SetContainer(CObject*);
   //---セッターとゲッター
   void              Active(bool);
   bool              Active(void) const;
   datetime          LastTime(void) const;
   double            LastOpen(void) const;
   double            LastHigh(void) const;
   double            LastLow(void) const;
   double            LastClose(void) const;
   string            SymbolName(void) const;
   int               Timeframe(void) const;
   void              WaitForNew(bool);
   bool              WaitForNew(void) const;
   //---処理
   virtual bool      TradeProcessed(void) const;
   virtual void      TradeProcessed(bool);
   virtual void      Check(void);
   virtual void      IsNewCandle(bool);
   virtual bool      IsNewCandle(void) const;
   virtual bool      Compare(MqlRates &) const;
   //---リカバリ
   virtual bool      Save(const int);
   virtual bool      Load(const int);
  };

チャート上の新しいロウソクの存在の確認は、以下に示すように、Checkメソッドを介して行われます:

CCandleBase::Check(void)
  {
   if(!Active())
      return;
   IsNewCandle(false);
   MqlRates rates[];
   if(CopyRates(m_symbol.Name(),(ENUM_TIMEFRAMES)m_period,1,1,rates)==-1)
      return;
   if(Compare(rates[0]))
     {
      IsNewCandle(true);
      TradeProcessed(false);
      m_last=rates[0];
     }
  }

新しい足をチェックする場合、EAのインスタンスは常にこのメソッドをティックごとに呼び出す必要があります。 その後、コーダーは、新しいロウソクがチャートに表示されたときに追加のタスクを実行できるように、CCxpertAdvisor を拡張するのは自由です。

上記のコードに示すように、足のオープン時間とオープン価格の比較は、次のコードに示すクラスの Compare メソッドを通じて行われます。

bool CCandleBase::Compare(MqlRates &rates) const
  {
   return (m_last.time!=rates.time ||
           (m_last.open/m_symbol.TickSize())!=(rates.open/m_symbol.TickSize()) || 
           (!m_wait_for_new && m_last.time==0));
  }

新しい足の存在をチェックするこのメソッドは、3つの条件に依存します。 少なくとも1つを満たすことは、チャート上の新しいロウソクの存在を示す真の結果を保証します:

  1. 最後に記録されたオープンタイムは、現在の足のオープン時刻と等しくありません。
  2. 最後に記録されたオープン価格は、現在の足のオープン価格と等しくありません
  3. 最後に記録されたオープン時間はゼロで、新しい足はその足の最初のティックである必要はありません

最初の2つの条件には、現在の足のレートと前回の記録済み状態との直接的な比較が含まれます。 3番目の条件は、EAが遭遇する最初のティックにのみ適用されます。 EAがチャートにロードされた直後、まだレート (オープン時間とオープン価格) の任意の以前のレコードを持っていないので、最後に記録されたオープン時間はゼロになります。 あるトレーダーは、EAの新しい足で検討し、またあるトレーダーはEAの初期化後にチャートに新しい足が表示されるのを待つことを好むでしょう.

前に説明したクラスの他の型と同様に、クラス CCandle にもコンテナ、CCandleManager があります。 次のコードは、CCandleManagerBase の宣言を示しています。

class CCandleManagerBase : public CArrayObj
  {
protected:
   bool              m_active;
   CSymbolManager   *m_symbol_man;
   CEventAggregator *m_event_man;
   CObject          *m_container;
public:
                     CCandleManagerBase(void);
                    ~CCandleManagerBase(void);
   virtual int       Type(void) const {return(CLASS_TYPE_CANDLE_MANAGER);}
   virtual bool      Init(CSymbolManager*,CEventAggregator*);
   virtual bool      Add(const string,const int);
   CObject          *GetContainer(void);
   void              SetContainer(CObject *container);
   bool              Active(void) const;
   void              Active(bool active);
   virtual void      Check(void) const;
   virtual bool      IsNewCandle(const string,const int) const;
   virtual CCandle *Get(const string,const int) const;
   virtual bool      TradeProcessed(const string,const int) const;
   virtual void      TradeProcessed(const string,const int,const bool) const;
   //---リカバリ
   virtual bool      Save(const int);
   virtual bool      Load(const int);
  };

CCandle のインスタンスは、ツールの名前と時間枠に基づいて作成されます。 CCandleManager を持つことで、同じEAで EURUSD M15 と EURUSD H1 の新しいロウソクの発生をチェックする能力を持ちます。例えば、特定のツールの複数のチャートを追跡することが容易になります。 同じシンボルとタイムフレームを持つ CCandle のインスタンスは冗長であり、回避する必要があります。 CCandle の特定のインスタンスを探している場合、1つは CCandleManager で見つかった適切なメソッドを呼び出し、シンボルとタイムフレームを指定するだけです。 CCandleManager は、適切な CCandle インスタンスを検索し、目的のメソッドを呼び出します。

別に新しいロウソクが発生することから、CCandle および CCandleManager は他の目的があります。つまり、EA内の与えられたシンボルそして時間枠にトレードが入ったかどうか確認します。 シンボルの最近のトレードは確認することができますが、時間枠にありません。 このフラグのトグルは、必要に応じて CExpertAdvisor 自体のインスタンスによって設定またはリセットする必要があります。 どちらのクラスでも、TradeProcessed メソッドを使用してトグルを設定できます。

ロウソク足マネージャの場合、TradeProcessed メソッド (ゲッターとセッター) は、リクエストされた CCandle のインスタンスを検索し、適切な値を適用するだけで対処します。

bool CCandleManagerBase::TradeProcessed(const string symbol,const int timeframe) const
  {
   CCandle *candle=Get(symbol,timeframe);
   if(CheckPointer(candle))
      return candle.TradeProcessed();
   return false;
  }

CCandle について、このプロセスは、そのクラスのメンバ、m_trade_processed のいずれかに新しい値の割り当てが含まれます。 次のメソッドは、上記のクラスメンバの値の設定を処理します。

bool CCandleBase::TradeProcessed(void) const
  {
   return m_trade_processed;
  }

CCandleBase::TradeProcessed(bool value)
  {
   m_trade_processed=value;
  }

OnTick ハンドラ

CExpertAdvisor の OnTick メソッドは、クラス内で最も使用される関数です。 アクションのほとんどがこのメソッドからです。 このメソッドの中核となる操作を次の図に示します。


CExpertAdvisorBase OnTick


このプロセスは、EAのティックフラグを切り替えることから始まります。 ティックの二重処理が発生しないようにするためです。 CExpertAdvisor の OnTick メソッドは、OnTick イベント関数内でのみ呼び出されるのが理想的ですが、OnChartEvent などの他の手段を通じて呼び出すこともできます。 このフラグがない場合、以前のティックを処理している間にOnTick メソッドが呼び出された場合、ティックは複数回処理され、ティックがトレードを生成する場合、多くの場合、重複するトレードになります。

EAが最新の相場データにアクセスできるようになり、以前のティックは再処理されないため、データの更新も必要になります。 EAがデータの更新に失敗した場合は、ティックプロセスフラグをリセットし、メソッドを終了して、新しいティックを待機します。

次のステップは、新しい足の検出とトレードシグナルのチェックです。 このチェックは、デフォルトではすべてのティックで行われます。 ただし、新しいシグナルが検出されたときにのみシグナルをチェックするように、このメソッドを拡張することが可能です (特にバックと最適化の間に、処理時間を短縮するため)。

このクラスはまた、現在のシグナルの反対側のポジションを逆にすることを意図したメンバ、m_position_reverse を提供します。 ここで実行される反転は、現在のポジションの中和に対してのみ行われます。 MetaTrader4とMetaTrader5ヘッジモードでは、現在のシグナルの反対側にあるトレードで決済します。 (現在のシグナルのものは決済されません) 。 MetaTrader5 のネットモードでは、ある時点で1つのポジションしか存在しないので、現在ポジションと同じボリュームの新しいポジションをエントリーします。

トレードシグナルは、主に m_signals を使用してチェックされています。しかし、新しい足のみでトレードなどの他の要因, とタイムフィルタ, また、EAが新しいトレードを実行することを防ぐことができます. すべての条件が満たされている場合にのみ、EA は新しいトレードをエントリーすることができます。

ティックの処理が終了すると、EAによってティックフラグが false に設定され、別のティックの処理が許可されます。

EAコンテナ

前の記事で説明した他のクラスオブジェクトと同様に、CExpertAdvisor クラスには指定されたコンテナもありますが、 CExpertAdvisors です。 次のコードは、基本クラス CExpertAdvisorsBase の宣言を示しています。

class CExpertAdvisorsBase : public CArrayObj
  {
protected:
   bool              m_active;
   int               m_uninit_reason;
   CObject          *m_container;
public:
                     CExpertAdvisorsBase(void);
                    ~CExpertAdvisorsBase(void);
   virtual int       Type(void) const {return CLASS_TYPE_EXPERTS;}
   virtual int       UninitializeReason(void) const {return m_uninit_reason;}
   //---ゲッターとセッター
   void              SetContainer(CObject *container);
   CObject          *GetContainer(void);
   bool              Active(void) const;
   void              Active(const bool);
   int               OrdersTotal(void) const;
   int               OrdersHistoryTotal(void) const;
   int               TradesTotal(void) const;
   //---初期化
   virtual bool      Validate(void) const;
   virtual bool      InitComponents(void) const;
   //---イベント
   virtual void      OnTick(void);
   virtual void      OnChartEvent(const int,const long&,const double&,const string&);
   virtual void      OnTimer(void);
   virtual void      OnTrade(void);
   virtual void      OnDeinit(const int,const int);
   //---リカバリ
   virtual bool      CreateElement(const int);
   virtual bool      Save(const int);
   virtual bool      Load(const int);
  };

このコンテナは、主に CExpertAdvisor クラスで検出されたパブリックメソッドをミラーリングします。 この例は、OnTick ハンドラです。 メソッドは、OnTick メソッドを呼び出すために、CExpertAdvisor の各インスタンスで繰り返し処理を行うだけです。

void CExpertAdvisorsBase::OnTick(void)
  {
   if(!Active()) return;
   for(int i=0;i<Total();i++)
     {
      CExpertAdvisor *e=At(i);
      e.OnTick();
     }
  }

このコンテナを使用すると、CExpertAdvisor の複数のインスタンスを格納することができます。 1つのチャートインスタンスで複数のEAを実行する唯一の方法です。 CExpertAdvisor の複数のインスタンスを初期化し、ポインタを単一の CExpertAdvisors コンテナの下に格納してから、コンテナの OnTick メソッドを使用して、各 CExpertAdvisor インスタンスの OnTick メソッドをトリガします。 CArrayObj クラスまたはその継承を使用して、MQL5 標準ライブラリの CExpert クラスの各インスタンスでも同じことを行うことができます。

データの永続性

CArrayObj クラスまたはその継承者を使用して、MQL5 標準ライブラリの CExpert クラスの各インスタンスでも同じことを行うことができます。 通常、データは多くの場合、プラットフォームに格納され、EAは、関数呼び出しを通じてプラットフォーム自体から必要なデータを取得します。 ただし、EAの実行中に動的に作成されたデータについては、通常はそうではありません。 EAで OnDeinit イベントがトリガされると、EAはすべてのオブジェクトを破棄し、データを失います。

OnDeinitは、トレードプラットフォーム全体 (メタトレーダー4またはメタトレーダー 5) を閉じたり、チャートからEAをアンロードしたり、EAのソースコードを再コンパイルしたりするなど、メソッドでトリガできます。 初期化をトリガーできる可能性のあるイベントの一覧は、 UninitializeReason関数を使用して見つけることができます。 EAがデータにアクセスできなくなると、初めてチャートにロードされたかのように動作する場合があります。

CExpertAdvisor クラスのボラティリティデータのほとんどは、COrderManager のインスタンスであるメンバの1つにあります。 EAが通常のルーチンを実行するときに、COrderと COrderStop (および子孫) のインスタンスが作成される場所です。 インスタンスは OnTick 中に動的に作成されるので、EAが再を実行しても再現されません。 したがって、EAは、ボラティリティデータを保存および取得するためのメソッドを実装する必要があります。 実装する1つの方法は、 CFileBinクラス CExpertFile の子孫を使用することです。 次のコードスニペットは、CExpertFileBase の基本クラスの宣言を示しています。

class CExpertFileBase : public CFileBin
  {
public:
                     CExpertFileBase(void);
                    ~CExpertFileBase(void);
   void              Handle(const int handle) { m_handle=handle; };
   uint              WriteBool(const bool value);
   bool              ReadBool(bool &value);
  };

ここでは、CFileBin を拡張して、 Boolean型のデータを読み書きするメソッドを明示的に宣言しています。

クラスファイルの最後に、CExpertFile クラスのインスタンスを宣言します。 このインスタンスは、ボラティリティデータを保存してロードする場合に、EA全体で使用されます。 または、 CObjectから継承されたSaveメソッドとLoadメソッドに依存するだけでなく、通常のメソッドでデータの保存と読み込みを処理することもできます。 しかし、これは非常に厳格なプロセスです。 多くの労力とコードは、CFile (またはその継承) を使用して保存できます。

//CExpertFileBase class definition
//+------------------------------------------------------------------+
#ifdef __MQL5__
#include "..\..\MQL5\File\ExpertFile.mqh"
#else
#include "..\..\MQL4\File\ExpertFile.mqh"
#endif
//+------------------------------------------------------------------+
CExpertFile file;
//+------------------------------------------------------------------+

オーダーマネージャは、保存メソッドを使用してボラティリティデータを保存します。

bool COrderManagerBase::Save(const int handle)
  {
   if(handle==INVALID_HANDLE)
      return false;
   file.WriteDouble(m_lotsize);
   file.WriteString(m_comment);
   file.WriteInteger(m_expiration);
   file.WriteInteger(m_history_count);
   file.WriteInteger(m_max_orders_history);
   file.WriteBool(m_trade_allowed);
   file.WriteBool(m_long_allowed);
   file.WriteBool(m_short_allowed);
   file.WriteInteger(m_max_orders);
   file.WriteInteger(m_max_trades);
   file.WriteObject(GetPointer(m_orders));
   file.WriteObject(GetPointer(m_orders_history));
   return true;
  }

データのほとんどはプリミティブ型であり、最後の2つを除いて、オーダーおよびヒストリカルオーダーコンテナです。 データについては、 CFileBinWriteObjectメソッドを使用して、書き込むオブジェクトの Save メソッドを呼び出すだけです。 次のコードは、COrderBase の Save メソッドを示しています。

bool COrderBase::Save(const int handle)
  {
   if(handle==INVALID_HANDLE)
      return false;
   file.WriteBool(m_initialized);
   file.WriteBool(m_closed);
   file.WriteBool(m_suspend);
   file.WriteInteger(m_magic);
   file.WriteDouble(m_price);
   file.WriteLong(m_ticket);
   file.WriteEnum(m_type);
   file.WriteDouble(m_volume);
   file.WriteDouble(m_volume_initial);
   file.WriteString(m_symbol);
   file.WriteObject(GetPointer(m_order_stops));
   return true;
  }

ここでご覧の通り、このプロセスはオブジェクトを保存するときだけ繰り返されます。 プリミティブデータ型の場合、データは通常どおりファイルに保存されます。 複雑なデータ型の場合、オブジェクトの Save メソッドは CFileBin の WriteObject メソッドを通じて呼び出されます。

CExpertAdvisor の複数のインスタンスが存在する場合、コンテナ CExpertAdvisors にはデータを保存する容量も必要です。

bool CExpertAdvisorsBase::Save(const int handle)
  {
   if(handle!=INVALID_HANDLE)
     {
      for(int i=0;i<Total();i++)
        {
         CExpertAdvisor *e=At(i);
         if(!e.Save(handle))
            return false;
        }
     }
   return true;
  }

このメソッドは、各 CExpertAdvisor インスタンスの Save メソッドを呼び出します。 1つのファイルハンドルは、EAファイルごとに1つのセーブファイルのみが存在することを意味します。 CExpertAdvisor インスタンスが独自の save ファイルを持つことは可能ですが、より複雑なアプローチになります。

より複雑な部分は、データの読み込みです。 データの保存では、一部のクラスメンバの値はファイルに書き込まれます。 一方、データを読み込むときには、オブジェクトインスタンスを保存する前と同じ状態で再作成する必要があります。 次のコードは、オーダーマネージャの Load メソッドを示しています。

bool COrderManagerBase::Load(const int handle)
  {
   if(handle==INVALID_HANDLE)
      return false;
   if(!file.ReadDouble(m_lotsize))
      return false;
   if(!file.ReadString(m_comment))
      return false;
   if(!file.ReadInteger(m_expiration))
      return false;
   if(!file.ReadInteger(m_history_count))
      return false;
   if(!file.ReadInteger(m_max_orders_history))
      return false;
   if(!file.ReadBool(m_trade_allowed))
      return false;
   if(!file.ReadBool(m_long_allowed))
      return false;
   if(!file.ReadBool(m_short_allowed))
      return false;
   if(!file.ReadInteger(m_max_orders))
      return false;
   if(!file.ReadInteger(m_max_trades))
      return false;
   if(!file.ReadObject(GetPointer(m_orders)))
      return false;
   if(!file.ReadObject(GetPointer(m_orders_history)))
      return false;
   for(int i=0;i<m_orders.Total();i++)
     {
      COrder *order=m_orders.At(i);
      if(!CheckPointer(order))
         continue;
      COrderStops *orderstops=order.OrderStops();
      if(!CheckPointer(orderstops))
         continue;
      for(int j=0;j<orderstops.Total();j++)
        {
         COrderStop *orderstop=orderstops.At(j);
         if(!CheckPointer(orderstop))
            continue;
         for(int k=0;k<m_stops.Total();k++)
           {
            CStop *stop=m_stops.At(k);
            if(!CheckPointer(stop))
               continue;
            orderstop.Order(order);
            if(StringCompare(orderstop.StopName(),stop.Name())==0)
              {
               orderstop.Stop(stop);
               orderstop.Recreate();
              }
           }
        }
     }
   return true;
  }

COrderManager の上記のコードは、CExpertAdvisor の Load メソッドと対照的にはるかに複雑です。 その理由は、オーダーマネージャとは異なり、CExpertAdvisor のインスタンスは、CExpertAdvisor の間に作成されるため、コンテナは CFileBin の ReadObject メソッドを使用するのではなく、各インスタンスの Load メソッドを呼び出す必要があります。

作成されなかったクラスインスタンスは、EAを再読み込みするときにも作成する必要があります。 これは、 CArrayObjCreateElementメソッドを拡張することによって実現されます。 オブジェクトは独自に作成するだけで、親オブジェクトまたはコンテナ、あるいはメインソースまたはヘッダーファイル自体から作成することができます。 この例はCOrdersBase で見つかった拡張 CreateElement メソッドで見ることができます。 このクラスでは、コンテナは COrders (COrdersBase の子孫) であり、作成されるオブジェクトはCOrder型です。

bool COrdersBase::CreateElement(const int index)
  {
   COrder*order=new COrder();
   if(!CheckPointer(order))
      return(false);
   order.SetContainer(GetPointer(this));
   if(!Reserve(1))
      return(false);
   m_data[index]=order;
   m_sort_mode=-1;
   return CheckPointer(m_data[index]);
  }

ここでは、要素を作成する以外に、また、アクティブなトレードのリスト (COrderManagerBase の m_orders クラスのメンバ) またはヒストリー (COrderManagerBase の m_orders_history) に属しているかどうかを区別するために、その親オブジェクトまたはコンテナを設定します。


この記事の例 #1-#4 は、前の記事で見つかった4つの例の変更されたバージョンです (「クロスプラットフォームEA: カスタムストップ、トレーリングストップ、損益)」を参照してください。 最も複雑な例を見てみましょう。custom_trail_ha_ma の修正されたバージョンexpert_custom_trail_ha_ma.mqh

OnInit関数の前に、次のグローバルオブジェクトインスタンスを宣言しました。

COrderManager *order_manager;
CSymbolManager *symbol_manager;
CSymbolInfo *symbol_info;
CSignals *signals;
CMoneys *money_manager;
CTimes *time_filters;

CExpert のインスタンスに置き換えます。 上記のいくつかは CExpetAdvisor 自体 (例えば、COrderManager) 内にありますが、残りは、(すなわちコンテナ) の間にインスタンス化される必要があります:

CExpertAdvisors experts;

メソッドの最初に、CExpertAdvisor のインスタンスを作成します。 また、最も基本的な設定をする Init メソッドを呼び出します。

int OnInit()
  {
//---
   CExpertAdvisor *expert=new CExpertAdvisor();
   expert.Init(Symbol(),Period(),12345,true,true,true);
//--- その他のコード
//---
   return(INIT_SUCCEEDED);
  }

CSymbolInfo/CSymbolManager は、CExpertAdvisor クラスのインスタンスが独自にクラスのインスタンスを作成できるため、インスタンス化する必要がなくなりました。

ユーザー定義関数は、新しいEAがを必要としなくなるため、削除しなければなりません。

コード内のコンテナのグローバル宣言を削除したので、そのコンテナの中から宣言する必要があります。 この例は、次のコードに示すように、CTimeFilters 関数内で見つかったタイムフィルタコンテナです。

CTimes *time_filters=new CTimes();

オーダーマネージャに以前に "追加" されたコンテナへのポインタは、代わりに CExpertAdvisor のインスタンスに追加されます。 オーダーマネージャに追加されていない他のすべてのコンテナは、CExpertAdvisor インスタンスにも追加する必要があります。 ポインターを格納する COrderManager のインスタンスになります。 CExpertAdvisor インスタンスは、ラッパーメソッドのみを作成します。

この後、CExpertAdvisor インスタンスを CExpertAdvisors のインスタンスに追加します。 次に、CExpertAdvisors インスタンスの InitComponents メソッドを呼び出します。 CExpertAdvisor とそのコンポーネントのすべてのインスタンスが初期化されるようになります。

int OnInit()
  {
//---
//--- その他のコード
   experts.Add(GetPointer(expert));
   if(!experts.InitComponents())
      return(INIT_FAILED);
//--- その他のコード
//---
   return(INIT_SUCCEEDED);
  }

最後に、EAが動作中に中断された場合にロードに必要なコードを挿入します。

int OnInit()
  {
//---
//---その他のコード   
 file.Open(savefile,FILE_READ);
   if(!experts.Load(file.Handle()))
      return(INIT_FAILED);
   file.Close();
//---
   return(INIT_SUCCEEDED);
  }

EAがファイルからの読み込みに失敗すると、 INIT_FAILEDが返されます。 ただし、保存ファイルが指定されていない (INVALID_HANDLEを生成する) イベントでは、CExpertAdvisors と CExpertAdvisor の Load メソッドは両方とも無効な値を受け取ったときに true を返すため、EAは初期化に失敗しません。 この方法にはリスクがありますが、保存ファイルを別のプログラムで開くことはほとんどありません。 チャート上で実行されているEAの各インスタンスに、排他的な保存ファイル (マジックナン足と同じように) があることを確認してください。

5番目の例は、前の記事では見つかりません。 むしろ、この記事の4つのEAをすべて1つのEAにまとめています。 EAのそれぞれの関数のわずかに変更されたバージョンを使用して、カスタム定義関数として宣言します。 戻り値の型は CExpertAdvisor * です。 EAの作成に失敗した場合は、 INIT_SUCCEEDEDではなくNULLが返されます。 次のコードは、EAのヘッダーファイルの更新された関数を示しています。

int OnInit()
  {
//---
   CExpertAdvisor *expert1=expert_breakeven_ha_ma();
   CExpertAdvisor *expert2=expert_trail_ha_ma();
   CExpertAdvisor *expert3=expert_custom_stop_ha_ma();
   CExpertAdvisor *expert4=expert_custom_trail_ha_ma();
      
   if (!CheckPointer(expert1))
      return INIT_FAILED;
   if (!CheckPointer(expert2))
      return INIT_FAILED;
   if (!CheckPointer(expert3))
      return INIT_FAILED;
   if (!CheckPointer(expert4))
      return INIT_FAILED;
   
   experts.Add(GetPointer(expert1));
   experts.Add(GetPointer(expert2));
   experts.Add(GetPointer(expert3));
   experts.Add(GetPointer(expert4));   
   
   if(!experts.InitComponents())
      return(INIT_FAILED);
   file.Open(savefile,FILE_READ);
   if(!experts.Load(file.Handle()))
      return(INIT_FAILED);
   file.Close();
//---
   return(INIT_SUCCEEDED);
  }

このEAは、CExpertAdvisor の各インスタンスを初期化することから始まります。 その後、CExpertAdvisor への各ポインタのチェックに進みます。 ポインターが動的でない場合、関数は INIT_FAILED を返し、初期化は失敗します。 各インスタンスがポインタのチェックに合格すると、ポインタは CExpertAdvisors のインスタンスに格納されます。 CExpertAdvisors インスタンス (EAインスタンスではなくコンテナ) は、そのコンポーネントを初期化し、必要に応じて以前のデータを読み込みます。

このEAは、カスタム定義関数を使用して CExpertAdvisor のインスタンスを作成します。 次のコードは、4番目のEAインスタンスの作成に使用される関数を示しています。

CExpertAdvisor *expert_custom_trail_ha_ma()
{
   CExpertAdvisor *expert=new CExpertAdvisor();
   expert.Init(Symbol(),Period(),magic4,true,true,true);
   CMoneys *money_manager=new CMoneys();
   CMoney *money_fixed=new CMoneyFixedLot(0.05);
   CMoney *money_ff=new CMoneyFixedFractional(5);
   CMoney *money_ratio=new CMoneyFixedRatio(0,0.1,1000);
   CMoney *money_riskperpoint=new CMoneyFixedRiskPerPoint(0.1);
   CMoney *money_risk=new CMoneyFixedRisk(100);

   money_manager.Add(money_fixed);
   money_manager.Add(money_ff);
   money_manager.Add(money_ratio);
   money_manager.Add(money_riskperpoint);
   money_manager.Add(money_risk);
   expert.AddMoneys(GetPointer(money_manager));

   CTimes *time_filters=new CTimes();
   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));
     }
   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));
     }
   if(timer_enabled)
     {
      CTimer *timer=new CTimer(timer_minutes*60);
      timer.TimeStart(TimeCurrent());
      time_filters.Add(GetPointer(timer));
     }

   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;
     }
   expert.AddTimes(GetPointer(time_filters));

   CStops *stops=new CStops();
   CCustomStop *main=new CCustomStop("main");
   main.StopType(stop_type_main);
   main.VolumeType(VOLUME_TYPE_PERCENT_TOTAL);
   main.Main(true);
//main.StopLoss(stop_loss);
//main.TakeProfit(take_profit);
   stops.Add(GetPointer(main));

   CTrails *trails=new CTrails();
   CCustomTrail *trail=new CCustomTrail();
   trails.Add(trail);
   main.Add(trails);

   expert.AddStops(GetPointer(stops));

   MqlParam params[1];
   params[0].type=TYPE_STRING;
#ifdef __MQL5__
   params[0].string_value="Examples\\Heiken_Ashi";
#else
   params[0].string_value="Heiken Ashi";
#endif
   SignalHA *signal_ha=new SignalHA(Symbol(),0,1,params,signal_bar);
   SignalMA *signal_ma=new SignalMA(Symbol(),(ENUM_TIMEFRAMES) Period(),maperiod,0,mamethod,maapplied,signal_bar);
   CSignals *signals=new CSignals();
   signals.Add(GetPointer(signal_ha));
   signals.Add(GetPointer(signal_ma));
   expert.AddSignal(GetPointer(signals));
//---
   return expert;
}

ご覧の通り、コードは非常に元のEAのヘッダーファイル (expert_custom_trail_ha_ma mqh) の関数に似ています。 他のカスタム定義関数も同じように編成されています。

最終ノート

この記事を締めくくる前に、このライブラリの使用を希望する場合、ライブラリの開発に関わる要因を認識する必要があります:

この記事の執筆時点では、この記事に掲載されているライブラリには、1万行以上のコード (コメントを含む) があります。 それもかかわらず、まだ進行中のタスクもあります。 MQL4 と MQL5 の両方の関数を十分に活用するためには、より多くのタスクを行う必要があります。

この著者は、メタトレーダーのヘッジモードの導入前に、このプロジェクトに取り組み始めました。. これはライブラリの更なる発展に大きく影響を及ぼした。 その結果、ライブラリは MetaTrader5 よりもMetaTrader4で使用されるルールを採用する方がより緊密になりがちです。 さらに、この著者は、互換性問題を経験しました。更新は、数年にわたって行われました。 この記事の執筆時点では、作成者は両方のプラットフォームのビルド更新を経験しましたが、時間の経過とともにより安定しています。 この傾向は今後改善すると予想されます。 にもかかわらず、互換性のない可能性がある将来のビルドの更新には対処する必要があります。

このライブラリは、メモリに保存されたデータに依存して、独自のトレードを追跡します。 これより、このライブラリを使用して作成されたEAは、実行中にEAが発生する可能性のある中断に対処するために、データの保存と読み込みが大きく依存します。 このライブラリの将来のタスク、およびクロスプラットフォームの互換性を目指す他のライブラリについては、MQL5 標準ライブラリの実装と同様にステートレスまたはニアステートレスの実装を実現する必要があります。

最後の発言として、この記事に掲載されているライブラリは、永続的な解決策とするべきではありません。 むしろ、MetaTrader4 から MetaTrader5 へのスムーズな移行の機会として使われるべきです。 MQL4 と MQL5 の間の非互換性は、新しいプラットフォームに移行しようとするトレーダーに巨大なバリケードをもたらします。 その結果、MQL5 コンパイラーと互換性を持たせるために、EAの MQL4 ソースコードをリファクタリングする必要があります。 この記事に記載されているライブラリは、EAを新しいプラットフォームに導入するための手段として用意されています。 これがトレーダーの助けになるかどうかはメタトレーダーの切り替えの意思決定によります。 切り替えることを決めた場合、非常に少ない調整で可能となります。 一方、古いプラットフォームを使用し続けることを決定した場合でも、MetaTrader4がレガシーソフトウェアになった場合、新しいプラットフォームに切り替えるためのオプションを提供されています。

結論

この記事では、この記事シリーズで説明したクロスプラットフォームEAのすべてのコンポーネントを統合するために使用される、CExpertAdvisor および CExpertAdvisors クラスのオブジェクトを紹介しました。 この記事では、2つのクラスをインスタンス化し、クロスプラットフォームEAの他のコンポーネントとリンクする方法について説明します。 また、新しい足の検出やボラティリティデータの保存と読み込みなど、EAが通常遭遇する問題に対するソリューションも紹介しています。

記事で使用されるプログラム

 # Name
Type
Description
1.
expert_breakeven_ha_ma.mqh
ヘッダーファイル
最初の例で使用されるメインヘッダーファイル
2.
expert_breakeven_ha_ma.mq4 EA
最初の例の MQL4 バージョンで使用されるメインソースファイル
3.
expert_breakeven_ha_ma.mq5 EA 最初の例の MQL5 バージョンで使用されるメインソースファイル
4.
 expert_trail_ha_ma.mqh ヘッダーファイル 2番目の例で使用されるメインヘッダーファイル
5.
 expert_trail_ha_ma.mq4 EA 2番目の例の MQL4 バージョンで使用されるメインソースファイル
6.
 expert_trail_ha_ma.mq5 EA 2番目の例の MQL5 バージョンで使用されるメインソースファイル
7.
 expert_custom_stop_ha_ma.mqh ヘッダーファイル 3番目の例で使用されるメインヘッダーファイル
8.
 expert_custom_stop_ha_ma.mq4 EA 3番目の例の MQL4 バージョンで使用されるメインソースファイル
9.
 expert_custom_stop_ha_ma.mq5 EA 3番目の例の MQL5 バージョンで使用されるメインソースファイル
10.
 expert_custom_trail_ha_ma.mqh ヘッダーファイル 4番目の例で使用されるメインヘッダーファイル
11.
 expert_custom_trail_ha_ma.mq4 EA 4番目の例の MQL4 バージョンで使用されるメインソースファイル
12.
 expert_custom_trail_ha_ma.mq5 EA 4番目の例の MQL5 バージョンで使用されるメインソースファイル
13.
 combined.mqh ヘッダーファイル 5番目の例で使用されるメインヘッダーファイル
14.
 combined.mq4 EA 5番目の例の MQL4 バージョンで使用されるメインソースファイル
15.
 combined.mq5 EA 5番目の例の MQL5 バージョンで使用されるメインソースファイル

この記事で紹介されているクラスファイル

#
Name
Type
Description
1. MQLx\Base\Expert\ExperAdvisorsBase ヘッダーファイル
CExpertAdvisors (CExpertAdvisor コンテナ、基本クラス)
2.
MQLx\MQL4\Expert\ExperAdvisors ヘッダーファイル CExpertAdvisors (MQL4 版)
3.
MQLx\MQL5\Expert\ExperAdvisors ヘッダーファイル
CExpertAdvisors (MQL5 バージョン)
4.
MQLx\Base\Expert\ExperAdvisorBase ヘッダーファイル
CExpertAdvisor (基本クラス)
5.
MQLx\MQL4\Expert\ExperAdvisor ヘッダーファイル
CExpertAdvisor (MQL4 version)
6.
MQLx\MQL5\Expert\ExperAdvisor ヘッダーファイル
CExpertAdvisor (MQL5 バージョン)
7.
MQLx\Base\Candle\CandleManagerBase ヘッダーファイル CCandleManager (CCandle コンテナ、基本クラス)
8.
MQLx\MQL4\Candle\CandleManager ヘッダーファイル CCandleManager (MQL4 版)
9.
MQLx\MQL5\Candle\CandleManager ヘッダーファイル CCandleManager (MQL5 バージョン)
10.
MQLx\Base\Candle\CandleBase ヘッダーファイル CCandle (基本クラス)
11.
MQLx\MQL4\Candle\Candle ヘッダーファイル CCandle (MQL4 版)
12.
MQLx\MQL5\Candle\Candle ヘッダーファイル
CCandle (MQL5 バージョン)
13.
MQLx\Base\File\ExpertFileBase ヘッダーファイル CExpertFile (基本クラス)
14.
MQLx\MQL4\File\ExpertFile ヘッダーファイル CExpertFile (MQL4 版)
15.
MQLx\MQL5\File\ExpertFile ヘッダーファイル CExpertFile(MQL5 version)


MetaQuotes Software Corp.により英語から翻訳された
元の記事: https://www.mql5.com/en/articles/3622

添付されたファイル |
MQL5.zip (143.3 KB)
拡散と収束の自動探索 拡散と収束の自動探索

本稿では、シンプル、隠れ、拡張、トリプル、クワドラプル、収束のあらゆる種類の拡散、またA、B、Cクラスの拡散を考察し、それらをチャート上で検索及び表示するためのユニバーサル指標が開発されます。

クロスプラットフォームEA: カスタムストップ、ブレイクイーブン、トレーリング クロスプラットフォームEA: カスタムストップ、ブレイクイーブン、トレーリング

この記事では、クロスプラットフォームEAでのカスタムストップレベルの設定方法について説明します。 また、時間の経過とともにストップレベルを設定するメソッドについても説明します。

適応型相場の実用的評価法 適応型相場の実用的評価法

この記事で提案するトレーディングシステムは、株価を分析するための数学的ツールです。 ディジタルフィルタリングと離散時系列のスペクトル推定を適用します。 戦略の理論的側面について説明し、テストEAを作成します。

バランスグラフを使用した戦略の最適化と、結果の「バランス+最大シャープレシオ」基準との比較 バランスグラフを使用した戦略の最適化と、結果の「バランス+最大シャープレシオ」基準との比較

本稿では、バランスグラフ分析に基づいたカスタム取引戦略最適化基準をさらにもう1つ考察します。線形回帰は、ALGLIBライブラリの関数を使用して計算されます。