English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
In 6 Schritten zum eigenen automatischen Handelssystem!

In 6 Schritten zum eigenen automatischen Handelssystem!

MetaTrader 5Beispiele | 27 Januar 2016, 09:23
2 405 0
MetaQuotes
MetaQuotes

Noch einmal etwas zu dem Assistenten in MQL5

Die uns umgebende Welt verändert sich rasant, und wir versuchen, damit Schritt zu halten. Wir haben keine Zeit, etwas Neues zu lernen, und das gilt für alle Menschen gleichermaßen. Händler sind auch bloß Menschen, sie wollen mit möglichst wenig Einsatz möglichst viel Ertrag. Und genau deshalb bietet das Bearbeitungsprogramm MetaEditor 5 so etwas Wunderbares wie den MQL5-Assistenten. Wie man mit seiner Hilfe ein einsatzbereites automatisches Handelssystem erstellt, wurde mehr als einmal beschrieben, angefangen mit der vereinfachten Fassung: „Der MQL5-Assistent für Laien“ bis hin zu der „Entwicklerausführung“: „MQL5 Wizard: Neue Version (New Version).

Und damit wäre alles klar, ein automatisches Handelssystem mit 5 Mausklicks, dann ab in das Prüfprogramm zur Optimierung seiner Parameter und schon kann das entwickelte Expert-System eingerichtet werden, um von einem Konto aus Handel zu treiben, ohne dass irgendwelche Feinschliffarbeiten erforderlich wären. Wäre da nicht das Problem, das unversehens auftritt, wenn ein Händler in MQL5 etwas Einzigartiges, noch nie und nirgends Programmiertes entwickeln möchte und sich anschickt, ein ganz eigenes Handelssignalmodul zu schreiben. Er öffnet dazu die Dokumentation zu MQL5, kommt zur Standardbibliothek und erblickt mit Schrecken...


Fünf furchtbare Klassen

In der Tat vereinfacht der MQL5-Assistent die Erstellung von Expert-Systemen ganz erheblich, man muss jedoch im Vorfeld genau untersuchen, was in ihn eingegeben wird. Um mithilfe des MQL5-Assistenten ein Expert-System automatisch zu erstellen, müssen seine Bestandteile an den fünf Basisklassen aus dem Abschnitt Basisklassen von Expert-Systemen festhalten:

  • CExpertBase - eine bestimmte Basisklasse für vier weitere Klassen.
  • CExpert - im Grunde die Klasse zur Erstellung eines automatischen Handelssystems und gleichzeitig die Klasse, die den Handel ausführt.
  • CExpertSignal - die Klasse zum Anlegen von Handelssignalen; um sie geht es auch in diesem Beitrag.
  • CExpertTrailing - die Klasse zum Nachziehen der Stop Loss-Verlustgrenze.
  • CExpertMoney - eine Klasse zur Ausübung der Kapitalverwaltung.

Vor uns liegt jetzt in ganzer Pracht der „große und schreckliche“ Ansatz namens Objektorientierte Programmierung (OOP). Aber es besteht kein Anlass zur Furcht, verfügt doch heutzutage nahezu jede und jeder über ein Mobiltelefon mit zahllosen Funktionen, obwohl kaum jemand weiß, wie das alles vor sich geht. Wir müssen das alles auch gar nicht näher untersuchen, wir befassen uns lediglich ein wenig mit der Klasse CExpertSignal und einigen ihrer Funktionen.


In diesem Beitrag arbeiten wir Schritt für Schritt alle Stadien der Erstellung eines Handelssignalmoduls ab und zwar ganz ohne Beschäftigung mit der OOP oder den Klassen selbst. Wenn Sie wollen, können Sie danach noch etwas weiter gehen.


1. Anlegen einer Klasse aus dem Nichts

Wir werden kein bereits vorhandenes Modul mit Handelssignalen nach unseren Bedürfnissen umgestalten, denn das wäre bestens geeignet uns zu verwirren. Stattdessen programmieren wir einfach eine eigene Klasse, aber zunächst legen wir mithilfe des Navigators in dem Bereich MQL5/Include/Expert/ einen neuen Ordner zur Speicherung unserer Signale an.



Wir klicken mit der rechten Maustaste auf den von uns angelegten Ordner und wählen „Neue Datei“, anschließend erstellen wir eine neue Klasse für unser künftiges Handelssignalmodul.


Wir füllen die Felder aus:

  • Class Name - Bezeichnung der Klasse. Wir legen ein Modul zur Erzeugung von Signalen bei der Überschneidung zweier gleitender Durchschnittswerte an, also nennen wir die Klasse eindeutig und nachvollziehbar MA_Cross.
  • Base Name - die Klasse, von der unsere abstammt. Und wir müssen von der Basisklasse CExpertSignal abstammen.

Wir klicken auf „Fertig“ und schon haben wir den Entwurf für unser Modul vor Augen. Bis hierhin verläuft alles gut und einfach. Wir schreiben lediglich in die erhaltene Datei noch die Deklaration #include hinein, damit das Kompilierprogramm weiß, wo besagte Basisklasse CExpertSignal zu finden ist.

#include "..\ExpertSignal.mqh"   // CExpertSignal is in the file ExpertSignal

Das Ergebnis ist:

//+------------------------------------------------------------------+
//|                                                     MA_Cross.mqh |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "..\ExpertSignal.mqh"   // CExpertSignal is in the file ExpertSignal
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class MA_Cross : public CExpertSignal
  {
private:

public:
                     MA_Cross();
                    ~MA_Cross();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MA_Cross::MA_Cross()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MA_Cross::~MA_Cross()
  {
  }
//+------------------------------------------------------------------+

Wir überprüfen die erhaltene Klasse auf die Fehlerlosigkeit ihrer Zusammenstellung und betätigen die Funktionstaste F7. Keine Fehlermeldung, also weiter.


2. Der Moduldeskriptor

Unsere Klasse ist vollkommen leer, sie enthält keine Fehler, und wir können sie testen, wir werden versuchen, in dem MQL5-Assistenten auf ihrer Grundlage ein neues Expert-System namens Expert Advisor anzulegen. Wir gehen weiter zur Auswahl des Handelssignalmoduls und stellen fest... Unser Modul ist nicht vorhanden.


Wo sollte es auch herkommen? Wir haben ihm keinerlei Erkennungsmerkmale gegeben, anhand derer der MQL5-Assistent erkennen kann, dass es sich bei unserer Klasse um etwas Nützliches handeln könnte. Beheben wir diese missliche Situation: Bei aufmerksamer Betrachtung eines beliebigen Moduls aus dem Standardlieferumfang wird ersichtlich, dass jedes von Ihnen in seiner Datei eine Kopfzeile aufweist. Das ist der nach bestimmten Regeln angelegte Deskriptor (die Bezeichnung) des Moduls. Besagte Regeln sind recht simpel.

Wir öffnen beispielsweise den ursprünglichen Programmcode des Moduls für Handelssignale auf der Grundlage des Indikators AMA (die Beschreibung der entsprechenden Logik findet sich unter: „Signale des durchschnittlichen gleitenden Durchschnittswerts (Adaptive Moving Average)“.) Anschließend starten wir den MQL5-Assistenten, in welchem wir dieses Modul auswählen. Zum Vergleich:

Der letzte Block des Deskriptors bezieht sich auf die Parameter des Moduls, wobei die erste Zeile die Modulbezeichnung enthält, die in dem MQL5-Assistenten angezeigt wird. Wie zu sehen ist, ist da nichts Kompliziertes. Der Deskriptor eines Moduls umfasst also stets folgende Einträge:

  • Title - die im MQL5-Assistenten angezeigte Bezeichnung des Moduls.
  • Type - die Fassung des Signalmoduls. Hier muss stets SignalAdvanced stehen.
  • Name - Wiedergabe der Bezeichnung des Moduls nach dessen Auswahl im MQL5-Assistenten; der „Name“ wird auch in den Kommentaren zur Beschreibung der externen Parameter des erstellten Expert-Systems (wünschenswerterweise anzugeben) verwendet.
  • ShortName - Präfix zur automatischen Erstellung von Bezeichnungen für externe Parameter in dem erstellten Expert-System (in der Form Signal_<ShortName>_<ParameterName>).
  • Class - Bezeichnung der in dem jeweiligen Modul enthaltenen Klasse.
  • Page - Parameter zum Abrufen der Informationen über das jeweilige Modul (verfügbar nur für Module des Standardlieferumfangs).

Es folgen die Beschreibungen der Parameter in der Form Parameter=Werteverzeichnis (list_of_values), in dem Folgendes in kommagetrennter Schreibweise aufgeführt wird:

  1. Der Name der Funktion zur Festlegung der Parameterwerte beim Aufrufen des Expert-Systems.
  2. Bei der Art des Parameters kann es sich um eine Aufzählung handeln.
  3. Der Standardwert für den Parameter, das heißt, der Wert, der dem Parameter zugewiesen wird, wenn er nicht im MQL5-Assistenten geändert wird.
  4. Die beim Starten des mithilfe des MQL5-Assistenten angelegten Expert-Systems angezeigte Beschreibung des Parameters.

Jetzt, da wir wissen wozu, erstellen wir den Deskriptor für unser Handelssignalmodul. Also, schreiben wir ein Modul für den Empfang von Handelssignalen an den Schnittpunkten zweier gleitender Durchschnittswerte. Das heißt, wir müssen mindestens vier externe Parameter kennen:

  • FastPeriod - den Zeitraum des schnell gleitenden Durchschnittswerts;
  • FastMethod - die Art der Glättung des schnell gleitenden Durchschnittswerts;
  • SlowPeriod - den Zeitraum des langsam gleitenden Durchschnittswerts;
  • SlowMethod - die Art der Glättung des langsam gleitenden Durchschnittswerts.

Es könnte noch die Abweichung und die Art der Kurse zur Berechnung jedes einzelnen Durchschnittswerts hinzugefügt werden, aber dadurch würde sich im Grunde nichts ändern. Somit sieht die aktuelle Variante aus, wie folgt:

// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Signals at the intersection of two MAs                     |
//| Type=SignalAdvanced                                              |
//| Name=My_MA_Cross                                                 |
//| ShortName=MaCross                                                |
//| Class=MA_Cross                                                   |
//| Page=Not needed                                                  |
//| Parameter=FastPeriod,int,13,Period of fast MA                    |
//| Parameter=FastMethod,ENUM_MA_METHOD,MODE_SMA,Method of fast MA   |
//| Parameter=SlowPeriod,int,21,Period of slow MA                    |
//| Parameter=SlowMethod,ENUM_MA_METHOD,MODE_SMA,Method of slow MA   |
//+------------------------------------------------------------------+
// wizard description end

Der Moduldeskriptor ist fertig, und wir haben Folgendes in ihm angegeben:

  1. den im MQL5-Assistenten angezeigten Namen: „Signale am Schnittpunkt zweier gleitender Durchschnittswerte“.
  2. vier externe Parameter zur Einrichtung der Handelssignale:
    • FastPeriod - den Zeitraum des schnell gleitenden Durchschnittswerts mit dem Standardwert 13;
    • FastMethod - die Art der Glättung des schnell gleitenden Durchschnittswerts, standardmäßig die einfache Glättung;
    • SlowPeriod - den Zeitraum des langsam gleitenden Durchschnittswerts mit dem Standardwert 21;
    • SlowMethod - die Art der Glättung des langsam gleitenden Durchschnittswerts, standardmäßig die einfache Glättung;

Wir speichern die Änderungen und kompilieren, es dürfen keine Fehler vorliegen! Zur Probe starten wir den MQL5-Assistenten und... oh Wunder, unser Modul kann ausgewählt werden, und in ihm werden all unsere Parameter angezeigt.


Herzlichen Glückwunsch, unser Handelssignalmodul sieht wie ein echtes aus.


3. Methoden zur Einstellung der Parameter

Jetzt ist es an der Zeit, sich der externen Parameter anzunehmen. Da unser Handelsmodul durch die Klasse MA_Cross präsentiert wird, müssen seine Parameter innerhalb eben dieser Klasse als private Mitglieder gespeichert werden. Fügen wir zu der Deklarierung der Klassen entsprechend der Anzahl der Parameter vier Zeilen hinzu. Die Art des Parameters haben wir bereits im Deskriptor angegeben, und wir kennen:

class MA_Cross : public CExpertSignal
  {
private:
   //--- Configurable module parameters
   int               m_period_fast;    // Period of the fast MA
   int               m_period_slow;    // Period of the slow MA
   ENUM_MA_METHOD    m_method_fast;    // Type of smoothing of the fast MA
   ENUM_MA_METHOD    m_method_slow;    // Type of smoothing of the slow MA

Nur wie gelangen die Werte der externen Parameter des Moduls in die entsprechenden Mitglieder unserer Klasse MA_Cross? Das alles erweist sich als äußerst einfach, es reicht, in der Klasse gleichnamige öffentliche Methoden zu deklarieren, und zwar durch Hinzufügung von vier Zeilen im öffentlichen Bereich:

class MA_Cross : public CExpertSignal
  {
private:
   //--- Configurable module parameters
   int               m_period_fast;    // Period of the fast MA
   int               m_period_slow;    // Period of the slow MA
   ENUM_MA_METHOD    m_method_fast;    // Type of smoothing of the fast MA
   ENUM_MA_METHOD    m_method_slow;    // Type of smoothing of the slow MA

public:
   //--- Constructor of class
                     MA_Cross();
   //--- Destructor of class
                    ~MA_Cross();
   //--- Methods for setting
   void              FastPeriod(int value)               { m_period_fast=value;        }
   void              FastMethod(ENUM_MA_METHOD value)    { m_method_fast=value;        }
   void              SlowPeriod(int value)               { m_period_slow=value;        }
   void              SlowMethod(ENUM_MA_METHOD value)    { m_method_slow=value;        }
   };

Wenn man mithilfe des MQL5-Assistenten ein Expert-System auf der Grundlage dieses Moduls erstellt und es in einem Diagramm ausführen lässt, werden diese vier Methoden bei Bereitstellung des Expert-Systems automatisch aufgerufen. Daraus lässt sich eine einfache Regel ableiten:

Regel zum Anlegen eines Parameters in einem Modul - für jeden von uns im Deskriptor deklarierten Parameter müssen in der Klasse ein privates Mitglied zur Speicherung seines Wertes sowie eine öffentliche Methode zur Festlegung des Wertes für ihn erstellt werden. Die Bezeichnung der Methode muss der des Parameters entsprechen.

Bleibt als letztes Moment die Einrichtung der Standardwerte unserer vier Parameter für den Fall, dass die Methoden zur Festlegung der Werte nicht aufgerufen werden. Alle deklarierten Variablen bzw. Mitglieder der Klasse müssen bereitgestellt werden. Das trägt dazu bei, zahlreiche schwer zu ermittelnde Fehler zu vermeiden.

Zur automatischen Bereitstellung eignet sich am besten ein Klassenkonstruktor. Er wird bei der Objekterstellung stets als Erster aufgerufen. Als Standardwerte werden die im Moduldeskriptor vorgegebenen Werte aufgerufen.

class MA_Cross : public CExpertSignal
  {
private:
   //--- Configurable module parameters
   int               m_period_fast;    // Period of the fast MA
   ENUM_MA_METHOD    m_method_fast;     // Type of smoothing of the fast MA
   int               m_period_slow;    // Period of the slow MA
   ENUM_MA_METHOD    m_method_slow;     // Type of smoothing of the slow MA

public:
   //--- Constructor of class
                     MA_Cross(void);
   //--- Destructor of class
                    ~MA_Cross(void);
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
MA_Cross::MA_Cross(void) : m_period_fast(13),          // Default period of the fast MA is 3
                           m_method_fast(MODE_SMA),    // Default smoothing method of the fast MA
                           m_period_slow(21),          // Default period of the slow MA is 21
                           m_method_slow(MODE_SMA)     // Default smoothing method of the slow MA
  {
  }

Die Bereitstellung der Klassenmitglieder erfolgt hier mithilfe eines Bereitstellungsverzeichnisses.

Wie zu sehen ist, haben wir die Indikatoren der gleitenden Durchschnittswerte bislang noch nicht benötigt. Wir haben eine einfache Regel entdeckt: So viele Parameter, wie von uns im Moduldeskriptor deklariert wurden, so viele Methoden und Mitglieder muss die Klasse enthalten, die von diesem Modul umgesetzt wird. Das alles ist ganz einfach! Wir dürfen jedoch auf keinen Fall vergessen, im Konstruktor die Standardwerte für die Parameter festzulegen.


4. Verbindliche Überprüfung der Richtigkeit der Eingangsparameter

Die Parameter für unser Handelsmodul sind erstellt und die Methoden zur Festlegung ihrer Werte programmiert, jetzt beginnt die nächste wichtige Etappe: die Überprüfung der Parameter auf ihre Richtigkeit. In unserem Fall müssen die Zeiträume der gleitenden Durchschnittswerte sowie die Art der Glättung für ihre Berechnung überprüft werden. Dazu müssen wir in der Klasse unsere eigene ValidationSettings()-Methode programmieren. Diese Methode wurde bereits in der übergeordneten Elternklasse CExpertBase bestimmt und wird in allen abgeleiteten Objektklassen zwangsweise einfach erneut festgelegt.

Wenn man sich allerdings in der objektorientierten Programmierung nicht auskennt, muss man sich lediglich Folgendes ins Gedächtnis rufen: Wir müssen in unserer Klasse die Funktion ValidationSettings() programmieren, sie erfordert keinerlei Parameter und gibt entweder „true“ oder „false“ aus.

class MA_Cross : public CExpertSignal
  {
...
   //--- Constructor of class
                     MA_Cross(void);
   //--- Destructor of class
                    ~MA_Cross(void);
   //--- Checking correctness of input data
   bool              ValidationSettings();
...
   };
//+------------------------------------------------------------------+
//| Checks input parameters and returns true if everything is OK     |
//+------------------------------------------------------------------+
bool MA_Cross:: ValidationSettings()
  {
   //--- Call the base class method
   if(!CExpertSignal::ValidationSettings())  return(false);
   //--- Check periods, number of bars for the calculation of the MA >=1
   if(m_period_fast<1 || m_period_slow<1)
     {
      PrintFormat("Incorrect value set for one of the periods! FastPeriod=%d, SlowPeriod=%d",
                  m_period_fast,m_period_slow);
      return false;
     }
//--- Slow MA period must be greater that the fast MA period
   if(m_period_fast>m_period_slow)
     {
      PrintFormat("SlowPeriod=%d must be greater than FastPeriod=%d!",
                  m_period_slow,m_period_fast);
      return false;
     }
//--- Fast MA smoothing type must be one of the four values of the enumeration
   if(m_method_fast!=MODE_SMA && m_method_fast!=MODE_EMA && m_method_fast!=MODE_SMMA && m_method_fast!=MODE_LWMA)
     {
      PrintFormat("Invalid type of smoothing of the fast MA!");
      return false;
     }
//--- Show MA smoothing type must be one of the four values of the enumeration
   if(m_method_slow!=MODE_SMA && m_method_slow!=MODE_EMA && m_method_slow!=MODE_SMMA && m_method_slow!=MODE_LWMA) 
     {
      PrintFormat("Invalid type of smoothing of the slow MA!");
      return false;
     }
//--- All checks are completed, everything is ok
   return true;
  }
Wie wir sehen, haben wir im öffentlichen Teil der Klasse MA_Cross zunächst die Deklaration der Methode ValidationSettings() und anschließend den Haupttext der Methode selbst in folgender Form hinzugefügt:
bool MA_Cross:: ValidationSettings()

An erster Stelle steht die Art des auszugebenden Wertes gefolgt von der Bezeichnung der Klasse sowie dem Gültigkeitsbereichsoperator (::) und nach alledem kommt die Bezeichnung der zuvor deklarierten Methode. Dabei darf nicht vergessen werden, dass die Bezeichnungen sowie die Art der Parameter in der Deklaration und der Beschreibung der Methode der Klasse übereinstimmen müssen. Im Übrigen wird uns das Kompilierprogramm einen solchen Fehler melden.

Beachten Sie bitte, dass zunächst die Methode der Basisklasse aufgerufen wird, und unsere Überprüfungen der Eingangsparameter erst danach ausgeführt werden.

//--- Call the base class method
   if(!CExpertSignal::ValidationSettings())  return(false);
//--- Our code to check the values of parameters

Ohne Hinzufügen dieser Zeile kann das erstellte Expert-System unser Handelssignalmodul nicht bereitstellen.


5. Wo sind unsere Indikatoren?

Jetzt, da alle Vorarbeiten an ihren Parametern abgeschlossen sind, ist es höchste Zeit, dass wir uns der Indikatoren selbst annehmen. Jedes Handelssignalmodul enthält die Methode InitIndicators(), die bei jeder Ausführung des erstellten Expert-Systems automatisch aufgerufen wird. Genau in dieser Methode müssen wir unser Modul mit den Indikatoren der gleitenden Durchschnittswerte versehen.

Zunächst deklarieren wir in der Klasse die Methode InitIndicators() und fügen ihren vorgefertigten Code ein:

public:
   //--- Constructor of class
                     MA_Cross(void);
   //--- Destructor of class
                    ~MA_Cross(void);
   //--- Methods for setting
   void              FastPeriod(int value)               { m_period_fast=value;        }
   void              FastMethod(ENUM_MA_METHOD value)    { m_method_fast=value;        }
   void              SlowPeriod(int value)               { m_period_slow=value;        }
   void              SlowMethod(ENUM_MA_METHOD value)    { m_method_slow=value;        }
   //--- Checking correctness of input data
   bool              ValidationSettings();
   //--- Creating indicators and timeseries for the module of signals
   bool              InitIndicators(CIndicators *indicators);
  };
...
//+------------------------------------------------------------------+
//| Creates indicators                                               |
//| Input:  a pointer to a collection of indicators                  |
//| Output: true if successful, otherwise false                      |
//+------------------------------------------------------------------+
bool MA_Сross::InitIndicators(CIndicators* indicators)
  {
//--- Standard check of the collection of indicators for NULL
   if(indicators==NULL)                           return(false);
//--- Initializing indicators and timeseries in additional filters
   if(!CExpertSignal::InitIndicators(indicators)) return(false);
//--- Creating our MA indicators
   ... Some code here
//--- Reached this part, so the function was successful, return true
   return(true);
  }

Also, da ist nichts Kompliziertes, wir deklarieren die Methode und erstellen anschließend einfach ihren Haupttext, wie wir es bereits bei der Methode ValidationSettings() getan haben. Die Hauptsache ist, nicht zu vergessen, die Bezeichnung der Klasse und den Operator (::) in die Definition der Funktion einzufügen. Jetzt haben wir das Ausgangsmaterial, in das wir den Code zur Erstellung gleitender Durchschnittswerte einfügen müssen. Wir wollen das ordentlich machen und legen in der Klasse für jeden Indikator eine eigene Funktion an, die im Erfolgsfall den Wert „true“ ausgibt. Die Namen dieser Funktionen können frei gewählt werden, da sie jedoch ihre jeweilige Zweckbestimmung widerspiegeln, nennen wir sie CreateFastMA() bzw. CreateSlowMA().

protected:
   //--- Creating MA indicators
   bool              CreateFastMA(CIndicators *indicators);
   bool              CreateSlowMA(CIndicators *indicators);
  };
//+------------------------------------------------------------------+
//| Creates indicators                                               |
//| Input:  a pointer to a collection of indicators                  |
//| Output: true if successful, otherwise false                      |
//+------------------------------------------------------------------+
bool MA_Cross::InitIndicators(CIndicators *indicators)
  {
//--- Standard check of the collection of indicators for NULL
   if(indicators==NULL) return(false);
//--- Initializing indicators and timeseries in additional filters
   if(!CExpertSignal::InitIndicators(indicators)) return(false);
//--- Creating our MA indicators
   if(!CreateFastMA(indicators))                  return(false);
   if(!CreateSlowMA(indicators))                  return(false);
//--- Reached this part, so the function was successful, return true
   return(true);
  }
//+------------------------------------------------------------------+
//| Creates the "Fast MA" indicator                                  |
//+------------------------------------------------------------------+
bool MA_Cross::CreateFastMA(CIndicators *indicators)
  {
... Some code
//--- Reached this part, so the function was successful, return true
   return(true);
  }
//+------------------------------------------------------------------+
//| Creates the "Slow MA" indicator                                  |
//+------------------------------------------------------------------+
bool MA_Cross::CreateSlowMA(CIndicators *indicators)
  {
... Some code
//--- Reached this part, so the function was successful, return true
   return(true);
  }

Das wär‘s, jetzt müssen wir nur noch den Code schreiben, der die Indikatoren der gleitenden Durchschnittswerte (MA) anlegt und ihre Deskriptoren (Handle) auf irgendeine Weise in das Handelsmodul integriert, damit es die Werte dieser Indikatoren verwenden kann. Aus diesem Grund wird ein Verweis auf eine Variable der Art CIndicators als Parameter übertragen. In der Dokumentation heißt es dazu:

Die Klasse CIndicators dient zur Erfassung von Instanzen der Klassen Zeitreihen und technische Indikatoren. Sie sorgt für die Erstellung von Instanzen dieser Klassen, ihre Speicherung und ihre Verwaltung (Datensynchronisierung, Zugriffs- und Speicherplatzverwaltung).

Das heißt, dass wir unsere Indikatoren anlegen und in dieser Sammlung unterbringen müssen. Da in dieser Sammlung jedoch nur Indikatoren des Typs CIndicator und ihre Nachfahren gespeichert werden können, müssen wir uns dieser bedienen. Dabei hilft uns CiCustom, eines der angesprochenen „Kinder“. Wir deklarieren zu jedem gleitenden Durchschnittswert im privaten Teil der Klasse ein Objekt vom Typ CiCustom:

class MA_Cross : public CExpertSignal
  {
private:
   CiCustom          m_fast_ma;            // The indicator as an object
   CiCustom          m_slow_ma;            // The indicator as an object
   //--- Configurable module parameters
   int              m_period_fast;   // Period of the fast MA
   ENUM_MA_METHOD    m_method_fast;    // Type of smoothing of the fast MA
   int              m_period_slow;   // Period of the slow MA
   ENUM_MA_METHOD    m_method_slow;    // Type of smoothing of the slow MA

Sie können natürlich als Ableitung von CIndicator auch eine eigene Indikatorklasse anlegen und alle erforderlichen Methoden für ihre Anwendung mit dem MQL5-Assistenten umsetzen. Für den hier vorliegenden Fall möchte ich jedoch zeigen, wie wir in dem Handelssignalmodul mithilfe von CiCustom jeden benutzerdefinierten Indikator verwenden können.

Und so sieht das im Programmcode aus:

//+------------------------------------------------------------------+
//| Creates the "Fast MA" indicator                                  |
//+------------------------------------------------------------------+
bool MA_Cross::CreateFastMA(CIndicators *indicators)
  {
//--- Checking the pointer
   if(indicators==NULL) return(false);
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_fast_ma)))
     {
      printf(__FUNCTION__+": Error adding an object of the fast MA");
      return(false);
     }
