English Русский 中文 Español 日本語 Português
Cross-Plattform Expert Advisor: Die Klassen CExpertAdvisor und CExpertAdvisors Classes

Cross-Plattform Expert Advisor: Die Klassen CExpertAdvisor und CExpertAdvisors Classes

MetaTrader 5Beispiele | 6 November 2017, 09:01
716 0
Enrico Lambino
Enrico Lambino

Inhaltsverzeichnis

  1. Einführung
  2. Die Klasse des Expert Advisors
  3. Initialisierung
  4. Erkennen der neuen Bar
  5. Funktionsweise von OnTick()
  6. Der Container der Expert Advisor
  7. Persistenz der Daten
  8. Beispiele
  9. Abschließende Bemerkungen
  10. Schlussfolgerung

Einführung

In früheren Artikeln zu diesem Thema wurden in den Beispielen die Komponenten der Expert Advisor mit Hilfe von benutzerdefinierten Funktionen über die Hauptheaderdatei des Expert Advisors verstreut. Dieser Artikel stellt die Klassen CExpertAdvisor und CExpertsAdvisors vor, die darauf abzielen, ein harmonischeres Zusammenspiel zwischen den verschiedenen Komponenten eines plattformübergreifenden Expert Advisor zu schaffen. Darüber hinaus werden einige häufig auftretende Probleme, wie z. B. das Laden und Speichern flüchtiger Daten und das Erkennen neuer Bars, behandelt.

Die Klasse des Expert Advisors

Die Klasse CExpertAdvisorBase wird im folgenden Codeausschnitt gezeigt. Hier werden die meisten Unterschiede zwischen MQL4 und MQL5 von den anderen Klassenobjekten behandelt, die in früheren Artikeln besprochen wurden.

class CExpertAdvisorBase : public CObject
  {
protected:
   //--- Parameter für den Handel
   bool              m_active;
   string            m_name;
   int               m_distance;
   double            m_distance_factor_long;
   double            m_distance_factor_short;
   bool              m_on_tick_process;
   //--- Parameter für die Signale
   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;
   //--- Signalobjekte
   CSignals         *m_signals;
   //--- Objekte des Handels   
   CAccountInfo      m_account;
   CSymbolManager    m_symbol_man;
   COrderManager     m_order_man;
   //--- Objekte für die Handelszeiten
   CTimes           *m_times;
   //--- Kerze
   CCandleManager    m_candle_man;
   //--- Ereignisse
   CEventAggregator *m_event_man;
   //--- Container
   CObject          *m_container;
public:
                     CExpertAdvisorBase(void);
                    ~CExpertAdvisorBase(void);
   virtual int       Type(void) const {return CLASS_TYPE_EXPERT;}
   //--- Initialisierung
   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;
   //--- Container
   void              SetContainer(CObject*);
   CObject          *GetContainer(void);
   //--- activation and deactivation
   bool              Active(void) const;
   void              Active(const bool);
   //--- Ändern und Abfragen       
   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);
   //--- Objektpointer
   CAccountInfo     *AccountInfo(void);
   CStop            *MainStop(void);
   CMoneys          *Moneys(void);
   COrders          *Orders(void);
   COrders          *OrdersHistory(void);
   CStops           *Stops(void);
   CSignals         *Signals(void);
   CTimes           *Times(void);
   //--- Order-Manager
   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;
   //--- Signal-Manager   
   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);
   //--- weitere Kerzen
   void              AddCandle(const string,const int);
   //--- Erkennung neuer Bars
   void              DetectNewBars(void);
   //-- events
   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);
   //--- Wiederherstellen
   virtual bool      Save(const int);
   virtual bool      Load(const int);

protected:
   //--- Kerzen-Manager   
   virtual bool      IsNewBar(const string,const int);
   //--- Order-Manager
   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);
   //--- Symbol-Manager
   virtual bool      RefreshRates(void);
   //--- Deinitialisierung
   void              DeinitAccount(void);
   void              DeinitCandle(void);
   void              DeinitSignals(void);
   void              DeinitSymbol(void);
   void              DeinitTimes(void);
  };

Die meisten innerhalb dieser Klasse deklarierten Klassenmethoden dienen als Hülle zu Methoden ihrer Komponenten. Die Schlüsselmethoden dieser Klasse werden in späteren Abschnitten besprochen.

Initialisierung

Während der Initialisierungsphase des Expert Advisors ist es unser primäres Ziel, die für die Handelsstrategie benötigten Objekte (z. B. Geldmanagement, Signale etc.) zu instanziieren und dann in die Instanz von CExpertAdvisor zu integrieren, die ebenfalls während OnInit() erstellt werden muss. Zu diesem Zweck brauchen wir nur eine einzige Programmzeile, die den entsprechenden Handler oder die Methode der Instanz von CExpertAdvisor aufruft, wenn eine der Ereignisfunktionen innerhalb des Expert Advisors ausgelöst wird. Dies ist sehr ähnlich wie CExpert in der Standardbibliothek von MQL5 verwendet wird.

