Anlegen eigener Trailing Stops

Dmitry Fedoseev | 12 Januar, 2016

Einleitung

Bevor wir mit der Erörterung des Gegenstandes dieses Beitrags beginnen, schlage ich vor, das I-Tüpfelchen zu setzen und noch einmal die Begriffe „Position“ und „Auftrag“ zu bestimmen:

Abbildung 1. Positionen und Aufträge

Abbildung 1. Positionen und Aufträge

In diesem Beitrag geht es um das Nachlaufen (Trailing) der Stop Loss-Grenze für Positionen. Bei ausstehenden Aufträgen hat dieses Vorgehen keinen Sinn, da der Auftragspreis selbst unmittelbar bewegt werden kann. Und wenn er sich in die Position (oder einen Teil von ihr) verwandelt, dann kann Ihnen auch das nachfolgende Material von Nutzen sein.

Eine Handelsposition kann nicht nur durch Betätigung der Schaltfläche „Schließen“ im Positionsmenü geschlossen werden (Abbildung 2).

Abbildung 2. Schließen einer Position mithilfe der Schaltfläche „Schließen“ des Positionsmenüs.

Abbildung 2. Schließen einer Position mithilfe der Schaltfläche „Schließen“ des Positionsmenüs. 1 - Öffnen des Kontextmenüs der Position, 2 - Auswahl von „Position schließen“ 
3 - Anklicken der Schaltfläche „Schließen“.

Darüber hinaus kann die Position automatisch geschlossen werden, wenn der Preis eine vorgegebene Gewinn- (Take Profit) oder Verlustgrenze (Stop Loss) erreicht. Im Unterschied zum Schließen der Position mithilfe der Schaltfläche „Schließen“ erfolgt das Schließen gemäß Stop Loss und Take Profit nicht an dem Ausgabegerät (durch den Händler selbst oder ein automatisches Handelssystem) sondern durch den Makler. Dadurch ist das Schließen der Position vollkommen gewährleistet unabhängig von dem Bestehen einer Verbindung und der Stromversorgung. Das macht die Verwendung von Stop Loss zu einem praktisch unverzichtbaren Element für die Arbeit eines Händlers.

Das Einzige, was der Händler tun muss, ist, den Makler anzuweisen, die Sicherheitsgrenze zu setzen. Mit anderen Worten, wir müssen den Stop Loss für die Position festlegen (oder die Position gleich mit dieser vorgegebenen Grenze eröffnen). Das Setzen der Stop Loss-Grenze erfolgt mithilfe des Befehls „Ändern“ des Kontextmenüs im Ausgabegerät. Dazu wählen wir in der Liste der Positionen des Ausgabegerätes mit dem Mauszeiger eine Position aus, betätigen die rechte Maustaste und wählen „Ändern oder löschen“. Danach geben wir in dem sich öffnenden Dialogfenster die gewünschte Stop Loss-Grenze ein und klicken auf „Ändern“ (Abbilddung 3).

Abbildung 3. Festlegen der Stop Loss-Grenze einer Position

Abbildung 3. Festlegen der Stop Loss-Grenze einer Position 1 - Öffnen des Kontextmenüs der Position, 2 - Anklicken von „Ändern oder löschen“, 3 - Festlegen des Wertes, 4 - Anklicken von „Ändern“. 

Die Stop Loss-Grenze der Position wird im Kursdiagramm neben ihrem Eröffnungspreis angezeigt (Abbildung 4).

Abbildung 4. Position mit Stop Loss. Die Grenze ist durch die Strichpunktlinie mit der Auszeichnung sl an ihrem linken Rand gekennzeichnet.

Abbildung 4. Position mit Stop Loss. Die Grenze ist durch die Strichpunktlinie mit der Auszeichnung sl an ihrem linken Rand gekennzeichnet.

Eine Stop Loss-Grenze für eine Position kann nicht nur festgelegt, sondern ihr Wert kann auch geändert werden. Zum Beispiel kann sie bei Preisänderungen in Richtung einer Gewinnsteigerung der Position hinter diesen hergezogen werden, um mögliche spätere Verluste verringert werden. Dieses Hinterherziehen der Sicherheitsgrenze wird auch als gleitende Stop-Grenze oder eben als Trailing Stop bezeichnet.

Es gibt zahlreiche Varianten von Trailing Stops: So kann man einen Stop Loss einfach in einem vorgegebenen Abstand zum jeweiligen Kurs festlegen. Es ist nicht möglich, den Stop Loss sofort umzusetzen, sondern erst nachdem die Position einen bestimmten Gewinn erreicht hat. Dabei wird er zunächst auf den Stand der Gewinnschwelle gesetzt. Das ist die in MetaTrader 5-Ausgabegeräten eingerichtete Standardvariante. Um den Standard-Trailing Stop anzuwenden, müssen das Kontextmenü der Position mit der rechten Maustaste aufgerufen und der Befehl „Trailing Stop“ aufgerufen werden (Abbildung 5).

Abbildung 5. Einschalten des Trailing Stops in einem Ausgabegerät.

Abbildung 5. Einschalten des Trailing Stops in einem Ausgabegerät. 1 - Öffnen des Kontextmenüs der Position, 2 - Anklicken von „Trailing Stop“, 3 - Auswahl (bzw. Eingabe) des Wertes
Der Befehl „Wert festlegen“ (Benutzerdefiniert)
befindet sich ganz unten im Kontextmenü und ist in der Abbildung nicht zu sehen.

Außer anhand der unmittelbaren Beobachtung des Preises kann ein Trailing Stop auch auf der Grundlage eines technischen Indikators arbeiten. Etwa auf Grundlage der gleitenden Durchschnittswerte, wodurch es möglich ist, nicht auf kurzzeitige Preisänderungen zu reagieren, oder anhand des Ichimoku-Indikators oder noch besser geeigneter; und selbst mit dem zu diesen Zwecken entwickelten Indikator Parabolic SAR (Stop And Reverse), siehe Abbildung 6.  

Abbildung 6. Der Indikator Parabolic SAR.

Abbildung 6. Der Indikator Parabolic SAR. 

Bei der prozedurorientierten Programmierung in MQL4 wurden Trailing Stops in der Regel als gesonderte Funktionen angelegt oder in andere Funktionen eingebettet. Bei dem in MetaTrader 4 enthaltenen Expert-System MACD Sample zum Beispiel ist die Trailing Stop-Funktion in die Funktion zur Schließung von Aufträgen am Markt integriert:

for(cnt=0;cnt<total;cnt++)
  {
   OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES);
   if(OrderType()<=OP_SELL &&         // check for opened position 
      OrderSymbol()==Symbol())        // check for symbol
     {
      if(OrderType()==OP_BUY)         // long position is opened
        {
         // should it be closed?
         if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && 
            MacdCurrent>(MACDCloseLevel*Point))
           {
            OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
            return(0); // exit
           }
         // check for trailing stop
         if(TrailingStop>0) 
           {
             
            if(Bid-OrderOpenPrice()>Point*TrailingStop)
              {
               if(OrderStopLoss()<Bid-Point*TrailingStop)
                 {
                  OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green);
                  return(0);
                 }
              }
           }
        }
      else // go to short position
        {
         // should it be closed?
         if(MacdCurrent<0 && MacdCurrent>SignalCurrent && 
            MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*Point))
           {
            OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position
            return(0); // exit
           }
         // check for trailing stop
         if(TrailingStop>0) 
           {
             
            if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
              {
               if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
                 {
                  OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
                  return(0);
                 }
              }
           }
        }
     }
  }

Die Objekt-Orientierte Sprache MQL5 eröffnet wesentlich mehr Möglichkeiten zur Entwicklung von Expert-Systemen. Sie ermöglicht die Erstellung universeller und multifunktionaler Klassen, die später leicht und schnell in praktisch jedes Expert-System eingefügt werden können. Mit der Entwicklung einer solchen Klasse werden wir uns in diesem Beitrag befassen.


1. Erstellen einer Trailing Stop-Basisklasse

Wie bereits gesagt besteht eine riesige Menge unterschiedlicher Trailing Stops, aber ihnen allen gemeinsam sind einige funktionale Gesichtspunkte:

Von der Art des Trailing Stops hängt lediglich der Wert der berechneten Stop Loss-Grenze ab. Auf diese Weise geht die wesentliche Funktionalität des Trailing Stops in die Basisklasse ein. Für von der Art des Trailing Stops abhängige Funktionalitäten werden Unterklassen angelegt. Die Aufrufung der Methoden dieser Unterklassen erfolgt mithilfe virtueller Methoden der Basisklasse.

Da der Einsatz technischer Indikatoren vorgesehen ist, müssen diese zur Gewährleistung ihrer stabilen Funktion regelmäßig abgerufen werden. Zu diesem Zweck kommt ein Zeitgeber (Timer) zum Einsatz. Geplant ist zudem die Möglichkeit der programmgesteuerten Ein-/Ausschaltung der Trailing Stops (bei Verwendung der Klasse innerhalb eines mechanischen Handelssystems) bzw. ihre Ein-/Ausschaltung mithilfe der Schaltfläche eines grafischen Objekts (Bei Verwendung der Klasse als Teil von Hilfs-Expert-Systemen). Diesen funktionalen Vorgaben entsprechend weist die Basisklasse folgende Methodenausstattung auf:

class CTrailingStop
  {
protected:
public:
   void CTrailingStop(){};
   void ~CTrailingStop(){};
   void Init(){};                   // Initialization of class
   bool StartTimer(){};             // Start timer
   void StopTimer(){};              // Stop timer
   void On(){};                     // Turn on trailing stop
   void Off(){};                    // Turn off trailing stop
   bool DoStoploss(){};             // Main method of controlling level of Stop Loss position
   void EventHandle(){};            // Method of processing chart events (pressing button to turn on trailing stop)
   void Deinit(){};                 // Deinitialization
   virtual bool Refresh(){};        // Refresh indicator
   virtual void Setparameters(){};  // Setting parameters and loading indicator
   virtual int Trend(){};           // Trend shown by indicator
   virtual double BuyStoploss(){};  // Stop Loss value for the Buy position
   virtual double SellStoploss(){}; // Stop Loss value for the Sell position
  };

Bei Aufruf der Methode Init() werden allgemeine, von der Art des verwendeten Trailing Stops unabhängige Parameter an sie weitergegeben. Während der Ausführung der Methode werden die Einstellung für die Arbeitsweise des Trailing Stops sowie die Vorbereitung der Variablen mit einigen Marktparametern vorgenommen.

1.1. Die Methode Init()

Die Methode Init() ist die erste Methode, die nach der Erstellung einer Klasseninstanz aufgerufen wird. An sie werden die allgemeinen, von der Art des Trailing Stops unabhängigen Parameter weitergegeben: Kürzel, Zeitrahmen, Trailing Stop-Modus (je Kursänderung (Tick) oder Balken), Verknüpfung oder Nichtverknüpfung des Indikators mit dem Diagramm, Anlegen oder Nichtanlegen einer Schaltfläche. Dann empfängt sie die Eigenschaften der Schaltfläche: die X-Koordinate der Schaltfläche, ihre Y-Koordinate, ihre Farbe und die Farbe ihrer Beschriftung.

Die für die weitere Arbeit erforderlichen Parameter werden in den Variablen der Klasse gespeichert. Außerdem werden bei der Ausführung der Methode Init() die grundlegenden unveränderlichen und für die Arbeit des Trailing Stops erforderlichen Marktparameter festgelegt: die Anzahl der Nachkommastellen sowie die Punktgröße. Zum Abschluss werden je nach Art des gewählten Trailing Stops die Bezeichnung der Schaltfläche sowie ihre Beschriftung festgelegt. Wenn die Verwendung einer Schaltfläche vorgegeben wurde, wird diese jetzt angelegt. 

In dem „geschützten“ Bereich deklarieren wir alle erforderlichen Variablen:

protected:
string m_symbol;             // symbol
ENUM_TIMEFRAMES m_timeframe; // timeframe
bool m_eachtick;             // work on each tick
bool m_indicator;            // show indicator on chart
bool m_button;               // show "turn on/turn off" button
int m_button_x;              // x coordinate of button
int m_button_y;              // y coordinate of button
color m_bgcolor;             // button color
color m_txtcolor;            // button caption color
int m_shift;                 // bar shift
bool m_onoff;                // turned on/turned off
int m_handle;                // indicator handle
datetime m_lasttime;         // time of trailing stop last execution
MqlTradeRequest m_request;   // trade request structure
MqlTradeResult m_result;     // structure of trade request result
int m_digits;                // number of digits after comma for price
double m_point;              // value of point
string m_objname;            // button name
string m_typename;           // name of trailing stop type
string m_caption;            // button caption

Jetzt programmieren wir die Methode Init() selbst:

//--- Trailing stop initialization method
void Init(string             symbol,
          ENUM_TIMEFRAMES timeframe,
          bool   eachtick  =   true,
          bool   indicator =  false,
          bool   button    =  false,
          int    button_x  =      5,
          int    button_y  =     15,
          color  bgcolor   = Silver,
          color  txtcolor  =   Blue)
  {
//--- set parameters
   m_symbol    = symbol;    // symbol
   m_timeframe = timeframe; // timeframe
   m_eachtick  = eachtick;  // true - work on each tick, false - false - work once per bar 
//--- set bar, from which indicator value is used
   if(eachtick)
     {
      m_shift=0; // created bar in per tick mode
     }
   else
     {
      m_shift=1; // created bar in per bar mode
     }
   m_indicator = indicator; // true - attach indicator to chart
   m_button    = button;    // true - create button to turn on/turn off trailing stop
   m_button_x  = button_x;  // x coordinate of button
   m_button_y  = button_y;  // y coordinate of button
   m_bgcolor   = bgcolor;   // button color
   m_txtcolor  = txtcolor;  // button caption color 
//--- get unchanged market history 
   m_digits=(int)SymbolInfoInteger(m_symbol,SYMBOL_DIGITS); // number of digits after comma for price
   m_point=SymbolInfoDouble(m_symbol,SYMBOL_POINT);         // value of point 
//--- creating button name and button caption
   m_objname="CTrailingStop_"+m_typename+"_"+symbol;        // button name
   m_caption=symbol+" "+m_typename+" Trailing";             // button caption 
//--- filling the trade request structure
   m_request.symbol=m_symbol;                               // preparing trade request structure, setting symbol
   m_request.action=TRADE_ACTION_SLTP;                      // preparing trade request structure, setting type of trade action
//--- creating button
   if(m_button)
     {
      ObjectCreate(0,m_objname,OBJ_BUTTON,0,0,0);                 // creating
      ObjectSetInteger(0,m_objname,OBJPROP_XDISTANCE,m_button_x); // setting x coordinate
      ObjectSetInteger(0,m_objname,OBJPROP_YDISTANCE,m_button_y); // setting y coordinate
      ObjectSetInteger(0,m_objname,OBJPROP_BGCOLOR,m_bgcolor);    // setting background color
      ObjectSetInteger(0,m_objname,OBJPROP_COLOR,m_txtcolor);     // setting caption color
      ObjectSetInteger(0,m_objname,OBJPROP_XSIZE,120);            // setting width
      ObjectSetInteger(0,m_objname,OBJPROP_YSIZE,15);             // setting height
      ObjectSetInteger(0,m_objname,OBJPROP_FONTSIZE,7);           // setting font size
      ObjectSetString(0,m_objname,OBJPROP_TEXT,m_caption);        // setting button caption 
      ObjectSetInteger(0,m_objname,OBJPROP_STATE,false);          // setting button state, turned off by default
      ObjectSetInteger(0,m_objname,OBJPROP_SELECTABLE,false);     // user can't select and move button, only click it
      ChartRedraw();                                              // chart redraw 
     }
//--- setting state of trailing stop
   m_onoff=false;                                                 // state of trailing stop - turned on/turned off, turned off by default 
  };

Es ist ersichtlich, dass beim Anlegen der Bezeichnung und der Beschriftung der Schaltfläche die ohne einen einzigen Wert bereitgestellte Variable m_typename verwendet wird. Die Zuweisung von Werten zu ihr erfolgt in den Konstruktoren der Unterklassen, sodass sie bei Verwendung unterschiedlicher Trailing Stop-Methoden jeweils den der Art des verwendeten Trailing Stops entsprechenden Wert aufweist. 

1.2. Die Methode StartTimer()

Die Methode StartTimer() setzt den allgemeinen Zeitgeber (Timer) des Expert-Systems in Gang.  

//--- Start timer
bool StartTimer()
  {
   return(EventSetTimer(1));
  };

Bei Verwendung eines Zeitgebers muss zu der Funktion OnTimer() unbedingt der Aufruf der Methode Refresh() hinzugefügt werden, um den Indikator von Zeit zu Zeit abfragen.

1.3. Die Methode StopTimer()

Die Methode StopTimer() sorgt für das Anhalten des Zeitgebers eines Expert-Systems.  

//--- Stop timer
void StopTimer()
  {
   EventKillTimer();
  };

Bei Abschluss der Arbeit des Expert-Systems ist es bei Verwendung eines Zeitgebers unverzichtbar diesen anzuhalten, und genau das tut diese Methode. Diese Methode wird bei Ausführung der Methode Deinit() einer Klasse aufgerufen.  

1.4. Die Methode On()

Die Methode On() ermöglicht die programmgesteuerte Einschaltung des Trailing Stops. Das Einschalten erfolgt durch die Einstellung der Variablen m_onoff auf den Wert „true“. Wenn bei der Bereitstellung der Klasse die Verwendung einer Schaltfläche vorgegeben wurde, wird diese jetzt in die gedrückte Stellung versetzt. 

