クロスプラットフォームEA: CExpertAdvisor と CExpertAdvisors クラス
目次
イントロダクション
このトピックに関連する以前の記事では、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つを満たすことは、チャート上の新しいロウソクの存在を示す真の結果を保証します:
- 最後に記録されたオープンタイムは、現在の足のオープン時刻と等しくありません。
- 最後に記録されたオープン価格は、現在の足のオープン価格と等しくありません
- 最後に記録されたオープン時間はゼロで、新しい足はその足の最初のティックである必要はありません
最初の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 メソッドは、クラス内で最も使用される関数です。 アクションのほとんどがこのメソッドからです。 このメソッドの中核となる操作を次の図に示します。
このプロセスは、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つを除いて、オーダーおよびヒストリカルオーダーコンテナです。 データについては、 CFileBinのWriteObjectメソッドを使用して、書き込むオブジェクトの 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を再読み込みするときにも作成する必要があります。 これは、 CArrayObjのCreateElementメソッドを拡張することによって実現されます。 オブジェクトは独自に作成するだけで、親オブジェクトまたはコンテナ、あるいはメインソースまたはヘッダーファイル自体から作成することができます。 この例は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 Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/3622
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索