Programmierung von EA-Modi mit Hilfe des Objekt-orientierten Ansatzes

Denis Kirichenko | 13 Juni, 2016

Einleitung

In diesem Beitrag erörtern wir die Programmierungs-Modi, in denen ein MQL5 EA arbeiten kann. Das Ziel dieses Beitrags ist, das Konzept zu beschreiben, dass "jeder Modus auf seine eigene Weise implementiert werden kann". Der Verfasser ist der Meinung, dass mit diesem Ansatz die Ausführung von Aufgaben in verschiedenen Entwicklungsphasen eines EAs weitaus effizienter vonstatten geht.

Zuerst betrachten wir uns, aus welchen einzelnen Etappen die Entwicklung eines EAs eigentlich besteht. Danach werden die Modi, in denen ein EA in MetaTrader 5 arbeiten kann, sowie seine Hilfsanwendungen untersucht. Und zum Schluss sehen wir uns die Entwicklung der Klassen-Hierarchie zur Implementierung des o. g. Konzepts an.


1. Entwicklungsphasen

Die Entwicklung eines Handelsroboters (EA) ist ein Prozess mit mehreren Aspekten. Die wichtigsten Blöcke hierbei sind die "Algorithmisierung" des Konzepts und sein Test. Es sei darauf hingewiesen, dass sowohl die Handelslogik des EAs als auch der Code-Algorithmus getestet werden.

Schematisch dargestellt sehen die Phasen dieses Prozesses folgendermaßen aus (Abb. 1).

Abb. 1 Entwicklungsphasen und Implementierung eines EAs

Abb. 1 Entwicklungsphasen und Implementierung eines EAs

Die fünfte Phase "Algorithmisches Handeln" bildet die Arbeit der Entwickler, Programmierer, Analysten und anderer an diesem Prozess beteiligter Spezialisten ab. Es kommt nicht selten vor, dass all diese Rollen von einer einzigen Person ausgefüllt werden - angenommen, ein Händler-Programmierer.

Dieses Schema kann selbstverständlich aktualisiert und erweitert werden. Meiner Meinung nach bildet es die wichtigsten Etappen in der Entwicklung eines EAs gut ab. Aufgrund des zyklischen Musters dieses Schemas kann der Code des EAs während seiner gesamten Lebensdauer ständig verbessert und modifiziert werden.

Hier sei jedoch angemerkt, dass jede Phase gewisse Tools, Know-How und Fähigkeiten erfordert.

Meiner Meinung nach stößt der Entwickler auf die folgende, einfache Variantenmatrix (Abb. 2).

Abb. 2 Variantenmatrix

Abb. 2 Variantenmatrix

Es sollte klar sein, dass es nur der Roboter, der eine gewinnbringenden Handelsstrategie mit einem qualitativ hochwertigen Code implementiert, bis zu fünften Etappe "Algorithmisches Handeln schafft".


2. Die Expert Advisor-Modi in MQL5

Die MQL5 Umgebung gestattet die Arbeit mit einem EA in verschiedenen Modi - nämlich insgesamt sieben. Weiter unten sehen wir uns jeden einzelnen Modus genauer an.

Vom Blickwinkel des Typs der Programmdatei kann man zwei Gruppen unterscheiden:

  1. Modi, die die Datei mit dem Quellcode und die ausführbare Datei brauchen und
  2. Modi, die nur die ausführbare Datei brauchen.

Modi zur Fehlersuche und Profilerstellung gehören zur ersten Gruppe.

Ein weiteres Kriterium zur Klassifizierung von Modi ist die Arbeit eines EAs in einem Strom echter oder historischer Notierungen. Alle Test-Modi sind mit historischen Notierungen verknüpft.

Die Programmierung definiert sechs Modi. Auf Grundlage der Ergebnisse kann man schlussfolgern, ob ein EA im Standardmodus (Freigabe) oder nicht arbeitet. Ein fertiges Programm (die Datei mit der *.ex5 Erweiterung), das für eine Arbeit in Finanzmärkten kodiert wurde. soll genau in diesem Modus arbeiten. Gleichzeitig ermöglicht ein fertiges Programm auch die Verwendung anderer Modi im Strategietester.

Erzeugen wir zunächst einmal eine Aufzählung, namens ENUM_MQL_MODE, der betrieblichen Modi des MQL-Programms:

