English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Schrittweiser Leitfaden für Anfänger zum Schreiben eines Expert Advisors in MQL5

Schrittweiser Leitfaden für Anfänger zum Schreiben eines Expert Advisors in MQL5

MetaTrader 5Handelssysteme | 12 Januar 2016, 13:33
25 785 26
Samuel Olowoyo
Samuel Olowoyo

Einleitung

Dieser Beitrag richtet sich an Anfänger, die wissen möchten, wie man einen einfachen Expert Advisor (EA) in der neuen MQL5-Sprache schreibt. Beginnen wir also zunächst mit der Festlegung dessen, was unser Expert Advisor können, und sehen uns dann an, wie wir festlegen können, wie er dies ausführen soll.


1. Handels-Strategie

Was unser EA ausführen wird:

  • Er überprüft einen bestimmten Indikator. Wenn eine (oder mehrere) bestimmte Bedingung(en) erfüllt ist (sind), platziert er einen Handel (entweder einen Short/Sell oder Long/Buy), je nach der aktuellen, erfüllten Bedingung.

Das nennt man Handels-Strategie. Bevor man einen EA schreiben kann, muss man zunächst die Strategie entwickeln, die man im EA automatisieren möchte. Modifizieren wir also in diesem Fall die o.g. Aussage, sodass sie die Strategie widerspiegelt, die wir in einen EA einbringen wollen.

  • Dazu verwenden wir einen Indikator, den sog. gleitenden Mittelwert, mit einem Zeitraum von 8 (man kann jeden Zeitraum wählen, doch für unsere Strategie hier, nehmen wir 8).

  • Unser EA soll einen Long (Buy) platzieren, wenn der gleitende Mittelwert-8 (in diesem Beitrag nenne ich den gleitenden Mittelwert einfach GM-8) nach oben geht und der Kurs knapp darüber liegt - und einen Short (Sell) platzieren, wenn der GM-8 nach unten geht und der Kurs knapp darunter liegt.
  • Wir verwenden hier aber auch noch einen anderen Indikator, den sog. ADX (Average Directional Movement) mit Zeitraum 8, der uns ebenfalls hilft, festzustellen, ob sich der Markt in eine Richtung bewegt oder nicht. Dies tun wir, weil wir den Handel nur eingeben wollen, wenn sich der Markt in eine Richtung bewegt und uns zurückhalten, wenn der Markt stagniert (sich also nicht bewegt). Zu diesem Zweck platzieren wir unseren Handel (Buy oder Sell) nur dann, wenn die obigen Bedingungen erfüllt sind und der ADX-Wert größer als 22 ist. Ist der ADX größer als 22, bewegt sich jedoch nach unten, oder ist ADX kleiner als 22, führen wir keinen Handel aus, selbst wenn Bedingung B erfüllt worden ist.
  • Zudem möchten wir uns auch durch Einrichtung eines Stop Loss von 30 Pips (kleinste mögliche Kursveränderung) schützen. Wir zielen hier wir auf einen Profit von 100 Pips.
  • Zudem soll unser EA auch nur dann nach Buy/Sell-Chancen Ausschau halten, wenn sich ein neuer Bar herausgebildet hat. Zudem möchten wir auch sicherstellen, dass wir eine Buy Position eröffnen, wenn die Buy-Bedingungen erfüllt sind und wir noch keine eröffnet haben. Und wir wollen eine Sell Position eröffnen, wenn die Sell-Bedingungen erfüllt sind und wir noch keine eröffnet haben.

Jetzt haben wir unsere Strategie entwickelt, Zeit also, unseren Code zu schreiben.


2. Einen Expert Advisor schreiben

2.1 MQL5 Wizard

Starten Sie zunächst den MetaQuotes Language Editor 5. Drücken Sie dann Ctrl+N oder klicken in der Menüleiste auf Neu

Abb. 1 Neues MQL5 Dokument starten

Abb. 1 Neues MQL5 Dokument starten

 Wählen Sie im MQL5 Assistenten-Fenster den Expert Advisor und klicken auf "Weiter", so wie in Abb. 2 gezeigt:

Abb. 2 Dokumenttyp wählen

Abb. 2 Programmtyp wählen

Geben Sie im nächsten Fenster im Namensfeld den Namen ein, den Sie Ihrem EA geben wollen. In unserem Beispiel habe ich My_First_EA eingegeben. Dann können Sie im Feld Verfasser Ihren Namen und im Link-Feld ggf. auch die Adresse Ihrer Website oder E-Mail eingeben.

Abb. 3 Allgemeine Eigenschaften des Expert Advisors

Abb. 3 Allgemeine Eigenschaften des Expert Advisors

Da wir ja einige der Parameter für unseren EA ändern können wollen, um zu sehen, welche Werte uns das beste Ergebnis liefern, fügen wir sie durch Anklicken von "Hinzufügen" hinzu.

Abb. 4 EA-Eingabeparameter einrichten

Abb. 4 EA-Eingabeparameter einrichten

In unserem EA möchten wir auch mit den Einstellungen Stop Loss, Take Profit, ADX-Zeitraum und Gleitender Mittelwertzeitraum arbeiten, also legen wir sie jetzt fest.

Doppelklick im Abschnitt Name und Eingabe des Parameters. Dann Doppelklick unter Typ, um den Datentyp für den Parameter auszuwählen, und schließlich Doppelklick unter Ursprungswert, um den Ursprungswert für den Parameter einzugeben

Ist dies alles geschehen, sollte man folgendes sehen:

Abb. 5 Datentypen der EA-Eingabeparameter

Abb. 5 Datentypen der EA-Eingabeparameter

Wie Sie oben sehen, habe ich für alle Parameter den ganzzahligen (int) Datentyp gewählt. Ein guter Zeitpunkt, um auf die Datentypen näher einzugehen.

  • char: Der char Typ benötigt 1 Byte Speicherplatz (8 Bits) und erlaubt einen Ausdruck in der binären Formel 2^8=256 Werte. Der char Typ kann positive und negative Werte enthalten. Sein Wertebereich reicht von -128 bis 127.
  • uchar : Der ganzzahlige uchar Typ braucht ebenfalls 1 Byte Speicherplatz, dient aber ausschließlich für positive Werte. Sein Mindestwert ist 0; sein Maximalwert ist 255. Der erste Buchstabe "u" im Namen des uchar Typs ist die Abkürzung für ungekennzeichnet .
  • short: Die Größe des short Typs ist 2 Byte (16 Bits). Daher erlaubt er den Ausdruck eines Wertebereichs von 216: 2^16 = 65.536. Da der short Typ gekennzeichnet ist, und sowohl positive als auch negative Werte enthält, liegt sein Wertebereich zwischen -32.768 und 32.767.
  • ushort: Der ungekennzeichnete short Typ ist der ushort Typ und ebenfalls 2 Bytes groß. Sein Minimalwert ist 0; sein Maximalwert 65.535.
  • int: Der int Typ ist 4 Byte (32 Bits) groß. Sein Minimalwert ist -2.147.483.648; sein Maximalwert ist 2.147.483.647.
  • uint:  uint. ist der ungekennzeichnete, ganzzahlige Typ. Er benötigt 4 Byte Speicherplatz und erlaubt den Ausdruck von ganzen Zahlen von 0 - 4.294.967.295.
  • long: Der long Typ ist 8 Bytes groß (64 Bits). Sein Minimalwert beträgt -9.223.372.036.854.775.808; sein Maximalwert 9.223.372.036.854.775.807.
  • ulong: Der ulongTyp braucht ebenfalls 8 Byte und kann Werte zwischen 0 und 18.446.744.073.709.551.615 enthalten.

Aus der o.g. Beschreibung der verschiedenen Datentypen geht hervor, dass die ungekennzeichneten, ganzzahligen Typen nicht zur Speicherung negativer Werte gedacht sind. Jeder Versuch, dort einen negativen Wert einzurichten kann also unerwartete Folgen haben. Möchte man z.B. negative Werte speichern, geht dies eben nicht innerhalb dieser ungekennzeichneten Typen (also uchar, uint, ushort, ulong).

Doch zurück zu unserem EA. Betrachtet man sich die Datentypen, wird man mir zustimmen, dass wir eher die char oder uchar Datentypen verwenden sollten, da die Daten, die wir in diesen Parametern speichern wollen, kleiner als 127  bzw. 255 sind. Damit kann man das Memory optimal verwalten. Doch aus Gründen der Verständlichkeit bleiben wir beim int Typ.

Hat man dann alle notwendigen Parameter eingerichtet, auf Fertig klicken, und der MetaQuotes Editor erzeugt das Code-Skelett, wie in der folgenden Abbildung gezeigt.


Teilen wir den Code in seine verschiedenen Bereiche auf, um ihn besser zu verstehen.

Im obersten Bereich (Kopfzeile) wird die Eigenschaft des EA festgelegt. Hier stehen die Werte, die Sie im MQL5-Assistenten in Abb. 3 eingegeben haben. 

In diesem Bereich des Codes kann man noch zusätzliche Parameter festlegen, wie description (kurze Textbeschreibung des EA), Konstanten deklarieren, zusätzliche Dateien mit aufnehmen oder Funktionen importieren. 


Beginnt eine Aussage mit dem Symbol #, nennt man sie eine Präprozessor-Direktive. Sie endet nicht mit einem Strichpunkt ‘;’. Andere Beispiele für Präprozessor-Direktiven sind u.a.:

#define : 

Die #define Direktive wird zur Deklaration von Konstanten verwendet. Sie wird in der Form

#define identifier token_string geschrieben

Sie ersetzt jedes Eintreten von identifier im Code durch den Wert token_string.

Beispiel:

#define ABC               100
#define COMPANY_NAME      "MetaQuotes Software Corp."

Jetzt wird jedes Auftreten von COMPANY_NAME durch den String  "MetaQuotes Software Corp." im Code ersetzt. Oder jedes Auftreten von ABC wird durch das Char (oder ganze Zahl) 100 im Code ersetzt.

Mehr zu Präprozessor-Direktiven finden Sie im MQL5 Handbuch. Setzen wir unsere Betrachtung fort.

Der zweite Bereich der Kopfzeile unseres Codes ist der Bereich Eingabeparameter:

 

Hier legen wir alle Parameter fest, die in unserem EA verwendet werden. Das umfasst alle Variablen, die von allen Funktionen, die wir in unseren EA schreiben, verwendet werden.

Die auf dieser Ebene deklarierten Variablen heißen globale Variablen, da jede Funktion in unserem EA, die sie braucht, auf sie zugreifen kann. Die Eingabeparameter sind Parameter, die nur außerhalb unseres EA verändert werden können. Wir können auch andere Variablen deklarieren, die wir im Laufe unseres EA bearbeiten werden, die jedoch in diesem Bereich nicht außerhalb unseres EA verfügbar sein werden. 