//--- Setting parameters of the fast MA
   MqlParam parameters[4];
//---
   parameters[0].type=TYPE_STRING;
   parameters[0].string_value="Examples\\Custom Moving Average.ex5";
   parameters[1].type=TYPE_INT;
   parameters[1].integer_value=m_period_fast;      // Period
   parameters[2].type=TYPE_INT;
   parameters[2].integer_value=0;                  // Shift
   parameters[3].type=TYPE_INT;
   parameters[3].integer_value=m_method_fast;      // Averaging method
//--- Object initialization  
   if(!m_fast_ma.Create(m_symbol.Name(),m_period,IND_CUSTOM,4,parameters))
     {
      printf(__FUNCTION__+": Error initializing the object of the fast MA");
      return(false);
     }
//--- Number of buffers
   if(!m_fast_ma.NumBuffers(1)) return(false);
//--- Reached this part, so the function was successful, return true
   return(true);
  }

In der Methode CreateFastMA() überprüfen wir zunächst den Verweis auf die Indikatorsammlung und fügen dieser anschließend den Verweis auf den schnellen Durchschnittswert m_fast_ma hinzu. Danach deklarieren wir das eigens zur Speicherung der Parameter des benutzerdefinierten Indikators entwickelte Gerüst MqlParam und füllen es mit Werten.