//+------------------------------------------------------------------+
//| MQL Mode                                                         |
//+------------------------------------------------------------------+
enum ENUM_MQL_MODE
  {
   MQL_MODE_RELEASE=0,       // Release
   MQL_MODE_DEBUG=1,         // Debugging
   MQL_MODE_PROFILER=2,      // Profiling  
   MQL_MODE_TESTER=3,        // Testing
   MQL_MODE_OPTIMIZATION=4,  // Optimization
   MQL_MODE_VISUAL=5,        // Visual testing 
   MQL_MODE_FRAME=6,         // Gathering frames
  };

Diese brauchen wir später dann zur Erkennung des Modus-Typs, in dem der EA arbeitet.


2.1 Funktion zur Identifizierung und Prüfung des Modus

Schreiben Sie eine einfache Funktion, die alle Modi durchgeht und Informationen ins Logbuch druckt.

//+------------------------------------------------------------------+
//| Checking all MQL modes                                           |
//+------------------------------------------------------------------+
void CheckMqlModes(void)
  {
//--- if it is debug mode
   if(MQLInfoInteger(MQL_DEBUG))
      Print("Debug mode: yes");
   else
      Print("Debug mode: no");
//--- if it is code profiling mode
   if(MQLInfoInteger(MQL_PROFILER))
      Print("Profile mode: yes");
   else
      Print("Profile mode: no");
//--- if it is test mode
   if(MQLInfoInteger(MQL_TESTER))
      Print("Tester mode: yes");
   else
      Print("Tester mode: no");
//--- if it is optimization mode
   if(MQLInfoInteger(MQL_OPTIMIZATION))
      Print("Optimization mode: yes");
   else
      Print("Optimization mode: no");
//--- if it is visual test mode
   if(MQLInfoInteger(MQL_VISUAL_MODE))
      Print("Visual mode: yes");
   else
      Print("Visual mode: no");
//--- if it is frame gathering optimization result mode
   if(MQLInfoInteger(MQL_FRAME_MODE))
      Print("Frame mode: yes");
   else
      Print("Frame mode: no");
  }

Die Arbeit dieser Funktion in jedem Modus wird geprüft und kann im OnInit() Ereignis-Handler aufgerufen werden.

Zum Zweck unseres Tests, erzeugen wir ein Template des EA mit dem Namen Test1_Modes_EA.mq5.

In den Eingabeparametern wird eine Option zur Festlegung des Modus, in dem der EA arbeiten soll, aktiviert. Es ist wichtig, sicherzustellen, dass der richtige Modus benannt wird, ansonsten wird die Information nicht exakt sein. Und das ist passiert.

Unten finden Sie den Freigabe-Modus.

CL      0       17:20:38.932    Test1_Modes_EA (EURUSD.e,H1)     Current mode: MQL_MODE_RELEASE
QD      0       17:20:38.932    Test1_Modes_EA (EURUSD.e,H1)     Debug mode: no
KM      0       17:20:38.932    Test1_Modes_EA (EURUSD.e,H1)     Profile mode: no
EK      0       17:20:38.932    Test1_Modes_EA (EURUSD.e,H1)     Tester mode: no
CS      0       17:20:38.932    Test1_Modes_EA (EURUSD.e,H1)     Optimization mode: no
RJ      0       17:20:38.932    Test1_Modes_EA (EURUSD.e,H1)     Visual mode: no
GL      0       17:20:38.932    Test1_Modes_EA (EURUSD.e,H1)     Frame mode: no

Im Freigabe-Modus wurden die Marker aller anderen Modi genullt. Die Funktion hat also festgestellt, dass es sich weder um den Fehlersuch-Modus (Debug-Modus: Nein), noch den Profilerstellungs-Modus (Profil-Modus: Nein), usw. handelte. Mit Hilfe der Negationsmethode kamen wir also zum Schluss, dass wir im Freigabe-Modus arbeiten.

Jetzt sehen wir uns an, wie der Fehlersuch-Modus identifiziert wurde.

HG      0       17:27:47.709    Test1_Modes_EA (EURUSD.e,H1)     Current mode: MQL_MODE_DEBUG
LD      0       17:27:47.710    Test1_Modes_EA (EURUSD.e,H1)     Debug mode: yes
RS      0       17:27:47.710    Test1_Modes_EA (EURUSD.e,H1)     Profile mode: no
HE      0       17:27:47.710    Test1_Modes_EA (EURUSD.e,H1)     Tester mode: no
NJ      0       17:27:47.710    Test1_Modes_EA (EURUSD.e,H1)     Optimization mode: no
KD      0       17:27:47.710    Test1_Modes_EA (EURUSD.e,H1)     Visual mode: no
RR      0       17:27:47.710    Test1_Modes_EA (EURUSD.e,H1)     Frame mode: no

