Wie man in MQL5 Indikatoren aufruft

KlimMalgin | 11 Januar, 2016

Einleitung

In MQL5 lassen sich Indikatoren auf verschiedene Arten aufrufen. Die Aufrufe werden meist mit Hilfe der IndicatorCreate() und iCustom() Funktionen ausgeführt. Darüber hinaus liefern diese Funktionen nur Indikator-Identifikatoren, via derer die weitere Arbeit an den Indikatoren ausgeführt wird. Was ist eigentlich ein Identifikator? Wie funktioniert die Arbeit mit den Funktionen IndicatorCreate() und iCustom()? Und wie erhält Ihr Expert Indikatordaten? Der folgende Beitrag beantwortet diese Fragen.

Erzeugung einer Quelldatei

Um mit unserem Expert beginnen zu können, erzeugen wir also zunächst seine Quelldatei. Dies geschieht größtenteils so wie in MetaEditor4 durch Aufruf des MQL5 Assistenten unter Datei -> Neues Menü. Im ersten Schritt bitte Expert Advisor auswählen und Weiter anklicken.


Im zweiten Schritt müssen wir einen Namen, die Kontaktangaben des Verfassers und die Eingabe-Parameter für den Expert Advisor eingeben. Hier gibt man nur den Namen des Advisors ein (Pflichtfeld) und den Autor ein. Die Eingabe-Parameter kann man später ggf. direkt in den Programmcode eintragen.


Nachdem man Fertigstellen angeklickt hat, erzeugt der Assistent den folgenden Quellcode:

//+------------------------------------------------------------------+
//|                                                   SampleMQL5.mq5 |
//|                                             Copyright KlimMalgin |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "KlimMalgin"
#property link      ""
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Struktur der Quelldatei

Werfen wir kurz einen Blick auf die Quelldatei-Struktur eines Expert.