Nach dem Anlegen einer Instanz von CExpertAdvisor wird als nächstes Init() aufzurufen. Der Code dieser Methode ist unten dargestellt:

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;
  }

Hier erstellen wir die Instanzen der meisten Komponenten, die häufig in Handelsstrategien vorkommen. Dazu gehören das zu verwendende Symbol oder Handelsinstrument (das in einen Objekttyp übersetzt werden muss) und der voreingestellte Zeitrahmen. Sie enthält auch die Regel, ob sie ihre Kernaufgaben bei jedem neuen Tick oder nur beim ersten Tick der Kerze ausführen soll, ob sie ein Maximum von nur einem Trade pro Kerze limitieren soll (um eine mehrfache Ausführung während derselben Kerze zu verhindern) und ob sie ihre Position beim entgegengesetzten Signal umkehren soll (bestehende Position schließen und basierend auf dem neuen Signal eine neue eröffnen).

Nach dem Anlegen einer Instanz von CExpertAdvisor wird als nächstes die Methode OnInit() aufzurufen. Der Code der genannten Methode von CExpertBase ist unten dargestellt:

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;
  }

Bei dieser Methode wird von jeder Komponente der Expert Advisor-Instanz Init() aufgerufen. Durch diese Methode werden auch die Methoden Validate() der einzelnen Komponenten aufgerufen wird, um zu überprüfen, ob die Validierung ihre Einstellungen bestätigen würde.

Erkennen der neuen Bar

Einige Handelsstrategien erfordern es, ausschließlich beim ersten Tick einer neuen Kerze zu handeln. Es gibt viele Möglichkeiten, das umzusetzen. Eine davon ist der Vergleich der Eröffnungszeit und des Eröffnungspreises der aktuellen Kerze mit der vorherigen, welches in der Klasse der Kerzen die Methode der Wahl ist. Der folgende Code zeigt die Deklaration von CCandleBase, von der CCandle abgeleitet ist:

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*);
   //--- Ändern und Abfragen
   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;
   //--- Verarbeitung
   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;
   //--- Wiederherstellen
   virtual bool      Save(const int);
   virtual bool      Load(const int);
  };

Die Überprüfung des Vorhandenseins einer neuen Kerze auf dem Chart erfolgt durch die Methode Check(), die unten dargestellt ist:

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];
     }
  }

Diese Methode, die das Auftreten einer neuen Bar prüft, sollte bei jedem Tick aufgerufen werden. Der Programmierer kann CCxpertAdvisor beliebig erweitern und zusätzliche Aufgaben übernehmen lassen, wenn eine neue Kerze auf dem Chart erscheint.

Wie im Code oben gezeigt, erfolgt der tatsächliche Vergleich der Zeit und Preis der Eröffnung der Bar durch die Methode Compare() der Klasse, die im Folgenden gezeigt wird:

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));
  }

Die Prüfung der Existenz eines neuen Bar hängt von drei Bedingungen ab. Ist auch nur Eine erfüllt, ist das Ergebnis 'true', die dadurch das Vorhandensein einer neuen Kerze auf dem Chart anzeigt:

  1. Die zuletzt gesicherte Eröffnungszeit entspricht nicht der Eröffnungszeit der aktuellen Bar
  2. Der zuletzt gesicherte Eröffnungspreis entspricht nicht der Eröffnungspreis der aktuellen Bar
  3. Die zuletzt gesicherte Eröffnungszeit ist Null, dann muss der aktuelle Tick nicht zwingend der erste der Bar sein.

Die ersten beiden Bedingungen beinhalten den direkten Vergleich der Zahlen der aktuellen Bar mit der vorherigen. Die dritte Bedingung gilt nur für den aller ersten Tick, den der Expert Advisor empfängt. Sobald ein Expert Advisor auf ein Chart geladen ist, hat er noch keinen Daten (Zeit und Preis der letzten Bar), so dass der Wert der letzten Eröffnungszeit Null ist. Einige Händler betrachten diese Bar als eine Neue für ihren Expert Advisor, während andere es vorziehen, den Expert Advisor nach seiner Initialisierung tatsächlich auf eine neue Bar warten zu lassen.

Wie bei anderen Klassentypen, die zuvor besprochen wurden, hätte auch die Klasse CCandle ihren Container CCandleManager. Der folgende Code zeigt die Deklaration von 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;
   //--- Wiederherstellen
   virtual bool      Save(const int);
   virtual bool      Load(const int);
  };