Dieser Modus wurde korrekt erkannt.

Jedes Programmier-Handbuch enthält Informationen dazu, dass ein Fehlersuchprogramm (oder Modus) die Suche und das Finden von Fehlern im Code erleichtert und zudem die Besonderheiten des Programms hervorhebt. Mehr Einzelheiten zur Fehlersuche in der MQL5-Umgebung finden Sie den dem Beitrag "Fehlersuche in MQL5 Programmen".

Dieser Modus wird in den Formalisierungs- und Konstruktionsphasen des Algorithmus eines Handelskonzepts am häufigsten eingesetzt.

Beim Programmieren wird die Fehlersuche entweder mittels der IS_DEBUG_MODE Makros oder der MQLInfoInteger() Funktion mit dem MQL_DEBUG Identifikator aktiviert.

Kommen wir jetzt zum Modus 'Profilerstellung'.

GS      0       17:30:53.879    Test1_Modes_EA (EURUSD.e,H1)     Current mode: MQL_MODE_PROFILER
OR      0       17:30:53.879    Test1_Modes_EA (EURUSD.e,H1)     Debug mode: no
GE      0       17:30:53.879    Test1_Modes_EA (EURUSD.e,H1)     Profile mode: yes
QM      0       17:30:53.879    Test1_Modes_EA (EURUSD.e,H1)     Tester mode: no
CE      0       17:30:53.879    Test1_Modes_EA (EURUSD.e,H1)     Optimization mode: no
FM      0       17:30:53.879    Test1_Modes_EA (EURUSD.e,H1)     Visual mode: no
GJ      0       17:30:53.879    Test1_Modes_EA (EURUSD.e,H1)     Frame mode: no

Die Funktion hat richtig eingeschätzt, dass der Profiler beteiligt war.

In diesem Modus kann geprüft werden, wie schnell das Programm arbeitet. Der Profiler überträgt die Information zum Zeitaufwand an die Programmblöcke. Dieses Instrument soll die Engpässe eines Algorithmus finden und aufzeigen. Diese Engpässe kann man zwar nicht immer komplett loswerden, doch ist eine diesbezügliche Information sehr hilfreich.

Profilerstellung wird entweder durch die IS_PROFILE_MODE Makros oder die MQLInfoInteger() Funktion mit dem MQL_PROFILER Identifikator aktiviert.

Sehen wir uns nun den Testmodus an. Diese Information taucht in der Registerkarte "Logbuch" des Strategietesters auf.

EG      0       17:35:25.397    Core 1  2014.11.03 00:00:00   Current mode: MQL_MODE_TESTER
OS      0       17:35:25.397    Core 1  2014.11.03 00:00:00   Debug mode: no
GJ      0       17:35:25.397    Core 1  2014.11.03 00:00:00   Profile mode: no
ER      0       17:35:25.397    Core 1  2014.11.03 00:00:00   Tester mode: yes
ED      0       17:35:25.397    Core 1  2014.11.03 00:00:00   Optimization mode: no
NL      0       17:35:25.397    Core 1  2014.11.03 00:00:00   Visual mode: no
EJ      0       17:35:25.397    Core 1  2014.11.03 00:00:00   Frame mode: no

Auch dieser Modus wurde korrekt erkannt.

Er ist der standardmäßig eingestellte Modus des EAs, wenn der Strategietester geöffnet wird.

Für diesen Modus gibt es keine Makros, daher können wir ihn in MQL5 nur mittels der MQLInfoInteger() Funktion mit dem MQL_TESTER Identifikator festlegen.

Als nächstes kommt die Optimierung. Ein Logbuch mit Einträgen wird im Ordner des Agenten gespeichert. In meinem Fall lautet sein Pfad folgendermaßen: %Program Files\MetaTrader5\tester\Agent-127.0.0.1-3000\logs

OH      0       17:48:14.010    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Current mode: MQL_MODE_OPTIMIZATION
KJ      0       17:48:14.010    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Debug mode: no
NO      0       17:48:14.010    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Profile mode: no
FI      0       17:48:14.010    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Tester mode: yes
KE      0       17:48:14.010    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Optimization mode: yes
LS      0       17:48:14.010    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Visual mode: no
QE      0       17:48:14.010    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Frame mode: no