Als benutzerdefinierter Indikator für die gleitenden Durchschnittswerte kommt der Indikator Custom Moving Average aus dem Lieferumfang des Ausgabegerätes zum Einsatz. Die Bezeichnung des Indikators muss unbedingt in Übereinstimmung mit Katalogordner data_folder/MQL5/Indicators/ angegeben werden. Da der im Lieferumfang enthaltene benutzerdefinierte Indikator ‚Custom Moving Average.mq5‘ sich in dem Katalogordner data_folder/MQL5/Indicators/Examples/ befindet, geben wir den Pfad zu ihm einschließlich der Ordnerbezeichnung Examples an:

parameters[0].string_value="Examples\\Custom Moving Average.ex5";

Bei einem Blick in den Code dieses Indikators, kann man alle erforderlichen Daten erkennen:

//--- input parameters
input int            InpMAPeriod=13;       // Period
input int            InpMAShift=0;         // Shift
input ENUM_MA_METHOD InpMAMethod=MODE_SMMA;  // Method

Die Werte des Gerüstes enthalten paarweise Art und Inhalt:

  1. Art des Parameters - „string“ (zur Übertragung der Indikatorbezeichnung);
  2. Bezeichnung der ausführbaren Datei des benutzerdefinierten Indikators - „Custom Moving Averages.exe“;
  3. Art des Parameters - „int“ (der Wert für den Zeitraum);
  4. Zeitraum für den gleitenden Durchschnittswert (MA);
  5. Art des Parameters - „int“ (Abweichungswert);
  6. Horizontale Abweichung des Durchschnittswertes auf den Balken;
  7. Art des Parameters - „int“ (ganzzahliger Aufzählungswert);
  8. Glättungsmethode.