Als nächstes kommt die Funktion zur EA-Initialisierung. Sie ist die erste Funktion, die aufgerufen wird, wenn der EA gestartet oder an ein Chart angehängt wird. Sie wird nur einmal aufgerufen 


Dieser Bereich ist die beste Stelle, um einige wichtige Prüfungen durchzuführen, um auch sicherzustellen, dass unser EA optimal funktioniert.

Wir können entscheiden, ob wir wissen wollen, ob der Chart genügend Bars hat, damit unser EA auch funktioniert, usw.

Es ist ebenfalls die beste Stelle, die Identifikatoren zu erhalten, die wir für unsere Indikatoren verwenden werden (ADX- und GM-Indikatoren).

 
 Die OnDeinit Funktion wird aufgerufen, wenn der EA aus dem Chart entfernt wird.

Für unseren EA geben wir die für unsere Indikatoren erzeugten Identifikatoren während der Initialisierung in diesem Bereich frei.


Diese Funktion verarbeitet das NewTick Ereignis, das dann erzeugt wird, wenn man für ein Symbol eine neue Quote erhält. 

Bitte beachten Sie, dass der Expert Advisor keine Handels-Operationen durchführen kann, wenn die Verwendung von Expert Advisors im Client-Terminal nicht zugelassen ist ("Auto Trading").

Abb. 6 Auto-Trading aktiviert

Abb. 6 Auto-Trading aktiviert

Der Großteil unserer Codes, die unsere vorhin entwickelte Handels-Strategie implementieren, wird innerhalb dieses Bereich geschrieben.

Da wir uns jetzt die verschiedenen Bereiche des Codes für unseren EA betrachtet haben, können wir das "Skelett" mit Inhalten füttern.

2.2 BEREICH EINGABE-PARAMETER

//--- input parameters
input int      StopLoss=30;      // Stop Loss
input int      TakeProfit=100;   // Take Profit
input int      ADX_Period=8;     // ADX Period
input int      MA_Period=8;      // Moving Average Period
input int      EA_Magic=12345;   // EA Magic Number
input double   Adx_Min=22.0;     // Minimum ADX Value
input double   Lot=0.1;          // Lots to Trade
//--- Other parameters
int adxHandle; // handle for our ADX indicator
int maHandle;  // handle for our Moving Average indicator
double plsDI[],minDI[],adxVal[]; // Dynamic arrays to hold the values of +DI, -DI and ADX values for each bars
double maVal[]; // Dynamic array to hold the values of Moving Average for each bars
double p_close; // Variable to store the close value of a bar
int STP, TKP;   // To be used for Stop Loss & Take Profit values

Wie Sie sehen, haben wir mehr Parameter hinzugefügt. Bevor wir uns die neuen Parameter genauer ansehen, sollten wir kurz etwas erläutern, das Sie jetzt bemerken. Mit Hilfe der beiden Schrägstriche ‘//’ können wir unsere Codes kommentieren. Und anhand der Kommentare wissen wir, wofür unsere Variablen stehen oder was wir gerade in diesem Moment in unserem Code machen. Und wir können dadurch unseren Code auch besser verstehen. Kommentare zu verfassen geht auf zwei grundlegende Arten:

// Other Parameters …

Einzeiliger Kommentar

/*

  This is a multi-line comment

*/

Mehrzeiliger Kommentar. Mehrzeilige Kommentare beginnen mit dem /* Symbolpaar und enden mit dem */ Symbolpaar.

Der Compiler vernachlässigt beim Erstellen des Codes alle Kommentare.

Die Verwendung von einzeiligen Kommentaren für die Eingabeparameter ist prima, damit unsere EA-Benutzer auch verstehen, wofür diese Parameter stehen. Bei den EA Eingabe-Eigenschaften sehen unsere Benutzer nicht den Parameter an sich, sondern nur folgende Kommentare:

Abb. 7 Expert Advisor Eingabeparameter

Abb. 7 Expert Advisor Eingabeparameter

Doch zurück zum Code…

Wir haben entschieden, unseren EA um zusätzliche Parameter zu ergänzen. EA_Magic ist die magische Anzahl aller Orders durch unseren EA. Der Minimal ADX-Wert (Adx_Min) ist als ein double Datentyp deklariert. Ein double wird zur Speicherung von Gleitkomma-Konstanten verwendet, die einen ganzzahligen Teil, einen Dezimalpunkt und einen Bruchteil beinhalten.

Beispiel:

double mysum = 123,5678;

double b7 = 0,09876;

Der zu handelnde Posten (Posten) stellt das Volumen des Finanzinstruments dar, das wir handeln wollen. Wir haben auch andere Parameter deklariert, die wir verwenden wollen:

Der adxHandle wird zur Speicherung des ADX-Indikator-Identifikators verwendet, während der maHandle den Identifikator für den GM-Indikator speichert. Die plsDI[], minDI[], adxVal[] sind dynamische Arrays, die die Werte von +DI, -DI und der primären ADX (des ADX-Indikators) für jeden Bar auf dem Chart haben. Der maVal[] ist ein dynamisches Array, das die Werte des GM-Indikators für jeden Bar auf dem Chart enthält.

Ach ja, was sind eigentlich dynamische Arrays? Dynamische Arrays sind Arrays, die ohne Dimensionen deklariert wurden. Mit anderen Worten: innerhalb der beiden eckigen Klammern wurde kein Wert festgelegt. Ein statisches Array hingegen, besitzt die zum Zeitpunkt der Deklaration definierten Dimensionen.

Beispiel:

double allbars[20]; // this will take 20 elements

p_close ist eine Variable, die wir zur Speicherung des Close price für den Bar verwenden, den wir zur Prüfung unserer Buy/Sell-Handel kontrollieren.

STP und TKP werden zur Speicherung der Stop Loss und Take Profit Werte in unserem EA verwendet.

2.3. BEREICH EA-INITIALISIERUNG

int OnInit()
  {
//--- Get handle for ADX indicator
   adxHandle=iADX(NULL,0,ADX_Period);
//--- Get the handle for Moving Average indicator
   maHandle=iMA(_Symbol,_Period,MA_Period,0,MODE_EMA,PRICE_CLOSE);
//--- What if handle returns Invalid Handle
   if(adxHandle<0 || maHandle<0)
     {
      Alert("Error Creating Handles for indicators - error: ",GetLastError(),"!!");
     }

Hier erhalten wir mit Hilfe der entsprechenden Indikatorfunktionen die Identifikatoren unseres Indikators.

Den ADX Indikator-Identifikator erhält man mit Hilfe der iADX Funktion. Er übernimmt das Chart symbol (NULL bedeutet auch das aktuelle Symbol des aktuellen Charts), den Chart Zeitraum/Zeitrahmen (0 bedeutet auch den aktuellen Zeitrahmen des aktuellen Charts), den ADX-Mittelwertzeitraum zur Indexberechnung (den wir zuvor im Eingabeparameter-Bereich) als Parameter oder Begründungen definiert haben.  

int  iADX(
   string           symbol,         // symbol name
   ENUM_TIMEFRAMES  period,         // period
   int              adx_period      // averaging period
   );

Den Gleitenden Mittelwert-Indikator-Identifikator erhält man mit Hilfe der iMA Funktion. Er hat die folgenden Begründungen:

  • das Chart symbol (das man mit Hilfe von _symbol, symbol() erhält oder NULL für das aktuelle Symbol des aktuellen Charts),
  • den Chart Zeitraum/Zeitrahmen (die man mit Hilfe von _period, period(), oder 0 für den aktuellen Zeitraum des aktuellen Charts erhält),
  • den Gleitenden Mittelwertzeitraum (den wir zuvor im Bereich Eingabeparameter festgelegt haben),
  • die Verschiebung des Indikators, der mit dem Kurs-Chart zusammenhängt (die Verschiebung hier ist 0),
  • den Gleitenden Mittelwert-Ausgleichstyp (kann jede der folgenden Mittelwertmethoden sein: Simple Averaging-MODE_SMA, Exponential Averaging-MODE_EMA, Smoothed Averaging-MODE_SMMA oder Linear-Weighted Averaging-MODE_LWMA), sowie
  • den für den Mittelwert verwendeten Kurs (in unserem Fall der Schlusskurs).
int  iMA(
   string               symbol,            // symbol name
   ENUM_TIMEFRAMES      period,            // period
   int                  ma_period,         // averaging period
   int                  ma_shift,          // horizontal shift
   ENUM_MA_METHOD       ma_method,         // smoothing type
   ENUM_APPLIED_PRICE   applied_price      // type of price or handle
   );

Über mehr Details zu diesen Indikatorfunktionen, informieren Sie sich bitte im MQL5 Handbuch. Dort erhalten Sie ein besseres Verständnis, wie man jeden Indikator einsetzt.

Wir hingegen versuchen, nach Fehlern zu suchen, falls die Funktion den Identifikator nicht erfolgreich geliefert hat. Dann würden wir eine Fehlermeldung: INVALID_HANDLE Fehler erhalten. Mittels der Warnfunktion lassen wir uns den Fehler mit Hilfe der GetlastError Funktion anzeigen.

//--- Let us handle currency pairs with 5 or 3 digit prices instead of 4
   STP = StopLoss;
   TKP = TakeProfit;
   if(_Digits==5 || _Digits==3)
     {
      STP = STP*10;
      TKP = TKP*10;
     }

Des Weiteren entscheiden wir, die Stop Loss und die Take Profit-Werte in den Variablen STP und TKP zu speichern, die wir zuvor deklariert haben. Warum machen wir das?

Die in den EINGABE-Parametern gespeicherten Werte sind Read-Only Werte, können also nicht verändert werden. Wir müssen hier also sicherstellen, dass unser EA mit allen Maklern wirklich gut funktioniert. Digits oder Digits() liefert die Anzahl der Dezimalziffern, die die Exaktheit des Kurses des aktuellen Chartsymbols festlegen. Für ein fünf- oder drei-ziffriges Kurschart multiplizieren wir sowohl Stop Loss und Take Profit mit 10.

2.4. BEREICH EA DE-INTIALISIERUNG

 

Da diese Funktion immer dann aufgerufen wird, wenn der EA deaktiviert oder aus einem Chart entfernt wird, geben wir alle Indikator-Identifikatoren frei, die wir während des Initialisierungsprozesses erzeugt haben. Wir haben zwei Identifikatoren erzeugt: einen für den ADX-Indikator und einen zweiten für den GM-Indikator.

Die Freigabe erfolgt mit Hilfe der IndicatorRelease() Funktion. Dazu ist nur eine Begründung notwendig (der Indikator-Identifikator)

bool  IndicatorRelease(
   int       indicator_handle,     // indicator handle
   );
Die Funktion entfernt einen Indikator-Identifikator und gibt den Berechnungsblock des Indikators frei, falls er nicht verwendet wird.

2.5 BEREICH EA-ONTICK (BEI KURSSCHWANKUNG)

Als Erstes müssen wir hier prüfen, ob unser aktuelles Chart ausreichend Bars hat. Alle Bars in der Historie eines Charts erhalten wir mit Hilfe der Bars Funktion. Dazu sind zwei Parameter nötig: Symbol (erhält man mittels _Symbol oder Symbol(), beide liefern das aktuelle Symbol für das aktuelle Chart, an das unser EA angehängt ist), und Zeitraum oder Zeitrahmen des aktuellen Charts (erhält man mittels Period oder Period(). Beide liefern den Zeitraum des aktuellen Charts, an das unser EA angehängt ist).

Sind insgesamt weniger als 60 Bars vorhanden, dann soll unser EA solange warten, bis wir auf dem Chart genügend Bars zur Verfügung haben.  Die Alert Funktion zeigt eine Meldung in einem separaten Fenster und nimmt alle, durch Kommata getrennte Werten, als Parameter/Begründungen auf. In unserem Fall haben wir nur einen Stringwert. Die Lieferung verlässt die Initialisierung unseres EA.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Do we have enough bars to work with
   if(Bars(_Symbol,_Period)<60) // if total bars is less than 60 bars
     {
      Alert("We have less than 60 bars, EA will now exit!!");
      return;
     }
// We will use the static Old_Time variable to serve the bar time.
// At each OnTick execution we will check the current bar time with the saved one.
// If the bar time isn't equal to the saved time, it indicates that we have a new tick.
   static datetime Old_Time;
   datetime New_Time[1];
   bool IsNewBar=false;

// copying the last bar time to the element New_Time[0]
   int copied=CopyTime(_Symbol,_Period,0,1,New_Time);
   if(copied>0) // ok, the data has been copied successfully
     {
      if(Old_Time!=New_Time[0]) // if old time isn't equal to new bar time
        {
         IsNewBar=true;   // if it isn't a first call, the new bar has appeared
         if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time);
         Old_Time=New_Time[0];            // saving bar time
        }
     }
   else
     {
      Alert("Error in copying historical times data, error =",GetLastError());
      ResetLastError();
      return;
     }