Ist der Optimierungsmodus aktiv, wird standardmäßig der Testmodus aktiviert.

Im Strategietester ist der Optimierungsmodus aktiv, wenn das Feld "Optimierung" in der Registerkarte "Einstellungen" nicht deaktiviert ist.

Um herauszufinden, ob der EA im Optimierungsmodus in MQL5 getestet wird, kann man die MQLInfoInteger() Funktion mit dem MQL_OPTIMIZATION-Identifikator aufrufen.

Als nächstes kommen wir zum Visualisierungsmodus.

JQ      0       17:53:51.485    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Current mode: MQL_MODE_VISUAL
JK      0       17:53:51.485    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Debug mode: no
KF      0       17:53:51.485    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Profile mode: no
CP      0       17:53:51.485    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Tester mode: yes
HJ      0       17:53:51.485    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Optimization mode: no
LK      0       17:53:51.485    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Visual mode: yes
KS      0       17:53:51.485    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Frame mode: no

Hier können wir sehen, dass die Modi 'visuelles Testen und 'Standard-Test' beteiligt sind.

Der EA arbeitet im Strategietester in diesem Modus, wenn das Feld "Visualisierung" in der Registerkarte "Einstellungen" angemarkert ist.

Das Testen eines MQL5-Programms im visuellen Testmodus geschieht, in dem man die MQLInfoInteger() Funktion mit dem MQL_VISUAL_MODE-Identifikator verwendet.

Der letzte Modus hat mit dem Umgang von Rahmen zu tun.

HI      0       17:59:10.177    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Current mode: MQL_MODE_FRAME
GR      0       17:59:10.177    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Debug mode: no
JR      0       17:59:10.177    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Profile mode: no
JG      0       17:59:10.177    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Tester mode: yes
GM      0       17:59:10.177    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Optimization mode: yes
HR      0       17:59:10.177    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Visual mode: no
MI      0       17:59:10.177    Test1_Modes_EA (EURUSD.e,H1)     2014.11.03 00:00:00   Frame mode: no

Interessanterweise erkannte die Funktion nur den Test- und Optimierungsmodus, da der Marker der Rahmen genullt war. Wird der Aufruf der Funktion an den OnTesterInit()-Handler übertragen, dann enthält das "Experts" Logbuch die folgenden Einträge

IO      0       18:04:27.663    Test1_Modes_EA (EURUSD.e,H1)     Current mode: MQL_MODE_FRAME
GE      0       18:04:27.663    Test1_Modes_EA (EURUSD.e,H1)     Debug mode: no
ML      0       18:04:27.663    Test1_Modes_EA (EURUSD.e,H1)     Profile mode: no
CJ      0       18:04:27.663    Test1_Modes_EA (EURUSD.e,H1)     Tester mode: no
QR      0       18:04:27.663    Test1_Modes_EA (EURUSD.e,H1)     Optimization mode: no
PL      0       18:04:27.663    Test1_Modes_EA (EURUSD.e,H1)     Visual mode: no
GS      0       18:04:27.663    Test1_Modes_EA (EURUSD.e,H1)     Frame mode: yes

Jetzt wird effektiv nur der erfassende Rahmenmodus entdeckt.

Dieser Modus wird im Strategietester verwendet, wenn das Feld "Optimierung" in der Registerkarte "Einstellungen" nicht deaktiviert ist. Wie die Erfahrung gezeigt hat, wird dieser Modus im Korpus der OnTesterInit(), OnTesterPass() und OnTesterDeinit()-Handler festgelegt.

Die MQLInfoInteger() Funktion mit dem MQL_FRAME_MODE-Identifikator kann die Identifizierung der Tatsache des Testens eines EAs im erfassenden Rahmenmodus erleichtern.

Unten steht der Code der Dienstfunktion MqlMode(), die automatisch den Modus festlegt, in dem EA arbeitet.