Nach dem Füllen des Gerüsts erfolgt die Bereitstellung des Indikators mithilfe der Methode Create() für alle erforderlichen Parameter: die Bezeichnung des Kürzels und des Zeitraums, für den die Berechnung erfolgt, die Art des Indikators aus der Aufzählung ENUM_INDICATOR, die Anzahl der Parameter des Indikators sowie das MqlParam-Gerüst selbst mit Parameterwerten. Zu guter Letzt wird dem Indikator die Anzahl der in ihm enthaltenen Indikatorpuffer mithilfe der Methode NumBuffers() mitgeteilt.

Die Methode CreateSlowMA() zur Erstellung des langsamen gleitenden Mittelwerts sieht ganz ähnlich aus. Bei der Verwendung der benutzerdefinierten Indikatoren in dem Modul ist stets zu bedenken, dass das mit dem MQL5-Assistenten angelegte Expert-System im Prüfprogramm ebenfalls ausgeführt wird. Deshalb müssen wir am Anfang unserer Datei unbedingt die Eigenschaft #property tester_indicator ergänzen, da sie dem Prüfprogramm die Fundorte der für den Betrieb unerlässlichen Indikatoren angibt:

#include "..\ExpertSignal.mqh"   // The CExpertSignal class is in the file ExpertSignal
#property tester_indicator "Examples\\Custom Moving Average.ex5"