//--- Turn on trailing stop
void On()
  {
   m_onoff=true; 
   if(m_button)
     { // if button is used, it is "pressed"
      if(!ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        {
         ObjectSetInteger(0,m_objname,OBJPROP_STATE,true);
        }
     }
  }

1.5. Die Methode Off()

Die Methode Off() ermöglicht das programmgesteuerte Ausschalten des Trailing Stops. Das Ausschalten erfolgt durch die Einstellung der Variablen m_onoff auf den Wert „false“. Wenn bei der Bereitstellung der Klasse die Verwendung einer Schaltfläche vorgegeben wurde, wird diese jetzt in die nicht gedrückte Stellung versetzt. 

//--- Turn off trailing stop
void Off()
  {
   m_onoff=false;
   if(m_button)
     { // if button is used, it is "depressed"
      if(ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        {
         ObjectSetInteger(0,m_objname,OBJPROP_STATE,false);
        }
     }
  }

1.6. Die Methode EventHandle()

Die Methode EventHandle() wird aus der Funktion OnChartEvent() aufgerufen und erhält entsprechend alle an diese weitergegebenen Parameter.

//--- Method of tracking button state - turned on/turned off
void EventHandle(const int id,const long  &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_OBJECT_CLICK && sparam==m_objname)
     { // there is an event with button
      if(ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        { // check button state
         On(); // turn on
        }
      else
        {
         Off(); // turn off
        }
     }
  }

Bei Eintreten des Ereignisses CHARTEVENT_OBJECT_CLICK, und wenn dieses Ereignis mit der Schaltfläche mit der Bezeichnung m_objname (die Bezeichnung des Objektes, mit dem sich das Ereignis zugetragen hat, wird in der Variablen sparam übermittelt) auftritt, wird je nach Stellung der Schaltfläche entweder die Methode On() oder Off() ausgeführt.

1.7. Die Methode Deinit()

Die Methode Deinit() muss bei Abschluss der Arbeit des Expert-Systems aufgerufen werden. Bei der Ausführung dieser Methode werden der Zeitgeber angehalten, das Handle des Indikators freigegeben und die Schaltfläche, sofern verwendet, gelöscht.  

//--- Method of deinitialization
void Deinit()
  {
   StopTimer();                  // stop timer
   IndicatorRelease(m_handle);   // release indicator handle
   if(m_button)
     {
      ObjectDelete(0,m_objname); // delete button
      ChartRedraw();             // chart redraw
     }
  }

Die virtuellen Methoden Refresh(), SetParameters(), Trend(), BuyStoploss() und SellStoploss() werden später, bei der Erstellung der Unterklassen des Trailing Stops, betrachtet. Jetzt widmen wir zunächst einmal ausführlicher der Hauptmethode der Basisklasse, der Methode DoStoploss().

1.8. Die Methode DoStoploss()

Bei der Methode DoStoploss() handelt es sich um die Hauptarbeitsmethode, die bei jeder Kursschwankung aus der Funktion OnTick() des Expert-Systems aufgerufen werden muss. Ist der Wert der Variablen m_onoff gleich „false“ (der Trailing Stop ist ausgeschaltet), erfolgt der sofortige Abschluss der Arbeit der Methode.  

if(!m_onoff)
  {
   return(true);// if trailing stop is turned off
  }

Des Weiteren, wenn der Trailing Stop balkenweise arbeitet, erfolgt eine Überprüfung der Zeit, ein Vergleich der Zeit des erstellten Balkens mit der Zeit der letzten erfolgreichen Ausführung der Funktion. Stimmen beide Zeiten überein, schließt die Methode ihre Arbeit ab.

datetime tm[1];
// get the time of last bar in per bar mode 
if(!m_eachtick)
  { 
   // if unable to copy time, finish method, repeat on next tick 
   if(CopyTime(m_symbol,m_timeframe,0,1,tm)==-1)
     {
      return(false); 
     }
   // if the bar time is equal to time of method's last execution - finish method
   if(tm[0]==m_lasttime)
     { 
      return(true);
     }
  }

Wenn der Trailing Stop eingeschaltet ist, und die Überprüfung der Zeit stattgefunden hat, wird der Hauptteil der Methode ausgeführt, die Werte des Indikators werden aktualisiert, die Methode Refresh() wird aufgerufen.

if(!Refresh())
  { // get indicator values
   return(false);
  }

Anschließend wird je nachdem, welcher Wert von der Methode Trend() ausgegeben wurde, der Trailing Stop entweder für die Position „Buy“ oder „Sell“ ausgeführt.

// depending on trend, shown by indicator, do various actions
switch (Trend())
  {
   // Up trend
   case 1: 
      // code of trailing stop for the buy position
      break;
   // Down trend
   case -1: 
      // code of trailing stop for the sell position
      break;
  }

Betrachten wir ihr Wirken am Beispiel der Position „Buy“.

if(PositionSelect(m_symbol,1000))
  {   //--- select position. if succeeded, then position exists
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
     {//--- if position is buy

      //--- get Stop Loss value for the buy position
      sl=BuyStoploss(); 
      //--- find out allowed level of Stop Loss placement for the buy position
      double minimal=SymbolInfoDouble(m_symbol,SYMBOL_BID)-m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
      //--- value normalizing
      sl=NormalizeDouble(sl,m_digits); 
      //--- value normalizing
      minimal=NormalizeDouble(minimal,m_digits); 
      //--- if unable to place Stop Loss on level, obtained from indicator, 
      //    this Stop Loss will be placed on closest possible level
      sl=MathMin(sl,minimal); 
      //--- value of Stop Loss position
      double possl=PositionGetDouble(POSITION_SL); 
      //--- value normalizing
      possl=NormalizeDouble(possl,m_digits); 
      if(sl>possl)
        {//--- if new value of Stop Loss if bigger than current value of Stop Loss, 
         //    an attempt to move Stop Loss on a new level will be made
         //--- filling request structure
         m_request.sl=sl; 
         //--- filling request structure
         m_request.tp=PositionGetDouble(POSITION_TP); 
         //--- request
         OrderSend(m_request,m_result); 
         if(m_result.retcode!=TRADE_RETCODE_DONE)
           {//--- check request result
            //--- log error message
            printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode); 
            //--- unable to move Stop Loss, finishing
            return(false); 
           }
        }
     }
  }

Wenn die Position ausgewählt werden kann, erfolgt die Überprüfung ihrer Art. Entspricht die Art der Position dem Trend, dann erhalten wir bei Verwendung der Methode BuyStoploss() den erforderlichen Stop Loss-Wert (in der Variablen sl). Als Nächstes bestimmen wir die zulässige Grenze, auf die der Stop Loss gesetzt werden kann. Wenn die berechnete Grenze näher liegt als die zulässige, passen wir den Wert der Variablen sl an. Danach erhalten wir den aktuellen Stop Loss-Wert der Position (in der Variablen possl), und vergleichen die Werte der Variablen sl und possl. Ist der neue Stop Loss-Wert besser als der aktuelle, dann passen wir die Position an.

Vor der Ausführung der Anpassung füllen wir die Felder sl und tp der MqlTradeRequest-Struktur. Der Variablen m_request.sl wird der erforderliche Stop Loss-Wert zugewiesen, während die Variable m_request.tp den bestehenden Take Profit-Wert erhält (dieser bleibt unverändert). Die übrigen Felder wurden bereits bei der Ausführung der Methode Init() gefüllt. Nach dem Füllen der Struktur wird die Funktion OrderSend() aufgerufen.

Nach Abschluss der Arbeit wird der Wert der Variablen m_result.retcode geprüft. Wenn der Wert nicht gleich TRADE_RETCODE_DONE ist, bedeutet das, dass die von der Funktion OrderSend() angeforderte Handlung aus irgendeinem Grund nicht ausgeführt werden konnte. Gleichzeitig erscheint im Protokoll eine Meldung mit der Fehlernummer und die Arbeit der Methode wird beendet. Wird die Funktion OrderSend() erfolgreich ausgeführt, wird die Zeit des Balkens gespeichert, den die Methode DoStoploss() zuletzt bearbeitet hat. Im Fall eines Fehlers erfolgt selbst bei balkenweisem Vorgehen bei der nächsten Kursänderung ein erneuter Versuch, die Methode auszuführen. Diese Versuche werden solange wiederholt, bis die Arbeit erfolgreich abgeschlossen ist.

Hier sehen wir den gesamten Code der Methode DoStopLoss():

bool DoStoploss()
  {
//--- if trailing stop is turned off
   if(!m_onoff)
     {
      return(true);
     }
   datetime tm[1];
//--- get the time of last bar in per bar mode
   if(!m_eachtick)
     {
      //--- if unable to copy time, finish method, repeat on next tick 
      if(CopyTime(m_symbol,m_timeframe,0,1,tm)==-1)
        {
         return(false);
        }
      //--- if the bar time is equal to time of method's last execution - finish method
      if(tm[0]==m_lasttime)
        {
         return(true);
        }
     }
//--- get indicator values
   if(!Refresh())
     {
      return(false);
     }
   double sl;
//--- depending on trend, shown by indicator, do various actions
   switch(Trend())
     {
      //--- Up trend
      case 1:
         //--- select position. if succeeded, then position exists
         if(PositionSelect(m_symbol))
           {
            //--- if position is buy
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
              {
               //--- get Stop Loss value for the buy position
               sl=BuyStoploss();
               //--- find out allowed level of Stop Loss placement for the buy position
               double minimal=SymbolInfoDouble(m_symbol,SYMBOL_BID)-m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
               //--- value normalizing
               sl=NormalizeDouble(sl,m_digits);
               //--- value normalizing
               minimal=NormalizeDouble(minimal,m_digits);
               //--- if unable to place Stop Loss on level, obtained from indicator, 
               //    this Stop Loss will be placed on closest possible level
               sl=MathMin(sl,minimal);
               //--- value of Stop Loss position
               double possl=PositionGetDouble(POSITION_SL);
               //--- value normalizing
               possl=NormalizeDouble(possl,m_digits);
               //--- if new value of Stop Loss if bigger than current value of Stop Loss, 
               //    an attempt to move Stop Loss on a new level will be made
               if(sl>possl)
                 {
                  //--- filling request structure
                  m_request.sl=sl;
                  //--- filling request structure
                  m_request.tp=PositionGetDouble(POSITION_TP);
                  //--- request
                  OrderSend(m_request,m_result);
                  //--- check request result
                  if(m_result.retcode!=TRADE_RETCODE_DONE)
                    {
                     //--- log error message
                     printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode);
                     //--- unable to move Stop Loss, finishing
                     return(false);
                    }
                 }
              }
           }
         break;
         //--- Down trend
      case -1:
         if(PositionSelect(m_symbol))
           {
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
              {
               sl=SellStoploss();
               //--- adding spread, since Sell is closing by the Ask price
               sl+=(SymbolInfoDouble(m_symbol,SYMBOL_ASK)-SymbolInfoDouble(m_symbol,SYMBOL_BID));
               double minimal=SymbolInfoDouble(m_symbol,SYMBOL_ASK)+m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
               sl=NormalizeDouble(sl,m_digits);
               minimal=NormalizeDouble(minimal,m_digits);
               sl=MathMax(sl,minimal);
               double possl=PositionGetDouble(POSITION_SL);
               possl=NormalizeDouble(possl,m_digits);
               if(sl<possl || possl==0)
                 {
                  m_request.sl=sl;
                  m_request.tp=PositionGetDouble(POSITION_TP);
                  OrderSend(m_request,m_result);
                  if(m_result.retcode!=TRADE_RETCODE_DONE)
                    {
                     printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode);
                     return(false);
                    }
                 }
              }
           }
         break;
     }