//+------------------------------------------------------------------+
//| Identify the current MQL mode                                    |
//+------------------------------------------------------------------+
ENUM_MQL_MODE MqlMode(void)
  {
   ENUM_MQL_MODE curr_mode=WRONG_VALUE;

//--- if it is debug mode
   if(MQLInfoInteger(MQL_DEBUG))
      curr_mode=MQL_MODE_DEBUG;
//--- if it is code profiling mode
   else if(MQLInfoInteger(MQL_PROFILER))
      curr_mode=MQL_MODE_PROFILER;
//--- if it is visual test mode
   else if(MQLInfoInteger(MQL_VISUAL_MODE))
      curr_mode=MQL_MODE_VISUAL;
//--- if it is optimization mode
   else if(MQLInfoInteger(MQL_OPTIMIZATION))
      curr_mode=MQL_MODE_OPTIMIZATION;
//--- if it is test mode
   else if(MQLInfoInteger(MQL_TESTER))
      curr_mode=MQL_MODE_TESTER;
//--- if it is frame gathering optimization result mode
   else if(MQLInfoInteger(MQL_FRAME_MODE))
      curr_mode=MQL_MODE_FRAME;
//--- if it is release mode
   else
      curr_mode=MQL_MODE_RELEASE;
//---
   return curr_mode;
  }

Da Standardtests im Optimierungs- und visuellen Testmodus identifiziert werden, muss nach dem Optimierungs- und Visualisierungsmodus natürlich auch der Standard-Testmodus angekreuzt werden.

Wenn wir einen Blick auf die Arbeit der Funktion im zweiten Template des Test2_Modes_EA.mq5 EAs werfen, erkennen wir deutlich, dass beim Start des Templates im Logbuch ein neuer Eintrag auftaucht. So wurde z.B. für den Profilerstellungs-Modus der unten stehende Eintrag gemacht:

HG      0       11:23:52.992    Test2_Modes_EA (EURUSD.e,H1)    Current mode: MQL_MODE_PROFILER

Jetzt haben wir die Einzelheiten der betrieblichen Modi des MQL5 Experts zur Erzeugung von Klassenmodellen entsprechend eines spezifischen Modus besprochen. Ihn werden nun im nächsten Abschnitt des Beitrags implementieren.


3. Template des EAs, der für die Arbeit in unterschiedlichen Modi konzipiert wurde

Ich schlage vor, wir gehen die Entwicklungsphasen eines EAs nochmal kurz durch.

In der Algorithmisierungs-Phase beschäftigt sich ein Programmierer am häufigsten mit Fehlersuche und Profilerstellung. Zum Testen der historischen Daten probiert er alle Modi des Strategietesters aus. Der Abschlussmodus (Freigabe-Modus) wird dann im Online-Handel verwendet.

Ich bin der Meinung, dass ein EA mehrere Facetten besitzen muss, und zwar in dem Sinn, dass die Anforderung der Entwicklungs- und Testphasen ins einem Code abgebildet werden müssen.

Hier wird dann der hauptsächliche Algorithmus bewahrt, sodass sich der EA, infolge dieses Algorithmus, in unterschiedlichen Modi auch unterschiedlich verhält. Das Set an Objekt-orientierten Programmierungs-Tools eignet sich perfekt zur Umsetzung dieses Gedankens.

Abb. 3 Klassenhierarchie des EA, der für die Arbeit in unterschiedlichen Modi konzipiert wurde

Abb. 3 Klassenhierarchie des EA, der für die Arbeit in unterschiedlichen Modi konzipiert wurde

Abb. 3 zeigt die Klassenhierarchie mit der Implementierung verschiedener Modi.

Die Basisklasse CModeBase, die alle gemeinsamen Dinge einschließt, besitzt zwei direkte Nachkommen: die CModeRelease und CModeTester Klassen. Die erste Klasse ist die übergeordnete Klasse der Fehlersuch-Klassen, und die zweite ist die übergeordnete Klasse für alle Klassen, die mit dem Test des EAs auf historischen Daten zusammenhängt.

Entwickeln wir den Gedanken der Kombination des prozeduralen und modularen Ansatzes bei der Entwicklung von Klassenmethoden im Modus-Kontext weiter. Betrachten wir uns diesbezüglich als Beispiel die folgende Handelslogik:

  1. Auf einem Signal öffnen, falls es keine offene Position gibt;
  2. Auf dem Signal schließen, falls es eine offene Position gibt;
  3. Trailing Stop bei offener Position.

Das Handelssignal wird vom Standard-Indikator MACD entdeckt, sobald ein neuer Bar auftaucht.

Ein Signal für Kaufen erscheint, wenn die Hauptlinie ansteigt und das Signal 1 im Negativbereich des MACD-Indikators kreuzt (Abb. 4).

Abb. 4 Signal für Kaufen

Abb. 4 Signal für Kaufen

Ein Signal für Verkaufen erscheint, wenn die Hauptlinie abfällt und das Signal 1 im Positivbereich des MACD-Indikators kreuzt (Abb. 5).

Abb. 5 Signal für Verkaufen