Anhand des Symbolnamens und des Zeitrahmens wird eine Instanz von CCandle erzeugt. Der CCandleManager würde es einem Expert Advisor erleichtern, mehrere Charts für ein gegebenes Instrument zu verfolgen, z. B. mit der Fähigkeit, das Auftreten einer neuen Kerze auf EURUSD M15 und EURUSD H1 im selben Expert Advisor zu überprüfen. Instanzen von CCandle, die das gleiche Symbol und den gleichen Zeitrahmen haben, sind redundant und sollten vermieden werden. Wenn Sie nach einer bestimmten Instanz von CCandle suchen, rufen Sie einfach die entsprechende Methode im CCandleManager auf und geben Sie das Symbol und den Zeitrahmen an. CCandleManager wiederum sucht nach der entsprechenden Instanz von CCandle und ruft die gewünschte Methode auf.

Neben der Prüfung des Auftretens einer neuen Kerze dienen CCandle und CCandleManager einem weiteren Zweck: der Prüfung, ob eine Position für ein bestimmtes Symbol und einen bestimmten Zeitrahmen von einem Expert Advisor eröffnet worden ist. Die jüngste Position eines Symbol kann überprüft werden, aber nicht für einen bestimmten Zeitrahmen. Dieser Merker (flag) sollte bei Bedarf von der Instanz des CExpertAdvisor selbst gesetzt oder zurückgesetzt werden. Er kann mit der Methode TradeProcessed() für beide Klassen gesetzt werden.

Die Methoden TradeProcessed() (Abfragen und Ändern) regeln das Finden der Instanz von CCandle und die Verarbeitung des entsprechenden Wertes:

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

Für CCandle weist unter anderem einem seiner Klassenmitglieder, m_trade_processed, einen neuen Wert zu. Die folgenden Methoden befassen sich mit der Zuweisung des Wertes des besagten Klassenmitglieds:

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

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

Funktionsweise von OnTick()

Die Methode OnTick() in CExpertAdvisor ist die am häufigsten verwendete Funktion innerhalb der Klasse. Dieser Methode veranlasst die meiste Aktionen. Ihre Arbeitsweise ist im folgenden Ablaufdiagramm dargestellt:


CExpertAdvisorBase OnTick()


Der Bearbeitung beginnt mit dem Umschalten des Merkers für den Tick des Expert Advisors. Dies stellt sicher, dass ein Tick nicht zweimal bearbeitet wird. Die Methode OnTick() des CExpertAdvisor sollte idealerweise nur innerhalb der Ereignisfunktion OnTick(), kann aber auch auf anderem Weg wie z. B. durch OnChartEvent, aufgerufen werden. Ohne diesen Merker könnte die Methode OnTick() der Klasse aufgerufen werden, während sie noch den letzten Tick verarbeitet, und so den Tick mehr als einmal verarbeiten, und wenn wegen dieses Ticks eine Position eröffnet werden würde, könnte diese wenn der Tick eine Position eröffnet, würde dies oft zu einer doppelten Position Position ein zweites Mal eröffnet werden.

Die Aktualisierung der Daten ist auch notwendig, da dadurch sichergestellt wird, dass der Expert Advisor Zugriff auf die aktuellsten Marktdaten hat und nicht mit veralteten Ticks arbeitet. Wenn der Expert Advisor die Daten nicht aktualisiert, würde er den Merker zurücksetzen, die Methode beenden und auf den neuen Tick warten.

Die nächsten Schritte sind das Erkennen neuer Bars und die Überprüfung von Handelssignalen. Die Überprüfung erfolgt standardmäßig bei jedem Tick. Es ist jedoch möglich, diese Methode so zu erweitern, dass sie nur dann Signale prüft, wenn ein neues Signal erkannt wird (um die Verarbeitungszeit zu beschleunigen, insbesondere beim Backtesting und der Optimierung).

Die Klasse verfügt auch über eine Variable, m_position_reverse, die Positionen gegenüber dem aktuellen Signal umkehrt. Die hier vorgenommene Umkehr soll nur die die aktuelle(n) Position(en) neutralisieren. Im MetaTrader 4 und MetaTrader 5 'hedging mode' wird die dem aktuellen Signale entgegengesetzte Position geschlossen (diejenigen, die dem aktuellen Signal entsprechen, werden nicht berührt). Im MetaTrader 5 'netting mode' kann es immer nur eine Position geben, so dass der Expert Advisor eine neue Position gleichen Volumens und in entgegengesetzter Richtung zur aktuellen Position eröffnet, um sie zu schließen.

Das Handelssignal wird meist mit m_signals geprüft, aber auch andere Faktoren wie z. B. der Handel bei einer neuen Bar und Zeitfilter können den Expert Advisor davon abhalten, neue Positionen zu eröffnen. Erst wenn alle Bedingungen erfüllt sind, kann die EA eine neue Position eröffnen.

Am Ende der Verarbeitung des Ticks setzt der Expert Advisor den Merker der Ticks auf 'false' und darf dann den nächsten Tick verarbeiten.