//--- remember the time of method's last execution
   m_lasttime=tm[0];
   return(true);
  }

Achten Sie auf die Codeunterschiede bei den Positionen „Buy“ und „Sell“. Für die „Sell“-Position steigt der von der Methode SellStoploss() ausgegebene Wert um den Wert des Spreads, weil die Position „Sell“ durch den „Ask“-Preis geschlossen wird. Entsprechend erfolgt das Herunterzählen der Stop Loss-Untergrenze für „Buy“ vom „Bid“-Preis aus, für „Sell“ vom „Ask“-Preis aus.

Damit ist die Erstellung der Basisklasse für Trailing Stops abgeschlossen. Fahren wir fort mit dem Anlegen von Unterklassen. 

2. Die Unterklasse Trailing Stop für den Indikator Parabolic SAR

Anhand der virtuellen Methoden der Klasse CTrailingStop sollte die Zusammensetzung der Unterklasse bereits klar sein: Die Methoden SetParameters(), Refresh(), Trend(), BuyStoploss() und SellStoploss() sowie der Konstruktor der Klasse zur Festlegung des Namens des Trailing Stops. Die Klasse erhält die Bezeichnung CParabolicStop. Da es sich bei der Klasse um eine Unterklasse der Klasse CTrailingStop handelt, wird das in ihrer Deklaration angegeben.

class CParabolicStop: public CTrailingStop

Aufgrund dieser Deklaration werden beim Aufruf der virtuellen Methoden der Klasse CParabolicStop die aus der Basisklasse übernommenen Methoden der gewählten Unterklasse ausgeführt.  

Sehen wir uns die Methoden der Unterklasse einmal genauer an.

2.1. Die Methode CParabolicStop()

Diese Methode hat denselben Namen wie die Klasse selbst, sie wird als Konstruktor bezeichnet. Sie wird in dem Moment automatisch ausgeführt, in dem die klasse geladen wird, noch bevor in dem Programm irgendwelche anderen Methoden der Klasse aufgerufen werden. Bei der Ausführung der Methode CParabolicStop() erfolgt die Zuweisung der Bezeichnung des Trailing Stops an die Variable m_typename. Diese Variable wird zur Erstellung der Bezeichnung und der Beschriftung der Schaltfläche verwendet (bei Ausführung der Methode Init() der Basisklasse).

void CParabolicStop()
  {
   m_typename="SAR"; // setting name of trailing stop type
  };

2.2. Die Methode SetParameters()

Bei Aufruf der Methode SetParameters() empfängt diese die Parameter für den Indikator und lädt sie in diesen. Wird bei der Ausführung der Methode Init() der Basisklasse der Parameter m_indicator gesetzt, dann wird der Indikator mit dem Diagramm verknüpft (Die Funktion ChartIndicatorAdd()).