Bei Verwendung mehrerer unterschiedlicher Indikatoren muss diese Zeile für jeden einzelnen von ihnen hinzugefügt werden. Damit wären dann alle Indikatoren an Bord. Richten wir der Bequemlichkeit halber noch zwei Methoden zum Abrufen der Werte der gleitenden Durchschnitte ein:

   //--- Checking correctness of input data
   bool              ValidationSettings(void);
   //--- Creating indicators and timeseries for the module of signals
   bool              InitIndicators(CIndicators *indicators);
   //--- Access to indicator data
   double            FastMA(const int index)             const { return(m_fast_ma.GetData(0,index)); }
   double            SlowMA(const int index)             const { return(m_slow_ma.GetData(0,index)); }

Wie wir sehen, sind die Methoden überaus schlicht, in ihnen wird die Methode GetData() der übergeordneten Klasse SIndicator verwendet, die den Wert aus dem angegebenen Indikatorpuffer an der angegebenen Stelle ausgibt.

Sollten Sie die Klassen zur Verarbeitung der klassischen Indikatoren aus dem Standardlieferumfang benötigen, so finden Sie diese in Abschnitt „Klassen für die Arbeit mit Indikatoren“. Jetzt sind wir bereit für den letzten Schritt.


6. Festlegung der Methoden LongCondition und ShortCondition

Schon befinden wir uns auf der Zielgeraden, alles ist vorbereitet, damit unser Modul mit der Ausgabe von Handelssignalen beginnen kann. Für diese Funktion zeichnen zwei Methoden verantwortlich, die in jeden Nachkommen der Klasse CExpertSignal eingeschrieben sein müssen:

  • LongCondition() zur Überprüfung der Erfüllung der Bedingungen für einen Kauf, sie gibt die Stärke des Signals „Long“ mit Werten von „0“ bis „100“ wieder.
  • ShortCondition() zur zur Überprüfung der Erfüllung der Bedingungen für einen Verkauf, sie gibt die Stärke des Signals „Short“ mit Werten von „0“ bis „100“ wieder.

Wenn die Funktion den Wert „0“ ausgibt, bedeutet das, dass kein Handelssignal vorliegt. Sind dagegen die Bedingungen für ein Signal erfüllt, können wir die Stärke des Signals beurteilen und einen beliebigen Wert bis maximal 100 ausgeben. Die Beurteilung der Signalstärke ermöglicht die Schaffung flexibler Handelssysteme auf der Grundlage mehrerer Module und Marktmodelle. Eine ausführliche Darstellung dazu bietet der Beitrag „MQL5 Wizard: Neue Version“ (New Version).

Weil wir es mit einem einfachen Handelssignalmodul zu tun haben, können wir uns darauf einigen, dass die Kauf- und Verkaufssignale mit derselben Stärke von jeweils 100 veranschlagt werden. Wir ergänzen die Klassendeklaration um die erforderlichen Methoden.

   ...
   bool              InitIndicators(CIndicators *indicators);
   //--- Access to data of the indicators
   double            FastMA(const int index)             const { return(m_fast_ma.GetData(0,index)); }
   double            SlowMA(const int index)             const { return(m_slow_ma.GetData(0,index)); }
   //--- Checking buy and sell conditions
   virtual int       LongCondition();
   virtual int       ShortCondition();

Außerdem legen wir eine Beschreibung der Funktionen an. Und so sieht die Überprüfung des Kaufsignals aus (beim Verkauf ist alles genauso).

//+------------------------------------------------------------------+
//| Returns the strength of the buy signal                           |
//+------------------------------------------------------------------+
int MA_Cross::LongCondition()
  {
   int signal=0;
//--- For operation with ticks idx=0, for operation with formed bars idx=1
   int idx=StartIndex();
//--- Values of MAs at the last formed bar
   double last_fast_value=FastMA(idx);
   double last_slow_value=SlowMA(idx);
//--- Values of MAs at the last but one formed bar
   double prev_fast_value=FastMA(idx+1);
   double prev_slow_value=SlowMA(idx+1);
//---If the fast MA crossed the slow MA from bottom upwards on the last two closed bars
   if((last_fast_value>last_slow_value) && (prev_fast_value<prev_slow_value))
     {
      signal=100; // There is a signal to buy
     }
//--- Return the signal value
   return(signal);
  }