//--- EA should only check for new trade if we have a new bar
   if(IsNewBar==false)
     {
      return;
     }
 
//--- Do we have enough bars to work with
   int Mybars=Bars(_Symbol,_Period);
   if(Mybars<60) // if total bars is less than 60 bars
     {
      Alert("We have less than 60 bars, EA will now exit!!");
      return;
     }

//--- Define some MQL5 Structures we will use for our trade
   MqlTick latest_price;     // To be used for getting recent/latest price quotes
   MqlTradeRequest mrequest;  // To be used for sending our trade requests
   MqlTradeResult mresult;    // To be used to get our trade results
   MqlRates mrate[];         // To be used to store the prices, volumes and spread of each bar
   ZeroMemory(mrequest);     // Initialization of mrequest structure

Der Expert Advisor beginnt nun zu Anfang eines neuen Bars mit Handels-Operationen, also muss zunächst das Problem mit der Erkennung neuer Bars gelöst werden. Das heißt: wir wollen sicherstellen, dass unser EA nicht bei jeder Schwankung nach Long/Short-Einrichtungen sucht, sondern nur nach Long/Short Positionen bei jedem neuen Bar. 

Dazu beginnen wir mit der Deklaration einer statischen Datetime-Variable Old_Time, die die Bar-Zeit speichert. Wir haben sie als statische Variable deklariert, da wir wollen, dass der Wert bis zum nächsten Aufruf der OnTick Funktion im Memory behalten wird. Dann können wir ihren Wert mit der neuen New_Time Variable vergleichen (ebenfalls ein Datetime Datentyp), die ein Array von einem Element ist, das die neue(aktuelle) Barzeit haben soll. Wir haben zudem auch eine Variable des Bool-Datentyps deklariert, IsNewBar, und ihren Wert auf false eingestellt. Das haben wir getan, da wie ihren Wert nur als TRUE möchten, wenn wir einen neuen Bar haben.

Die Zeit des aktuellen Bars erhalten wir mit Hilfe der CopyTime Funktion. Sie kopiert die Bar-Zeit in das New_Time Array mit einem Element. Ist dies erfolgreich, vergleichen wir die Zeit des neuen Bars mit der des vorigen. Sind die Zeiten ungleich, heißt das, dass wir einen neuen Bar haben und wir setzen die Variable IsNewBar auf TRUE und speichern den Wert der aktuellen Barzeit in der Variable Old_Time.

Die Variable IsNewBar gibt an, dass wir einen neuen Bar haben. Ist der Wert FALSE, beenden wir die Ausführung der OnTick Funktion.  

Sehen wir uns den Code an:

if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time);

Er sucht nach der Ausführung des Debug-Modus und druckt die Nachricht über die Bar-Zeiten aus, wenn im Debug-Modus. Darauf gehen wir später ein.

Als Nächstes wollen wir hier prüfen, ob wir ausreichend Bars zum Arbeiten haben. Warum machen wir das noch einmal? Einfach, um sicherzugehen, dass unser EA auch korrekt funktioniert. Es sei darauf hingewiesen, dass die OnInit Funktion zwar nur einmal aufgerufen wird, wenn der EA an ein Chart angehängt ist, die OnTick Funktion jedoch bei jeder neuen Kursschwankung aufgerufen wird (Kursnotierung).

Sie haben sicherlich bemerkt, dass wir dies hier erneut anders gemacht haben. Wir haben beschlossen, die insgesamten Bars in der History zu speichern, die wir vom Ausdruck

int Mybars=Bars(_Symbol,_Period);

in einer neuen Variable, Mybars, erhalten haben, die innerhalb der OnTick Funktion deklariert wurde. Dieser Variablentyp ist eine lokale Variable, anders als die Variable, die wir im Bereich EINGABEPARAMETER unseres Code deklariert haben. Während die im Bereich Eingabeparameter unseres Codes deklarierten Variablen für alle Funktionen innerhalb unseres Codes zur Verfügung stehen, die sie evtl. brauchen, sind innerhalb einer einzigen Funktion deklarierte Variablen begrenzt und stehen nur dieser einen Funktion zur Verfügung. Sie können nicht außerhalb dieser Funktion verwendet werden.

Als Nächstes haben wir einige Variablen des Strukturtyps MQL5 deklariert, die in diesem Bereich des EA verwendet werden. MQL5 besitzt eine ganze Menge eingebauter Strukturen, die es den EA-Entwicklern ganz erheblich leichter machen. Sehen wir uns die Strukturen nacheinander an.

MqlTick

Diese Struktur dient zur Speicherung der letzten Kurse der Symbole.

struct MqlTick
  {
   datetime     time;          // Time of the last prices update
   double       bid;           // Current Bid price
   double       ask;           // Current Ask price
   double       last;          // Price of the last deal (Last)
   ulong        volume;        // Volume for the current Last price
  };

Jede Variable die zu einem MqlTick Typ deklariert wurde, kann leicht für den Erhalt der aktuellen Werte von Briefkurs, Geldkurs, Letzter undVolumen verwendet werden, sobald die SymbolInfoTick() Funktion aufgerufen wird.

Wir haben also latest_price als Typ MqlTick deklariert, damit wir mit ihrer Hilfe die Brief- und Geldkurse erhalten können.

MqlTradeRequest

Diese Struktur wird zur Ausführung aller Handel-Anfragen für eine Handels-Operation verwendet. Sie enthält alle, für die Ausführung eines Handel-Abschlusses notwendigen Felder.

struct MqlTradeRequest
  {
   ENUM_TRADE_REQUEST_ACTIONS    action;       // Trade operation type
   ulong                         magic;        // Expert Advisor ID (magic number)
   ulong                         order;        // Order ticket
   string                        symbol;       // Trade symbol
   double                        volume;       // Requested volume for a deal in lots
   double                        price;        // Price
   double                        stoplimit;    // StopLimit level of the order
   double                        sl;           // Stop Loss level of the order
   double                        tp;           // Take Profit level of the order
   ulong                         deviation;    // Maximal possible deviation from the requested price
   ENUM_ORDER_TYPE               type;          // Order type
   ENUM_ORDER_TYPE_FILLING       type_filling;  // Order execution type
   ENUM_ORDER_TYPE_TIME          type_time;     // Order execution time
   datetime                      expiration;    // Order expiration time (for the orders of ORDER_TIME_SPECIFIED type)
   string                        comment;       // Order comment
  };

Jede Variable, die zum Typ MqlTradeRequest deklariert wurde, kann zum Senden von Orders für unsere Handels-Operationen verwendet werden. Wir haben hier mrequest als einen MqlTradeRequest Typ deklariert.

MqlTradeResult

Das Ergebnis jeder Handels-Operation wird als eine spezielle , vorab definierte Struktur des MqlTradeResult Typs geliefert. Jede Variable, die zum Typ MqlTradeRequest deklariert wurde, kann auf die Ergebnisse der Handels-Anfrage.

struct MqlTradeResult
  {
   uint     retcode;          // Operation return code
   ulong    deal;             // Deal ticket, if it is performed
   ulong    order;            // Order ticket, if it is placed
   double   volume;           // Deal volume, confirmed by broker
   double   price;            // Deal price, confirmed by broker
   double   bid;              // Current Bid price
   double   ask;              // Current Ask price
   string   comment;          // Broker comment to operation (by default it is filled by the operation description)
  };

mresult wurde hier als Typ MqlTradeResult deklariert.

MqlRates

Kurs (Eröffnung, Schluss, Hoch, Niedrig), die Zeit, die Volumen jedes Bars und die Differenz zwischen Brief und Geld (Spread) für ein Symbol sind in dieser Struktur gespeichert.  Jedes Array, das als Typ MqlRates deklariert wird, kann zur Speicherung des Kurses, der Volumen und der Spread-Historie für ein Symbol verwendet werden.

struct MqlRates
  {
   datetime time;         // Period start time
   double   open;         // Open price
   double   high;         // The highest price of the period
   double   low;          // The lowest price of the period
   double   close;        // Close price
   long     tick_volume;  // Tick volume
   int      spread;       // Spread
   long     real_volume;  // Trade volume
  };

Hier haben wir ein mrate[] Array deklariert  , in dem diese Informationen gespeichert werden.

/*
     Let's make sure our arrays values for the Rates, ADX Values and MA values 
     is store serially similar to the timeseries array
*/
// the rates arrays
   ArraySetAsSeries(mrate,true);
// the ADX DI+values array
   ArraySetAsSeries(plsDI,true);
// the ADX DI-values array
   ArraySetAsSeries(minDI,true);
// the ADX values arrays
   ArraySetAsSeries(adxVal,true);
// the MA-8 values arrays
   ArraySetAsSeries(maVal,true);

