MetaTrader 5 herunterladen

20 Handelssignale in MQL5

12 Januar 2016, 15:02
Sergey Gritsay
0
408

Einleitung

Händler suchen Gesetzmäßigkeiten im Verhalten der Kurse, sie versuchen, Regeln zu entwickeln, anhand derer es möglich ist, mit hoher Wahrscheinlichkeit einen günstigen Zeitpunkt zum Kaufen oder Verkaufen zu bestimmen. Um ein vollautomatisches System zu schaffen, muss man wissen, wie man von dem Eintreten dieser Zeitpunkte erfährt, wie man Handelssignale erhält und deutet.

Die Signale informieren die Händler über mögliche Punkte zum Einstieg in eine Position, aber nicht alle Signale müssen unbedingt ausgeführt werden. Zusätzliche Kriterien können sogar den Großteil der Signale herausfiltern, aber das ist für uns nicht von Belang. Gegenstand dieses Beitrages ist, wie man die beliebtesten Handelssignale in MQL5 programmiert.


1. Welche Signale kennen wir?

Die Methoden zur Bestimmung des Zeitpunktes für den Eintritt in einen Markt können in verschiedene Arten unterteilt werden:

  • die Überschneidung der gleitenden Durchschnittswerte
  • das Durchbrechen einer Bereichsgrenze
  • das Verlassen des Bereich des Überkaufs/Überverkaufs aufgrund der Anzeigen des stochastischen Oszillators
  • das Zurückprallen von Grenzen eines Kanals
  • das Durchbrechen der Grenzen eines Kanals
  • Trendänderungen


2. Wie kann man das besser programmieren?

Es gibt zahlreiche Möglichkeiten, einen Programmcode zu schreiben, er kann unter anderem komplett in den Funktionen OnStart() und OnTick() geschrieben werden. Ausführlichere Informationen zu ihnen erhalten Sie in den Dokumenten auf dieser Webseite oder in dem in den MetaEditor integrierten Benutzerhandbuch; aber dieses Vorgehen ist nicht effektiv, und bisweilen müssen einzelne Teile des Codes unverändert mehrmals geschrieben werden. Deshalb wählen wir einen anderen Weg: wir verwenden benutzerdefinierte Funktionen, die wir von jedem Teil unseres Programmcodes aus aufrufen können.

Zur einfachen Anwendung der erstellten Funktionen fassen wir sie in einem gesonderten Block als externe Include-Datei unter der Bezeichnung SignalTrade zusammen; sie wird im Verzeichnis ...\MQL5\Include gespeichert. Das ermöglicht die problemlose Verknüpfung dieses Moduls mit jedem Programm. 

Obwohl alle Signale unterschiedlich aussehen, haben sie viel gemeinsam. Es gibt drei Arten von Signalen, die von einer Funktion erzeugt werden können:

  • Kaufsignale
  • Verkaufssignale
  • keine Signale

Erstellen wir also eine diesen Signalen entsprechende Nummerierung:

  •  1 - Kaufsignal
  • -1 - Verkaufssignal
  •  0 - kein Signal

Wir programmieren jetzt den Prototyp einer Funktion, die ein Signal ausgibt. Dabei unterteilen wir unsere Funktion in kleine Blöcke, in denen jeweils eine bestimmte Operation ausgeführt wird. Die zur Speicherung der Daten erforderlichen Variablen werden am Anfang der Funktion deklariert und initialisiert. Anschließend erfolgt das Laden und die Prüfung der erforderlichen Daten aus dem erstellten Indikator. Die Prüfung der Daten ist notwendig, um dem Auftreten unvorhergesehener Folgen der Arbeit mit den Daten sowie mit dem Programm im Ganzen während des Programmlaufs vorzubeugen. 

Nachdem die Daten geladen und geprüft wurden, beginnen wir mit dem Anlegen des Signals. Sobald das Signal angelegt ist, verlassen wir die Funktion und geben das erhaltene Signal in der Form eines der oben aufgeführten Werte an sie zurück.

int TradeSignal()
  {
   //--- zero means absence of signal
   int sig=0;
   
   //--- check the handles of indicators

   //--- if all the handles are invalid, create them

   //--- if the handles are valid, copy the values from indicators

   //--- check the copied data

   //--- in case of an error of copying, exit from the function

   //--- perform the indexation of the array as a timeseries

   //--- in case of an indexation error, exit from the function

   //--- check conditions and set the value for sig
 
   //--- return the trade signal
   return(sig);
  }


3. Beispiele für 20 Handelssignale

Zum Empfang der Handelssignale werden wir unterschiedliche Indikatoren verwenden. In MQL5 werden die Indikatoren mithilfe einer Reihe besonderer Funktionen der Art iMA, iAC, iMACD, iIchimoku usw. aufgerufen, die jeweils eine Kopie des entsprechenden technischen Indikators im allgemeinen Zwischenspeicher des Ausgabegerätes anlegen. Wenn bereits eine Kopie dieses Indikators mit denselben Parametern vorhanden ist, wird keine neue Kopie erstellt, stattdessen wird der Zähler der Verknüpfungen der vorhandenen Kopie um eine Stelle heraufgesetzt.

Diese Funktionen geben den Handle der entsprechenden Kopie des Indikators aus. Weiterhin können wir unter Verwendung dieses Handles die von dem entsprechenden Indikator berechneten Daten abrufen. Die Daten des entsprechenden Puffers (technische Indikatoren bewahren die berechneten Daten in ihren internen Zwischenspeichern auf, deren Anzahl je nach Art des Indikators zwischen 1 und 5 liegen kann) können mithilfe der Funktion CopyBuffer() in ein MQL5-Programm kopiert werden.

Auf die Daten eines Indikators kann nicht unmittelbar nach seiner Erstellung zugegriffen werden, da die Berechnung der Indikatorwerte einige Zeit in Anspruch nimmt, weshalb es am besten ist, die Handle der Indikatoren in OnInit() anzulegen. Die Funktion iCustom() erstellt den entsprechenden benutzerdefinierten Indikator und gibt bei erfolgreicher Erstellung dessen Handle aus. Benutzerdefinierte Indikatoren können bis zu 512 Indikatorpuffer enthalten, deren Inhalte auch mithilfe der Funktion CopyBuffer() sowie des bezogenen Handles abgerufen werden können.

Wir legen jetzt zu jedem Signal eine unserem Prototypen TradeSignal() entsprechende Funktion an und nummerieren diese wie folgt: TradeSignal_01() - TradeSignal_20(). Anhand des Beispiels des Signals für die Überschneidung der gleitenden Durchschnittswerte werden wir den Aufbau der Funktion zum Anlegen eines Signals ausführlicher erörtern, um anschließend die Funktionen für die anderen Signalarten auf vergleichbare Weise zu programmieren.