Beachten Sie bitte, dass die Variable idx deklariert wurde, welcher der Ausgabewert der Funktion StartIndex() der übergeordneten Klasse CExpertBase zugewiesen wird. Die Funktion StartIndex() gibt eine „0“ aus, wenn das Expert-System bei jeder Kursänderung (Tick) tätig werden soll, in diesem Fall beginnt die Analyse ab dem aktuellen Balken. Wenn das Expert-System für die Arbeit mit Eröffnungskursen entwickelt wurde, gibt die Funktion StartIndex() eine „1“ aus, und die Analyse beginnt mit dem zuletzt angelegten Balken.

Standardmäßig gibt die Funktion StartIndex() eine 1 aus, was bedeutet, dass das mithilfe des MQL5-Assistenten erstellte Expert-System nur bei Eröffnung eines neuen Balkens aktiv wird, auf während der Erstellung des aktuellen Balkens auftretende Kursänderungen dagegen nicht reagiert.

Wie man diesen Modus einstellt, und wozu er erforderlich sein kann, wird etwas weiter unten unter dem „Schlussstrich“ ausgeführt.

Das Modul ist einsatzbereit, fangen wir also gleich damit an, im MQL5-Assistenten ein auf ihm beruhendes automatisches Expert-System, einen Trading Robot oder Expert Advisor, anzulegen.


Überprüfung des Expert-Systems im Prüfprogramm

Um die Leistungsfähigkeit unseres Moduls zu überprüfen, erstellen wir auf seiner Grundlage mithilfe des MQL5-Assistenten ein Expert-System und führen es in dem Diagramm aus. Im den „Eingangsparametern“ des sich öffnenden Startfensters werden die Parameter des Moduls MA_Cross angezeigt.

Alle übrigen Parameters hat der MQL5-Assistent bei der Erstellung des Expert-Systems auf der Grundlage der gewählten Module für die Kapitalverwaltung und die Sicherung der Positionen (Trailing Stop) ebenfalls hinzugefügt. Dementsprechend haben wir uns ausschließlich auf die Programmierung des Handelssignalmoduls konzentriert und eine fertige Lösung erhalten. Darin besteht der wesentliche Vorzug der Verwendung des MQL5-Assistenten! 

Bleibt uns nur noch die Überprüfung unseres Expert-Systems anhand des Strategieprüfprogramms (Strategy Tester) von MetaTrader 5. Dazu versuchen wir, eine schnelle Optimierung der wesentlichen Parameter vorzunehmen.

Bei diesen Einstellungen der Eingangsparameter sind bis zur vollständigen Optimierung mehr als eine halbe Million Durchläufe erforderlich. Deshalb wählen wir die schnelle Optimierung (genetischer Algorithmus) und nutzen zusätzlich das MQL5 Cloud Network zur Beschleunigung der Optimierung. Nach 10 Minuten ist die Optimierung abgeschlossen, und wir haben die Ergebnisse.


Wie wir sehen, haben uns die Erstellung eines Expert-Systems in MQL5 und die Optimierung der Eingangsparameter erheblich weniger Zeit gekostet, als wenn wir unsere Dienstlogik zur Verwaltung unserer Positionen selbst programmiert und uns dann auch noch mit ihrer Prüfung sowie der Suche nach den optimalen Algorithmen befasst hätten. 


Schlussstrich

Dieser Punkt kann übersprungen oder zu einem späteren Zeitpunkt wieder aufgegriffen werden, wenn Sie sich mit dem Programmieren von Handelssignalmodulen vertraut gemacht haben.

Wenn man den mithilfe des MQL5-Assistenten erzeugten Quellcode des Expert-Systems öffnet, trifft man dort auf die globale Variable Expert_EveryTick mit dem Wert „false“. Und eben auf der Grundlage dieser Variablen gibt die Funktion StartIndex(), die dem Expert-System meldet, in welchem Modus es zu arbeiten hat, ihren Wert aus.

//+------------------------------------------------------------------+
//|                                                 TestMA_Cross.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include                                                          |
//+------------------------------------------------------------------+
#include <Expert\Expert.mqh>
//--- available signals
#include <Expert\MySignals\MA_Cross.mqh>
//--- available trailing
#include <Expert\Trailing\TrailingNone.mqh>
//--- available money management
#include <Expert\Money\MoneyFixedLot.mqh>
//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
//--- inputs for expert
input string         Expert_Title             ="TestMA_Cross";  // Document name
ulong               Expert_MagicNumber       =22655;          // Expert Advisor ID
bool                  Expert_EveryTick             =false;          // Work of the EA inside the bar
//--- inputs for main signal
input int            Signal_ThresholdOpen     =10;             // Signal threshold value to open [0...100]
input int            Signal_ThresholdClose    =10;             // Signal threshold value to close [0...100]

Wird für Expert_EveryTick der Wert „true“ festgesetzt und der Code zusammengestellt, so analysiert das Expert-System jede auftretende Kursänderung und fällt dementsprechend Entscheidungen aufgrund der Werte des aktuellen noch nicht abgeschlossenen Balkens. Machen Sie das nur, wenn Sie genau wissen, wie es geht. Nicht alle Handelssysteme sind für den Einsatz innerhalb der Balken ausgelegt.

Zudem können Sie für den Parameter Expert_EveryTick auch noch das Schlagwort „input“ hinzufügen, dann wird Ihnen ein neuer Eingangsparameter für das Expert-System angezeigt, den Sie bei Ausführung des Expert-Systems in einem Diagramm oder einem Prüfprogramm eingeben können:

input bool          Expert_EveryTick         =false;          // Work of the EA inside the bar

Jetzt ist es an der Zeit, ein kurzes Fazit zu ziehen.