Der Container der Expert Advisor

Ähnlich wie andere, in früheren Artikeln besprochene Klassenobjekte sollte auch die Klasse CExpertAdvisor den für sie bestimmten Container, CExpertAdvisors, haben. Der folgende Code zeigt die Deklaration seiner Basisklasse 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;}
   //--- Abfragen und Ändern
   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;
   //--- Initialisierung
   virtual bool      Validate(void) const;
   virtual bool      InitComponents(void) const;
   //--- Ereignisse
   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);
   //--- Wiederherstellen
   virtual bool      CreateElement(const int);
   virtual bool      Save(const int);
   virtual bool      Load(const int);
  };

Dieser Container spiegelt in erster Linie die öffentlichen Methoden der Klasse CExpertAdvisor wider. Ein Beispiel dafür ist die Arbeitsweise von OnTick(). Die Methode iteriert einfach über jede Instanz von CExpertAdvisor, um deren Methoden OnTick() aufzurufen:

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

Mit diesem Container ist es möglich, mehrere Instanzen von CExpertAdvisor zu speichern. Dies ist wahrscheinlich die einzige Möglichkeit, mehrere Expert Advisor auf ein und demselben Chart auszuführen. Dazu einfach mehrere Instanzen von CExpertAdvisor initialisieren, ihre Pointer in einem einzigen Container von CExpertAdvisors speichern und dann die Methode OnTick() des Containers verwenden, um jeweils OnTick() von jeder Instanz von CExpertAdvisor aufzurufen. Dasselbe kann auch mit jeder Instanz der Klasse CExpert aus der Standardbibliothek von MQL5 unter Verwendung der Klasse CArrayObj Klasse oder ihrer abgeleiteten Klassen geschehen.

Persistenz der Daten

Einige Daten, die in einer Instanz von CExpertAdvisor verwendet werden, befinden sich nur im Arbeitsspeicher des Computers. In der Regel werden die Daten häufig in der Plattform gespeichert und der Expert Advisor erhält die benötigten Daten durch einen Funktionsaufruf von der Plattform selbst. Bei Daten, die dynamisch angelegt werden, während der Expert Advisor läuft, ist dies jedoch in der Regel nicht der Fall. Wenn die Funktion OnDeinit() ausgelöst wird,verliert der Expert Advisor in der Regel alle seine Daten.

OnDeinit() kann auf verschiedene Arten ausgelöst werden, wie z. B. das Schließen der gesamten Handelsplattform (MetaTrader 4 oder MetaTrader 5), das Entfernen des Expert Advisors vom Chart oder sogar durch eine Neukompilierung des Quellcodes des Expert Advisors. Die vollständige Liste der möglichen Ereignisse, die eine Deinitialisierung auslösen können, finden Sie über die Funktion UninitializeReason(). Wenn ein Expert Advisor den Zugriff auf diese Daten verliert, kann er sich so verhalten, als würde er zum ersten Mal auf dem Chart gestartet worden.

Die meisten flüchtigen Daten der Klasse CExpertAdvisor befinden sich in einem ihrer Mitglieder, der Instanz des COrderManager. Hier werden die Instanzen COrder und COrderStop (und deren Ableitungen) angelegt, wenn der Expert Advisor seine gewohnte Routine ausführt. Da diese Instanzen dynamisch während OnTick() erzeugt werden, werden sie beim Neuinitialisieren des Expert Advisors nicht neu erzeugt. Daher sollte der Expert Advisor eine Methode implementieren, um diese flüchtigen Daten zu speichern und abzurufen. Eine Möglichkeit, dies zu implementieren, ist die Verwendung einer abgeleiteten Klasse von CFileBin, CExpertFile. Der folgende Code-Ausschnitt zeigt die Deklaration von CExpertFileBase, seiner Basisklasse:

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);
  };

Hier erweitern wir CFileBin um Methoden zum Lesen und Schreiben von Daten des Typs Boolean.

Am Ende der Klassendatei wird eine Instanz der Klasse CExpertFile angelegt. Diese Instanz wird im gesamten Expert Advisor verwendet, wenn es darum geht, flüchtige Daten zu speichern oder zu laden. Alternativ könnte man auch einfach den von CObject ererbten Methoden Save und Load vertrauen und das Speichern und Laden der Daten wie gewohnt abwickeln. Dies kann jedoch ein sehr anspruchsvoller Vorgang sein. Viel Aufwand und Programmzeilen können durch die alleinige Nutzung von CFile (oder dessen Nachkommen) eingespart werden.

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

Der Order-Manager speichert flüchtige Daten durch seine Methode Save():

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;
  }