// Method of setting parameters and loading the indicator
bool SetParameters(double sarstep=0.02,double sarmaximum=0.2)
  {
   m_handle=iSAR(m_symbol,m_timeframe,sarstep,sarmaximum); // loading indicator
   if(m_handle==-1)
     {
      return(false); // if unable to load indicator, method returns false
     }
   if(m_indicator)
     {
      ChartIndicatorAdd(0,0,m_handle); // attach indicator to chart
     }
    
   return(true);
  }

2.3. Die Methode Refresh()

Die Methode Refresh() sorgt für den Eingang des neuen Preises sowie für die Aktualisierung der Werte des Indikators. Für den Wert des Preises ist im geschützten Bereich („protected“) der Klasse das Datenfeld pricebuf deklariert, für den Indikatorwert das Datenfeld indbuf. Beide Datenfelder (Arrays) umfassen jeweils nur ein Element, erforderlich ist nur jeweils ein Wert für den Preis und für den Indikator von dem zu erstellenden oder erstellten Balken (je nachdem, welcher m_shift-Parameter bei der Bereitstellung der Basisklasse festgelegt wurde).

// Method of getting indicator values
bool Refresh()
  {
   if(CopyBuffer(m_handle,0,m_shift,1,indbuf)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }
    
   if(CopyClose(m_symbol,m_timeframe,m_shift,1,pricebuf)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }    
   return(true); 
  }

2.4. Die Methode Trend()

Die Methode Trend() überprüft die Lage des Preises in Bezug auf die Linie des Indikators. Befindet sich der Preis oberhalb der Linie, handelt es sich um einen steigenden Trend, und die Methode gibt den Wert „1“ aus. Liegt der Preis unterhalb der Linie, ist der Trend absteigend, und die Methode gibt den Wert „-1“ aus. Es ist nicht ausgeschlossen (selten, aber möglich), dass der Preis auf der Indikatorlinie liegt. In diesem Fall wird der Wert „0“ ausgegeben.  

// Method of finding trend
int Trend()
  {
   if(pricebuf[0]>indbuf[0])
     { // price is higher than indicator line, up trend
      return(1);
     }
   if(pricebuf[0]<indbuf[0])
     { // price is lower than indicator line, down trend
      return(-1);
     }    
   return(0);
  }

2.5. Die Methoden BuyStoploss() und SellStoploss()

Da der Indikator Parabolic SAR lediglich über eine Linie verfügt, sind beide Methoden absolut identisch. Sie geben den bei der Ausführung der Methode Refresh() erhaltenen Wert aus.

// Method of finding out Stop Loss level for buy
virtual double BuyStoploss()
  {
   return(indbuf[0]);
  };
// Method of finding out Stop Loss level for sell
virtual double SellStoploss()
  {
   return(indbuf[0]);
  };

Damit ist der Trailing Stop sozusagen erst einmal fertig. Bisher enthält er nur eine Unterklasse, aber er kann bereits verwendet werden. Wir erstellen ihn als gesonderte Include-Datei und speichern ihn im Ordner .\MQL5\Include unter der Bezeichnung Sample_TrailingStop.mqh (die Datei befindet sich im Anhang zu diesem Beitrag). 

3. Hinzufügen eines Trailing Stops für den Indikator Parabolic SAR zu einem Expert-System

Versuchen wir, den angelegten Trailing Stop zu irgendeinem Expert-System hinzuzufügen, z. B. zu dem Expert Advisor My_First_EA aus dem Artikel Schrittweiser Leitfaden für Anfänger zum Schreiben eines Expert Advisors in MQL5.

3.1. Wir öffnen die Datei mit dem Expert-System My_First_EA im MetaEditor und speichern sie unter dem Namen My_First_EA_SARTrailing.

3.2. Wir fügen die Trailing Stop-Datei an. In den oberen Teil des Programmcodes des Expert-Systems fügen wir die folgende Zeile ein (bevorzugt VOR der Deklaration der externen Variablen): 

#include <Sample_TrailingStop.mqh> // include Trailing Stop class

3.3. Unterhalb der externen Variablen legen wir eine Instanz der Klasse CParabolicStop unter der Bezeichnung Trailing an.

CParabolicStop Trailing; // create class instance 

 3.4. In der Funktion OnInit() stellen wir die Klasse bereit und stellen ihre Parameter ein. Zunächst deklarieren wir die externen Variablen mit den Parametern des Indikators:

input double TrailingSARStep=0.02;
input double TrailingSARMaximum=0.2;

Anschließend fügen wir den Code in Funktion OnInit() ein.

Trailing.Init(_Symbol,PERIOD_CURRENT,true,true,false); // Initialize (set basic parameters)
if(!trailing.setparameters(TrailingSARStep,TrailingSARMaximum))
  { // Set parameters of used trailing stop type
   Alert("trailing error");
   return(-1);
  } 
Trailing.StartTimer(); // Start timer
Trailing.On();         // Turn on

3.5. Im Code des Expert-Systems suchen wir die Funktion OnTimer(). In dem Expert-System My_First_EA wird die Funktion OnTimer() nicht verwendet, das heißt, wir müssen sie hinzufügen, und wir erweitern sie um den Aufruf der Methode Refresh(). 

void OnTimer()
  {
   Trailing.Refresh();
  }

3.6. Ganz oben in der Funktion OnTick() ergänzen wir den Aufruf der Methode DoStoploss().

3.7. Jetzt stellen wir das Expert-System zusammen und versuchen es zu testen. Die Ergebnisse des Tests des Expert-Systems sind in den Abbildungen 7 (ohne Trailing Stop) und 8 (mit Trailing Stop) dargestellt .

Abbildung 7. Testergebnisse des Expert-Systems ohne Trailing Stop.

Abbildung 7. Testergebnisse des Expert-Systems ohne Trailing Stop.   

Abbildung 8. Testergebnisse des Expert-Systems mit Trailing Stop.

Abbildung 8. Testergebnisse des Expert-Systems mit Trailing Stop. 

Die gesteigerte Leistungsfähigkeit bei Verwendung eines Trailing Stops ist unübersehbar.

Die Datei My_First_EA_SARTrailing.mq5 befindet sich im Anhang zu diesem Beitrag.

4. Die Unterklasse Trailing Stop für den Indikator NRTR