Als nächstes richten wir die Arrays ein, in denen wir die Bar-Details als Reihen speichern. Damit wird sichergestellt, dass die Werte, die in die Arrays kopiert werden, auch indiziert werden, wie z.B. die Zeitreihen, also 0, 1, 2, 3, (um mit den Bar-Indices übereinzustimmen). Dazu verwenden wir die ArraySetAsSeries() Funktion.

bool  ArraySetAsSeries(
   void  array[],     // array by reference
   bool  set          // true denotes reverse order of indexing
   );

Hier muss darauf hingewiesen werden, dass dies auch einmal im Initialisierungsbereich unseres Codes gemacht werden kann. Ich habe mich jedoch dazu entschlossen, dies aus Gründen besser Verständlichkeit hier zu tun.

//--- Get the last price quote using the MQL5 MqlTick Structure
   if(!SymbolInfoTick(_Symbol,latest_price))
     {
      Alert("Error getting the latest price quote - error:",GetLastError(),"!!");
      return;
     }

Wir verwenden jetzt die SymbolInfoTick Funktion, um die letzte Kursnotierung zu erhalten. Diese Funktion besitzt zwei Begründungen: Chartsymbol und die MqlTick Strukturvariable (latest_price). Sollte es hier zu einem Fehler kommen, berichten wir ihn.

//--- Get the details of the latest 3 bars
   if(CopyRates(_Symbol,_Period,0,3,mrate)<0)
     {
      Alert("Error copying rates/history data - error:",GetLastError(),"!!");
      return;
     }

Als Nächstes kopieren wir die Information über die letzten drei Bars in unser Mqlrates Array und zwar mit Hilfe der CopyRates Funktion. Die CopyRates Funktion wird genutzt, um historische Daten der MqlRates Struktur eines spezifizierten Symbol-Zeitraums in einem MqlRates Typ-Array in genau festgelegter Anzahl zu bekommen. 

int  CopyRates(
   string           symbol_name,       // symbol name
   ENUM_TIMEFRAMES  timeframe,         // period
   int              start_pos,         // start position
   int              count,             // data count to copy
   MqlRates         rates_array[]      // target array to copy
   );

Den Symbolnamen erhalten wir mittels ‘_symbol’ ; den aktuellen Zeitraum/Zeitrahmen erhalten wir mittels ‘_period’ Für die Startposition beginnen wir beim aktuellen Bar, Bar 0, und zählen nur drei Bars: Bar 0, 1, und 2. Das Ergebnis wird im mrate[] Array gespeichert.

Das mrate[] Array enthält jetzt alle Informationen zum Kurs, der Uhrzeit, Volumen und Spread für die Bars 0 , 1 und 2.  Details jedes dieser Bars erhalten wir folgendermaßen:

mrate[bar_number].bar_property

Wir erhalten z.B. die folgende Information für jeden Bar:

mrate[1].time   // Bar 1 Start time
mrate[1].open   // Bar 1 Open price
mrate[0].high   // Bar 0 (current bar) high price, etc

Als nächstes kopieren wir alle Indikatorwerte in die dynamischen Arrays, die wir mit Hilfe der CopyBuffer Funktion deklariert haben.

int  CopyBuffer(
   int       indicator_handle,     // indicator handle
   int       buffer_num,           // indicator buffer number
   int       start_pos,            // start position
   int       count,                // amount to copy
   double    buffer[]              // target array to copy
   );

Der Indikator-Identifikator ist der Identifikator, den wir im OnInit Bereich erzeugt haben. Was die Anzahl der Buffer betrifft, hat der ADX-Indikator drei (3) Buffer:

  • 0 - MAIN_LINE,
  • 1 - PLUSDI_LINE,
  • 2 - MINUSDI_LINE.

Der GM-Indikator besitzt nur einen (1) Buffer:

  • 0 – MAIN_LINE.

Aus dem vorliegenden Bar kopieren wir (0) in die zwei vorigen Bars. Die zu kopierende Menge an Datensätzen ist also 3 (Bars 0, 1 und 2). Der buffer[] umfasst die dynamischen Ziel-Arrays, die wir zuvor deklariert haben – adxVal, plsDI, minDI und maVal.

Wie Sie hier erkennen, versuchen wir jeden Fehler zu erfassen, der beim Kopiervorgang auftreten kann. Sollte ein Fehler auftreten, ist hier erst einmal Schluss.

In diesem Zusammenhang muss unbedingt darauf hingewiesen werden, dass die CopyBuffer() und CopyRates() Funktion im Erfolgsfall die Gesamtzahl aller kopierten Datensätze liefert; bei einem Fehler liefert sie eine Erhöhung um -1. Deshalb suchen wir in den Fehlerprüfungsfunktionen hier nach einem Wert < 0 (Null).

//--- Copy the new values of our indicators to buffers (arrays) using the handle
   if(CopyBuffer(adxHandle,0,0,3,adxVal)<0 || CopyBuffer(adxHandle,1,0,3,plsDI)<0
      || CopyBuffer(adxHandle,2,0,3,minDI)<0)
     {
      Alert("Error copying ADX indicator Buffers - error:",GetLastError(),"!!");
      return;
     }
   if(CopyBuffer(maHandle,0,0,3,maVal)<0)
     {
      Alert("Error copying Moving Average indicator buffer - error:",GetLastError());
      return;
     }

Zu diesem Zeitpunkt möchten wir nachsehen, ob wir bereits eine Buy- oder Sell-Position offen haben, wollen also sicherstellen, dass wir nur EINEN Sell- oder Buy-Handel zu einer Uhrzeit geöffnet haben. Wir wollen keinen neuen Buy öffnen, wenn schon einer offen ist. Das gleiche gilt auch für Sell.

Um dies zu erreichen, deklarieren wir zunächst zwei Variablen vom Bool-Datentyp (Buy_openedund Sell_opened), die einen TRUE-Wert zeigen, falls wir entweder für Buy oder Sell eine Position geöffnet haben.

//--- we have no errors, so continue
//--- Do we have positions opened already?
    bool Buy_opened=false;  // variable to hold the result of Buy opened position
    bool Sell_opened=false; // variable to hold the result of Sell opened position
    
    if (PositionSelect(_Symbol) ==true)  // we have an opened position
    {
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
         {
            Buy_opened = true;  //It is a Buy
         }
         else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
         {
            Sell_opened = true; // It is a Sell
         }
    }

Mittel der Handel-Funktion PositionSelect finden wir heraus, ob wir eine offene Position haben. Diese Funktion liefert uns bei einer bereits geöffneten Position TRUE, und FALSE, wenn das nicht der Fall ist.

bool  PositionSelect(
   string  symbol      // Symbol name 
 );

Als primäre Begründung/Parameter, nimmt sie das Symbol (Währungspaar) her, das wir überprüfen wollen. Wir benutzen hier das _symbol, da wir das aktuelle Symbol (Währungspaar) überprüfen wollen.

Liefert dieser Ausdruck TRUE, möchten wir wissen, ob die offene Position eine Buy- oder Sell-Position ist. Das geht mit Hilfe der PositionGetInteger Funktion, da sie uns die Art der geöffneten Position liefert, wenn wir sie zusammen mit dem POSITION_TYPE Modifikator verwenden. Er liefert den Positionstyp-Identifikator: entweder einen  POSITION_TYPE_BUY oder POSITION_TYPE_SELL.

long  PositionGetInteger(
   ENUM_POSITION_PROPERTY  property_id      // Property identifier
   );

In unserem Fall haben wir ihn verwendet, um festzustellen, welche Positionen bereits geöffnet sind. Ist es eine Sell-Position, speichern wir einen TRUE-Wert in Sell_opened; bei einer Buy-Position speichern wir einen TRUE-Wert in Buy_opened. Diese zwei Variablen können wir später verwenden, wenn wir in unserem Code dann nach Sell- oder Buy-Bedingungen nachsehen wollen.

Jetzt speichern wir aber erst einmal den Schlusskurs für den Bar, den wir für unsere Buy/Sell Einrichtung verwenden werden. Wissen Sie noch...? Wir haben dafür vorher eine Variable deklariert

// Copy the bar close price for the previous bar prior to the current bar, that is Bar 1

   p_close=mrate[1].close;  // bar 1 close price

Also können wir nun zum nächsten Schritt übergehen.

/*
    1. Check for a long/Buy Setup : MA-8 increasing upwards, 
    previous price close above it, ADX > 22, +DI > -DI
*/
//--- Declare bool type variables to hold our Buy Conditions
   bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]); // MA-8 Increasing upwards
   bool Buy_Condition_2 = (p_close > maVal[1]);         // previuos price closed above MA-8
   bool Buy_Condition_3 = (adxVal[0]>Adx_Min);          // Current ADX value greater than minimum value (22)
   bool Buy_Condition_4 = (plsDI[0]>minDI[0]);          // +DI greater than -DI

//--- Putting all together   
   if(Buy_Condition_1 && Buy_Condition_2)
     {
      if(Buy_Condition_3 && Buy_Condition_4)
        {
         // any opened Buy position?
         if (Buy_opened) 
         {
            Alert("We already have a Buy Position!!!"); 
            return;    // Don't open a new Buy Position
         }
         mrequest.action = TRADE_ACTION_DEAL;                                // immediate order execution
         mrequest.price = NormalizeDouble(latest_price.ask,_Digits);          // latest ask price
         mrequest.sl = NormalizeDouble(latest_price.ask - STP*_Point,_Digits); // Stop Loss
         mrequest.tp = NormalizeDouble(latest_price.ask + TKP*_Point,_Digits); // Take Profit
         mrequest.symbol = _Symbol;                                         // currency pair
         mrequest.volume = Lot;                                            // number of lots to trade
         mrequest.magic = EA_Magic;                                        // Order Magic Number
         mrequest.type = ORDER_TYPE_BUY;                                     // Buy Order
         mrequest.type_filling = ORDER_FILLING_FOK;                          // Order execution type
         mrequest.deviation=100;                                            // Deviation from current price
         //--- send order
         OrderSend(mrequest,mresult);

Jetzt ist es an der Zeit, mit der Suche nach einer Buy-Chance zu beginnen.

Dazu analysieren wir den Ausdruck oben, da er die zuvor von uns entworfene Strategie abbildet. Für jede unserer Bedingungen, die vor dem Platzieren eines Order erfüllt sein muss, deklarieren wir eine Variable des bool Typs. Eine BoolTyp-Variable kann nur TRUE oder FALSE besitzen. Unsere Buy-Strategie ist also in vier Bedingungen herunter gebrochen.  Wird eine dieser Bedingungen erfüllt oder befriedigt, wird ein TRUE-Wert in unserer BoolTyp-Variable gespeichert. Ansonsten wird dort ein FALSE-Wert gespeichert. Doch sehen wir uns das der Reihe nach an.

bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]);