Die meisten dieser Daten sind von primitiven Typen, mit Ausnahme der letzten beiden, die Container der aktuellen und vergangenen Aufträge. Für diese Daten wird die Methode WriteObject von CFileBin verwendet, die lediglich die Methode Save() des zu schreibenden Objekts aufruft. Der folgende Code zeigt Save() von COrderBase:

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;
  }

Wie wir hier sehen können, wiederholt sich der Vorgang beim Speichern von Objekten. Bei primitiven Datentypen werden die Daten einfach wie gewohnt in einer Datei gespeichert. Bei komplexen Datentypen wird die Methode des Objekts Save() über die Methode WriteObject von CFileBin aufgerufen.

In Fällen, in denen der CExpertAdvisor mehrfach vorhanden ist, sollte der Container CExpertAdvisors auch die Möglichkeit haben, Daten zu speichern:

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;
  }

Die Methode ruft die Methode Save() aus jeder Instanz von CExpertAdvisor auf. Durch das einzige Handle auf eine Datei gibt es nur eine einzige Datei pro Expert Advisor. Es wäre auch möglich, dass jede Instanz von CExpertAdvisor eine eigene Sicherungsdatei erhält, aber das wäre die kompliziertere Vorgehensweise.

Der schwierigere Teil ist das Lesen der Daten. Beim Speichern der Daten werden die Werte einiger Klassenmitglieder einfach in eine Datei geschrieben. Andererseits müssten durch das Lesen der Daten die Objektinstanzen vor dem Sichern idealerweise im gleichen Zustand wiederhergestellt werden. Der folgende Code zeigt die Methode Load() des Order-Managers:

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;
  }

Der obige Code für den COrderManager ist im Gegensatz zur Methode Load() aus CExpertAdvisor wesentlich komplizierter. Der Grund dafür ist, dass im Gegensatz zum Order-Manager die Instanzen von CExpertAdvisor während OnInit() erstellt werden und der Container daher einfach die Methode Load() von jeder Instanz von CExpertAdvisor aufrufen müsste, anstatt die Methode ReadObject von CFileBin zu verwenden.

Klasseninstanzen, die nicht in OnInit() angelegt wurden, müssen beim Neuladen des Expert Advisors ebenfalls angelegt werden. Dies wird durch die Erweiterung der Methode CreateElement von CArrayObj erreicht. Ein Objekt kann sich nicht einfach selbst erstellen, also muss es durch sein übergeordnetes Objekt oder Container erstellt werden, oder auch nur aus der Quell- oder Headerdatei selbst. Ein Beispiel hierfür ist die erweiterte Methode CreateElement() von COrdersBase. Unter dieser Klasse ist der Container COrders (abgeleitet von COrdersBase) und das zu erstellende Objekt vom Typ 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]);
  }

Hier haben wir nicht nur das Element erstellt, sondern auch dessen übergeordnetes Objekt oder Container eingestellt, um zu unterscheiden, ob es zur Liste der offenen Positionen (m_orders class Mitglied von COrderManagerBase) oder der geschlossenen (m_orders_history of COrderManagerBase) gehört.

Beispiele

Die Beispiele #1-#4 in diesem Artikel sind modifizierte Versionen der vier Beispiele aus dem vorherigen Artikel (siehe Cross-Plattform Expert Advisor: Eigene Stopps, Breakeven und Trailing). Betrachten wir das komplexeste Beispiel, expert_custom_trail_ha_ma_ma.mqh, eine modifizierte Version von custom_trail_ha_ma.mqh.

Vor der Funktion OnInit() haben wir die folgenden globalen Objektinstanzen deklariert:

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

Wir ersetzen dies durch eine Instanz von CExpert. Einige der oben genannten Elemente befinden sich im CExpetAdvisor selbst (z. B. COrderManager), während der Rest in OnInit() instantiiert werden muss (z. B. Container):

CExpertAdvisors experts;

Ganz am Anfang der Methode erstellen wir eine Instanz von CExpertAdvisor. Wir rufen auch seine Methode Init() auf, die die grundlegendsten Einstellungen vornimmt:

int OnInit()
  {
//---
   CExpertAdvisor *expert=new CExpertAdvisor();
   expert.Init(Symbol(),Period(),12345,true,true,true);
//--- eigener Code
//---
   return(INIT_SUCCEEDED);
  }

CSymbolInfo / CSymbolManager muss nicht mehr instanziiert werden, da die Instanz der Klasse CExpertAdvisor selbst Instanzen dieser Klassen erzeugen kann.

Die benutzerdefinierte Funktion könnte ebenfalls entfernt werden, da unser neuer Expert Advisor diese nicht mehr benötigt.

Wir haben die globale Deklaration für die Container in unserem Code entfernt, so dass sie in OnInit() angelegt werden müssen. Ein Beispiel hierfür ist der Container für Zeitfilter (CTimeFilters), wie im folgenden Code innerhalb der Funktion OnInit() dargestellt:

CTimes *time_filters=new CTimes();