Abb. 5 Signal für Verkaufen

Die Position wird geschlossen, entweder wenn das gegensätzliche Signal auftaucht oder per Stop Loss, der dann platziert wird, wenn der Modus der Positions-Stützung aktiviert ist.

Die Definition der Basisklasse CModeBase lautet dann folgendermaßen:

//+------------------------------------------------------------------+
//| Class CModeBase                                                  |
//| Purpose: a base class for MQL-modes                              |            
//+------------------------------------------------------------------+
class CModeBase
  {
//--- === Data members === --- 
private:
   //--- a macd object & values
   CiMACD            m_macd_obj;
   double            m_macd_main_vals[2];
   double            m_macd_sig_vals[2];

protected:
   long              m_pos_id;
   bool              m_is_new_bar;
   uint              m_trailing_stop;
   uint              m_trail_step;
   //--- trade objects
   CSymbolInfo       m_symbol_info;
   CTrade            m_trade;
   CPositionInfo     m_pos_info;
   CDealInfo         m_deal_info;
   //--- mql mode
   ENUM_MQL_MODE     m_mql_mode;

   //--- a new bar object
   CisNewBar         m_new_bar;
   //--- current tick signal flag
   bool              m_is_curr_tick_signal;
   //--- close order type
   ENUM_ORDER_TYPE   m_close_ord_type;
   
//--- === Methods === --- 
public:
   //--- constructor/destructor
   void              CModeBase();
   void             ~CModeBase(void){};
   //--- initialization
   virtual bool      Init(int _fast_ema,int slow_ema,int _sig,ENUM_APPLIED_PRICE _app_price);
   virtual void      Deinit(void){};

   //--- Modules
   virtual void      Main(void){};

   //--- Procedures
   virtual void      Open(void){};
   virtual void      Close(void){};
   virtual void      Trail(void){};

   //--- Service
   static ENUM_MQL_MODE CheckMqlMode(void);
   ENUM_MQL_MODE     GetMqlMode(void);
   void              SetMqlMode(const ENUM_MQL_MODE _mode);
   void              SetTrailing(const uint _trailing,const uint _trail_step);

protected:
   //--- Functions
   ENUM_ORDER_TYPE   CheckOpenSignal(const ENUM_ORDER_TYPE _open_sig);
   ENUM_ORDER_TYPE   CheckCloseSignal(const ENUM_ORDER_TYPE _close_sig);
   ENUM_ORDER_TYPE   CheckTrailSignal(const ENUM_ORDER_TYPE _trail_sig,double &_sl_pr);
   //---
   double            GetMacdVal(const int _idx,const bool _is_main=true);

private:
   //--- Macros
   bool              RefreshIndicatorData(void);
   //--- Normalization
   double            NormalPrice(double d);
   double            NormalDbl(double d,int n=-1);
   double            NormalSL(const ENUM_ORDER_TYPE _ord_type,double op,double pr,
                              uint SL,double stop);
   double            NormalTP(const ENUM_ORDER_TYPE _ord_type,double op,double pr,
                              uint _TP,double stop);
   double            NormalLot(const double _lot);
  };

Eine Basisklasse kann alles mögliche beinhalten, solange es in den "ererbten" Klassen verwendet wird.

Die MACD-Daten sind für Nachkommen nicht verfügbar, da sie durch private Mitglieder dargestellt werden.

Hier sei darauf hingewiesen, dass es unter diesen Methoden auch virtuelle gibt: Main(), Open(), Close(), Trail(). Ihre Implementierung hängt in großem Maße vom Modus ab, in dem der EA aktuell arbeitet. Diese Methoden bleiben für die Basisklasse leer.

Darüber hinaus umfasst die Basisklasse Methoden, die dieselbe Handelslogik für alle MQL-Modi haben können. Zu ihnen gehören alle Signal-Modi:

Man sollte nicht vergessen, dass es in diesem Beitrag nicht um das Schreiben eines Codes für alle MQL-Modusarten geht. Als Beispiel seien der Standard- und der visuelle Testmodus angeführt.


3.1 Testmodus

Nachdem der Algorithmus kodiert und erstellt worden ist, probiere ich die Strategie meistens auf historischen Daten im Strategietester aus, um zu sehen, ob alles so funktioniert, wie gedacht.

Am häufigsten muss man hierbei überprüfen wie exakt das System Handelssignale implementiert. Auf jeden Fall ist das grundlegende Ziel in dieser Phase, dass der EA gestartet wird und zu handeln beginnt.

