Der NRTR Indikator und Handelsmodule basierend auf NRTR für MQL5 Wizard

9 Januar 2018, 14:34
Dmitrii Troshin
2
573

Einleitung

Betrachten wir einen Indikator, der einen dynamischen Preiskanal zeichnet. Basierend auf diesem Kanal wird ein Expert Advisor erstellt. Es ist bekannt, dass solche Systeme gut in einem Trendmarkt arbeiten, aber viele falsche Signale bei Seitwärtsbewegungen erzeugen. Aus diesem Grund werden zusätzliche Trendindikatoren benötigt. Die Auswahl eines passenden Indikators ist keine einfache Aufgabe, weil sie häufig von konkreten Marktbedingungen abhängt. Deswegen wäre es eine gute Lösung, die Möglichkeit zu finden, den ausgewählten Indikator zu einem fertigen Handelssystem schnell hinzuzufügen.

Dafür verfolgen wir den folgenden Ansatz. Wir erstellen ein spezielles Modul von Handelssignalen für MQL5 Wizard. Für jeden ausgewählten Trendindikator können wir schnell ein gleiches Modul erstellen, das nur die Signale "Ja"/"Nein" erzeugt und angibt, ob ein Trend vorhanden ist oder nicht. Da man bei der Erstellung eines Handelssystems mehrere Module verwenden kann, können wir verschiedene Indikatoren ganz einfach kombinieren.

Der NRTR Indikator

Die Idee des NRTR Indikators, Nick Rypock Trailing Reverse, gehört Konstantin Kopyrkin. Interessante Information: hinter dem Namen "Nick Rypock" verbirgt sich der Familienname "Kopyrkin", rückwärts geschrieben. 

Aber zurück zum Indikator. Er stellt einen dynamischen Preiskanal dar. Der Autor veranschaulicht die Grundidee durch die folgende Abbildung:

NRTR

Ein Handelssystem basierend auf dem NRTR Indikator gehört zu Ausbruchsystemen. Ein Kaufsignal wird erzeugt, wenn der Preis das vorherige Hoch einer bestimmten Periode übersteigt; ein Verkaufssignal wird erzeugt, wenn der Preis unter das Tief fällt. Bei einer Trendwende werden in solchen Systemen Hochs und Tiefs des vorherigen Trends verwendet. Um das zu vermeiden, wird die Berechnungsperiode in unserem System dynamisch festgelegt.

Der Autor selbst bezeichnet NRTR als einen Trendindikator des Ausbruchs eines dynamischen Preiskanals.

Er funktioniert wie folgt: bei einem Aufwärtstrend liegt die Linie des Indikators (der Kanal) unterhalb des Preishochs im angegebenen Zeitintervall. Die Linie eines Abwärtstrends liegt oberhalb von Preisen in einem konstanten Abstand vom Preistief im angegebenen Zeitintervall.

Dabei wird die Periode des Preiskanals für die Berechnung des Indikators ab dem Moment der Entstehung eines Trends dynamisch erhöht. Bei diesem Ansatz beeinflusst der Preis der vorherigen Berechnungsperiode den Indikator nicht.

Auf der Abbildung sieht man, dass der Indikator dem Trend in einem bestimmten Abstand folgt. Danach befindet er sich in einem festen Abstand von den lokalen Hochs H1 und H2. Das lokale Hoch H3 ist kleiner als der vorherige und wird bei der Berechnung nicht berücksichtigt.

Dann durchbricht der Preis den Kanal im Punkt L3. Das ist ein Verkaufssignal. Der Wert im Punkt L3 wird zum neuen Minimum. Ab diesem Moment beginnt die neue Periode, d.h. alle vorherigen Preise werden einfach zurückgesetzt und bei den Berechnungen nicht mehr berechnet. Während sich der Trend entwickelt, wird das Tief auf L3-L4-L5 aktualisiert. Die Periode des dynamischen Preiskanals nimmt auch zu, bis sich der Trend ändert oder bis die Periodenlänge den zulässigen Höchstwert erreicht.

Die Breite des Kanals wird in Prozent vom Extremwert berechnet oder kann von der Preisvolatilität abhängen. In dem vorliegenden Artikel werden beide Ansätze implementiert.

Ein Kauf-/Verkaufssignal wird erzeugt, wenn der Preis die Kanallinie durchbricht. Wenn der Preis die Unterstützungslinie durchbricht, wird ein Kaufsignal gebildet. Wenn der Preis die Widerstandslinie durchbricht, wird ein Verkaufssignal gebildet.

Nun müssen wir die Beschreibung der Arbeit des Indikators in die Programmiersprache MQL5 übersetzen. Fangen wir an.