Statt dessen werden der Instanz des CExpertAdvisors die Pointer auf die Container hinzugefügt, die zuvor dem Order-Manager "hinzugefügt" wurden. Alle anderen Container, die nicht zum Order-Manager hinzugefügt werden, müssen ebenfalls der Instanz CExpertAdvisor übergeben werden. Es wäre die Instanz des COrderManager, die die Pointer speichern würde. Die Instanz CExpertAdvisor erzeugt nur Methoden für die Hülle.

Danach fügen wir die Instanz CExpertAdvisor zu einer Instanz von CExpertAdvisors hinzu. Anschließend rufen wir die Methode InitComponents() der Instanz CExpertAdvisors auf. Damit wäre die Initialisierung aller Instanzen von CExpertAdvisor und deren Komponenten sichergestellt.

int OnInit()
  {
//---
//--- eigener Code
   experts.Add(GetPointer(expert));
   if(!experts.InitComponents())
      return(INIT_FAILED);
//--- eigener Code
//---
   return(INIT_SUCCEEDED);
  }

Schließlich fügen wir den Code ein, der zum Laden benötigt wird, wenn der laufende Expert Advisor beendet wurde:

int OnInit()
  {
//---
//--- eigener Code   
 file.Open(savefile,FILE_READ);
   if(!experts.Load(file.Handle()))
      return(INIT_FAILED);
   file.Close();
//---
   return(INIT_SUCCEEDED);
  }

Wenn der Expert Advisor die Datei nicht lädt, gibt er INIT_FAILED zurück. Falls jedoch keine Sicherungsdatei greifbar ist (und daher ein INVALID_HANDLE erzeugen werden würde), wird die Initialisierung des Expert Advisors nicht fehlschlagen, da die Methode Load() von CExpertAdvisors und CExpertAdvisor beim Erhalt eines ungültigen Handles beide 'true' zurückgeben. Es besteht ein gewisses Risiko bei diesem Ansatz, aber es ist sehr unwahrscheinlich, dass eine gesicherte Datei von einem anderen Programm geöffnet wird. Stellen Sie einfach sicher, dass jede Instanz des Experten-Beraters, die auf einem Chart läuft, exklusiv ihre eigene Sicherungsdatei hat (so wie die 'magic number').

Das fünfte Beispiel ist nicht im vorherigen Artikel enthalten. Vielmehr fasst sie alle vier Expert Advisor in diesem Artikel zu einem einzigen zusammen. Er verwendet einfach eine leicht modifizierte Versionen der Funktion OnInit() jedes der Expert Advisor und deklariert sie als benutzerdefinierte Funktion. Sein Rückgabewert ist vom Typ CExpertAdvisor*. Wenn das Erstellen des Expert Advisors fehlgeschlagen wäre, würde er statt INIT_SUCCEEDEDED der Wert NULL zurückgeben. Der folgende Code zeigt die aktualisierte OnInit() der kombinierten Expert Advisor:

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);
  }

Der Expert Advisor beginnt mit der Instanziierung jeder Instanz von CExpertAdvisor. Anschließend wird jeder der Pointer auf CExpertAdvisor überprüft. Wenn der Pointer nicht dynamisch ist, gibt die Funktion INIT_FAILED zurück, und die Initialisierung scheitert. Wenn jede der Instanzen die Prüfung von Pointern bestanden hat, werden diese Pointer in einer Instanz von CExpertAdvisors gespeichert. Die Instanz CExpertAdvisors (der Container, nicht die Instanz des Expert Advisor) würde dann seine Komponenten initialisieren und ggf. die vorherigen Daten laden.

Der Expert Advisor erstellt mit Hilfe von benutzerdefinierten Funktionen eine Instanz des CExpertAdvisor. Der folgende Code zeigt die Funktion, mit der die 4. Instanz des Expertenberaters angelegt wird:

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;
}

Wie wir sehen können, sieht der Code sehr ähnlich aus wie OnInit() der ursprünglichen Headerdatei des Expert Advisors (expert_custom_trail_ha_ma.mqh). Die anderen benutzerdefinierten Funktionen sind ebenfalls auf die gleiche Weise organisiert.

Abschließende Anmerkungen

Vor dem Ende dieses Artikel sollten sich jeder Leser, der diese Bibliothek nutzen möchte, über diese Faktoren informieren, die zur Entwicklung der Bibliothek beitragen:

Zum jetzigen Zeitpunkt umfasst die Bibliothek dieses Artikels über 10.000 Programmzeilen (einschließlich der Kommentare). Trotzdem ist sie immer noch in Arbeit. Um die Möglichkeiten von MQL4 und MQL5 voll auszuschöpfen, muss noch mehr Arbeit geleistet werden.