Der Indikator NRTR (Nick Rypock Trailing Reverse) lädt seinem Namen (Kursrückgangsnachlauf) und seiner äußeren Erscheinung (Abbildung 9) nachgerade zu dem Versuch ein, einen Trailing Stop für ihn anzulegen.

Abbildung 9. Der Indikator NRTR.

Abbildung 9. Der Indikator NRTR.

Der Indikator zeichnet die Vergleichslinie (die Unterstützungs- oder die Widerstandslinie) sowie die Ziellinie. Überschreitet der Preis die Ziellinie, so wird die Vergleichslinie unter Außerachtlassung geringfügiger Kursschwankungen in Richtung der Preisbewegung verschoben. Wenn der Preis die Vergleichslinie schneidet, gilt das als Trendänderung, bei der sowohl die Vergleichslinie als auch die Ziellinie relativ zum Preis verschoben werden. Die Unterstützungs- und die Ziellinie erscheinen bei einem aufsteigenden Trend in blauer Farbe, bei einem absteigenden Trend dagegen in Rot.

Wir legen einen weiteren Trailing Stop an, jetzt für den Indikator NRTR.

Dazu deklarieren wir eine weitere Klasse CNRTRStop, die in die Basisklasse CNRTRStop eingeht. 

class CNRTRStop: public CTrailingStop

Die Unterklasse Trailing Stop für den Indikator NRTR wird über genau denselben Methodenbestand verfügen wie die Unterklasse Trailing Stop für den Indikator Parabolic SAR mit Ausnahme des Konstruktors, der jetzt CNRTRStop() heißt.  

4.1. Die Methode CNRTRStop()

Jetzt schreiben wir der Variablen m_typename im Konstruktor der Klasse den NRTR-Wert gemäß dem verwendeten Indikator zu.

void CNRTRStop()
  {
   m_typename="NRTR"; // setting name of trailing stop type
  };

4.2. Die Methode SetParameters()

Bei Aufruf der Methode SetParameters() empfängt diese die Parameter für den Indikator NRTR und lädt sie in diesen. Anschließend wird der Indikator je nachdem, welche grundlegenden Parameter für den Trailing Stop eingerichtet wurden, in das Diagramm eingebunden.

// Method of setting parameters and loading the indicator
bool SetParameters(int period,double k)
  {
   m_handle=iCustom(m_symbol,m_timeframe,"NRTR",period,k); // loading indicator
   if(m_handle==-1)
     { // if unable to load indicator, method returns false
      return(false); 
     }
   if(m_indicator)
     {
       
      ChartIndicatorAdd(0,0,m_handle); // attach indicator to chart
     }
   return(true);
  }

4.3. Die Methode Refresh()

Mit der Methode Refresh() werden zwei Puffer des Indikators NRTR kopiert, der Puffer für die Unterstützungslinie und der Puffer für die Widerstandslinie.  

 // Method of getting indicator values
bool Refresh()
  {
   if(CopyBuffer(m_handle,0,m_shift,1,sup)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }
    
   if(CopyBuffer(m_handle,1,m_shift,1,res)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }
    
   return(true);
  }

Für die Werte des Indikators sind im „geschützten“ Bereich der Klasse zwei Datenfelder deklariert: double sup[] und double res[].

protected:
double sup[1]; // value of support level
double res[1]; // value of resistance level

4.4. Die Methode Trend()

Die Methode Trend() prüft, welche der Linien zum jeweils gegenwärtigen Zeitpunkt vorhanden ist. Ist es die Unterstützungslinie, bedeutet das, dass der Indikator einen aufsteigenden Trend anzeigt. Die Methode gibt entsprechend den Wert „1“ aus. Handelt es sich dagegen um die Widerstandslinie, lautet der von der Methode ausgegebene Wert „-1“. 

// Method of finding trend
int Trend()
  {
   if(sup[0]!=0)
     { // there is support line, then it is up trend
      return(1);
     }
   if(res[0]!=0)
     { // there is resistance line, then it is down trend
      return(-1);
     }
    
   return(0);
  }

4.5. Die Methode BuyStoploss()

Die Methode BuyStoploss() gibt den Wert der Unterstützungslinie aus. 

// Method of finding out Stop Loss level for buy
double BuyStoploss()
  {
   return(sup[0]);
  }

4.6. Die Methode SellStoploss()

Die Methode SellStoploss() gibt den Wert der Widerstandslinie aus.  

// Method of finding out Stop Loss level for sell
double SellStoploss()
  {
   return(res[0]);
  }

Damit ist die Klasse Trailing Stop vollständig. 

5. Hinzufügen eines Trailing Stops für den Indikator NRTR zu einem Expert-System

Wie wir es bereits mit dem Trailing Stop für den Indikator Parabolic SAR gemacht haben, ergänzen wir das Expert-System My_First_EA jetzt um den Trailing Stop für den Indikator NRTR.

5.1. Wir öffnen die Datei mit dem Expert-System My_First_EA im MetaEditor und speichern sie unter dem Namen My_First_EA_NRTRTrailing.

5.2. Wir ersetzen die externen Parameter des Trailing Stops für den Indikator Parabolic SAR durch diejenigen für den Indikator NRTR.

input int TrailingNRTRPeriod = 40;
input double TrailingNRTRK   =  2;

5.3. Anstelle einer Instanz der Klasse CParabolicStop erstellen wir eine Instanz der Klasse CNRTRStop. Ihr Code befindet sich hinter den externen Variablen. 

CNRTRStop Trailing; // create class instance 

5.4. In der Funktion OnInit() ersetzen wir die Aufrufparameter der Methode SetParameters() durch die Parameter des Indikators NRTR.

Trailing.SetParameters(TrailingNRTRPeriod,TrailingNRTRK)

5.5. Jetzt stellen wir das Expert-System zusammen und versuchen es zu testen. 

Abbildung 10. Testergebnisse des Expert-Systems mit einem NRTR-Trailing Stop.

Abbildung 10. Testergebnisse des Expert-Systems mit einem NRTR-Trailing Stop.

Das Arbeitsergebnis des Expert-Systems mit einem Trailing Stop (Abb. 10) hat sich im Vergleich zur Arbeit eines Expert-Systems ohne Trailing Stop (Abb. 7) so gut wie gar nicht verändert. Die Verwendung eines Trailing Stops für den Indikator Parabolic SAR hat sich für dieses Expert-System als wirkungsvoller erwiesen. Das lässt darauf schließen, das die Vorhaltung mehrerer Trailing Stops bei der Entwicklung von Expert-Systemen überaus nützlich sein kann - zur Durchführung von Experimenten sowie zur Auswahl der passendsten Art des Trailing Stops.  