Die CModeTester Klasse für reguläre Tests kann folgendermaßen Implementiert werden:

//+------------------------------------------------------------------+
//| Class CModeTester                                                |
//| Purpose: a class for the tester mode                             |            
//| Derives from class CModeBase.                                    |
//+------------------------------------------------------------------+
class CModeTester : public CModeBase
  {
//--- === Methods === --- 
public:
   //--- constructor/destructor
   void              CModeTester(void){};
   void             ~CModeTester(void){};

   //--- Modules
   virtual void      Main(void);

   //--- Procedures
   virtual void      Open(void);
   virtual void      Close(void);
   virtual void      Trail(void);
  };

Das Hauptmodul wird so implementiert:

//+------------------------------------------------------------------+
//| Main module                                                      |
//+------------------------------------------------------------------+
void CModeTester::Main(void)
  {
//--- 1) closure
   this.Close();
//--- 2) opening
   this.Open();
//--- 3) trailing stop
   this.Trail();
  }

Erstellen Sie für den Modus 'reguläres Testen' eine Möglichkeit, Informationen über Handelssignale in das Logbuch drucken zu können.

Fügen Sie auch Strings mit Indikatorwerten hinzu, die als die Quelle des Handelssignals betrachtet werden.

Nachfolgend finden Sie einen Auszug aus dem Logbuch über ein Signal zum Öffnen einer Position, gefolgt von einem Signal zum Schließen.