Hier betrachten wir die GM-8 Werte der Bars 0, 1 und 2. Ist ein Wert von GM-8 auf dem aktuellen Bar größer als sein Wert auf dem vorigen Bar 1, und ist auch der GM-8-Wert auf Bar 1 größer als der Wert auf Bar 2, bedeutet das, dass GM-8 nach oben ansteigt. Damit ist eine unserer Bedingungen für eine Buy-Einrichtung erfüllt.

bool Buy_Condition_2 = (p_close > maVal[1]); 

Dieser Ausdruck prüft, ob der Schlusskurs bei Bar 1 größer ist, als der GM-8-Wert desselben Zeitraums (Bar 1 Zeitraum). Ist der Kurs höher, dann ist auch unsere zweite Bedingung erfüllt und wir können die anderen Bedingungen prüfen. Werden jedoch die beiden o.g. Bedingungen nicht erfüllt, dann brauchen wir uns um die anderen Bedingungen auch nicht zu kümmern. Aus diesem Grund haben wir beschlossen, die nächsten Ausdrücke innerhalb dieser zwei Ursprungsbedingungen (Ausdrücke) mit einzuschließen.

bool Buy_Condition_3 = (adxVal[0]>Adx_Min);

Jetzt wollen wir sehen, ob der aktuelle ADX-Wert (ADX-Wert auf Bar 0) größer ist als der Mindest-ADX-Wert, der in den Eingabeparametern deklariert wurde. Liefert dieser Ausdruck TRUE, ist also der aktuelle ADX-Wert tatsächlich größer als der erforderliche Mindestwert, wollen wir zudem sicherstellen, dass auch der plusDI-Wert größer ist als der minusDI-Wert. Dies gelingt uns mit dem nächsten Ausdruck.

bool Buy_Condition_4 = (plsDI[0]>minDI[0]);

Sind all diese Bedingungen erfüllt, d.h. sie liefern uns ein TRUE, müssen wir sicherstellen, dass wir keine neue Buy-Position eröffnen, wenn schon eine offen ist. Jetzt prüfen wir den Wert der Buy_opened Variable, die wir an anderer Stelle in unserem Code deklariert haben.

// any opened Buy position?
if (Buy_opened) 
   {
      Alert("We already have a Buy Position!!!"); 
      return;    // Don't open a new Buy Position
   }

Wenn Buy_openend TRUE ist, dann öffnen wir natürlich keine weitere Buy-Position, also zeigen wir eine Warnmeldung an, die uns entsprechend informiert und gehen dann zurück, damit unser EA nun auf die nächste Kursschwankung wartet. Ergibt Buy_opened jedoch FALSE, legen wir unsere Datensätze mit Hilfe der Variable vom Typ MqlTradeRequest (mrequest) an, die wir zuvor zum Abschicken unseres Order deklariert haben.

  • Die Handlung hier, vom Typ einer Handel-Operation, ist TRADE_ACTION_DEAL, da wir ja eine Order zur sofortigen Ausführung platzieren. Wollen wir eine Order verändern, benutzen wir dazu TRADE_ACTION_MODIFY. Und zum Löschen einer Order TRADE_ACTION_REMOVE.  Zur Abfrage des jüngsten Briefkurses haben wir den MqlTick Typ latest_price verwendet.  Den Stop loss Orderkurs erhält man durch Subtraktion unseres StopLoss in Punkten vom Briefkurs. Den take profit Orderkurs erhält man durch Addition unseres TakeProfit in Punkten zum Briefkurs. Sie merken hier zudem, dass wir die NormalizeDouble Funktion für den Briefkurs und die StopLoss und TakeProfit Werte verwendet haben. Es empfiehlt sich diese Kurse immer auf die Ziffern des Währungspaars zu normalisieren, bevor man es an den Handel-Server schickt. 
  • Symbol  ist das aktuelle Symbol (_Symbol oder Symbol()). die Ordertyp ist die Art die Order, den wir platzieren. In diesem Fall eine Buy-Order ORDER_TYPE_BUY. Für eine Sell-Order ist das logischerweise dann ORDER_TYPE_SELL.
  • Ordertyp_filling ist die Art die Order-Ausführung: ORDER_FILLING_FOK bedeutet, dass der Abschluss ausschließlich in einem spezifizierten Volumen zum gleichen oder besseren Kurs als der spezifizierte Orderkurs ausgeführt werden kann. Gibt es kein ausreichendes Angebotsvolumen auf dem Ordersymbol, wird die Order nicht ausgeführt.

Die OrderSend() Funktion besitzt zwei Begründungen: die Variable vom Typ MqlTradeRequest und die Variable vom Typ MqlTradeResult.

bool  OrderSend(
   MqlTradeRequest&  request      // query structure
   MqlTradeResult&   result       // structure of the answer
   );

Wie Sie sehen, haben wir die Variable vom Typ MqlTradeRequest und die Variable vom Typ MqlTradeResult bei der Platzierung unserer Order mit Hilfe von OrderSend verwendet.

         // get the result code
         if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed
           {
            Alert("A Buy order has been successfully placed with Ticket#:",mresult.order,"!!");
           }
         else
           {
            Alert("The Buy order request could not be completed -error:",GetLastError());
            ResetLastError();           
            return;
           }

Da unser Order abgeschickt worden ist, prüfen wir nun mit Hilfe der Variable vom Typ MqlTradeResult das Ergebnis unserer Order. Wurde unsere Order erfolgreich ausgeführt, möchten wir darüber informiert werden. Wenn nicht, wollen wir das natürlich auch wissen. Mit der ‘mresult’ Variable vom Typ MqlTradeResult können wir auf den Lieferungs-Code der Operation zugreifen und erhalten auch die Order-Ticketnummer, falls die Order platziert ist.

Der Lieferungs-Code 10009 gibt an, dass die OrderSend Anfrage erfolgreich abgeschlossen wurde; Code 10008 zeigt, dass unsere Order platziert wurde. Deshalb haben wir nachgesehen, ob einer dieser beiden Codes auftaucht. Sobald einer von beiden angezeigt wird, wissen wir sicher, dass unsere Order abgeschlossen oder platziert worden ist.

Zur Überprüfung einer Sell-Möglichkeit, gehen wir genau in umgekehrter Reihenfolge vor wie bei der Buy-Möglichkeit, außer dass hier unser ADX größer sein muss als der angegebene Mindestwert.

/*
    2. Check for a Short/Sell Setup : MA-8 decreasing downwards, 
    previous price close below it, ADX > 22, -DI > +DI
*/
//--- Declare bool type variables to hold our Sell Conditions
   bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]);  // MA-8 decreasing downwards
   bool Sell_Condition_2 = (p_close <maVal[1]);                         // Previous price closed below MA-8
   bool Sell_Condition_3 = (adxVal[0]>Adx_Min);                         // Current ADX value greater than minimum (22)
   bool Sell_Condition_4 = (plsDI[0]<minDI[0]);                         // -DI greater than +DI
   
 //--- Putting all together
   if(Sell_Condition_1 && Sell_Condition_2)
       {
         if(Sell_Condition_3 && Sell_Condition_4)
           {
            // any opened Sell position?
            if (Sell_opened) 
            {
                Alert("We already have a Sell position!!!"); 
                return;    // Don't open a new Sell Position
            }
            mrequest.action = TRADE_ACTION_DEAL;                                 // immediate order execution
            mrequest.price = NormalizeDouble(latest_price.bid,_Digits);          // latest Bid price
            mrequest.sl = NormalizeDouble(latest_price.bid + STP*_Point,_Digits); // Stop Loss
            mrequest.tp = NormalizeDouble(latest_price.bid - TKP*_Point,_Digits); // Take Profit
            mrequest.symbol = _Symbol;                                         // currency pair
            mrequest.volume = Lot;                                            // number of lots to trade
            mrequest.magic = EA_Magic;                                        // Order Magic Number
            mrequest.type= ORDER_TYPE_SELL;                                     // Sell Order
            mrequest.type_filling = ORDER_FILLING_FOK;                          // Order execution type
            mrequest.deviation=100;                                           // Deviation from current price
            //--- send order
            OrderSend(mrequest,mresult);

Wie auch im Abschnitt zu Buy, deklarieren wir hier eine Variable vom Bool  Typ für jede unserer Bedingungen, die vor der Platzierung einer Order erfüllt sein muss. Eine Variable vom Booltyp kann nur TRUE oder FALSE enthalten. Also wird unsere Sell-Strategie in vier Bedingungen herunter gebrochen.  Wird eine dieser Bedingungen erfüllt oder befriedigt, wird ein TRUE-Wert in unserer BoolTyp-Variable gespeichert. Ansonsten wird dort ein FALSE-Wert gespeichert. Sehen wir uns auch dies der Reihe nach an, so wie wir es vorhin bei Buy gemacht haben.

   bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]);

Hier betrachten wir die GM-8 Werte der Bars 0, 1 und 2. Ist ein Wert von GM-8 auf dem aktuellen Bar kleiner als sein Wert auf dem vorigen Bar 1, und ist auch der GM-8-Wert auf Bar 1 kleiner als der Wert auf Bar 2, bedeutet das, dass GM-8 nach unten abfällt. Damit ist eine unserer Bedingungen für eine Sell-Einrichtung erfüllt.

   bool Sell_Condition_2 = (p_close <maVal[1]); 

Dieser Ausdruck prüft, ob der Schlusskurs bei Bar 1 geringer ist als der GM-8-Wert desselben Zeitraums (Bar 1 Zeitraum). Ist der Kurs geringer, dann ist auch unsere zweite Bedingung erfüllt und wir können die anderen Bedingungen prüfen. Werden jedoch die beiden o.g. Bedingungen nicht erfüllt, dann brauchen wir uns um die anderen Bedingungen auch nicht zu kümmern. Aus diesem Grund haben wir beschlossen, die nächsten Ausdrücke innerhalb dieser zwei Ursprungsbedingungen (Ausdrücke) mit einzuschließen.

   bool Sell_Condition_3 = (adxVal[0]>Adx_Min); 

Jetzt wollen wir sehen, ob der aktuelle ADX-Wert (ADX-Wert auf Bar 0) größer ist als der Mindest-ADX-Wert, der in den Eingabeparametern deklariert wurde. Liefert dieser Ausdruck TRUE, ist also der aktuelle ADX-Wert tatsächlich größer als der erforderliche Mindestwert, wollen wir zudem sicherstellen, dass auch der minusDI-Wert größer ist als der plusDI-Wert. Dies gelingt uns mit dem nächsten Ausdruck

bool Sell_Condition_4 = (plsDI[0]<minDI[0]);

Sind diese Bedingungen erfüllt, d.h. sie liefern uns ein TRUE, müssen wir sicherstellen, dass wir keine neue Buy-Position eröffnen, wenn schon eine offen ist. Jetzt prüfen wir den Wert der Buy_opened Variable, die wir an anderer Stelle in unserem Code deklariert haben.