Die Datei My_First_EA_NRTRTrailing.mq5 befindet sich im Anhang zu diesem Beitrag.

6. Ein Assistent für das Expert-System

Beim Anlegen der Basisklasse für Trailing Stops wurde die Möglichkeit der Steuerung des Ein- und Ausschaltens des Trailing Stops mithilfe einer Schaltfläche vorgesehen. Lassen Sie uns einen Assistenten für unser Expert-System anlegen, der die Positionen in unterschiedlichen Kürzeln mithilfe verschiedener Arten von Trailing Stops begleitet. Das Expert-System eröffnet keine Positionen, es verfolgt lediglich bereits eröffnete.

6.1. Im MetaEditor legen wir ein neues Expert-System mit dem Namen Sample_TrailingStop an.

6.2. Wir fügen die Include-Datei Sample_TrailingStop.mqh ein. 

#include <Sample_TrailingStop.mqh> // include Trailing Stop class

6.3. Jetzt deklarieren wir die externen Parameter für die Indikatoren.

input double SARStep=0.02;     // Step of Parabolic
input double SARMaximum=0.02;  // Maximum of Parabolic
input int NRTRPeriod=40;       // NRTR period
input double NRTRK=2;          // NRTR factor

6.4. Wir deklarieren die Datenfelder (Arrays) mit den Kürzeln, mit denen das Expert-System arbeiten darf.

string Symbols[]={"EURUSD","GBPUSD","USDCHF","USDJPY"};

6.5. Hier deklarieren wir die Datenfelder zum Laden der Klassen.

CParabolicStop *SARTrailing[];
CNRTRStop *NRTRTrailing[];

6.6. In der Funktion OnInit() des Expert-Systems ändern wir die Größe der Datenfelder zum Laden der Klassen in Übereinstimmung mit dem Datenfeld „Symbols“. 

ArrayResize(SARTrailing,ArraySize(Symbols));  // resize according to number of used symbols
ArrayResize(NRTRTrailing,ArraySize(Symbols)); // resize according to number of used symbols 

6.7. In dem Zyklus (Loop) zu jedem Element des Datenfeldes laden wir eine Instanz der Klasse.

for(int i=0;i<ArraySize(Symbols);i++)
  { // for all symbols
   SARTrailing[i]=new CParabolicStop(); // create CParabolicStop class instance
   SARTrailing[i].Init(Symbols[i],PERIOD_CURRENT,false,true,true,5,15+i*17,Silver,Blue);    // initialization of CParabolicStop class instance 
   if(!SARTrailing[i].SetParameters(SARStep,SARMaximum))
     { // setting parameters of CParabolicStop class instance 
      Alert("trailing error");
      return(-1);
     }
   SARTrailing[i].StartTimer();         // start timer
//----
   NRTRTrailing[i]=new CNRTRStop();     // create CNRTRStop class instance
   NRTRTrailing[i].Init(Symbols[i],PERIOD_CURRENT,false,true,true,127,15+i*17,Silver,Blue); // initialization of CNRTRStop class instance
   if(!NRTRTrailing[i].SetParameters(NRTRPeriod,NRTRK))
     { // setting parameters of CNRTRcStop class instance 
      Alert("trailing error");
      return(-1);
     }
   NRTRTrailing[i].StartTimer();        // start timer 
  }

Hinweis: bei Aufruf der Init()-Methoden erfolgt die Berechnung der Koordinaten der Schaltflächen. Links haben wir die Schaltflächen zum Einschalten der Trailing Stops für den Indikator Parabolic SAR, rechts für NRTR.  

6.8. In die Funktion OnTick() nehmen wir den Aufruf der Methode DoStoploss() für jede Instanz des Trailing Stops auf.

for(int i=0;i<ArraySize(Symbols);i++)
  {
   SARTrailing[i].DoStoploss();
   NRTRTrailing[i].DoStoploss();
  }

6.9. Dann fügen wir die Verarbeitung von Ereignissen hinzu. 

void OnChartEvent(const int         id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam 
                  )
  {
   for(int i=0;i<ArraySize(Symbols);i++)
     {
      SARTrailing[i].EventHandle(id,lparam,dparam,sparam);
      NRTRTrailing[i].EventHandle(id,lparam,dparam,sparam);
     }
    
  }

6.10. Mithilfe der Funktion Deinit() bereinigen wir alle Klasseninstanzen und löschen sie.

for(int i=0;i<ArraySize(Symbols);i++)
  {
   SARTrailing[i].Deinit(); 
   NRTRTrailing[i].Deinit();
   delete(SARTrailing[i]);  // delete object
   delete(NRTRTrailing[i]); // delete object
  }

Abschließend stellen wir das Expert-System zusammen und verknüpfen es mit dem Diagramm, auf dem dann unsere Indikatoren und Schaltflächen erscheinen (Abb. 11) - Das Expert-System ist einsatzbereit.  

Abbildung 11. Schaltflächen und Indikatoren im Diagramm nach Auslösung des Expert-Systems Sample_TrailingStop.

Abbildung 11. Schaltflächen und Indikatoren im Diagramm nach Auslösung des Expert-Systems Sample_TrailingStop. 

Jetzt müssen wir nur noch die erforderliche Schaltfläche betätigen, um die entsprechende Position zu begleiten, sobald sie eröffnet wird.

Die Datei Sample_TrailingStop.mq5 befindet sich im Anhang zu diesem Beitrag.

Fazit

Frischen wir unsere Erinnerung an die Verwendung der Klasse CTrailingStop bei der Erstellung eines mechanischen Handelssystems etwas auf:

1. Einbeziehen der Include-Datei Sample_TrailingStop.mqh.

2. Deklarieren der externen Variablen mit den Parametern des Indikators des verwendeten Trailing Stops.

3. Anlegen der Klasseninstanz.

4. Hinzufügen des Aufrufs der Methoden Init(), SetParameters(), StartTimer() und On() aus der Funktion OnInit() des Expert-Systems.

5. Hinzufügen des Aufrufs der Methode Refresh() aus der Funktion OnTimer().

6. Hinzufügen des Aufrufs der Methode DoStopLoss() aus der Funktion OnTick().

7. Hinzufügen des Aufrufs der Methode Deinit() aus der Funktion OnDeinit(). 


Sieben Schritte, weniger als 5 Minuten, und schon verfügt Ihr Expert-System über die Funktion eines Trailing Stops.