Der Autor begann mit der Arbeit an diesem Projekt vor der Einführung des 'hedging mode' in MetaTrader 5. Dies hat die Weiterentwicklung der Bibliothek stark beeinflusst. Infolgedessen ist die Bibliothek tendenziell näher den Konventionen von MetaTrader 4, als denen von MetaTrader 5. Darüber hinaus hat der Autor auch einige Kompatibilitätsprobleme mit einigen Build-Updates der letzten Jahre erfahren, die zu einigen, kleineren und größeren Anpassungen am Code führten (und Verzögerungen bei der Veröffentlichung einiger Artikel). Zum Zeitpunkt des Verfassens dieses Artikels erfuhr der Autor, dass die Aktualisierungen für beide Plattformen weniger häufig und stabiler sein werden. Dieser Trend wird sich voraussichtlich weiter verbessern. Dennoch müssten eventuelle Inkompatibilitäten durch zukünftige Versionen weiterhin behoben werden.

Die Bibliothek stützt sich auf gespeicherte Daten, um den eigenen Handel zu verfolgen. Die mit dieser Bibliothek erstellten Expert Advisor sind daher in hohem Maße darauf angewiesen, dass das Speichern und Lesen von Daten zur Bewältigung eventueller Unterbrechungen des Expert Advisors benötigt werden. Künftige Arbeiten an dieser Bibliothek sowie an jeder anderen Bibliothek, die auf eine plattformübergreifende Kompatibilität abzielt, sollten darauf ausgerichtet sein, eine zustandsfreie oder nahezu zustandsfreie Implementierung zu erreichen, ähnlich wie sie in der Standardbibliothek für MQL5 implementiert ist.

Als letzte Bemerkung sollte die in diesem Artikel enthaltene Bibliothek nicht als dauerhafte Lösung angesehen werden. Vielmehr sollte es als Gelegenheit für einen reibungsloseren Übergang von MetaTrader 4 zu MetaTrader 5 genutzt werden. Die Inkompatibilitäten zwischen MQL4 und MQL5 stellen eine große Blockade für Händler dar, die beabsichtigen, auf die neue Plattform umzusteigen. Dafür muss der MQL4-Quellcode der Expert Advisor überarbeitet werden, um mit dem MQL5-Compiler kompatibel zu sein. Die in diesem Artikel vorgestellte Bibliothek dient dazu, einen Expert Advisor für die neue Plattform einzusetzen, der den Quellcode des allgemeinen Expert Advisor nur wenig oder gar nicht anpasst. Dies kann dem Händler bei seiner Entscheidung helfen, ob er MetaTrader 4 weiter verwenden oder zu MetaTrader 5 wechseln soll. Für den Fall, dass er sich für einen Wechsel entscheidet, sind nur sehr geringe Anpassungen erforderlich, und der Händler kann mit seinen Expert Advisor wie gewohnt arbeiten. Auf der anderen Seite, wenn er beschließt, auf der alten Plattform zu bleiben, hat er eine Option, um später schnell zur neuen Plattform zu wechseln, sobald der MetaTrader 4 für ihn unattraktiv wird.

Schlussfolgerung

In diesem Artikel werden die Objekte der Klassen CExpertAdvisor und CExpertAdvisors vorgestellt, mit denen alle Komponenten eines plattformübergreifenden Expert Advisors integriert werden, die in dieser Artikelserie diskutiert werden. Der Artikel beschreibt, wie die beiden Klassen instanziiert und mit den anderen Komponenten eines plattformübergreifenden Expert Advisor verknüpft werden. Außerdem werden einige Lösungen für Probleme vorgestellt, mit denen sich Expert Advisor in der Regel konfrontiert sehen, wie z. B. das Erkennen der neuen Bar und das Speichern und Lesen flüchtiger Daten.

Die Programme dieses Artikels

 # Name
Typ
Beschreibung
1.
expert_breakeven_ha_ma.mqh
Header-Datei
Header-Datei, des ersten Beispiels
2.
expert_breakeven_ha_ma.mq4 Expert Advisor
Datei mit dem Hauptquellcode für die Version MQL4 des ersten Beispiels
3.
expert_breakeven_ha_ma.mq5 Expert Advisor Datei mit dem Hauptquellcode für die Version MQL5 des ersten Beispiels
4.
 expert_trail_ha_ma.mqh Header-Datei Header-Datei, des zweiten Beispiels
5.
 expert_trail_ha_ma.mq4 Expert Advisor Datei mit dem Hauptquellcode für die Version MQL4 des zweiten Beispiels
6.
 expert_trail_ha_ma.mq5 Expert Advisor Datei mit dem Hauptquellcode für die Version MQL5 des zweiten Beispiels
7.
 expert_custom_stop_ha_ma.mqh Header-Datei Header-Datei, des dritten Beispiels
8.
 expert_custom_stop_ha_ma.mq4 Expert Advisor Datei mit dem Hauptquellcode für die Version MQL4 des dritten Beispiels