// any opened Sell position?
            if (Sell_opened) 
            {
                Alert("We already have a Sell position!!!"); 
                return;    // Don't open a new Sell Position
            }

Wenn Sell_opened TRUE ist, öffnen wir natürlich keine weitere Sell-Position, also zeigen wir eine Warnmeldung an, die uns entsprechend informiert und gehen dann zurück, damit unser EA nun auf die nächste Kursschwankung wartet. Ergibt Sell_opened jedoch FALSE, richten wir unsere Sell Handelsanfrage genauso ein, wie zuvor für die Buy-Order.

Der größte Unterschied liegt hier darin, wie wir unseren Stop Loss und Take Profitkurs berechnet haben. Und da wir ja verkaufen, verkaufen wir zum Geldkurs, und deshalb haben wir unsere MqlTickTyp Variable latest_price verwendet, um den den jüngsten Geldkurs zu erhalten. Der andere Typ, der oben schon erklärt wurde, ist ORDER_TYPE_SELL.

Wir haben hier zudem die NormalizeDouble Funktion verwendet, und zwar für den Geldkurs und die StopLoss nd TakeProfit Werte. Es empfiehlt sich diese Kurse immer auf die Ziffern des Währungspaars zu normalisieren, bevor man es an den Handelsserver schickt. 

Wie bei unserer Buy-Order auch, müssen wir jetzt nachsehen, ob unsere Sell-Order erfolgreich war oder nicht. Dazu haben wir den gleichen Ausdruck wie in der Buy-Order verwendet.

         if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed
           {
            Alert("A Sell order has been successfully placed with Ticket#:",mresult.order,"!!");
           }
         else
           {
            Alert("The Sell order request could not be completed -error:",GetLastError());
            ResetLastError();
            return;
           }
        }


3. Debugging (Fehlersuche) und Test unseres Expert Advisors

Jetzt ist es Zeit, unseren EA zu testen, um zu erfahren, ob unsere Strategie auch funktioniert oder nicht. Außerdem ist es möglich, dass es einen oder zwei Fehler im EA-Code gibt. Das erklären wir im nächsten Schritt.

3.1 DEBUGGING

Unseren Code auf Fehler zu untersuchen, ermöglicht uns, die Leistung unseres Codes Zeile für Zeile prüfen zu können (wenn wir Haltepunkte setzen). Zudem stellen wir somit hier und da Fehler oder Macken im Code fest und können rasch die notwendigen Korrekturen machen, bevor wir ihn schließlich für den echten Handel einsetzen.

Im Folgenden erklären wir Schritt für Schritt den Fehlersuchprozess unseres Expert Advisors, zunächst, mit Hilfe von gesetzten Haltepunkten und später, dann ohne sie. Dazu vergewissern Sie sich bitte, dass der Editor nicht geschlossen ist. Wählen wir zunächst also das Chart, das wir für den Test unseres EA nutzen wollen. Dazu klickt man im Editor-Menü auf Tools und dann auf Optionen, wie unten gezeigt:

Abb. 8 Einstellen der Debugging-Optionen

Abb. 8 Einstellen der Debugging-Optionen 

Sobald das Optionen-Fenster erscheint, müssen das zu verwendende, aktuelle Währungspaar und der Zeitraum/Zeitrahmen gewählt werden. Danach "OK" klicken.

Abb. 9 Debugger-Optionen Fenster

Bevor wir den Debugger (Fehlersuchprogramm) starten, müssen wir noch die Haltepunkte einrichten. Haltepunkte gestatten uns die Kontrolle des Verhaltens/Leistung unseres Codes an bestimmten, gewählten Zeilen oder Orten kontrollieren zu können. Anstatt den gesamten Code auf einmal durchforsten zu müssen, hält der Debugger bei jedem Haltepunkt an, und wartet auf Ihre nächste klare Aktion. Auf diese Weise können wir unseren Code immer dann, wenn er jedes Set an Haltepunkten erreicht, analysieren und sein Verhalten kontrollieren. Außerdem können wir auch die Werte einiger unserer Variablen bewerten, um herauszufinden, ob alles so abläuft, wie wir es uns vorstellen.

Ein Haltepunkt wird eingefügt, indem man zu derjenigen Codezeile geht, wo man einen Haltepunkt setzen will. Doppelklick auf der linken Seite auf dem grauen Feld knapp neben dem Rand der Codezeile, sodass ein kleines, blaues rundes Feld mit einem weißen Quadrat innen erscheint. Alternativ kann man auch den Cursor überall dort auf der Codezeile positionieren, wo ein Haltepunkt entstehen soll und dann F9 drücken. Um den Haltepunkt zu entfernen, nochmals F9 drücken und mit Doppelklick bestätigen.

Abb. 10 Setzen eines Haltepunkts

Abb. 10 Setzen eines Haltepunkts

In unserem Code setzen wir Haltepunkte in fünf verschiedenen Zeilen. 

Zum besseren Verständnis, nenne ich sie zusätzlich auch noch 1 - 5.

Um fortzufahren, setzt man Haltepunkte in den sieben Codezeilen, wie in folgender Abbildung gezeigt: Haltepunkt 1  haben wir oben bereits gesetzt.

Abb. 11 Setzen zusätzlicher Haltepunkte

Abb. 11 Setzen zusätzlicher Haltepunkte

Haben wir dann alle Haltepunkte gesetzt, können wir mit der Fehlersuche für unseren Code beginnen.

Zum Starten des Debuggers, F5 drücken oder die grüne Schaltfläche in der Werkzeugleiste des MetaEditors anklicken:

 Abb. 12 Starten des Debuggers

Abb. 12 Starten des Debuggers

Als erstes erstellt der Editor den Code. Wenn bereits hier ein Fehler auftritt, zeigt er uns dies an. Ist jedoch alles in Ordnung, erscheint die Meldung "Code erfolgreich erstellt".

Abb. 13 Erstellungsbericht

Abb. 13 Erstellungsbericht

Man sollte hier die Tatsache beachten, dass ein erfolgreich erstellter Code nicht bedeutet, dass der Code komplett fehlerfrei ist. Je nachdem wie der jeweilige Code geschrieben ist, können Laufzeitfehler auftreten. Falls z.B. irgendein Ausdruck aufgrund irgendeines kleinen Fehlers nicht richtig bewertet, wird der Code zwar richtig erstellt, läuft ggf. jedoch später nicht korrekt ab. Schluss mit der Theorie, sehen wir uns das jetzt in Aktion an…

Hat das Fehlersuchprogramm die Erstellung des Codes abgeschlossen, wird man zum Handels-Terminal weitergeleitet und hängt den EA an das Chart an, das zuvor in den Einstellungen unter MetaEditor Optionen angegeben wurde. Zugleich wird einem der Bereich Eingabeparameter des EA gezeigt. Da wir jetzt noch nichts anpassen, klicken wir einfach auf "OK".

Abb. 14 Expert Advisor Eingabeparameter fürs Debugging

Abb. 14 Expert Advisor Eingabeparameter fürs Debugging

Der EA ist nun deutlich oben rechts auf dem Chart zu erkennen.

Wenn er das OnTick() startet, hört er wieder auf, sobald Haltepunkt 1 erreicht ist.

Abb. 15 Debugger hört am ersten Haltepunkt auf

Abb. 15 Debugger hört am ersten Haltepunkt auf

Auf dieser Codezeile erscheint ein grüner Pfeil. Dieser gibt an, dass die Fehlersuche in vorigen Codezeile ausgeführt wurde und jetzt die vorliegende Zeile geprüft werden kann.

Noch einige kurze Erklärungen, bevor wir weitermachen. Wenn man sich die Werkzeugleiste im Editor betrachtet, erkennt man dort drei Schaltflächen mit gekrümmten Pfeilen, die zuvor grau waren und nun aktiviert sind. Der Grund dafür: der Debugger läuft jetzt ab. Mittels dieser Schaltflächen/Befehle wird unser Code via verschiedener Schritte untersucht (eintreten, übertreten oder austreten)

Abb. 16 Befehl 'eintreten'

Abb. 16 Befehl 'eintreten'

Mit Eintreten geht das Programm von einem Schritt der Ausführung zum nächsten, und 'tritt' in jede aufgerufene Funktion innerhalb dieser Codezeile 'ein'. Zum Aufrufen des Befehls, entweder auf die entspr. Schaltfläche oder F11 klicken. (Diesen Befehl verwenden wir in unserer schrittweisen Fehlersuche für unseren Code.)

Abb. 17 Befehl 'übertreten'

Abb. 17 Befehl 'übertreten'

Mit Übertreten hingegen, tritt das Programm nicht in jede der aufgerufenen Funktionen innerhalb dieser Codezeile ein. Zum Aufrufen des Befehls, entweder auf die entspr. Schaltfläche oder F10 klicken.

Abb. 18 Befehl 'austreten'

Abb. 18 Befehl 'austreten'

Zur Ausführung eines Programmschritts auf einer Ebene höher, diese Schaltfläche oder Shift+F11 klicken.

Im unteren Bereich des Editors wird außerdem auch das Toolbox-Fenster angezeigt. Der Debug-Seite in diesem Fenster hat folgende Kopfzeilen:

  • Datei: Zeigt den Namen der aufgerufenen Datei.
  • Funktion: Zeigt die aktuelle Funktion aus der aufgerufenen Datei.
  • Zeile: Zeigt die Ziffer der Codezeile in der Datei aus der die Funktion aufgerufen wird.
  • Ausdruck: Hier kann man den Namen jedes Ausdrucks/Variable aus unserem Code eingeben, die man überprüfen möchte. 
  • Wert: Zeigt den Wert des Ausdrucks/Variable an, den/die wir im Ausdrucks-Feld eingegeben haben.
  • Typ: Zeigt den Datentyp des überprüften Ausdrucks/Variable an.

Doch zurück zu unserer Fehlersuche…

Als nächstes müssen wir die Variablen/Ausdrücke aus unserem Code eingeben, die wir gerne kontrollieren möchten. Bitte darauf achten, nur diejenigen Ausdrücke/Variablen zu kontrollieren, die für den Code auch wirklich ausschlaggebend sind. In unserem Beispiel überprüfen wir folgendes:

  • Old_Time (alte Bar-Uhrzeit)
  • New_Time[0] (aktuelle Bar-Uhrzeit)
  • IsNewBar (Markierung zur Anzeige des neuen Bars)
  • Mybars (Bars insgesamt in der Historie) – von ihnen hängt unser EA ab

Andere Zeilen wie z.B. die ADX-Werte, die GM-8 Werte usw. können hinzugefügt werden.

Um den Ausdruck/Variable hinzuzufügen, Doppelklick oder Rechtsklick im Bereich Ausdruck und Hinzufügen wählen,wie in der Abbildung oben gezeigt.