HE      0       13:34:04.118    Core 1  2014.11.14 22:15:00   ---=== Signal to open: SELL===---
FI      0       13:34:04.118    Core 1  2014.11.14 22:15:00   A bar before the last one, main: 0.002117; signal: 0.002109
DL      0       13:34:04.118    Core 1  2014.11.14 22:15:00   The last bar, main: 0.002001; signal: 0.002118
LO      0       13:34:04.118    Core 1  2014.11.14 22:15:00   market sell 0.03 EURUSD.e (1.25242 / 1.25251 / 1.25242)
KH      0       13:34:04.118    Core 1  2014.11.14 22:15:00   deal #660 sell 0.03 EURUSD.e at 1.25242 done (based on order #660)
GE      0       13:34:04.118    Core 1  2014.11.14 22:15:00   deal performed [#660 sell 0.03 EURUSD.e at 1.25242]
OD      0       13:34:04.118    Core 1  2014.11.14 22:15:00   order performed sell 0.03 at 1.25242 [#660 sell 0.03 EURUSD.e at 1.25242]
IK      0       13:34:04.118    Core 1  2014.11.14 22:15:00   CTrade::OrderSend: market sell 0.03 EURUSD.e [done at 1.25242]
IL      0       13:34:04.118    Core 1  2014.11.17 13:30:20   
CJ      0       13:34:04.118    Core 1  2014.11.17 13:30:20   ---=== Signal to close: SELL===---
GN      0       13:34:04.118    Core 1  2014.11.17 13:30:20   A bar before the last one, main: -0.001218; signal: -0.001148
QL      0       13:34:04.118    Core 1  2014.11.17 13:30:20   The last bar, main: -0.001123; signal: -0.001189
EP      0       13:34:04.118    Core 1  2014.11.17 13:30:20   market buy 0.03 EURUSD.e (1.25039 / 1.25047 / 1.25039)
FG      0       13:34:04.118    Core 1  2014.11.17 13:30:20   deal #661 buy 0.03 EURUSD.e at 1.25047 done (based on order #661)
OJ      0       13:34:04.118    Core 1  2014.11.17 13:30:20   deal performed [#661 buy 0.03 EURUSD.e at 1.25047]
PD      0       13:34:04.118    Core 1  2014.11.17 13:30:20   order performed buy 0.03 at 1.25047 [#661 buy 0.03 EURUSD.e at 1.25047]
HE      0       13:34:04.118    Core 1  2014.11.17 13:30:20   CTrade::OrderSend: market buy 0.03 EURUSD.e [done at 1.25047]

Beachten Sie bitte, dass es im Strategietester das bekannte Logbuch "Experts" nicht gibt. Alle Informationen findet man in der Registerkarte "Logbuch", die alle Einträge über Aktivitäten enthält, die während des Tests und der Optimierung des Strategietesters ausgeführt wurden.

Und deshalb muss man nach den benötigten Strings suchen. Muss die Einstiegsinformation getrennt vermerkt werden, kann sie in eine Datei geschrieben werden.

Die Strategie für Standard-Tests ist im Code des TestMode_tester.mq5 EA implementiert.


3.2 Visueller Testmodus

Manchmal muss man sich eines Live-Charts bedienen, um herauszufinden, wie ein EA eine aktuelle Situation verarbeitet.

Einfache Visualisierung erlaubt hier nicht nur, zu erkennen, wie das Handelssystem auf Kursschwankungen reagiert, sondern gestattet auch einen Vergleich am Ende des Tests von ähnlichen Kursmodellen.

Die Definition der CModeVisual Klasse für visuelle Tests kann z.B. so aussehen:

//+------------------------------------------------------------------+
//| Class CModeVisual                                                |
//| Purpose: a class for the tester mode                             |            
//| Derived from class CModeBase.                                    |
//+------------------------------------------------------------------+
class CModeVisual : public CModeTester
  {
//--- === Data members === --- 
private:
   CArrayObj         m_objects_arr;
   double            m_subwindow_max;
   double            m_subwindow_min;
   
//--- === Methods === --- 
public:
   //--- constructor/destructor
   void              CModeVisual(void);
   void             ~CModeVisual(void);

   //--- Procedures
   virtual void      Open(void);
   virtual void      Close(void);

private:
   bool              CreateSignalLine(const bool _is_open_sig,const bool _is_new_bar=true);
   bool              CreateRectangle(const ENUM_ORDER_TYPE _signal);
   void              RefreshRectangles(void);
  };

Die Klasse enthält verborgene Mitglieder. Ein Mitglied der m_objects_arr Klasse implementiert ein dynamisches Array des Typs CArrayObj. Hierhin gehören z.B. graphische Objekte wie Linien und Rechtecke. Zwei andere Klassenmitglieder (m_subwindow_max, m_subwindow_min) kontrollieren die Maximal- und Minimalgröße des Unterfenster des Indikators.

Private Methoden sind für die Arbeit mit graphischen Objekten zuständig.

Diese Klasse enthält nicht die Main() und die Trail() Methoden. Es werden ihre übergeordneten analogen Methoden CModeTester::Main() bzw. CModeTester::Trail() aufgerufen.

Im virtuellen Testmodus können graphische Objekte erzeugt werden. Dies ist in keinem anderen Modus des Strategietesters möglich.

So kann z.B. jedes Mal, wenn ein Signal zum Einstieg auftaucht, dies auf dem Chart als rote senkrechte Linie gezeichnet werden und analog, als blaue senkrechte Linie, sobald ein Signal zum Ausstieg auftaucht. Und der Platz zwischen dem Einstieg- und Ausstiegspunkt kann mit einem Rechteck entsprechender Färbung im Unterfester des Indikators gefüllt werden.

So ist z.B. bei einer Long Position das Rechteck hellblau - und bei einer Short Position rosa (Abb. 6).

Abb. 6 Graphische Objekte im visuellen Testmodus

Abb. 6 Graphische Objekte im visuellen Testmodus

Die Höhe des Rechtecks hängt von den Maximal- und Minimalwerten des Unterfensters des Charts zum Zeitpunkt der Erzeugung des Rechtecks ab. Damit alle Rechtecke die gleiche Größe haben, sollte der Code um einen Block zur Änderung der Rechteck-Koordinaten ergänzt werden, für den Fall nämlich, dass sich die Koordinaten des Chart-Unterfensters ändern.

Im Unterfenster des MACD-Indikators erhalten wir also folgende Bereiche: ohne Farbe (keine Position), rosa (Short Position), hellblau (Long Position).

Die Strategie für den regulären Testmodus wird im Code desTestMode_visual_tester.mq5 EAs implementiert.


Fazit

Ich habe in diesem Beitrag versucht, die Modus-Fähigkeiten des MetaTrader 5 Terminals und der MQL5-Sprache zu veranschaulichen. In diesem Zusammenhang sei darauf hingewiesen, dass ein Multi-Modus Ansatz beim Programmieren eines Handels-Algorithmus einerseits Mehrkosten bedeutet, und andererseits dadurch aber auch jede einzelne Entwicklungsphase nacheinander separat betrachtet werden kann. Die Objekt-orientierte Programmierung stellt in diesem Fall eine einfallsreiche Hilfe für Programmierer dar.

Auf die Modi 'Optimierung' und 'Rahmen sammelnd' wird in weiteren Beiträgen zu den statistischen Eigenschaften eines Handelssystems noch im Einzelnen eingegangen.