6 Schritte zur Erstellung eines Handelssignalmoduls

Wenn Sie sich mit MQL5 vertraut gemacht haben, müssen Sie kein Expert-System mehr aus dem Nichts programmieren. Es genügt vollkommen, ein eigenes Handelssignalmodul anzulegen und auf dessen Grundlage automatisch ein Handelssystem einschließlich der Module für Trailing Stops und zur Verwaltung des Handelsaufkommens zu erstellen. Und selbst wenn Sie in OOP nicht besonders bewandert sind oder sich nicht zu sehr in den Aufbau von Handelsklassen vertiefen möchten, reicht es, 6 Schritte zu tun:

  1. Erstellung einer neuen Klasse unter Verwendung des MQL5-Assistenten in dem eigenen Ordner MQL5/Include/MySignals/. In dem Ordner wird auch unser Handelssignalmodul gespeichert.
  2. Anlegen des Moduldeskriptors, in dem die Parameter, ihre Art und ihre Standardwerte angegeben werden.
  3. Deklaration der Modulparameter in der Klasse selbst sowie das Hinzufügen der Bereitstellungsmethoden im Konstruktor.
  4. Prüfung der Eingangsparameter auf ihre Richtigkeit, ohne den Aufruf der Methode ValidationSettings() der Basisklasse CExpertSignal zu vergessen.
  5. Anlegen der Objektindikatoren und Hinzufügen der vorgegebenen Bereitstellungsmethode InitIndicators().
  6. Festlegung der Bedingungen für das Vorliegen von Handelssignalen in den Methoden LongCondition() bzw. ShortCondition().

Alle Schritte sind recht einfach und erfordern nur geringe Kenntnisse in der MQL5-Programmierung. Es genügt, einmal ein eigenes Modul nach Anweisung zu programmieren, danach wird Sie die Überprüfung einer Handelsidee kaum mehr als eine Stunde kosten, ganz ohne aufreibenden Codierungs- und Prüfungsaufwand.  


Vom Einfachen zum Komplexen

Denken Sie immer daran, dass die von Ihrem im MQL5-Assistenten zusammengestellten automatischen Handelssystem umgesetzte Handelsstrategie so vielschichtig sein wird wie das Handelssignalmodul, das es verwendet. Bevor Sie jedoch damit beginnen, ein komplexes Handelssystem auf der Grundlage zahlreicher Ein- und Ausgaberegeln zu errichten, gliedern Sie es in mehrere einfache Systeme auf und prüfen Sie diese einzeln.

Man kann auf der Grundlage einfacher Module stets eine komplexe Handelsstrategie aus vorgefertigten Handelssignalmodulen zusammenstellen, aber das ist schon wieder ein Thema für einen anderen Artikel!

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/367

Beigefügte Dateien |
testma_cross.mq5 (7.14 KB)
ma_cross.mqh (11.57 KB)
MetaTrader 5 - Jenseits aller Vorstellungen! MetaTrader 5 - Jenseits aller Vorstellungen!
Die Plattform MetaTrader 5 wurde aus dem nichts entwickelt und übertrifft ihre Vorgänger natürlich bei Weitem. Die neue Handelsplattform bietet unbegrenzte Möglichkeiten für den Handel auf allen Finanzmärkten. Darüber hinaus wird ihr Funktionsumfang ständig erweitert, um immer mehr nützliche Instrumente und eine immer größere Benutzerfreundlichkeit zu bieten. Deshalb ist es nicht ganz einfach, all die zahlreichen Vorzüge von MetaTrader 5 aufzuzählen. Wir haben uns bemüht, sie in einem Artikel kurz darzustellen, und waren überrascht, dass der Beitrag alles andere als kurz geraten ist.
Elementare Handelssysteme unter Verwendung von Semaphorindikatoren Elementare Handelssysteme unter Verwendung von Semaphorindikatoren
Wenn wir ein beliebiges vielschichtiges Handelssystem gründlich untersuchen, werden wir feststellen, dass es auf einem Satz einfacher Handelssignale beruht. Deshalb ist es gar nicht erforderlich, das sich Entwickler gleich zu Beginn ihrer Tätigkeit sofort an der Programmierung komplexer Algorithmen versuchen. In diesem Beitrag wird ein Beispiel für ein Handelssystem vorgestellt, das sich zum Abschluss von Geschäften Indikatoren des Typs Semaphor bedient.
Veranschaulichung einer Strategie im Prüfprogramm von MetaTrader 5 Veranschaulichung einer Strategie im Prüfprogramm von MetaTrader 5
Jeder kennt den Spruch: „Ein Bild sagt mehr als tausend Worte.“ Man kann zig Bücher über Paris oder Venedig lesen, aber die geistigen Vorstellungen vermitteln einem nie dasselbe Gefühl wie ein Spaziergang durch die abendlichen Straßen. Der Vorteil der Veranschaulichung oder der grafischen Darstellung lässt sich auf jeden beliebigen Lebensbereich übertragen, auch auf die Arbeit an den Finanzmärkten, zum Beispiel in Form der Analyse der Kurse anhand von Indikatoren auf Diagrammen sowie natürlich in Form der grafischen Darstellung der Überprüfung von Strategien. Dieser Beitrag beinhaltet Beschreibungen aller grafischen Darstellungsmöglichkeiten des Strategieprüfprogramms von MetaTrader 5.
Objektorientierter Ansatz zum Aufbau von Panels mit mehreren Timeframes und Währungen Objektorientierter Ansatz zum Aufbau von Panels mit mehreren Timeframes und Währungen
In diesem Beitrag wird die Nutzung der objektorientierten Programmierung zum Erstellen von Panels mit mehreren Timeframes und Währungen für MetaTrader 5 beschrieben. Das Hauptziel ist der Aufbau eines universell einsatzfähigen Panels, das für die Darstellung verschiedener Arten von Daten genutzt werden kann – beispielsweise Preise, Preisänderungen, Indikatorwerte oder benutzerdefinierte Kauf-/Verkaufsbedingungen –, ohne den Code des Panels selbst verändern zu müssen.