Anschließend den zu kontrollierenden oder zu beobachtenden Ausdruck/Variable eingeben.

Abb. 19 Fenster zur Beobachtung der Ausdrücke

Abb. 19 Fenster zur Beobachtung der Ausdrücke

Hier alle erforderlichen Variablen/Ausdrücke eingeben…

Abb. 20 Hinzufügen von Ausdrücken oder Variablen, die beobachtet werden sollen

Abb. 20 Hinzufügen von Ausdrücken oder Variablen, die beobachtet werden sollen

Sollte die Variable noch nicht deklariert sein, wird ihr Typ mit "Unbekannter Identifikator" angegeben (außer bei statischen Variablen).

Und weiter gehts…

Abb. 21 Befehl 'eintreten' in Aktion

Abb. 21 Befehl 'eintreten' in Aktion

Eintreten oder F11 anklicken  und sehen, was passiert. Schaltfläche oder F11 gedrückt halten, bis Haltepunkt 2 erreicht ist usw., bis Haltepunkt 4 erreicht ist (vgl. Abb. unten) und dabei das Fenster zur Beobachtung der Ausdrücke im Auge behalten.


Abb. 22 Ausdrücke oder Variablen beobachten

Abb. 22 Ausdrücke oder Variablen beobachten

Abb. 23 Ausdrücke oder Variablen beobachten

Abb. 23 Ausdrücke oder Variablen beobachten

Abb. 24 Ausdrücke oder Variablen beobachten

Abb. 24 Ausdrücke oder Variablen beobachten

Sobald eine neue Kursschwankung auftritt, wird zur ersten Codezeile der OnTick() Funktion zurückgegangen. Und alle Werte unserer Variablen/Ausdruck werden nun aufgrund dieser neuen Kursschwankung zurückgesetzt, es sei denn, irgendeine von ihnen ist als statische Variable deklariert. In unserem Fall haben wir nur eine statische Variable: Old_Time.

Abb. 25 Variablen-Werte bei Auftreten einer neuen Kursschwankung

Abb. 25 Variablen-Werte bei Auftreten einer neuen Kursschwankung

Um den Vorgang erneut ablaufen zu lassen, weiterhin  F11 gedrückt halten und die Variablen im Fenster zur Beobachtung der Ausdrücke im Auge behalten. Dann kann das Fehlersuchprogramm angehalten und alle Haltepunkte entfernt werden.

Und wie wir sehen, druckt der EA im Debug-Modus die Nachricht aus: "Neuen Bar hier gefunden...".

Abb. 26 Expert Advisor druckt die Nachricht im Debug-Modus aus

Abb. 26 Expert Advisor druckt die Nachricht im Debug-Modus aus

Nun kann der Fehlersuchprozess erneut gestartet werden, diesmal jedoch ohne Verwendung von Haltepunkten Achten Sie dabei auf jede Kursschwankung und darauf, ob eine der Buy/Sell-Bedingungen erfüllt ist. Da wir unseren Code ja so geschrieben haben, dass er uns sagt, ob eine Order erfolgreich platziert wurde oder nicht, erhalten wir logischerweise einen Warnhinweis.

Abb. 27 Expert Advisor platziert einen Handel während Debugging

Abb. 27. Expert Advisor platziert einen Handel während Debugging

Ihr EA braucht hierfür einige Minuten, also können Sie sich ruhig einen Kaffee holen. Wenn Sie dann wieder zurück sind, sind Sie bereits um ein paar Euro reicher (nein, nein... nur Spaß!). Jetzt klicken Sie im MetaEditor auf STOP (rote Schaltfläche), um den Debugging-Vorgang anzuhalten.

Abb. 28 Debugger anhalten

Abb. 28 Debugger anhalten

Wir haben hier tatsächlich überprüft, dass unser Expert Advisor nur nach einem Handel bei der Eröffnung eines neuen Bars sucht, und dass unser EA tatsächlich funktioniert. Es gibt immer noch viel Platz für weitere Anpassungen unseres EA-Codes.

Ich möchte an dieser Stelle darauf hinweisen, dass das Handels-Terminal mit dem Internet verbunden sein muss, da sonst die Fehlersuche nicht funktioniert, da das Terminal ja keine Handels ausführen kann.

3.2 TESTEN WIR UNSERE EXPERT ADVISOR-STRATEGIE

Jetzt testen wir unsere EA-Strategie mit Hilfe des in das Handels-Terminal eingebauten Strategie-Testers.  Zum Aufrufen des Strategie-Testers, CONTROL+R drücken oder in der Menüleiste des Terminals das Menü Anzeige aufrufen und dort, wie unten gezeigt, auf Strategie-Tester drücken

Abb. 26 Den Strategie-Test starten

Abb. 26 Den Strategie-Test starten

Der Strategie-Tester erscheint im unteren Bereich des Terminals. Um all seine Einstellungen ansehen zu können, muss er aufgezogen/vergrößert werden. Dazu bewegen Sie Ihren Mauszeiger auf den mit dem den roten Pfeil markierten Punkt (s. unten).

Abb. 27 Das Strategie-Tester Fenster

Abb. 27 Das Strategie-Tester Fenster

Der Mauszeiger wird jetzt zu einem Pfeil mit zwei Spitzen: Maus gedrückt halten und die Zeile nach oben ziehen. Aufhören, wenn Sie alles im Einstellungs-Tab klar erkennen können. 

Abb. 28 Der Strategie-Tester Einstellungs-Tab

Abb. 28 Der Strategie-Tester Einstellungs-Tab

  1. Wählen Sie den Expert Advisor, den Sie testen wollen
  2. Wählen Sie das Währungspaar. das Sie dazu verwenden wollen
  3. Wählen Sie den entsprechenden Zeitraum/Zeitrahmen
  4. Wählen Sie benutzerdefinierter Zeitraum und richten die Daten in 5 ein
  5. Richten Sie die Daten für den benutzerdefinierten Zeitraum ein, der für den Test verwendet werden soll
  6. Ausführung = Normal
  7. Wählen Sie den Betrag, der getestet werden soll (in USD )
  8. Stellen Sie die Optimierung auf 'Deaktivieren' (wir wollen jetzt nicht optimieren, sondern nur testen)
  9. Wenn Sie für den Test bereit sind, auf diese Schaltfläche klicken.

Doch bevor wir auf Start klicken, sehen wir uns noch kurz die anderen Tabs im Tester an

Agents-Tab

Der vom Tester für den Test verwendete Prozessor. Je nach Prozessortyp Ihres Computers. Meiner ist ein Prozessor mit nur einem Core.

Abb. 29 Agents-Tab des Strategie-Testers

Abb. 29 Agents-Tab des Strategie-Testers

Ist der Agent gezeigt, erscheint eine ähnliche Konfiguration wie in der Abbildung unten

Abb 30 Agents-Tab des Strategie-Testers während eines Tests

Abb. 30 Agents-Tab des Strategie-Testers während eines Tests

Logbuch-Tab

Hier werden alle Ereignisse, die während der Testphase ablaufen, angezeigt

Abb. 31 Logbuch-Tab des Strategie-Testers zeigt Handels-Aktivitäten

Abb. 31 Logbuch-Tab des Strategie-Testers zeigt Handels-Aktivitäten

Eingaben-Tab

Hier können Sie die Eingabeparameter für den Expert Advisor spezifizieren

Abb. 32 Eingaben-Tab des Strategie-Testers

Abb. 32 Eingaben-Tab des Strategie-Testers

Wenn wir unseren Expert Advisor optimieren, müssen wir die Werte im umringelten Bereich festlegen.

  • Start ist der Wert, bei dem der Tester beginnen soll.
  • Step ist die Zuwachsrate des von Ihnen ausgewählten Werts, und
  • Stop ist der Wert bei dem der Tester mit der Erhöhung des Werts für diesen Parameter aufhört.

Da wir ja in diesem Beispiel unseren Expert Advisor nicht optimieren, geht uns das hier erst einmal nichts an. 

Ist alles korrekt eingestellt, gehen wir zurück zum Einstellungen-Tab und klicken auf Start. Der Tester beginnt nun mit seiner Arbeit. Jetzt können Sie sich gerne noch einen Kaffee holen. Oder wenn Sie eher wie ich gestrickt sind, verfolgen Sie jetzt jedes Ereignis mit und wenden sich dann dem Logbuch-Tab zu. 

Grafik-Tab

Sobald Sie Meldungen bzgl. Order entdecken, die an den Logbuch-Tab gesendet wurden, können Sie sich gerne einem NEUEN Tab namens Grafik zuwenden, den Sie gerade angelegt haben. Sobald Sie zu diesem Tab wechseln, wird Ihnen eine Grafik gezeigt, deren Kurve entweder an- oder absteigt, je nach den Ergebnissen Ihrer Handel.

Abb. 33 Die graphische Darstellung der Ergebnisse des Expert Advisor Tests

Abb. 33 Die graphische Darstellung der Ergebnisse des Expert Advisor Tests

Ergebnis-Tab

Ist der Test anschließend abgeschlossen, erscheint ein weiterer Tab mit den Ergebnissen. Auf diesem Ergebnis-Tab sehen Sie eine Zusammenfassung des Tests, den wir soeben haben ablaufen lassen.

 Abb. 34 Der Ergebnis-Tab des Strategie-Testers mit der Zusammenfassung der Testergebnisse

 Abb. 34 Der Ergebnis-Tab des Strategie-Testers mit der Zusammenfassung der Testergebnisse

Hier sehen Sie den insgesamtn Bruttogewinn, Nettogewinn, alle Handel, alle Handel mit Verlust uvm. Es ist wirklich interessant, dass wir innerhalb des für unseren Test gewählten Zeitraums ca. USD 1.450,00 erwirtschaftet haben. Wenigstens etwas.

Lassen Sie mich an dieser Stelle auf eine Sache besonders hinweisen: Sie werden bemerken, dass sich die Einstellungen für die Expert Advisor-Parameter, die Sie im Strategie-Tester sehen, stark von den ursprünglichen Einstellungen in den Eingabeparametern für den Expert Advisor unterscheiden. Ich habe Ihnen gerade gezeigt, dass Sie jeden dieser Eingabeparameter verändern können, um das Optimale aus Ihrem Expert Advisor herauszuholen. Anstatt einen Zeitraum von 8 für jeden Gleitenden Mittelwert und  ADX zu verwenden, habe ich für den gleitenden Mittelwert 10 und für ADX 14 genommen. Ich habe auch Stop Loss von 30 auf 35 verändert. Und schließlich habe ich mich auch noch für eine Zeitrahmen von 2 Stunden entschieden. Es handelt sich hier um den Strategie-Tester - nicht vergessen!

Wenn Sie einen vollständigen Testbericht sehen möchten, dann gehen Sie irgendwo im Ergebnis-Tab mit Rechtsklick hinein, und ein Menü erscheint. In diesem Menü wählen Sie dann Als Berichtt speichern.'