Der Programmcode beginnt mit einer Kopfzeile. Sie ist ein Kommentar samt Dateiname und Verfasser. Nach dem Titel kommen die Programmeigenschaften (#property) des Experten. Hier wird automatisch die Programmversion deklariert, sowie der Name des Verfasser und der Link zu Ihrer Seite, wenn Sie einen eingegeben haben. Nach der Beschreibung der Eigenschaften werden die Eingabe-Parameter spezifiziert (das haben wir bis jetzt noch nicht gemacht), und dann die globalen Werte deklariert.

Ich möchte hier darauf hinweisen, dass die Eigenschaften in der primären mql5-Datei festgelegt werden sollten. Denn alle Eigenschaften aus inklusiven Dateien werden ignoriert!

Als nächstes kommen drei Funktionen, die Ereignisse bearbeiten:

Indikatoren verknüpfen

Die Arbeitsmethoden mit Indikatoren haben sich in MQL5 geändert! Um in MQL4 den Wert eines Indikators an einem bestimmten Bar zu erhalten, übergab der Index diesen Bar als Parameter an die iCustom() Funktion. Jetzt erhalten wir nicht mehr den Wert des Indikator-Buffers, sondern stattdessen seinen Identifikator.  Und mit Hilfe dieses Identifikators, erhalten wir die Indikatorwerte in einem gegebenen Intervall mit Hilfe einer speziellen Funktion, dem sog. CopyBuffer(). Was ist ein Identifikator?

Identifikator ist eine eindeutige Kennung, die zur Verwendung bei einem gewissen Indikator erstellt wurde. Sie wird als ganze Zahl dargestellt und im int-Typ einer Variable gespeichert. In MQL5 kann man den Indikator-Identifikator auf zwei Arten erhalten:\

Beide Methoden sind gleichermaßen sinnvoll, daher liegt es an Ihnen, mit welcher Sie arbeiten wollen. Betrachten wir sie uns jetzt etwas genauer.

Einen Indikator-Identifikator mit Hilfe der Funktion IndicatorCreate() erhalten

Als Ergebnis der erfolgreichen Ausführung der IndicatorCreate() Funktion erhalten wir den Indikator-Identifikator. Im Falle eines Scheiterns liefert die Funktion INVALID_HANDLE, mit einem Wert von -1. Sollte der aufgerufene Indikator Parameter haben, muss man vor dem Aufruf der Funktion ein Array von MqlParam Typ-Parametern füllen. Jedes Element dieses Arrays wird als eine Struktur des Typs MqlParam mit 4 Feldern dargestellt. Das erste Feld gibt an, welches der restlichen drei Felder als Parameter für den Indikator verwendet wird.

Schreiben wir also den Code, der Identifikatoren für die Gleitenden Mittelwert- und ParabolicSAR-Indikatoren erzeugt. Wir deklarieren zunächst die globalen Variablen, die die Identifikator speichern, und geben dann ein Array des Typs MqlParam an, um die Parameter für die Indikatoren festzulegen:

int MA1 = 0,            // Declaring variable to store fast MA handle
    MA2 = 0,            // Declaring variable to store slow MA handle
    SAR = 0;            // Declaring variable to store SAR handle
    
MqlParam params[];      // Array for storing indicators parameters

Dieser Code muss unmittelbar nach der Festlegung der Expert-Eigenschaften platziert werden.

Wenn also die Variablen deklariert sind, kann das Parameter-Array befüllt und die IndicatorCreate() Funktion aufgerufen werden. Dies macht man besser in der OnInit() Funktion, da der Indikator nur einmal am Anfang des Programms erzeugt wird, und nicht bei jeder Kursschwankung!

Befüllen wir also das Parameter-Array und rufen den ersten Indikator auf:

int OnInit()
{
//---
   // Setting the params size equal to number of called indicator parameters
   ArrayResize(params,4);

   // Setting the period of fast MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=5;
   // Offset
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Calculation method: simple averaging
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Price type for calculation: the Close prices
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA1 = IndicatorCreate(Symbol(), // Symbol used to calculate the indicator
                         0,        // Timeframe. 0 means current
                         IND_MA,   // Indicator type from the ENUM_INDICATOR enumeration
                         4,        // Number of parameters passed in the params array
                         params    // Parameters array
                         );   
//---
return(0);
}

Da das params Array ja bereits als ein dynamisches Array deklariert wurde, d.h. die Größe des Arrays ist nicht in Klammern gesetzt, müssen wir vor seiner Verwendung logischerweise zunächst seine Größe festlegen. Mit Hilfe der Funktion ArrayResize() legen wir die Größe fest, die der Anzahl der Parameter gleicht, die wir in das Array übertragen wollen. Für den gleitenden Mittelwert sind vier Parameter notwendig.

Der erste ist der Zeitraum des gleitenden Mittelwerts (MA). Er wird als ganze Zahl festgelegt. Daher legen wir im Feld type des ersten Parameters den TYPE_INT Wert fest, damit so unsere IndicatorCreate() Funktion "versteht", dass sie aus dem integer_value Feld einen Wert holen soll. Da alle Parameter für den gleitenden Mittelwert ein int Typ sind, werden sie entsprechend eingerichtet.

Ist das Array befüllt, kann die IndicatorCreate() Funktion aufgerufen werden. Ihr Output wird in der MA1 Variable gespeichert. Beim Aufrufen der Funktion sollte es nun zu keinen Schwierigkeiten kommen. Außerdem müssen nur fünf Parameter an sie übertragen werden:

Das ist alles! Wir haben jetzt den schnellen MA-Identifikator und müssen nur noch die Identifikatoren für die langsamen MA- und SAR-Indikatoren erhalten. Als Ergebnis sieht die OnInit() Funktion so aus:

int OnInit()
  {
//---

   // Setting the params size equal to number of called indicator's parameters
   ArrayResize(params,4);

//*    Calling indicators   *
//***************************
   // Setting the period of fast MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=5;
   // Offset
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Calculation method: simple averaging
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Price type for calculation: the Close prices
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA1 = IndicatorCreate(Symbol(), 0, IND_MA, 4, params);
   
   // Setting the period of slow MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=21;
   // Offset   
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Calculation method: simple averaging
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Price type for calculation: the Close prices
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA2 = IndicatorCreate(Symbol(), 0, IND_MA, 4, params);
   
   
   // Changing array size to store the SAR indicator parameters
   ArrayResize(params,2);
   // Step
   params[0].type         =TYPE_DOUBLE;
   params[0].double_value = 0.02;
   // Maximum
   params[1].type         =TYPE_DOUBLE;
   params[1].double_value = 0.2;
   
   SAR = IndicatorCreate(Symbol(), 0, IND_SAR, 2, params);
   
//---
   return(0);
  }

Um den langsamen MA-Identifikator zu erhalten, kann man nur den Zeitraum von 5 auf 21 verändern. Und wenn der ParabolicSAR aufgerufen wird, werden die Gleitkommazahlen (doppelt) als seine Parameter übertragen. Dies muss beim Befüllen des Arrays berücksichtigt werden. Daher muss TYPE_DOUBLE in das Typen-Feld bzw. der Parameter an sich in das double_value Feld eingegeben werden. Das SAR verlangt auch nur zwei Parameter, daher verändert sich die Größe des Arrays mit Hilfe von ArrayResize() zu 2.

Und hier endet jetzt erst einmal die IndicatorCreate() Funktion. Meiner Meinung nach ist diese Methode des Aufrufs von Indikatoren in einem objektorientierten Ansatz am bequemsten, um mit Indikatoren zu arbeiten, wenn die Bibliothek von Klassen erzeugt wurde. Mit dieser Bibliothek lassen sich umfangreiche Projekte weitaus leichter bearbeiten. Was das Schreiben und Testen von schnellen Experten betrifft, geht das am besten mit der folgenden Methode.

Identifikator mit Hilfe der Funktion technische Indikatoren erhalten

Die zweite Methode, mit der man Indikator-Identifikatoren erhält, ist der Aufruf einer der Funktionen technische Indikatoren. Jede dieser Funktionen soll den Identifikator eines der Standard-Indikatoren, die zusammen mit MetaTrader 5 kommen, erhalten. Sie alle haben ein gewisses Parameter-Set, je nach Indikator, für den sie gedacht sind. So wird z.B. für den CCI Indikator die iCCI() Funktion aufgerufen, was dann so aussieht:

   CCI = iCCI(
              Symbol(),            // symbol name
              PERIOD_CURRENT,      // period
              20,                  // averaging period
              PRICE_CLOSE          // price type or handle
              );

Ein anderer Indikator-Identifikator kann dann als letzter Parameter in der Funktion technische Indikatoren anstelle des Kurs-Typs aufgerufen werden. Der auf diese Weise aufgerufene Indikator, wird dann nicht auf Basis der Kursdaten, sondern mit Hilfe von Daten eines anderen Indikators berechnet.

Die iCustom() Funktion ist unter den anderen Funktionen technische Indikatoren von besonderem Interesse, da sie alle Indikatoren, inkl. benutzerdefinierte, aufrufen kann. Schreiben wir also jetzt einen Code, der dem o.g. ähnelt, doch diesmal mit Hilfe der iCustom() Funktion:

int OnInit()
  {
//---

   MA1 = iCustom(NULL,0,"Custom Moving Average",
                          5,          // Period
                          0,          // Offset
                          MODE_SMA,   // Calculation method
                          PRICE_CLOSE // Calculating on Close prices
                 );

   MA2 = iCustom(NULL,0,"Custom Moving Average",
                          21,         // Period
                          0,          // Offset
                          MODE_SMA,   // Calculation method
                          PRICE_CLOSE // Calculating on Close prices
                 );

   SAR = iCustom(NULL,0,"ParabolicSAR",
                          0.02,        // Step
                          0.2          // Maximum
                 );

//---
   return(0);
  }

Bedenken Sie hierbei bitte, dass es am besten ist, den Identifikator einmal während des Expert-Starts zu erhalten, also machen wir dazu einen Aufruf in der OnInit() Funktion.

Die iCustom() Funktion enthält die folgenden Parameter:

  1. Name des Arbeitstools, mit dessen Daten der Indikator berechnet wird. NULL - d.h. das aktuelle Tool. Sie können hier auch Symbol () anstelle von NULL verwenden. Das bleibt sich gleich. Bei Symbol() wird der Name eines Instruments in iCustom() übertragen. Bei NULL findet iCustom() das Finanzinstrument von alleine.
  2. Chartzeitraum. Zur Festlegung des Zeitraums kann man die vorab festgelegten Chart-Zeitrahmen verwenden oder auch 0 - was den aktuellen Zeitraum bezeichnet.
  3. Indikatorname. Der aufgerufene Indikator sollte erstellt und im Ordner "MQL5\Indikatoren\" oder einem seiner Unterordner liegen.
  4. Übrige Parameter. Diese Werte werden als Eingabe-Parameter für den aufgerufenen Indikator verwendet. Daher müssen ihr Typ und ihre Abfolge miteinander übereinstimmen. Werden sie nicht extra angegeben, werden die Standardwerte verwendet.

Wie bereits erwähnt, ist dies die weitaus bequemere Methode für die Arbeit mit Indikatoren, wenn Sie keine Bibliothek haben, mit der sie bereits gearbeitet haben. Wenn wir jetzt die Identifikatoren haben, kann die Arbeit mit den Indikatorwerten losgehen!

Indikator-Daten erhalten

Das Coole bei der Arbeit mit Indikatordaten in MQL5 ist, dass wir jetzt die Möglichkeit haben, Daten nur in den Intervallen zu erhalten, die wir brauchen. Die Intervalle lassen sich auf verschiedene Art und Weise einstellen:

Das Indikatordatum wird mit Hilfe der CopyBuffer() Funktion gelesen. Welche Methode man zum Erhalt von Daten verwendet, hängt davon ab, welche Eingabe-Parameter auf diese Funktion übertragen werden.

Schreiben wir also jetzt den Code, der die Indikatordaten in die Arrays kopiert (ihre Identifikatoren haben wir vorher bereits bekommen):

void OnTick()
  {
//---

// Dynamic arrays to store indicators values
double _ma1[],
       _ma2[],
       _sar[];

// Setting the indexing in arrays the same as in timeseries, i.e. array element with zero
// index will store the values of the last bar, with 1th index - the last but one, etc.
   ArraySetAsSeries(_ma1, true);
   ArraySetAsSeries(_ma2, true);
   ArraySetAsSeries(_sar, true);

// Using indicators handles, let's copy the values of indicator
// buffers to arrays, specially prepared for this purpose
   if (CopyBuffer(MA1,0,0,20,_ma1) < 0){Print("CopyBufferMA1 error =",GetLastError());}
   if (CopyBuffer(MA2,0,0,20,_ma2) < 0){Print("CopyBufferMA2 error =",GetLastError());}
   if (CopyBuffer(SAR,0,0,20,_sar) < 0){Print("CopyBufferSAR error =",GetLastError());}


//---
  }

Ihre gesamte Arbeit konzentriert sich jetzt auf die Funktion OnTick() (bei Kursschwankung), da sich bei jeder Kursschwankung die Indikatordaten ändern und daher aktualisiert werden müssen.

Deklarieren wir also zunächst dynamische Arrays für jeden Indikator-Buffer. Meiner Meinung nach ist es wesentlich bequemer mit Indikatordaten zu arbeiten, wenn die Array-Indizierung die gleiche wie bei Zeitreihen ist. Im Array-Element 0 finden sich die Daten des letzten Bar und im Element 1 die des vorletzten Bars usw. Die Indizierungsmethode wird mit Hilfe der ArraySetAsSeries() Funktion eingerichtet.

Ist die Indizierungsmethode eingerichtet, können wir die Arrays zu füllen beginnen. Zur Berechnung jedes Indikators rufen wir die CopyBuffer() Funktion mit dem folgenden Parameter-Set auf:

  1. Indikator-Identifikator, welche Daten erhalten werden sollen.
  2. Indikator-Buffer Nummer. Da die MA- und SAR-Indikatoren einen Buffer haben, stellen wir sie als 0 ein. Bei mehr als einem Buffer ist der zweite Buffer Nummer 1, der dritte Nummer 2 usw.
  3. Ursprungs-Bar, bei dem die Zählung beginnt.
  4. Anzahl der Bars, die kopiert werden sollen.
  5. Das Array{/0} in das die Daten kopiert werden sollen.

Bei erfolgreichen Aufruf liefert die CopyBuffer() Funktion die Zahl der kopierten Elemente. Bei Scheitern liefert sie -1. Im Falle eines Scheiterns muss der Fehler verfolgt werden: ist also der gelieferte Wert geringer als 0, schreiben wir den Fehler mit Hilfe der Print() Funktion in das Expert-Logbuch.

Fazit

Damit ist jedoch die Arbeit mit Indikatoren noch lange nicht beendet. Dieser Beitrag hat sich nur mit den grundlegenden Methoden für den Zugriff auf ihre Daten beschäftigt. Aus Bequemlichkeitsgründen empfiehlt es sich, einen objektorientierten Ansatz zu verfolgen, der in MQL5 ziemlich ordentlich implementiert ist!