Indikator schreiben: vom Einfachen zum Schwierigen

Zunächst einmal müssen wir das Verhalten des Indikators festlegen. Der Indikator basiert auf Close-Preisen. Natürlich werden Indikatorwerte auf Basis historischer Daten eindeutig interpretiert. Aber was wenn der Preis die Unterstützungs-/Widerstandslinie auf einer nicht vollständigen Kerze durchbricht? In dieser Implementierung ändert sich der Trend nicht und es wird kein Signal erzeugt, bis die Kerze vollständig ist. Von einer Seite können wir einen Teil der Bewegung verlieren. Wenn die Bewegung, zum Beispiel, mit einer großen Kerze beginnt, die den Kanal durchbricht, wird eine Position erst bei der nächsten Kerze eröffnet. Von der anderen Seite schützen wir uns vor zahlreichen falschen Ausbrüchen.

NB: dieser Indikator hat mehrere Variationen; im Artikel wird die Originalversion des Autors beschrieben.

In der CodeBase kann man eine Umsetzung dieses Indikators finden, in welcher die Periode nur teilweise dynamisch ist. Bei einer Trendwende wird die Periode zurückgesetzt, aber dann kann sie in Theorie unbegrenzt steigen. D.h. die Unterstützunglinie wird als MathMax() vom vorherigen Wert und dem aktuellen Schlusskurs berechnet. Bei solcher Implementierung kann die Unterstützungslinie nur steigen, und die Widerstandslinie - nur fallen. In der Originalversion galten alle vorherigen Werte als veraltet und wurden nicht berücksichtigt. Hier werden Max/Min mithilfe von ArrayMaximum/Minimum(close,i,dynamic_period) ermittelt. Bei diesem Ansatz können die Unterstützungs-/Widerstandslinien sowohl steigen, als auch fallen. Daraus folgt, dass die Unterstützungslinie in einigen Fällen bei einer "langsamen" Seitwärtsbewegung während kurzer dynamischer Perioden tief nach unten fallen kann. Aber solche "glatten" langen Trends kommen sehr selten vor, und es gibt auch keine idealen Methoden. Wie gesagt, unser Prinzip ist, uns an die ursprüngliche Idee des Autors zu halten.

Kommen wir nun zu Zeitreihen. Standardmäßig haben Preis-Arrays (close) in MQL5 den Wert ArraySetAsSeries=false. In MQL4 haben Preis-Arrays das Flag einer Zeitreihe, und Close[0] war der Close-Kurs des Balkens ganz rechts (den Balken ganz links sehen wir in der Regel nicht). Bitte merken Sie sich, dass in diesem Artikel ArraySetAsSeries(close,true) ist.

Nun kommen wir zur Ausführung. Wir haben vier Indikatorpuffer: zwei für Unterstützungs-/Widerstandslinien und zwei für Kauf-/Verkaufssignale.

#property indicator_chart_window
#property indicator_buffers 4 
#property indicator_plots   4

//Indicators lienes style
#property indicator_type1  DRAW_LINE
#property indicator_color1 Green
#property indicator_style1 STYLE_DASH

#property indicator_type2  DRAW_LINE
#property indicator_color2 Red
#property indicator_style2 STYLE_DASH

#property indicator_type3  DRAW_ARROW
#property indicator_color3 Green

#property indicator_type4  DRAW_ARROW
#property indicator_color4 Red

Deklarieren wir Indikatorpuffer und externe Parameter des Indikators

input int    period =12;      //dynamische Periode
input double percent =0.2;    //Prozent des Einzugs 

double Buff_Up[],Buff_Dn[];  
double Sign_Up[],Sign_Dn[];

Signale werden als Pfeile dargestellt. Alle anderen Parameter setzen wir in der Funktion OnInit(). Ich verwende DRAW_ARROW mit dem Parameter 236,238 aus den Symbolen der Schriftart Wingdings. Parameter für ein Signal "nach oben", zum Beispiel:

   SetIndexBuffer(2,Sign_Up,INDICATOR_DATA);
   PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetInteger(2,PLOT_ARROW,236);
   PlotIndexSetInteger(2,PLOT_LINE_WIDTH,1);
   ArraySetAsSeries(Sign_Up,true);