9.
 expert_custom_stop_ha_ma.mq5 Expert Advisor Datei mit dem Hauptquellcode für die Version MQL5 des dritten Beispiels
10.
 expert_custom_trail_ha_ma.mqh Header-Datei Header-Datei, des vierten Beispiels
11.
 expert_custom_trail_ha_ma.mq4 Expert Advisor Datei mit dem Hauptquellcode für die Version MQL4 des vierten Beispiels
12.
 expert_custom_trail_ha_ma.mq5 Expert Advisor Datei mit dem Hauptquellcode für die Version MQL5 des vierten Beispiels
13.
 combined.mqh Header-Datei The main header file used in the fifth example
14.
 combined.mq4 Expert Advisor Datei mit dem Hauptquellcode für die Version MQL4 des fünften Beispiels
15.
 combined.mq5 Expert Advisor Datei mit dem Hauptquellcode für die Version MQL5 des fünften Beispiel

Die Dateien der Klassen dieses Artikels

#
Name
Typ
Beschreibung
1. MQLx\Base\Expert\ExperAdvisorsBase Header-Datei
CExpertAdvisors (CExpertAdvisor container, base class)
2.
MQLx\MQL4\Expert\ExperAdvisors Header-Datei CExpertAdvisors (MQL4 version)
3.
MQLx\MQL5\Expert\ExperAdvisors Header-Datei
CExpertAdvisors (MQL5 version)
4.
MQLx\Base\Expert\ExperAdvisorBase Header-Datei
CExpertAdvisor (base class)
5.
MQLx\MQL4\Expert\ExperAdvisor Header-Datei
CExpertAdvisor (MQL4 version)
6.
MQLx\MQL5\Expert\ExperAdvisor Header-Datei
CExpertAdvisor (MQL5 version)
7.
MQLx\Base\Candle\CandleManagerBase Header-Datei CCandleManager (CCandle container, base class)
8.
MQLx\MQL4\Candle\CandleManager Header-Datei CCandleManager (MQL4 version)
9.
MQLx\MQL5\Candle\CandleManager Header-Datei CCandleManager (MQL5 version)
10.
MQLx\Base\Candle\CandleBase Header-Datei CCandle (base class)
11.
MQLx\MQL4\Candle\Candle Header-Datei CCandle (MQL4 version)
12.
MQLx\MQL5\Candle\Candle Header-Datei
CCandle (MQL5 version)
13.
MQLx\Base\File\ExpertFileBase Header-Datei CExpertFile(base class)
14.
MQLx\MQL4\File\ExpertFile Header-Datei CExpertFile(MQL4 version)
15.
MQLx\MQL5\File\ExpertFile Header-Datei CExpertFile(MQL5 version)


Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/3622

Beigefügte Dateien |
MQL5.zip (143.3 KB)
Wir betrachten die adaptive Trendfolgemethode in der Praxis Wir betrachten die adaptive Trendfolgemethode in der Praxis
Das besondere Merkmal des im Artikel vorgestellten Handelssystems besteht in der Verwendung mathematischer Werkzeuge für die Analyse von Börsenkursen. Im System werden digitale Filter und die Spektralschätzung diskreter Zeitreihen verwendet. Es werden theoretische Aspekte der Strategie beschrieben und ein Expert Advisor für das Testen der Strategie erstellt.
Automatische Suche nach Divergenzen und Konvergenzen Automatische Suche nach Divergenzen und Konvergenzen
Der Artikel behandelt alle Arten von Divergenzen: einfach, versteckt, erweitert, dreifache, vierfache, Konvergenzen, sowie Divergenzen der Klassen A, B und C. Es wurde ein universeller Indikator für deren Ermittlung und Darstellung auf dem Chart entwickelt.
Optimieren einer Strategie unter Verwendung einer Kurve der Salden und dem Vergleich der Ergebnisse mit dem Kriterium "Balance + max Sharpe Ratio" Optimieren einer Strategie unter Verwendung einer Kurve der Salden und dem Vergleich der Ergebnisse mit dem Kriterium "Balance + max Sharpe Ratio"
In diesem Artikel betrachten wir ein weiteres Kriterium für die Optimierung einer Strategie auf Basis einer grafischen Analyse der Salden. Die linearen Regression wird mit der Funktion aus der Bibliothek ALGLIB berechnet.
Cross-Plattform Expert Advisor: Eigene Stopps, Breakeven und Trailing Cross-Plattform Expert Advisor: Eigene Stopps, Breakeven und Trailing
Dieser Artikel beschreibt, wie nutzerdefinierte Stopps in einem plattformübergreifenden Expert Advisor eingerichtet werden können. Darüber hinaus wird eine eng verwandte Methode diskutiert, mit der das Nachziehen von Stopps für die Dauer einer Position entwickelt werden können.