3.1. Überschneidung der gleitenden Durchschnittswerte

Abbildung 1. Die Überschneidung von zwei gleitenden Durchschnittswerten

Abbildung 1. Die Überschneidung von zwei gleitenden Durchschnittswerten

Mithilfe der Funktion TradeSignal_01() erhalten wir ein Signal bezüglich der Überschneidung von zwei gleitenden Durchschnittswerten (im Weiteren kurz МА): einen schnellen mit einer Erfassungsperiode von 8 und einen langsameren mit 16.

In unserer Funktion stellen wir lediglich den Umstand der Überschneidung fest und geben den Wert des entsprechenden Signals an die Funktion weiter. Entsprechend unserer Vorgaben und unseres Prototyps wird die Funktion folgendermaßen aussehen:

int TradeSignal_01()
  {
//--- zero means that there is no signal
   int sig=0;

//--- check the handles of indicators
   if(h_ma1==INVALID_HANDLE)//--- if the handle is invalid
     {
      //--- create is again                                                      
      h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
      //--- exit from the function
      return(0);
     }
   else //--- if the handle is valid
     {
      //--- copy value of the indicator to the array
      if(CopyBuffer(h_ma1,0,0,3,ma1_buffer)<3) //--- if the array of data is less than required
         //--- exit from the function
         return(0);
      //--- set the indexation in the array as in a timeseries                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- in case of an indexation error, exit from the function
         return(0);
     }

   if(h_ma2==INVALID_HANDLE)//--- if the handle is invalid
     {
      //--- create it again                                                      
      h_ma2=iMA(Symbol(),Period(),16,0,MODE_SMA,PRICE_CLOSE);
      //--- exit from the function
      return(0);
     }
   else //--- if the handle is valid 
     {
      //--- copy values of the indicator to the array
      if(CopyBuffer(h_ma2,0,0,2,ma2_buffer)<2) //--- if there is less data than required
         //--- exit from the function
         return(0);
      //--- set the indexation in the array as in a timeseries                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- in case of an indexation error, exit from the function
         return(0);
     }

//--- check the condition and set a value for the sig
   if(ma1_buffer[2]<ma2_buffer[1] && ma1_buffer[1]>ma2_buffer[1])
      sig=1;
   else if(ma1_buffer[2]>ma2_buffer[1] && ma1_buffer[1]<ma2_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

Erörtern wir jetzt jeden einzelnen Teil des Programmcodes. Am Anfang der Funktion deklarieren wir eine lokale Variable, in der die Art des Signals enthalten ist, und initialisieren sie mit dem Wert Null, was dem Nichtvorhandensein eines Signals entspricht.

int sig=0;

Des Weiteren prüfen wir die Gültigkeit des Handles; ist der Handle ungültig, erstellen wir ihn neu und verlassen die Funktion, da die Berechnung des Indikators einige Zeit in Anspruch nimmt. Das geschieht, um Fehler beim Kopieren der Daten aus dem Indikatorpuffer zu vermeiden.

   if(h_ma1==INVALID_HANDLE)//--- if the handle is invalid
     {
      //--- create it again                                                      
      h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
      //--- exit from the function
      return(0);
     }

Wenn der Handle gültig ist, kopieren wir die Daten in das Array. Für die Analyse der Lage reicht es aus, die Daten aus den letzten drei Balken zu dem schnellen MA sowie die letzten beiden zu dem langsameren zu kopieren. Dazu verwenden wir folgende Funktion:

int  CopyBuffer(
   int       indicator_handle,     // handle of an indicator
   int       buffer_num,           // number of buffer of an indicator
   int       start_pos,            // where to start from 
   int       count,                // amount to be copied
   double    buffer[]              // an array to copy data to
   );

Wir prüfen nach: wenn dort weniger Daten vorliegen, als wir benötigen, bedeutet das, dass beim Kopieren ein Fehler aufgetreten ist, weswegen weitere Zugriffe auf das Array, in dem die Daten gespeichert sein sollten, zu einem Fehler führen. Um dem vorzubeugen, verlassen wir die Funktion. Zudem müssen wir die Kennziffern in dem Array als Zeitleiste anlegen, dazu gibt es folgende Funktion:

bool  ArraySetAsSeries(
   void  array[],     // array by a link
   bool  set          // true means that the indexation order is reversed
   );

Wenn bei der Vergabe der Kennziffern in dem Array ein Fehler aufgetreten ist, müssen wir die Funktion ebenfalls verlassen, um keine unzutreffenden Ergebnisse zu erhalten.

else //--- if the handle is valid 
     {
      //--- copy values of the indicator to the array
      if(CopyBuffer(h_ma1,0,0,3,ma1_buffer)<3) //--- if there is less data than required
         //--- exit from the function
         return(0);
      //--- set the indexation in the array like in a timeseries                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- in case of an indexation error, exit from the function
         return(0);
     }

Jetzt, da die Indikatoren fertig sind und alle erforderlichen Daten eingeholt worden sind, kommen wir zum Wesentlichen: der Erstellung des Signals.

Zur Ausblendung der unsteten Schwankungen des Signals in dem aktuellen Balken untersuchen wir nur den ersten und den zweiten geschlossenen Balken.

Wir erstellen ein Kaufsignal. Dazu nehmen wir den Wert des schnellen MA im zweiten Balken und vergleichen ihn mit dem Wert des langsameren MA im ersten Balken, weiterhin vergleichen wir den Wert des schnellen MA im ersten Balken mit dem des langsameren MA im ersten Balken. Wenn der Wert des schnellen MA im zweiten Balken niedriger ist als der des langsameren im ersten, und der des schnellen im ersten höher als der des langsameren MA im ersten Balken, bedeutet das, dass eine Überschneidung des langsameren MA durch den schnellen von unten nach oben erfolgt ist, was für uns das Signal zum Kauf darstellt. Ist unsere Bedingung erfüllt (“true“), so schreiben wir der Variablen sig den Wert 1 zu.

Das Verkaufssignal wird genauso angelegt. Wenn der Wert des schnellen MA im zweiten Balken größer ist als der des langsameren im ersten, und der Wert des schnellen im ersten niedriger als der des langsameren MA im ersten Balken, bedeutet das, dass der langsamere MA von dem schnellen von oben nach unten geschnitten wird. Ist unsere Bedingung erfüllt (“true“), so schreiben wir der Variablen sig den Wert -1 zu. Wenn beide Bedingungen nicht erfüllt (“false“) sind, entspricht das dem Nichtvorliegen eines Signals, also erhält die Variable sig den Wert 0. Jetzt sind die Signale angelegt, und wir übermitteln die erhaltene Signalart an unsere Funktion TradeSignal_01().

//--- check the condition and set a value for the sig
   if(ma1_buffer[2]<ma2_buffer[1] && ma1_buffer[1]>ma2_buffer[1])
      sig=1;
   else if(ma1_buffer[2]>ma2_buffer[1] && ma1_buffer[1]<ma2_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);

3.2. Überschneidung der Haupt- und der Signallinie von MACD

Abbildung 2. Die Überschneidung der Haupt- und der Signallinie des Indikators MACD

In der Funktion TradeSignal_02() erhalten wir das Signal bezüglich der Überschneidung der Signal- und der Hauptlinie des Indikators MACD. Schneidet die Signallinie die Hauptlinie von oben nach unten, so gilt das als Kaufsignal. Wenn die Signallinie die Hauptlinie von unten nach oben schneidet, gilt das entsprechend als Signal für einen Verkauf. In allen sonstigen Fällen wird das Nichtvorhandensein eines Signals konstatiert.

int TradeSignal_02()
  {
   int sig=0;

   if(h_macd==INVALID_HANDLE)
     {
      h_macd=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_macd,0,0,2,macd1_buffer)<2)
         return(0);
      if(CopyBuffer(h_macd,1,0,3,macd2_buffer)<3)
         return(0);
      if(!ArraySetAsSeries(macd1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(macd2_buffer,true))
         return(0);
     }

//--- check the condition and set a value for sig
   if(macd2_buffer[2]>macd1_buffer[1] && macd2_buffer[1]<macd1_buffer[1])
      sig=1;
   else if(macd2_buffer[2]<macd1_buffer[1] && macd2_buffer[1]>macd1_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.3. Durchbrechen des Bereichs des Preiskanals

Abbildung 3. Durchbrechen der Unter- und der Obergrenze des Preiskanals

In der Funktion TradeSignal_03() erhalten wir das Signal bezüglich des Durchbrechens der Ober- oder der Untergrenze des Preiskanals.

Wenn der Preis die obere Grenze des Preiskanals (Price Channel) durchbricht, und der Schlusskurs oberhalb dieser Grenze festgelegt wird, ist das das Signal zu kaufen, durchbricht der Preis dagegen die untere Grenze, und der Schlusskurs wird unterhalb dieser Grenze festgelegt, ist das das Verkaufssignal. In allen sonstigen Fällen gilt das Signal als nicht vorhanden.

Im Gegensatz zu den beiden vorgenannten Funktionen benötigen wir hier ein Datenfeld zur Speicherung der Schlusskurse. Um sie abzurufen, verwenden wir folgende Funktion:

int  CopyClose(
   string           symbol_name,       // symbol name
   ENUM_TIMEFRAMES  timeframe,          // period
   int              start_pos,         // where to start from 
   int              count,             // amount to be copied
   double           close_array[]      // array for copying close prices to
   );
int TradeSignal_03()
  {
   int sig=0;

   if(h_pc==INVALID_HANDLE)
     {
      h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_pc,0,0,3,pc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_pc,1,0,3,pc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(pc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(pc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the conditions and set a value for sig
   if(Close[1]>pc1_buffer[2])
      sig=1;
   else if(Close[1]<pc2_buffer[2])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.4. Durchbrechen des Bereichs des anpassungsfähigen ADX-Kanals

Abbildung 4. Das Durchbrechen der unter- und der Obergrenze des anpassungsfähigen ADX-Kanals

In der Funktion TradeSignal_04() erhalten wir das Signal bezüglich des Durchbrechens der Ober- oder der Untergrenze des anpassungsfähigen ADX-Kanals.

Wenn der Preis die obere Grenze des anpassungsfähigen ADX-Kanals durchbricht, und der Schlusskurs oberhalb dieser Grenze festgelegt wird, ist das das Signal zu kaufen, durchbricht der Preis dagegen die untere Grenze, und der Schlusskurs wird unterhalb dieser Grenze festgelegt, ist das das Verkaufssignal. In allen sonstigen Fällen gilt das Signal als nicht vorhanden.

int TradeSignal_04()
  {
   int sig=0;

   if(h_acadx==INVALID_HANDLE)
     {
      h_acadx=iCustom(Symbol(),Period(),"AdaptiveChannelADX",14);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_acadx,0,0,2,acadx1_buffer)<2)
         return(0);
      if(CopyBuffer(h_acadx,1,0,2,acadx2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(acadx1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(acadx2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for the sig
   if(Close[1]>acadx1_buffer[1])
      sig=1;
   else if(Close[1]<acadx2_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.5. Verlassen der Überkauf-/Überverkaufsbereiche durch den stochastischen Oszillator

Abbildung 5. Überschreiten der Überkauf-/Überverkaufsgrenzen durch den stochastischen Oszillator

Anhand der Funktion TradeSignal_05() erhalten wir das Signal im Fall des Verlassens der Bereiche des Überkaufs/Überverkaufs durch den stochastischen Oszillator, als Begrenzung dieser Bereiche dienen die Ebenen mit den Werten 80 bzw. 20.

Wir kaufen, wenn der Oszillator (%K oder %D) zunächst unter eine bestimmte Ebene fällt (üblicherweise 20) und dann über sie hinweg steigt. Wir verkaufen, wenn der Oszillator zunächst über eine bestimmte Ebene steigt (üblicherweise 80) und anschließend unter sie fällt.

int TradeSignal_05()
  {
   int sig=0;

   if(h_stoh==INVALID_HANDLE)
     {
      h_stoh=iStochastic(Symbol(),Period(),5,3,3,MODE_SMA,STO_LOWHIGH);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_stoh,0,0,3,stoh_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(stoh_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(stoh_buffer[2]<20 && stoh_buffer[1]>20)
      sig=1;
   else if(stoh_buffer[2]>80 && stoh_buffer[1]<80)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.6. Verlassen der Überkauf-/Überverkaufsbereiche durch den Indikator RSI

Abbildung 6. Überschreiten der Überkauf-/Überverkaufsgrenzen durch den Indikator RSI

Durch die Funktion TradeSignal_06() erhalten wir das Signal für das Verlassen der Bereiche des Überkaufs/Überverkaufs durch den Indikator RSI, als Begrenzung dieser Bereiche dienen die Ebenen mit den Werten 70 bzw. 30.

Wir kaufen, wenn der Indikator RSI zunächst unter eine bestimmte Ebene fällt (üblicherweise 30) und sie anschließend übersteigt. Wir verkaufen, wenn der Indikator RSI zunächst über eine bestimmte Ebene steigt (üblicherweise 70) und anschließend unter sie fällt.

int TradeSignal_06()
  {
   int sig=0;

   if(h_rsi==INVALID_HANDLE)
     {
      h_rsi=iRSI(Symbol(),Period(),14,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_rsi,0,0,3,rsi_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(rsi_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(rsi_buffer[2]<30 && rsi_buffer[1]>30)
      sig=1;
   else if(rsi_buffer[2]>70 && rsi_buffer[1]<70)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
 
3.7. Verlassen der Überkauf-/Überverkaufsbereiche durch den Indikator CCI

Abbildung 7. Überschreiten der Überkauf-/Überverkaufsgrenzen durch den Indikator CCI

Durch Verwendung der Funktion TradeSignal_07() erhalten wir das Signal für das Verlassen der Bereiche des Überkaufs/Überverkaufs durch den Indikator CCI, als Begrenzung dieser Bereiche dienen die Ebenen mit den Werten 100 bzw. -100.

Wir kaufen, wenn der Indikator CCI zunächst unter die Ebene -100 fällt und sie anschließend übersteigt. Wir verkaufen, wenn der Indikator CCI zunächst über die Ebene 100 steigt und anschließend unter sie fällt.

int TradeSignal_07()
  {
   int sig=0;

   if(h_cci==INVALID_HANDLE)
     {
      h_cci=iCCI(Symbol(),Period(),14,PRICE_TYPICAL);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_cci,0,0,3,cci_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(cci_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(cci_buffer[2]<-100 && cci_buffer[1]>-100)
      sig=1;
   else if(cci_buffer[2]>100 && cci_buffer[1]<100)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.8. Verlassen der Überkauf-/Überverkaufsbereiche durch den Indikator Williams‘ %

Abbildung 8. Überschreiten der Überkauf-/Überverkaufsgrenzen durch den Indikator Williams‘ %

Mithilfe der Funktion TradeSignal_08() erhalten wir das Signal für das Verlassen der Bereiche des Überkaufs/Überverkaufs durch den Indikator Williams‘ %, als Begrenzung dieser Bereiche dienen die Ebenen mit den Werten -20 bzw. -80.

Wir kaufen, wenn der Indikator Williams‘ % zunächst die Ebene -80 fällt und sie anschließend übersteigt. Wir verkaufen, wenn der Indikator Williams‘ % zunächst über die Ebene -20 steigt und anschließend unter sie fällt.

int TradeSignal_08()
  {
   int sig=0;

   if(h_wpr==INVALID_HANDLE)
     {
      h_wpr=iWPR(Symbol(),Period(),14);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_wpr,0,0,3,wpr_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(wpr_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(wpr_buffer[2]<-80 && wpr_buffer[1]>-80)
      sig=1;
   else if(wpr_buffer[2]>-20 && wpr_buffer[1]<-20)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }
3.9. Zurückprallen von den Grenzen der Bollinger-Bänder

Abbildung 9. Das Zurückprallen des Preises von den Grenzen der Bollinger-Bänder

Mit der Funktion TradeSignal_09() erhalten wir ein Signal, sobald der Preis von den Grenzen der Bollinger-Bänder zurückprallt.

Wenn der Preis das obere der Bollinger-Bänder durchbricht oder berührt und von dort zurückgeworfen wird, ist das das Signal für den Verkauf, durchbricht oder berührt der Preis dagegen das untere Bollinger-Band, so dient dies als Kaufsignal.

int TradeSignal_09()
  {
   int sig=0;

   if(h_bb==INVALID_HANDLE)
     {
      h_bb=iBands(Symbol(),Period(),20,0,2,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_bb,1,0,2,bb1_buffer)<2)
         return(0);
      if(CopyBuffer(h_bb,2,0,2,bb2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(bb1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(bb2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[2]<=bb2_buffer[1] && Close[1]>bb2_buffer[1])
      sig=1;
   else if(Close[2]>=bb1_buffer[1] && Close[1]<bb1_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.10. Zurückprallen von den Grenzen des Standardabweichungskanals

Abbildung 10. Das Zurückprallen des Preises von den Grenzen des Standardabweichungskanals

Mithilfe der Funktion TradeSignal_10() erhalten wir ein Signal, sobald der Preis von den Grenzen des Standardabweichungskanals zurückprallt.

Wenn der Preis die obere Grenze des Standardabweichungskanals durchbricht oder berührt und von dort zurückgeworfen wird, gilt das als Signal für den Verkauf, durchbricht oder berührt der Preis dagegen die untere Grenze des Standardabweichungskanals, so ist dies das Kaufsignal.

int TradeSignal_10()
  {
   int sig=0;

   if(h_sdc==INVALID_HANDLE)
     {
      h_sdc=iCustom(Symbol(),Period(),"StandardDeviationChannel",14,0,MODE_SMA,PRICE_CLOSE,2.0);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_sdc,0,0,2,sdc1_buffer)<2)
         return(0);
      if(CopyBuffer(h_sdc,1,0,2,sdc2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(sdc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(sdc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[2]<=sdc2_buffer[1] && Close[1]>sdc2_buffer[1])
      sig=1;
   else if(Close[2]>=sdc1_buffer[1] && Close[1]<sdc1_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.11. Zurückprallen von den Grenzen des Preiskanals

Abbildung 11. Das Zurückprallen des Preises von den Grenzen des Preiskanals

In der Funktion TradeSignal_11() erhalten wir ein Signal, sobald der Preis von den Grenzen des Preiskanals zurückprallt.

Durchbricht oder berührt der Preis die obere Grenze des Preiskanals und wird von dort zurückgeworfen, so gilt das als Signal für den Verkauf, wenn der Preis dagegen die untere Grenze des Preiskanals durchbricht oder berührt, so handelt es sich um das Kaufsignal.

int TradeSignal_11()
  {
   int sig=0;

   if(h_pc==INVALID_HANDLE)
     {
      h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_pc,0,0,4,pc1_buffer)<4)
         return(0);
      if(CopyBuffer(h_pc,1,0,4,pc2_buffer)<4)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(pc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(pc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[1]>pc2_buffer[2] && Close[2]<=pc2_buffer[3])
      sig=1;
   else if(Close[1]<pc1_buffer[2] && Close[2]>=pc1_buffer[3])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.12. Zurückprallen von den Grenzen des Hüllkurvenkanals (Envelopes)

Abbildung 12. Das Zurückprallen des Preises von den Grenzen des Hüllkurvenkanals

Durch Verwendung der Funktion TradeSignal_12() erhalten wir ein Signal, sobald der Preis von den Grenzen des Hüllkurvenkanals zurückprallt.

Wenn der Preis die obere Grenze des Hüllkurvenkanals durchbricht oder berührt und von dort zurückgeworfen wird, gilt das als Signal für den Verkauf, durchbricht oder berührt der Preis dagegen die untere Grenze des Hüllkurvenkanals, so dient uns das als Kaufsignal.

int TradeSignal_12()
  {
   int sig=0;

   if(h_env==INVALID_HANDLE)
     {
      h_env=iEnvelopes(Symbol(),Period(),28,0,MODE_SMA,PRICE_CLOSE,0.1);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_env,0,0,2,env1_buffer)<2)
         return(0);
      if(CopyBuffer(h_env,1,0,2,env2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(env1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(env2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[2]<=env2_buffer[1] && Close[1]>env2_buffer[1])
      sig=1;
   else if(Close[2]>=env1_buffer[1] && Close[1]<env1_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.13. Durchbrechen des Donchian-Kanals

Abbildung 13. Das Durchbrechen der Grenzen des Donchian-Kanals

Mithilfe der Funktion TradeSignal_13() erhalten wir ein Signal, sobald der Preis die Grenzen des Donchian-Kanals durchbricht.

Wenn der Preis die obere Grenze des Donchian-Kanals durchbricht, und der Schlusskurs oberhalb dieser Grenze festgelegt wird, ist das das Signal zu kaufen, durchbricht der Preis dagegen die untere Grenze, und der Schlusskurs wird unterhalb dieser Grenze festgelegt, dient das als Verkaufssignal.

int TradeSignal_13()
  {
   int sig=0;

   if(h_dc==INVALID_HANDLE)
     {
      h_dc=iCustom(Symbol(),Period(),"Donchian Channels",24,3,-2);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_dc,0,0,3,dc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_dc,1,0,3,dc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(dc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(dc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[1]>dc1_buffer[2])
      sig=1;
   else if(Close[1]<dc2_buffer[2])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.14. Durchbrechen des Silberkanals (Silver Channel)

Abbildung 14. Das Durchbrechen der Grenzen des Silberkanals

Durch Verwendung der Funktion TradeSignal_14() erhalten wir ein Signal, sobald der Preis die Grenzen des Silberkanals durchbricht.  Der Silberkanalindikator zeichnet 8 Grenzlinien, die auch als Stütz- oder Widerstandsebenen dienen können. Um das Signal abzurufen, verwenden wir die beiden mittleren Grenzen. 

Wenn der Preis die obere Grenze des Donchian-Kanals durchbricht, und der Schlusskurs oberhalb dieser Grenze festgelegt wird, ist das das Signal zu kaufen, durchbricht der Preis dagegen die untere Grenze, und der Schlusskurs wird unterhalb dieser Grenze festgelegt, so dient das als Verkaufssignal.

int TradeSignal_14()
  {
   int sig=0;

   if(h_sc==INVALID_HANDLE)
     {
      h_sc=iCustom(Symbol(),Period(),"Silver-channels",26,38.2,23.6,0,61.8);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_sc,0,0,2,sc1_buffer)<2)
         return(0);
      if(CopyBuffer(h_sc,1,0,2,sc2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(sc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(sc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[2]<sc1_buffer[1] && Close[1]>sc1_buffer[1])
      sig=1;
   else if(Close[2]>sc2_buffer[1] && Close[1]<sc2_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.15. Durchbrechen des Gallagher-Kanals

Abbildung 15. Das Durchbrechen der Grenzen des Gallagher-Kanals

Mithilfe der Funktion TradeSignal_15() erhalten wir ein Signal, sobald der Preis die Grenzen des Gallagher-Kanals durchbricht. Der Indikator des Gallagher-Kanals setzt sich aus den Höchst- und Tiefstwerten für 10 Tage zusammen. 

Wenn der Preis die obere Grenze des Gallagher-Kanals durchbricht, und der Schlusskurs oberhalb dieser Grenze festgelegt wird, ist das das Kaufsignal, durchbricht der Preis dagegen die untere Grenze des Gallagher-Kanals, und der Schlusskurs wird unterhalb dieser Grenze festgelegt, ist das das Signal für den Verkauf.

int TradeSignal_15()
  {
   int sig=0;

   if(h_gc==INVALID_HANDLE)
     {
      h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
      return(0);
     }
   else
     {
      if(CopyBuffer(h_gc,0,0,3,gc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_gc,1,0,3,gc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(gc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(gc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[1]>gc1_buffer[2])
      sig=1;
   else if(Close[1]<gc2_buffer[2])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.16. Trendveränderung gemäß dem Indikator NRTR

Abbildung 16. Die Ermittlung einer Trendveränderung mithilfe des Indikators NRTR

In der Funktion TradeSignal_16() erhalten wir das Signal bei jeder Änderung des NRTR-Trends.

Weist der Indikator NRTR eine aufsteigende Tendenz auf, so ist das das Signal für den Kauf. Zeigt der NRTR jedoch eine absteigende Tendenz, ist das das Verkaufssignal.

int TradeSignal_16()
  {
   int sig=0;

   if(h_nrtr==INVALID_HANDLE)
     {
      h_nrtr=iCustom(Symbol(),Period(),"NRTR",40,2.0);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_nrtr,0,0,2,nrtr1_buffer)<2)
         return(0);
      if(CopyBuffer(h_nrtr,1,0,2,nrtr2_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(nrtr1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(nrtr2_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(nrtr1_buffer[1]>0)
      sig=1;
   else if(nrtr2_buffer[1]>0)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.17. Trendveränderung gemäß dem Alligator-Indikator

Abbildung 17. Die Veränderung des Trends gemäß dem Alligator-Indikator

Mithilfe der Funktion TradeSignal_17() erhalten wir das Signal bei jeder Änderung des Trends gemäß dem Alligator-Indikator.

Sind Kiefer, Zähne und Lippen geschlossen oder ineinander verschränkt, schickt sich der Alligator an zu schlafen oder er schläft bereits. Während er schläft, nimmt sein Hunger zu, je länger er schläft, desto hungriger ist er, wenn er aufwacht. Beim Aufwachen ist das Erste, was er tut, sein Maul zu öffnen und zu gähnen. Dann beginnt er, den Geruch von Nahrung aufzunehmen, Fleisch von einem Bullen oder einem Bären, und macht sich auf die Jagd nach ihnen. Sobald der Alligator genug gefressen hat, verliert er das Interesse am Futter-Preis (die Linien im Konto konvergieren), das ist der Zeitpunkt für die Gewinnfixierung.

int TradeSignal_17()
  {
   int sig=0;

   if(h_al==INVALID_HANDLE)
     {
      h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_al,0,0,2,al1_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,1,0,2,al2_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,2,0,2,al3_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(al1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al3_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(al3_buffer[1]>al2_buffer[1] && al2_buffer[1]>al1_buffer[1])
      sig=1;
   else if(al3_buffer[1]<al2_buffer[1] && al2_buffer[1]<al1_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.18. Trendveränderung gemäß dem Indikator AMA

Abbildung 18. Die Veränderung des Trends gemäß dem Indikator AMA

Durch Verwendung der Funktion TradeSignal_18() erhalten wir das Signal bei jeder Änderung des AMA-Trends.

Wenn der Indikator AMA eine aufsteigende Tendenz erkennen lässt, so ist das das Signal für den Kauf, ist er dagegen nach unten gerichtet, ist das das Verkaufssignal.

int TradeSignal_18()
  {
   int sig=0;

   if(h_ama==INVALID_HANDLE)
     {
      h_ama=iAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ama,0,0,3,ama_buffer)<3)
         return(0);
      if(!ArraySetAsSeries(ama_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(ama_buffer[2]<ama_buffer[1])
      sig=1;
   else if(ama_buffer[2]>ama_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.19. Änderung der Farbe des Awesome Oszillators

Abbildung 19. Die Ermittlung einer Trendveränderung mithilfe des Indikators Awesome Oszillator

Durch die Funktion TradeSignal_19() erhalten wir ein Signal, sobald sich die Farbe des Histogramms des Awesome Oszillators ändert.

Eine der Besonderheiten von MQL5 ist die Möglichkeit, für Indikatoren Puffer anzulegen, in denen die in den Eigenschaften #property indicator_colorN festgelegten Kennziffern für die Linienfarben gespeichert werden können. Ist die Farbe des Awesome Oszillator-Histogramms grün, so dient das als Kaufsignal, ändert sie sich dagegen in rot, heißt das: Wir verkaufen!

int TradeSignal_19()
  {
   int sig=0;

   if(h_ao==INVALID_HANDLE)
     {
      h_ao=iAO(Symbol(),Period());
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ao,1,0,20,ao_buffer)<20)
         return(0);
      if(!ArraySetAsSeries(ao_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(ao_buffer[1]==0)
      sig=1;
   else if(ao_buffer[1]==1)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.20. Trendveränderung gemäß dem Indikator Ichimoku

Abbildung 20. Die Ermittlung einer Trendveränderung mithilfe des Indikators Ichimoku

In der Funktion TradeSignal_20() erhalten wir das Signal bei jeder Änderung des Ichimoku-Trends. Dazu untersuchen wir die Überschneidung der als Tenkan-sen bzw. Kijun-sen bezeichneten Linien.

Das Kaufsignal wird erzeugt, wenn die Tenkan-sen-Linie die Kijun-sen-Linie von unten nach oben schneidet. In umgekehrter Richtung erscheint das Verkaufssignal.

int TradeSignal_20()
  {
   int sig=0;

   if(h_ich==INVALID_HANDLE)
     {
      h_ich=iIchimoku(Symbol(),Period(),9,26,52);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ich,0,0,2,ich1_buffer)<2)
         return(0);
      if(CopyBuffer(h_ich,1,0,2,ich2_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(ich1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(ich2_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(ich1_buffer[1]>ich2_buffer[1])
      sig=1;
   else if(ich1_buffer[1]<ich2_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }


4. Machen wir daraus einen Indikator

Jetzt, da wir die fertigen Signalblöcke zur Hand haben, können wir einen Indikator programmieren, der die Signale gemäß der jeweils gewählten Methode ausgibt. Mithilfe des erstellten Templates können wir den Empfang der Signale von einem beliebigen Indikator programmieren, wir müssen dazu lediglich die Signalbedingung korrekt formulieren und sie dem Programmcode hinzufügen.

Wir schreiben einen Indikator mit fest eingebundenen Parametern. Die Signale der Indikatoren stellen wir auf der rechten Seite des Schaubildes in Form von Pfeilen dar (Pfeil nach oben: Kaufen; Pfeil nach unten: Verkaufen; Kreuz statt Pfeil: kein Signal). Zum Zeichnen der Pfeile wird die übliche Schriftart Wingdings verwendet. Darüber hinaus müssen wir auch noch einige weitere Funktionen zur Ausgabe der Informationen über die Signale in dem Schaubild zu einem Kürzel anlegen. Wir werden sie in einem gesonderten Block in Form einer Bibliothek zusammenfassen, die zum Schreiben von Programmen unter Hinzufügung der eigenen Funktionen verwendet werden kann. Wir nennen diese Bibliothek LibFunctions.

In der ersten Zeile unseres künftigen Indikators programmieren wir die Einbindung der Datei mit den Funktionen zur Erzeugung der Signale und importieren die Funktionen, die für die grafische Darstellung der Signale erforderlich sind, außerdem deklarieren wir auf globaler Ebene die Variablen, die die Art des von dem Indikator empfangenen Signals aufnehmen werden.

//--- Connect necessary libraries of functions
#include <SignalTrade.mqh>
//--- Import of functions from the LibFunctions library
#import "LibFunctions.ex5"
void SetLabel(string nm,string tx,ENUM_BASE_CORNER cn,ENUM_ANCHOR_POINT cr,int xd,int yd,string fn,int fs,double yg,color ct);
string arrow(int sig);
color Colorarrow(int sig);
#import
//+------------------------------------------------------------------+
//| Declare variables for storing signals of indicators              |
//+------------------------------------------------------------------+
int SignalMA;
int SignalMACD;
int SignalPC;
int SignalACADX;
int SignalST;
int SignalRSI;
int SignalCCI;
int SignalWPR;
int SignalBB;
int SignalSDC;
int SignalPC2;
int SignalENV;
int SignalDC;
int SignalSC;
int SignalGC;
int SignalNRTR;
int SignalAL;
int SignalAMA;
int SignalAO;
int SignalICH;

Wie oben bereits erwähnt werden die Indikatoren nur einmal in das Ausgabegerät heruntergeladen und die Anzeiger (Handle) zu diesen Indikatoren angelegt, deshalb programmieren wir ihre Erstellung in der Funktion OnInit(), denn diese Funktion wird nach dem Aufrufen des Programms einmal ausgeführt.

int OnInit()
  {
//--- create indicator handles
   h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
   h_ma2=iMA(Symbol(),Period(),16,0,MODE_SMA,PRICE_CLOSE);
   h_macd=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE);
   h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
   h_acadx=iCustom(Symbol(),Period(),"AdaptiveChannelADX",14);
   h_stoh=iStochastic(Symbol(),Period(),5,3,3,MODE_SMA,STO_LOWHIGH);
   h_rsi=iRSI(Symbol(),Period(),14,PRICE_CLOSE);
   h_cci=iCCI(Symbol(),Period(),14,PRICE_TYPICAL);
   h_wpr=iWPR(Symbol(),Period(),14);
   h_bb=iBands(Symbol(),Period(),20,0,2,PRICE_CLOSE);
   h_sdc=iCustom(Symbol(),Period(),"StandardDeviationChannel",14,0,MODE_SMA,PRICE_CLOSE,2.0);
   h_env=iEnvelopes(Symbol(),Period(),28,0,MODE_SMA,PRICE_CLOSE,0.1);
   h_dc=iCustom(Symbol(),Period(),"Donchian Channels",24,3,-2);
   h_sc=iCustom(Symbol(),Period(),"Silver-channels",26,38.2,23.6,0,61.8);
   h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
   h_nrtr=iCustom(Symbol(),Period(),"NRTR",40,2.0);
   h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN);
   h_ama=iAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
   h_ao=iAO(Symbol(),Period());
   h_ich=iIchimoku(Symbol(),Period(),9,26,52);
   return(0);
  }

Alle wesentlichen Berechnungen erfolgen in der Funktion OnCalculate(), in der wir auch den übrigen Programmcode des Indikators unterbringen.

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---assign the signal value to the variable
   SignalMA    = TradeSignal_01();
   SignalMACD  = TradeSignal_02();
   SignalPC    = TradeSignal_03();
   SignalACADX = TradeSignal_04();
   SignalST    = TradeSignal_05();
   SignalRSI   = TradeSignal_06();
   SignalCCI   = TradeSignal_07();
   SignalWPR   = TradeSignal_08();
   SignalBB    = TradeSignal_09();
   SignalSDC   = TradeSignal_10();
   SignalPC2   = TradeSignal_11();
   SignalENV   = TradeSignal_12();
   SignalDC    = TradeSignal_13();
   SignalSC    = TradeSignal_14();
   SignalGC    = TradeSignal_15();
   SignalNRTR  = TradeSignal_16();
   SignalAL    = TradeSignal_17();
   SignalAMA   = TradeSignal_18();
   SignalAO    = TradeSignal_19();
   SignalICH   = TradeSignal_20();

//--- draw graphical objects on the chart in the upper left corner
   int size=((int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS)/22);
   int i=0;
   int x=10;
   int y=0;
   int fz=size-4;

   y+=size;
   SetLabel("arrow"+(string)i,arrow(SignalMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalMA));
   x+=size;
   SetLabel("label"+(string)i,"Moving Average",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalMACD),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalMACD));
   x+=size;
   SetLabel("label"+(string)i,"MACD",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalPC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalPC));
   x+=size;
   SetLabel("label"+(string)i,"Price Channell",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalACADX),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalACADX));
   x+=size;
   SetLabel("label"+(string)i,"Adaptive Channel ADX",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalST),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalST));
   x+=size;
   SetLabel("label"+(string)i,"Stochastic Oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalRSI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalRSI));
   x+=size;
   SetLabel("label"+(string)i,"RSI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalCCI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalCCI));
   x+=size;
   SetLabel("label"+(string)i,"CCI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalWPR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalWPR));
   x+=size;
   SetLabel("label"+(string)i,"WPR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalBB),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalBB));
   x+=size;
   SetLabel("label"+(string)i,"Bollinger Bands",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalSDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalSDC));
   x+=size;
   SetLabel("label"+(string)i,"StDevChannel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalPC2),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalPC2));
   x+=size;
   SetLabel("label"+(string)i,"Price Channell 2",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalENV),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalENV));
   x+=size;
   SetLabel("label"+(string)i,"Envelopes",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalDC));
   x+=size;
   SetLabel("label"+(string)i,"Donchian Channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalSC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalSC));
   x+=size;
   SetLabel("label"+(string)i,"Silver-channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalGC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalGC));
   x+=size;
   SetLabel("label"+(string)i,"Galaher Channel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalNRTR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalNRTR));
   x+=size;
   SetLabel("label"+(string)i,"NRTR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAL),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAL));
   x+=size;
   SetLabel("label"+(string)i,"Alligator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAMA));
   x+=size;
   SetLabel("label"+(string)i,"AMA",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAO),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAO));
   x+=size;
   SetLabel("label"+(string)i,"Awesome oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalICH),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalICH));
   x+=size;
   SetLabel("label"+(string)i,"Ichimoku Kinko Hyo",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);

   return(rates_total);
  }

So, unser Indikator ist fertig! Zu guter Letzt zeigt unser Diagramm folgendes Bild :



5. Machen wir daraus einen Expert Advisor

Ungefähr auf die gleiche Weise können wir auch einen Expert Advisor programmieren, der die Indikatorsignale in einem Diagramm anzeigt. Wir programmieren ein Informationssystem mit grafischen Steuerelementen. Wir können den gewünschten Indikator auswählen und seine Parameter über die grafische Benutzeroberfläche einstellen.


Wir gehen nicht auf die Einrichtung der grafischen Benutzeroberfläche ein, die erforderlichen Informationen dazu bietet der Artikel Schaffung aktiver Bedienfelder für den Handel in MQL5.

Um die Indikatoreinstellungen über unsere grafische Benutzeroberfläche zu ändern, müssen wir unsere SignalTrade.mqh-Bibliothek etwas überarbeiten und nennen sie deshalb SignalTradeExp.mqh.

Vor allem benötigen wir weitere Variablen zum Speichern der Indikatoreinstellungen.

//--- input parameters Moving Average
int                periodma1=8;
int                periodma2=16;
ENUM_MA_METHOD     MAmethod=MODE_SMA;
ENUM_APPLIED_PRICE MAprice=PRICE_CLOSE;
//--- input parameters MACD
int                FastMACD=12;
int                SlowMACD=26;
int                MACDSMA=9;
ENUM_APPLIED_PRICE MACDprice=PRICE_CLOSE;
//--- input parameters Price Channel
int                PCPeriod=22;
//--- input parameters Adaptive Channel ADX
int                ADXPeriod=14;
//--- input parameters Stochastic Oscillator
int                SOPeriodK=5;
int                SOPeriodD=3;
int                SOslowing=3;
ENUM_MA_METHOD     SOmethod=MODE_SMA;
ENUM_STO_PRICE     SOpricefield=STO_LOWHIGH;
//--- input parameters RSI
int                RSIPeriod=14;
ENUM_APPLIED_PRICE RSIprice=PRICE_CLOSE;
//--- input parameters CCI
int                CCIPeriod=14;
ENUM_APPLIED_PRICE CCIprice=PRICE_TYPICAL;
//--- input parameters WPR
int                WPRPeriod=14;
//--- input parameters Bollinger Bands
int                BBPeriod=20;
double             BBdeviation=2.0;
ENUM_APPLIED_PRICE BBprice=PRICE_CLOSE;
//--- input parameters Standard Deviation Channel
int                SDCPeriod=14;
double             SDCdeviation=2.0;
ENUM_APPLIED_PRICE SDCprice=PRICE_CLOSE;
ENUM_MA_METHOD     SDCmethod=MODE_SMA;
//--- input parameters Price Channel 2
int                PC2Period=22;
//--- input parameters Envelopes
int                ENVPeriod=14;
double             ENVdeviation=0.1;
ENUM_APPLIED_PRICE ENVprice=PRICE_CLOSE;
ENUM_MA_METHOD     ENVmethod=MODE_SMA;
//--- input parameters Donchian Channels
int                DCPeriod=24;
int                DCExtremes=3;
int                DCMargins=-2;
//--- input parameters Silver-channels
int                SCPeriod=26;
double             SCSilvCh=38.2;
double             SCSkyCh=23.6;
double             SCFutCh=61.8;
//--- input parameters NRTR
int                NRTRPeriod   =  40;
double             NRTRK        =  2.0;
//--- input parameters Alligator
int                ALjawperiod=13;
int                ALteethperiod=8;
int                ALlipsperiod=5;
ENUM_MA_METHOD     ALmethod=MODE_SMMA;
ENUM_APPLIED_PRICE ALprice=PRICE_MEDIAN;
//--- input parameters AMA
int                AMAperiod=9;
int                AMAfastperiod=2;
int                AMAslowperiod=30;
ENUM_APPLIED_PRICE AMAprice=PRICE_CLOSE;
//--- input parameters Ichimoku Kinko Hyo
int                IKHtenkansen=9;
int                IKHkijunsen=26;
int                IKHsenkouspanb=52;

Wir ersetzen die konstanten Werte in den Indikatoren durch Variablen. Alles andere bleibt unverändert.

h_ma1=iMA(Symbol(),Period(),periodma1,0,MAmethod,MAprice);

Ein nicht unerheblicher Aspekt ist die sparsame Nutzung des Speicherplatzes, bei Änderungen an den Einstellungen muss die Kopie des Indikators mit den alten Einstellungen entfernt und stattdessen die neue geladen werden. Das geschieht mithilfe folgender Funktion:

bool  IndicatorRelease(
   int       indicator_handle,     // indicator handle
   );
   if(id==CHARTEVENT_OBJECT_ENDEDIT && sparam=="PIPSetEditMA2")
     {
      periodma2=(int)ObjectGetString(0,"PIPSetEditMA2",OBJPROP_TEXT);
      ObjectSetString(0,"PIPSetEditMA2",OBJPROP_TEXT,(string)periodma2);
      //--- unload old copy of the indicator
      IndicatorRelease(h_ma2);
      //--- create new copy of the indicator
      h_ma2=iMA(Symbol(),Period(),periodma2,0,MAmethod,MAprice);
      ChartRedraw();
     }

Fazit

So haben wir gelernt, Informationen von Indikatoren abzulesen und sie an den Expert Advisor weiterzugeben. Auf diese Weise ist es möglich, aus jedem Indikator Signale zu gewinnen.

Hinweise

  • Die Dateien der Indikatoren SignalTrade.mq5, AdaptiveChannelADX.mq5, Donchian Channels.mq5, NRTR.mq5, Price Channel.mq5, PriceChannelGalaher.mq5, Silver-channels.mq5 und StandardDeviationChannel.mq5 müssen in den Ordner ...\MQL5\Indicators kopiert werden.
  • Die Include-Dateien SignalTrade.mqh und SignalTradeExp.mqh müssen in den Ordner ...\MQL5\Include kopiert werden.
  • Die Funktionsbibliothek LibFunctions.mq5 muss in den Ordner ...\MQL5\Libraries kopiert werden.
  • Der Expert Advisor ExpSignalTrade.mq5 muss in den Ordner ...\MQL5\Experts kopiert werden.

Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/130

Beigefügte Dateien |
indicators.zip (8.9 KB)
expsignaltrade.mq5 (165.84 KB)
signaltrade.mq5 (10.57 KB)
signaltrade.mqh (20.16 KB)
signaltradeexp.mqh (23.26 KB)
Wie programmiert man einen Indikator auf der Grundlage eines anderen Indikators? Wie programmiert man einen Indikator auf der Grundlage eines anderen Indikators?

In MQL5 können wir einen Indikator sowohl aus dem Nichts als auch auf der Grundlage eines anderen bereits vorhandenen, in das Ausgabegerät integrierten oder selbst entwickelten Indikators programmieren. Und auch dabei haben wir zwei Möglichkeiten: einen Indikator durch Hinzufügen neuer Berechnungen und grafischer Möglichkeiten zu verbessern, oder mithilfe der Funktionen iCustom() oder IndicatorCreate() einen in das Ausgabegerät integrierten bzw. einen selbst entwickelten Indikator zu verwenden.

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.

Der Prototyp eines automatischen Handelssystems Der Prototyp eines automatischen Handelssystems

In diesem Beitrag werden die Grundlagen für die Erstellung von Algorithmen und Elementen für Handelssysteme zusammengefasst und systematisch geordnet. Es geht hier um die Entwicklung eines Algorithmus‘ für ein automatisches Handelssystem (im Weiteren: das Expert-System). Als Beispiel ziehen wir die Klasse CExpertAdvisor heran, sie kann zur schnellen und einfachen Entwicklung von Handelssystemen verwendet werden.

Anlegen eigener Trailing Stops Anlegen eigener Trailing Stops

Die Grundregel für Händler: Lass‘ Gewinne wachsen, trenn‘ dich von Verlusten! In diesem Beitrag betrachten wir eine der grundlegenden Techniken zur Befolgung dieser Regel: die Verschiebung der schützenden Verlustbegrenzung (Stop Loss Level) nach einer Gewinnsteigerung einer Position, m. a. W.: den Trailing Stop Level, die nachlaufende Stop-Grenze. Sie finden das schrittweise Vorgehen zur Einrichtung einer Klasse zur nachlaufenden Verlustbegrenzung bei den Indikatoren SAR und NRTR. Jedermann ist in der Lage, diese Trailing Stops in die eigenen Expert-Systeme einzufügen oder sie eigenständig zur Überwachung der Positionen des eigenen Kontos zu verwenden.