Am Anfang der Berechnungen in der Funktion OnCalculate() werden die Verfügbarkeit der benötigten Daten und der erste Start der Berechnung des Indikators überprüft.

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
{ 
  
  int start =0;                                           //Berechnungspunkt
  
  int trend =0;                                           //Trendwert,  1 - nach oben, -1 - nach unten
  static int trend_prev =0;
  
  double value =0;                                        //Indikatorwerte 
  static double value_prev =0;
  
  int dyn_period =1;                                     //Wert der Periode                                    
  static int curr_period =1;
  
  
  double maxmin =0;                                       //Variable für Berechnungen
  
  ArraySetAsSeries(close,true);
  
  if(rates_total<period) return(0);
  
     if(prev_calculated==0)                              //den ersten Start der Berechnung des Indikators überprüfen
  { 
      start=rates_total-1;                               // Startindex für die Berechnung aller Balken
      trend_prev =1;
      value=close[start]*(1-0.01*percent);      
  }
  
  else
     { 
      start=rates_total-prev_calculated;                  // Startindex für die Berechnung neuer Balken 
     }

trend =trend_prev;
value =value_prev;
dyn_period =curr_period;

Hier werden die wichtigsten Variablen definiert. Für die Werte des Trends und des Kanals werden zwei Variablen definiert, eine von denen statisch ist. Die statische Variable speichert den Wert bis zur nächsten Berechnungsschleife. Sie ändert sich nur auf einem vollständigen Balken. Wenn der Kanal auf einem nicht vollständigen Balken durchbrochen wird, ändern sich nur lokale nicht statische Variablen. Wenn der Preis in den Kanal zurückkehrt, bleibt der vorherige Trend bestehen.

Nun schreiben wir die Berechnungsschleife unter Berücksichtigung der oben beschriebenen Anmerkungen.

trend =trend_prev;
value=value_prev;
dyn_period =curr_period;    
//-------------------------------------------------------------------+
//                        Berechnungsschleife   
//-------------------------------------------------------------------+  
for(int i=start;i>=0;i--)
{ 
    Buff_Up[i] =0.0;
    Buff_Dn[i] =0.0;
    Sign_Up[i] =0.0;
    Sign_Dn[i] =0.0;
    
    if(curr_period>period) curr_period=period;
    if(dyn_period>period) dyn_period=period;
    
 //if trend ascending   
    if(trend>0)
    { 
    maxmin =close[ArrayMaximum(close,i,dyn_period)];
    value =maxmin*(1-percent*0.01);
    
    if(close[i]<value)
      { 
      maxmin =close[i];
      value =maxmin*(1+percent*0.01);
      trend =-1;
      dyn_period =1;
      }
    }
  
//  if trend descending
    else
    { 
    maxmin =close[ArrayMinimum(close,i,dyn_period)];
    value =maxmin*(1+percent*0.01);
    if(close[i]>value)
      { 
      maxmin =close[i];
      value =maxmin*(1-percent*0.01);
      trend =1;
      dyn_period =1;
      }
    }  
 // trend changes 
  
      if(trend>0) Buff_Up[i] =value;
      if(trend<0) Buff_Dn[i] =value;

      if(trend_prev<0  &&  trend>0) 
      { 
      Sign_Up[i] =value;
      Buff_Up[i] =0.0;
      }
      if(trend_prev>0 && trend<0)
      { 
      Sign_Dn[i] =value;
      Buff_Dn[i] =0.0;
      }

  dyn_period++;
  
  if(i)
  { 
  trend_prev =trend;
  value_prev =value;
  if(dyn_period==2)curr_period =2;
  else curr_period++;
  }

}

In der Schleife wird die dynamische Periode auf den angegebenen Wert beschränkt. Wenn der Close-Kurs den Kanal durchbricht, werden neue Werte der Unterstützung/des Widerstands ermittelt, es wird überprüft, ob sich der Trend geändert hat. Der letzte Operator if() überprüft, ob der Balken vollständig ist. Nur wenn der Balken vollständig ist, ändern sich die Werte trend_prev und value_prev, und daher kann ein Kauf- bzw. Verkaufssignal erzeugt werden. Die dynamische Periode kann auch hier zurückgesetzt werden.

Der komplette Code des Indikators ist in der angehängten Datei NRTR.mq5 zu finden.

Betrachten wir die Arbeit des Indikators, indem wir zwei NRTR mit verschiedenen Parametern auf einen Chart ziehen. Der erste hat die Periode 12 und eine Breite von 0.1%, die Periode des zweiten beträgt 120, die Breite - 0.2%.


Auf dem Bild sieht man deutlich, dass die Unterstützungslinie bei einer kleinen Periode sowohl steigen, als auch fallen kann. Das ist damit verbunden, dass die Preiswerte über die Grenzen der dynamischen Periode hinausgehen. Bei längeren Perioden fällt die Unterstützungslinie in der Regel nicht.

Volatilität und NRTR

Beim vorherigen Durchlauf wird eine feste Abweichung des Preiskanals in Prozent verwendet. Es wäre logischer, den Kanal bei der Erhöhung der Volatilität zu erweitern, und bei der Senkung — zu verengen. Um die Volatilität auf dem Markt einzuschätzen wird gewöhnlich der ATR (average true range) Indikator verwendet. Der ATR-Wert kann für das Setzen der Breite des Kanal verwendet werden. Man kann ihn selbst berechnen oder einen fertigen technischen Indikator nehmen, der im Standardpaket des Terminals enthalten ist.

Um die Breite des Kanals mit der Volatilität zu verbinden, ersetzen wir die Abweichung in Prozent durch den Wert des ATR Indikators. Ein Koeffizient wird für das Skalieren verwendet. Standardmäßig ist er gleich 1. Für den ATR Indikator deklarieren wir einen zusätzlichen Indikatorpuffer: double Buff_ATR[]. Den Prozent ersetzen wir durch den Koeffizienten K=1. Um die ATR-Werte zu erhalten, erstellen wir einen Pointer auf den Indikator:

handle_atr =iATR(_Symbol,PERIOD_CURRENT,period);

Die ATR-Periode kann sich von der vorhandenen dynamischen Periode unterscheiden. Eine logische Lösung ist, diese gleich zu machen, und die Anzahl der Parameter bleibt ebenso gleich.

Hier führe ich nur die neu hinzugefügten Zeilen an.

#property indicator_buffers 5 
#property indicator_plots   4
.............................
input double K =1;            //Koeffizient der Skalierung
double Buff_ATR[];
int handle_atr;
.............................
SetIndexBuffer(4,Buff_ATR,INDICATOR_CALCULATIONS);
ArraySetAsSeries(Buff_ATR,true);
         
handle_atr =iATR(_Symbol,PERIOD_CURRENT,period);
.....................................................
int OnCalculate(){
.....................................................
  if(CopyBuffer(handle_atr,0,0,start+1,Buff_ATR)==-1)
  { 
  return(0);
  Print("Kopieren von Daten in den ATR-Puffer fehlgeschlagen");
  }
.....................................................

//if trend ascending  
  if(trend>=0)
  { 
  maxmin =close[ArrayMaximum(close,i,dyn_period)];
  value =maxmin-K*Buff_ATR[i];
  
  if(close[i]<value)
   { 
   maxmin =close[i];
   value =maxmin+K*Buff_ATR[i];
   trend =-1;
   dyn_period =1;
   }
  }
 
}

Die Werte der Linien werden als value = maxmin(+-)K*Buff_ATR[i], berechnet. Der komplette Code des Indikators ist in der angehängten Datei NRTRvolatile.mq5 zu finden.

Starten wir beide Indikatoren mit den gleichen Parametern im Chart und vergleichen wir ihr Verhalten.


Auf dem Bild sieht man, dass bei einer niedrigen Volatilität und kleinen Werten des ATR "klebt" die NRTRvolatile Linie an den Preischart quasi fest. Danach wenn die Volatilität steigt, bewegt sich die Linie weg von ihn.

Nun kommen wir zum Schreiben eines Expert Advisors basierend auf unserem Indikator. Wie oben erwähnt, schreiben wir dafür ein Modul von Handelssignalen.

Handelsmodul für MQL5 Wizard

Häufig ist es einfacher, solche Module basierend auf bereits existierenden mithilfe der Copy-Paste-Methode zu schreiben. Aber in diesem Fall ist es am einfachsten von Anfang an zu beginnen, als zu erklären, wo und was man korrigieren oder ersetzen muss.

Beschreiben wir die allgemeine Struktur aller Module.

  • Modul-Descriptor
  • Handelsparameter und Funktionen für deren Initialisierung
  • Überprüfung von Eingabeparametern
  • Verbinden des ausgewählten Indikators mit dem angegeben Modul
  • Beschreibung der Handelsstrategie

Zunächst einmal erstellen wir einen separaten Unterordner im Ordner "Signale" für eigene "selbst geschriebene" Signale. Zum Beispiel, Include\Expert\MySignals. Klicken wir mit der rechten Maustaste auf dem ausgewählten Ordner und wählen wir "Neue Datei" im Kontextmenü aus. Dann erscheint der MQL5 Wizard. Wählen wir "Neue Klasse" im Menü aus. Nennen wir sie NRTRsignal. Alle Signale werden von der Basisklasse CExpertSignal abgeleitet, geben wir das im Wizard an.


Fügen wir dem vom Wizard generierten Code den Pfad zur Basisklasse CExpertSignal hinzu: #include "..\ExpertSignal.mqh"

//+------------------------------------------------------------------+
//|                                                   SignalNRTR.mqh |
//|                                                       Orangetree |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Orangetree"
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "..\ExpertSignal.mqh"                  // Klasse CExpertSignal 
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class SignalNRTR : public CExpertSignal
  { 
private:

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

Der Anfang ist geschafft.

Damit MQL5 Wizard unseren Code als Signalmodul identifizieren kann, erstellen wir einen Descriptor für das Modul nach dem Bild und Gleichnis von Signalen.

// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Signals of indicator 'NRTR'                                |
//| Type=SignalAdvanced                                              |
//| Name=NRTR                                                        |
//| ShortName=NRTR                                                   |
//| Class=SignalNRTR                                                 |
//| Page=????                                                        |
//| Parameter=PeriodDyn,int,12,Periode des dynamischen Kanals           |
//| Parameter=PercentDev,double,0.1,Breite des Kanals in Prozent      |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+
//| Class SignalNRTR.                                                |
//| Purpose: Class of generator of trade signals based on            |
//|          the 'NRTR' indicator.                                   |
//| Is derived from the CExpertSignal class.                         |
//+------------------------------------------------------------------+

Der Descriptor beginnt mit "wizard description start" und endet  mit " wizard description end". Der Descriptor beinhaltet den Namen des Moduls und externe Parameter. Sobald wir das Modul zusammen mit dem Descriptor kompilieren, wird es zum Menü des Wizards hinzugefügt: Neue Datei/Expert Advisor(generieren)/Allgemeine Parameter/Parameter der Signale für Expert Advisors/Hinzufügen.

Modul

Wir müssen Variablen für das Speichern der externen Parameter und der Methoden deren Initialisierung hinzufügen.

Die Namen der Methoden für die Initialisierung der externen Parameter müssen mit den Namen der externen Parameter im Descriptor übereinstimmen.

class SignalNRTR : public CExpertSignal
  { 
protected:
   int m_period_dyn;                                   //Periode des Kanals
   double m_percent_dev;           //Breite des Kanals in Prozent
 
public:
                     SignalNRTR();
                    ~SignalNRTR();
   //--- methods of setting adjustable parameters
   void              PeriodDyn(int value)                 { m_period_dyn=value;}
   void              PercentDev(double value)             { m_percent_dev=value;}
   
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
SignalNRTR::SignalNRTR() : m_period_dyn(12),
                           m_percent_dev(0.1)
  { 
  //--- initialization of protected data
   m_used_series=USE_SERIES_OPEN+USE_SERIES_HIGH+USE_SERIES_LOW+USE_SERIES_CLOSE;
  }

Die Klassenmitglieder werden mithilfe der Liste der Initialisierung initialisiert. Die vom Wizard generierten Methoden "private" können durch "protected" ersetzt werden, das ist aber fakultativ.

Die Methode virtual bool ValidationSettings() in der Klasse CExpertBase erlaubt es, die Richtigkeit der eingegebenen Parameter zu überprüfen.

Wir müssen den Prototyp der Methode der erstellten Klasse hinzufügen und ihn neu definieren. Wir müssen z.B. überprüfen, ob die Periode größer als 1 ist, und der Prozent der Abweichung positiv ist.

//+------------------------------------------------------------------+
//| Die Methode prüft die Eingabeparameter                              |
//+------------------------------------------------------------------+
bool SignalNRTR:: ValidationSettings()
  { 
   // Aufruf der Methode der Basisklasse
   if(!CExpertSignal::ValidationSettings())  return(false);
   
   // Die Periode muss größer als 1 sein
   if(m_period_dyn<2)
   { 
   Print("Die Periode muss größer als 1 sein");
   return false;
   }
   // Der Wert der Breite des Kanals muss positiv sein
   if(m_percent_dev<=0)
   { 
   Print("Der Wert der Breite des Kanals muss positiv sein");
   return false;
   }
   
   return true;
  }

Bitte beachten Sie: zuerst wird die Methode der Basisklasse aufgerufen.

Um einen konkreten Indikator zu unserem Modul hinzuzufügen, verwenden wir die Methode InitIndicators(). Erstellen wir den Prototyp dieser Methode in unserer Klasse virtual bool InitIndicators(CIndicators *indicators), und fügen wir seine Beschreibung hinzu. Dafür sind Standardverfahren der Prüfung des Pointers des Indikators und die Initialisierung von Indikatoren und Zeitreihen in zusätzlichen Filtern vorhanden.

//+------------------------------------------------------------------+
//| Create indicators.                                               |
//+------------------------------------------------------------------+
bool SignalNRTR::InitIndicators(CIndicators *indicators)
   { 
//--- check pointer
   if(indicators==NULL)
      return(false);
//--- initialization of indicators and timeseries of additional filters
   if(!CExpertSignal::InitIndicators(indicators))
      return(false);  
//--- create and initialize NRTR indicator
if(!InitNRTR(indicators))
      return(false);
//--- ok
   return(true);
   }

Wir erstellen und initialisieren unseren Indikator in der Zeile InitNRTR(indicators). Wir müssen den Prototyp und die Beschreibung der Funktion InitNRTR(indicators) hinzufügen.

//+------------------------------------------------------------------+
//| Create NRTR indicators.                                          |
//+------------------------------------------------------------------+  
bool SignalNRTR::InitNRTR(CIndicators *indicators)
   { 
//--- check pointer
   if(indicators==NULL)
      return(false);
//--- add object to collection
   if(!indicators.Add(GetPointer(m_nrtr)))
     { 
      printf(__FUNCTION__+": error adding object");
      return(false);
     }
//--- NRTR Parameter setzen
   MqlParam parameters[3];
+//---+
   parameters[0].type=TYPE_STRING;
   parameters[0].string_value="Orangetree\\NRTR.ex5";
   parameters[1].type=TYPE_INT;
   parameters[1].integer_value=m_period_dyn;      // Periode
   parameters[2].type=TYPE_DOUBLE;
   parameters[2].double_value=m_percent_dev;      // Breite des Kanals
//--- initialize object
   if(!m_nrtr.Create(m_symbol.Name(),m_period,IND_CUSTOM,3,parameters))
     { 
      printf(__FUNCTION__+": error initializing object");
      return(false);
     }
//--- ok
   return(true);   
   }

Der Indikator wird mithilfe der Struktur MqlParam und der Methode Create() erstellt.

Wir haben den Code vorbereitet. Nun müssen wir einen Handelsalgorithmus schreiben. Dafür verwenden wir die Methoden LongCondition() und ShortCondition(). Fügen wir Methoden für das Erhalten von Signalen des Indikators hinzu.

   //--- methods of getting data
   double            UpSignal(int index)                   { return(m_nrtr.GetData(2,index));}
   double            DnSignal(int index)                   { return(m_nrtr.GetData(3,index));}

Die Funktion GetData() erhält den Wert des Indikatorpuffers nach seinem Index und dem Index des Balkens. Der Indikator selbst beinhaltet das Signal über eine Trendwende. Deswegen sind die Bedingungen für das Eröffnen einer Position ganz einfach. Kaufen, wenn der Indikator ein Signal "nach oben" gibt, und verkaufen, wenn ein Signal "nach unten" auftritt.

//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int SignalNRTR::LongCondition(void)
   { 
   int idx   =StartIndex();
   if(UpSignal(idx))
      return 100;
   else return 0;
   }
//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//+------------------------------------------------------------------+
int SignalNRTR::ShortCondition(void)
   { 
   int idx   =StartIndex();
   if(DnSignal(idx))
      return 100;
   else return 0;
   }

In diesen Funktionen wird nicht nur der Algorithmus, sondern auch die Besonderheiten des Verhaltens des Algorithmus festgelegt. Hier wird die Funktion StartIndex() verwendet.

In der Beschreibung der Funktion virtual int StartIndex() steht: "Wenn das Flag der Analyse des aktuellen Balkens auf true gesetzt wurde, wird 0 zurückgegeben (die Analyse erfolgt ab dem aktuellen Balken). Wenn das Flag der Analyse nicht gesetzt wurde, wird 1 zurückgegeben (die Analyse erfolgt ab dem letzten vollständigen Balken)." Unser Handelssystem analysiert Signale auf vollständigen Balken. Die Funktion StartIndex() gibt standardmäßig 1 zurück, das entspricht genau unserer Strategie. Diese Eigenschaft wird als Parameter Expert_EveryTick mit dem Wert false im Expert Advisor dargestellt.

Damit ist die Erstellung des Moduls von Handelssignalen beendet.

Um die Ausführung zu überprüfen, verwenden wir den Strategietester.


Optimieren wir Handelsparameter. Für die Optimierung verwenden wir die Daten für den Zeitraum vom 25.09.17 bis 18.10.17 auf EURUSD, Zeitrahmen H1. Nur die Parameter des Indikators wurden optimiert: die Periode und die Breite des Kanals. Stop Loss und Take Profit = 0.


Auf der Abbildung ist das Ergebnis für die Periode 48 und eine Breite des Kanals von 0.25% dargestellt.

Kombinationen NRTR + verschiedene Trendindikatoren

Nehmen wir an, wir wollen dass Signale unseres Indikators bestätigt werden. Da der Indikator gut bei einem Trend arbeitet, ist es logisch, noch einen Trendindikator unserem System hinzuzufügen. In diesem Artikel stellen wir fertige Signalmodule des MQL5 Wizards bereit. Setzen wir diese Vorgehensweise fort und fügen wir dem Expert Advisor einen Trendindikator über ein Signalmodul hinzu.

Es ist nicht einfach, einen Trend zu ermitteln. Aus diesem Grund erläutern wir hier nicht, welcher der Trendindikatoren besser oder schlechter ist. Wählen wir einen Standardindikator im Terminal aus, denn unser Ziel ist es, das Verfahren der Verbindung eines Indikators über ein Handelsmodul zu trainieren. Das Ergebnis des Handels interessiert uns in dieser Phase nicht.

Alle Schritte der Erstellung eines Moduls wurden im vorherigen Abschnitt betrachtet. Deshalb kann man die Methode "Copy/Paste" verwenden und von seinen Vorteilen profitieren. Nehmen wir ein fertiges Modul, und ersetzen wir alle notwendigen Zeilen.

Nehmen wir an, wir haben den ADX Indikator in der Kategorie "Trendindikatoren" ausgewählt. Das ist ein Indikator aus der Sammlung technischer Indikatoren. Für den Verweis auf den Indikator kann man den speziell reservierten Pointer CiADX und nicht den Pointer auf den benutzerdefinierten Indikator CiCustom verwenden. Über die Methode Create kann man ihn auf eine leicht unterschiedliche Weise erstellen, aber dabei muss man den Pfad nicht angeben.

protected:
   CiADX m_adx;                                    // object-indicator
   int m_period_adx;                               //ADX Periode
....................... anderer Code.....................................

//--- initialize object
   if(!m_adx.Create(m_symbol.Name(),m_period,m_period_adx))
     { 
      printf(__FUNCTION__+": error initializing object");
      return(false);
     }

Dieser Indikator hat nur einen Parameter - die Periode. Dies muss im Descriptor des Moduls und in den Funktionen des Setzens von Parametern angegeben werden. Darüber hinaus muss man Funktionen für das Erhalten von Werten der ADX Puffer hinzufügen.

//| Parameter=PeriodADX,int,14,Periode des ADX Indikators 
.....................................................

//--- methods of setting adjustable parameters
void              PeriodADX(int value)                { m_period_adx=value;}
.....................................................

//--- methods of getting data
double            MainADX(int index)                   { return(m_adx.Main(index));}
double            ValueIDPlus(int index)               { return(m_adx.Plus(index));}
double            ValueIDMinus(int index)              { return(m_adx.Minus(index));}

Wir machen alle Schritte, die im Abschnitt über die Erstellung eines Handelsmoduls beschrieben wurden. Ersetzen wir NRTR durch ADX, wo es nötig ist. Die Handelsbedingungen entnehmen wir der Beschreibung des ADX Indikators, ohne zu überprüfen. Der klassische Indikator hat folgende Bedingungen:

  • Kaufen, wenn +DI >-DI und ADX steigt.
  • Verkaufen, wenn +DI <-DI und ADX steigt.
//+------------------------------------------------------------------+
//| "Voting" that trend is "Down".                                   |
//+------------------------------------------------------------------+
int SignalADX::LongCondition(void)
   { 
   int idx   =StartIndex();
   if(ValueIDPlus(idx)>ValueIDMinus(idx)&&MainADX(idx)>MainADX(idx+1))
      return (100);
   else
      return (0);
   }
//+------------------------------------------------------------------+
//| "Voting" that trend is "UP".                                    |
//+------------------------------------------------------------------+
int SignalADX::ShortCondition(void)
   { 
   int idx   =StartIndex();
   if(ValueIDPlus(idx)<ValueIDMinus(idx)&&MainADX(idx)>MainADX(idx+1))
      return (100);
   else
      return (0);
   }

Das ist der Grundprinzip. Wir erzeugen ein Kaufsignal, wenn wir denken, dass der Indikator einen Aufwärtstrend zeigt, und einen Verkaufssignal, wenn es einen Abwärtstrend gibt. Einfachheitshalber ist das Gewicht des Signals auf 100 gesetzt.

Öffnen wir wieder den MQL5 Wizard. Bei der Erstellung des Expert Advisors wählen wir zwei Handelssignale aus — SignalNTRTR und ADXTrendSignal. Wenn es mehrere Signale gibt, wird ihr Durchschnittswert berechnet. Deswegen setzen wir die Gewichtskoeffizienten der beiden Signale auf 1. Den Grenzenwert für das Eröffnen setzen wir auf 100. Alle anderen Parameter außer der Periode und der Breite des Kanals setzen wir auf Null. Starten wir den Tester und prüfen, ob alle richtig funktioniert.


Fazit

Fassen wir zusammen: Wir haben den Trendindikator des Ausbruchs des dynamischen Preiskanals NRTR betrachtet. Es wurden zwei Versionen implementiert: mit einer festen Abweichung der Trendlinien von Extrema in Prozent und mit einer Abweichung, die von der Volatilität auf dem Markt abhängt.

Beide Varianten sind im Anhang zu finden. Auf Basis des NRTR Indikators wurde ein Modul von Handelssignalen geschrieben; mithilfe des MQL5 Wizards wurde ein Expert Advisor generiert.

Als Beispiel für die Anwendung des NRTR zusammen mit Trendindikatoren nach der oben beschriebenen Methode wurde ein Modul für den ADX Indikator erstellt. Im MQL5 Wizard wurde ein Test-Experte auf Basis von NRTR + ADX erzeugt. Die Kombinationen NRTR + ein Trendinidkator wurden nicht optimiert, weil die Auswahl eines Trendindikators sehr subjektiv ist und in diesem Artikel nicht betrachtet wird. Der Ansatz basiert auf der Philosophie der Module und Kombinationen.

Bei der Arbeit mit den angehängten Dateien muss man beachten, dass die Pfade zu Indikatoren und Handelssignalen (z.B. für den Tester) zum Ordner angegebenen werden müssen, in welchem das Modul oder der Indikator installiert ist. Zum Beispiel, in meinem Fall - zur Datei SignalNRTR:

   parameters[0].string_value="NRTR.ex5";

Geben Sie den Pfad in Übereinstimmung mit dem Installationsordner Ihrer Indikatoren.

Dateien:

#NameTypBeschreibung
1NRTR.mq5IndikatorQuellcode des betrachteten Indikators
2NRTRvolatile.mq5IndikatorQuellcode des Indikators, der Preisvolatilität berücksichtigt
3SignalNRTR.mqhHandelsmodulModul von Handelssignalen. Es wird für die Erzeugung von Expert Advisors im MQL5 Wizard verwendet
 ADXTrendSignal.mqhHandelsmodul Testmodul des Trendindikators

Die Dateien im Ordner MQL5.zip sind entsprechend den Verzeichnissen in MetaEditor platziert.

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

Beigefügte Dateien |
NRTR.mq5 (11.09 KB)
NRTRvolatile.mq5 (11.93 KB)
SignalNRTR.mqh (14.21 KB)
ADXTrendSignal.mqh (12.54 KB)
MQL5.zip (8.39 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (2)
Otto Pauser
Otto Pauser | 13 Jan 2018 in 01:46

The path to the includefiles should be corrected to

#include <Expert\\ExpertSignal.mqh>
Dmitrii Troshin
Dmitrii Troshin | 13 Jan 2018 in 12:26

 both variants are possible. Right variant

#include <Expert\ExpertSignal.mqh>

 you can see it in another modules in folder Include\Expert\Signal

Handeln nach den Ebenen von DiNapoli Handeln nach den Ebenen von DiNapoli

Der Artikel beschäftigt sich mit der Möglichkeit, mit einem Expert Advisor und den Standardelementen aus MQL5 die DiNapoli-Ebenen zu handeln. Es wird die Leistungsfähigkeit getestet und die Ergebnisse besprochen.

Nachthandel während der asiatischen Handelszeit: wie man im Plus bleibt Nachthandel während der asiatischen Handelszeit: wie man im Plus bleibt

Der Artikel beschäftigt sich mit dem Begriff des Nachthandels, Handelsstrategien und deren Implementierung in MQL5. Es wurden Tests durchgeführt und Schlussfolgerungen gezogen.

Die Momentum-Pinball Handelsstrategie Die Momentum-Pinball Handelsstrategie

In diesem Artikel setzen wir die Programmierung der Handelsstrategien fort, die im Buch "Street Smarts: High Probability Short-Term Trading Strategies" von L. Raschke und L. Connors beschrieben ist. Diesmal beschäftigen wir uns mit dem System Momentum-Pinball: Erstellen von zwei Indikatoren, dem Handelsroboter und dem Signalteil.

Risikobewertung durch die Abfolge von Positionen von Finanzanlagen. Fortsetzung Risikobewertung durch die Abfolge von Positionen von Finanzanlagen. Fortsetzung

Der Artikel entwickelt die im vorhergehenden Teil vorgeschlagenen Ideen und führt sie weiter aus. Er beschreibt die Probleme der Ertragsverteilung, der grafischen Darstellung und untersucht statistische Gesetzmäßigkeiten.