Abb. 35 Testergebnis wird gespeichert

Abb. 35 Testergebnis wird gespeichert

Sobald das Dialogfenster 'Speichern' erscheint, geben Sie Ihrem Bericht einen Namen (oder übernehmen den standardmäßig angebotenen) und klicken dann auf 'Speichern'. Der komplette Bericht wird dann im HTML-Format gespeichert.

Um das Chart für den soeben durchgeführten Test ansehen zu können, müssen Sie auf Chart Öffnen klicken und das Chart erscheint

Abb. 36 Das Chart mit dem Test 

Abb. 36 Das Chart mit dem Test

Und das ist alles! Wir haben jetzt unseren Expert Advisor erfolgreich geschrieben und geteset und haben zudem auch ein Ergebnis, mit dem wir arbeiten können. Jetzt müssen Sie zurück zum Einstellungen-Tab des Strategie-Testers und den Test für andere Zeitrahmen/Zeiträume ebenfalls durchführen.

Aufgabe

Ich möchte gerne, dass Sie den Test bei verschiedenen Währungspaaren, in verschiedenen Zeitrahmen, bei verschiedenen Stop Loss und verschiedenen Take Profit durchführen und prüfen, wie der Expert Advisor arbeitet. Sie können sogar neue gleitende Mittelwert- und ADX-Werte ausprobieren. Wie ich vorhin schon erwähnt habe, ist dies die Essenz des Strategie-Testers. Und außerdem würde ich mich freuen, wenn Sie mir Ihre Ergebnisse mitteilen.

Fazit

Dieser Beitrag hat uns Schritt für Schritt die grundlegenden Etappen näher gebracht, die für das Schreiben eines einfachen Expert Advisors auf Grundlage einer entwickelten Handels-Strategie erforderlich sind. Ebenso haben wir uns angesehen, wie wir unseren Expert Advisor mit Hilfe eines Fehlersuchprogramms (Debugger) auf Fehler überprüfen können. Und schließlich haben wir die Leistung unseres Expert Advisors mit Hilfe des Strategie-Testers geprüft. Dadurch haben wir festgestellt, wie leistungsfähig und robust die neue MQL5 Sprache ist. Unser Expert Advisor ist allerdings bei weitem noch nicht perfekt oder vollständig. Denn für seinen Einsatz im echten Handel müssen noch eine Menge weitere Anpassungen vorgenommen werden.

Es gibt noch viel zu lernen, und deshalb würde ich Sie bitten, diesen Beitrag noch einmal, zusammen mit dem MQL5 Handbuch durchzugehen, und alles, was Sie hier gelernt haben, auszuprobieren. Ich versichere Ihnen, dass es nicht mehr lange dauert, bis Sie ein toller Expert Advisor Entwickler sind.

Frohes Kodieren!


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

Beigefügte Dateien |
my_first_ea.mq5 (11.86 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (26)
ALGO
ALGO | 2 März 2023 in 18:38

Hallo allerseits,

ich habe Samuels EA derzeit auf einem Demokonto laufen und bin durchaus zufrieden. Gute Programmierung!

Er zeigt immer mal wieder gute Handelsabläufe, aber er hat eine Schwäche:

SELL-Optionen häufen sich bei Wendepunkten im Tal der Chartverläufe und dasselbe an entsprechenden Scheitelpunkten mit BUY-Optionen, die dann erstmal massiv als Negativa zu buche schlagen. Ich würde das gerne umprogrammieren, d. h. BUYs im Tag, SELLs auf dem Scheitel, und scheitere mit meinen Versuchen bislang. Ich beherrsche auch nur rudimentär MQL5.

Kann mir jemand helfen?

SG und vielen Dank!

Carl Schreiber
Carl Schreiber | 3 März 2023 in 08:57

So etwas funktioniert meist nicht, weil dann auch die anderen Punkte, an denen der EA eine profitable Position eingeht, umgedreht werden  ....

Lies aber mal das hier:

EA-freelancer Pflichtenheft : https://www.mql5.com/de/articles/4368
Indi : https://www.mql5.com/de/articles/4304
How to Order a Trading Robot in MQL5 and MQL4 : https://www.mql5.com/de/articles/117

Mit den dortigen Hinweisen kannst Du Deine Idee genauer präzisieren, sei es nur für Dich, um zu sehen, ob es geht, oder sei es, um jemanden zu bitten es zu tun, mit oder ohne Bezahlung.

Carl Schreiber
Carl Schreiber | 2 Nov. 2023 in 12:14
Joosy #:

Hallo zusammen😊

@Mario31415927

Das liegt ganz einfach daran, dass im Ticker, jedes Mal, wenn eine neue Periode- bzw. neuer Balken (wie auch immer) die Variablen:

zurückgesetzt werden, unabhängig davon, ob bereits Positionen offen sind oder nicht.

Lässt sich ja im Code relativ schnell finden.

 

Hier stellt sich mir allerding die Frage, ob das beabsichtig wurde, und wenn ja, wie werden dann alle Positionen wieder geschlossen?

Vielleicht verstehe ich auch den Ordertyp TRADE_ACTION_DEAL in dem Zusammenhang nicht?

Den Ablauf der Handelsorder könnte man auch einfach auf die Handelsklasse Trade anpassen. Wie z.B.:

Schöne Grüße:-)

 

Lies mal: https://www.mql5.com/de/articles/232

Das erklärt den Unterschied und die Zusammenhänge von Orders, Positionen und Deals. Letztere spiegeln die Buchungsvorgänge auf Brokerseite.

Johann Kern
Johann Kern | 2 Nov. 2023 in 12:51
Carl Schreiber #:

Lies mal: https://www.mql5.com/de/articles/232

Das erklärt den Unterschied und die Zusammenhänge von Orders, Positionen und Deals. Letztere spiegeln die Buchungsvorgänge auf Brokerseite.

Hallo Carl,

danke für den Hinweis!

 

Ich muss mich allerdings berichtigen.

Es ist zwar richtig, dass die Variablen bei jeder neuen Zeitperiode zurückgesetzt werden:

   bool Buy_opened = false, Sell_opened = false; // variables to hold the result of the opened position

Aber diese werden anschließend wieder gesetzt, wenn dementsprechend eine Position vorliegt.

   if(PositionSelect(_Symbol) == true) { // we have an opened position

      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {

         Buy_opened = true;  //It is a Buy

      }

      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {

         Sell_opened = true; // It is a Sell

      }

   }

 Bei mir tritt jedoch das Phänomen auf, dass die Buy (POSITION_TYPE_BUY) korrekterweise abgefragt wird, jedoch nicht die Sell (POSITION_TYPE_SELL). Die Variable wir einfach nicht mehr gesetzt??

  Muss den Code Schritt- für Schritt durch-debuggen. Das kann doch nur ein logischer Fehler sein😉

Johann Kern
Johann Kern | 2 Nov. 2023 in 12:57
Joosy #:
         Sell_opened = true; // It is a Sell

Ohje, ich steh heute sowas von auf der Leitung.

Es wird ja auch nur einmal abgefragt. Das ist der Fehler;-)

Hier sollten wie folgt alle Positionen abgesucht werden;

   bool Buy_opened = false, Sell_opened = false; // variables to hold the result of the opened position
   
   for(int i=0; i < PositionsTotal(); i++) {
      ticket = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == EA_Magic) {
         if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { 
            Buy_opened = true; 
         }
         if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { 
            Sell_opened = true; 
         }
      }   
   }
Erstellen einer Anzeigetafel unter Verwendung der Klassen aus der Standardbibliothek und Google Chart API Erstellen einer Anzeigetafel unter Verwendung der Klassen aus der Standardbibliothek und Google Chart API
Die Programmiersprache MQL5 ist in erster Linie auf die Schaffung automatisierter Handelssysteme und komplexer Hilfsmittel für technische Aktienanalysen angelegt. Darüber hinaus ermöglicht es uns jedoch auch die Entwicklung interessanter Informationssysteme zur Verfolgung von Marktlagen und verschafft uns eine Rückkopplung mit dem Händler. Dieser Beitrag beschreibt die Bestandteile der Standardbibliothek von MQL5 und liefert Beispiele für ihre praktische Verwendung, um diese Ziele zu erreichen. Außerdem liefert er ein Beispiel für die Verwendung von Google Chart API zur Erstellung von Diagrammen.
Neue Möglichkeiten mit MetaTrader5 Neue Möglichkeiten mit MetaTrader5
MetaTrader 4 erfreute sich bei Händlern auf der ganzen Welt großer Beliebtheit und es sah lange so aus, als wären alle nun wunschlos glücklich. Mit seiner hohen Arbeitsgeschwindigkeit, seiner robusten Zuverlässigkeit, einem Riesenfeld an Möglichkeiten zum Schreiben von Indikatoren, Expert Advisors und informierten Handelssystemen sowie seiner Fähigkeit, aus über 100 Maklern auswählen zu können, hat sich dieses Terminal deutlich vom Rest abgesetzt. Doch die Zeit steht nicht still und deshalb stehen wir jetzt vor der Wahl: MetaTrade 4 oder MetaTrade 5? In diesem Beitrag sollen die wichtigsten Unterschiede dieses Terminals der 5. Generation aus aktuellem Blickwinkel beschrieben werden.
Geldverwaltungsfunktionen in einem Expert Advisor Geldverwaltungsfunktionen in einem Expert Advisor
Die Entwicklung von Handelsstrategien konzentriert sich in erster Linie auf die Suche nach Mustern für den Marktein- und -austritt sowie auf die Aufrechterhaltung von Positionen. Wenn wir in der Lage sind, einige Muster in Regeln für den automatisierten Handel zu gießen, steht der Händler vor der Frage der Berechnung der Menge der Positionen, der Größe der Margen sowie der Aufrechterhaltung eines soliden Bestandes an verpfändbaren Mitteln zur Sicherung offener Positionen im automatisierten Handel. In diesem Beitrag verwenden wir die Programmiersprache MQL5 zur Konstruktion einfacher Beispiele für die Durchführung dieser Berechnungen.
Erzeugung von Kursschwankungs-Indikatoren in MQL5 Erzeugung von Kursschwankungs-Indikatoren in MQL5
In diesem Beitrag geht es um die Erzeugung von zwei Indikatoren: dem Kursschwankung-Indikator, der das Chart der Kursschwankungen des Kurses zeichnet und dem Kursschwankungs-"Kerzen" Indikator, der "Kerzen" mit der angegebenen Anzahl von Kursschwankungen zeichnet. Jeder dieser Indikatoren schreibt die eingehenden Kurse in eine Datei und verwendet die gespeicherten Daten dann nach einem Neustart des Indikators (diese Daten können auch von anderen Programmen verwendet werden).