English Русский 中文 Español 日本語 Português
Universeller Expert Advisor: Ein benutzerdefiniertes Trailing Stop (Part 6)

Universeller Expert Advisor: Ein benutzerdefiniertes Trailing Stop (Part 6)

MetaTrader 5Beispiele | 28 Juni 2016, 15:02
2 784 2
Vasiliy Sokolov
Vasiliy Sokolov

Inhaltsverzeichnis


Einleitung

Dieses Material führt die Serie über die Artikel über den sogenannten 'Universellen Expert Advisor' — Eine spezielle Zusammensetzung von Klassen (Trading-Engine), welche es dem Anwender erlauben seine eigene Strategie zu erzeugen. In den vorherigen Teilen dieses Artikels, haben wir verschiedene Module der Engine besprochen, welche die Basisfunktionalität erweitert haben. Die meisten dieser Module sind Teile der CStrategy Klasse, oder sie implementieren Objekte, mit welchen die Klasse direkt arbeiten kann. 

In diesem Teil werden wir die Funktionalität der CStrategy weiterentwickeln und eine neue Funktion hinzufügen: Die Unterstützung von Trailingstops.. Anders als die anderen CStrategy Module, sind die Trailingstop-Algorithmenextern in Relation zu der Trading Engine. Das bedeutet, dass die Anwesenheit, wie auch die Abwesenheit dieser Funktion, auf keinen Fall die Funktionalität der CStrategy beeinflusst. Diese Eigenschaft kann durch eine spezielle Programmiertechnik, genannt composition erreicht werden. Diese Technik wird in diesen Artikel weiter unten beschrieben. Wir werden auch noch weitere Funktionen, die zusätzliche Module und Klassen verwenden, welche nach den strikten Regeln der objektorientierten Programmierung entwickelt werden, hinzufügen.

 

Implementierungsoptionen für die Trailing-Funktion

Ein Trailing Stop ist ein Algorithmus, dessen einzige Aufgabe es ist, das Stop Loss in Abhängigkeit von einem bestimmten Kurs nach zu ziehen, damit eine Position vor größeren Verlusten geschützt ist. Natürlich gibt es viele Algorithmen für die Verwaltung von Stop Loss. Ein Trailingstop Verwaltungsalgorithmus kann als eine separate Methode innerhalb der CStrategy class implementiert werden. Zum Beispiel kann der Algorithmus die aktuelle Position als ein Parameter erhalten und den Tradelevel zurückgeben, wohin der Stop Loss bewegt werden soll:

class CStrategy
   {
public:
   double TrailingMode1(CPosition* pos);
   };

Somit wäre es möglich das Management von Trailingstop direkt in der Strategie zu verwalten:

void CMovingAverage::SupportBuy(const MarketEvent &event,CPosition *pos)
  {
   double new_sl = TrailingMode1(pos);
   pos.StopLossValue(new_sl);
  }

Da es aber eine ganze Menge von Trailingstop-Managementfunktionen geben kann, ist es sehr unvorteilhaft, diese in der CStrategy zu platzieren. Das Trailingstop ist ein externes Modul in Bezug auf die Basisalgorithmen des Expert Advisors. Es handelt sich hier nicht um eine notwendige Funktion für die Operation der Trading Engine, und seine Aufgabe ist lediglich den Handelsprozess zu vereinfachen. Daher sollte das Fehlen der Trailingstop-Funktionen keinen Effekt auf die Funktionen der CStrategy oder einer Strategie, welche kein Trailingstop verwendet, haben. Auf der anderen Hand, sollte die Existenz eines Stopp-Management-Algorithmus den Programmcode in seiner Lesbarkeit nicht schwieriger machen oder den Code der Basisklasse vergrößern. Aus diesem Grund werden alle Trailingstop-Funktionen in einer separaten Klasse und in separaten Dateien platziert, welche nach Bedarf an die CStrategy-Klasse angeschlossen werden können.

Als Alternative können wir noch eine Trailing-Funktion in der CPosition Klasse implementieren In diesem Fall sieht die Operation eines Trailingstop ungefähr so aus:

void CMovingAverage::SupportBuy(const MarketEvent &event,CPosition *pos)
  {
   pos.TrailingMode1();
  }

Aber dieses verlagert lediglich das Problem von der CStrategy Klasse zu der CPosition. In diesem Falle würde die CPosition ungewöhnlichen Code beinhalten, obwohl es für die Verwaltung von Positionen nützlich sein könnte.  

Zudem benötigen die Trading-Algorithmen die Konfiguration von verschiedenen Parametern. Zum Beispiel müssen wir für ein klassisches Trailingstop den Abstand in Pips zwischen dem erreichten Kurswert und den Stop-Loss-Wert angeben, welchen wir verwalten wollen. Daher müssen wir zudem Algorithmus selbst auch noch seine Parameter abspeichern. Wenn wir diese Daten in Infrastruktur-Klassen, so wie es die CPosition oder die CStrategy ist, dann würde dieses nur die Struktur der Variablen dieser Klassen durcheinanderbringen und somit die Operation mit diesen Klassen signifikant komplizierter machen.

 

Standardisierung der Trailing-Funktionen Die CTrailing Klasse

Meistens ist die effektivste Lösung auch die simpelste. Der Fall des Trailingstops ist hierbei keine Ausnahme. Wenn wir uns vorstellen, dass die Trailingstop-Funktion eine eigene spezielle Klasse ist, welche alle ihre Parameter und Funktionen beinhaltet, dann sind alle die oben genannten Probleme gelöst. Wenn wir also die Trailingstop-Funktionen als eine unabhängige Klasse entwickeln, dann werden alle Daten und Methoden nicht mit den Methoden der CStrategy Klasse und anderen wichtigen Objekten, wie zum Beispiel CPosition Klasse durcheinandergeraten.

Wenn wir nun diese Klasse entwickeln wollen, müssen wir noch zwei Fragen lösen:

  1. Die innere Struktur der Trailingstop-Klasse Die Standardisierung ihrer Operation.
  2. Die Interaktion dieser Klasse mit anderen Modulen der CStrategy Engine.

Lassen Sie uns nun die erste Frage betrachten: Offensichtlich hat jedes Trailingstop seinen eigenen einzigartigen Satz von Parametern. Daher ist die Standardisierung dieser Sätze nicht möglich. Auf der anderen Hand, haben alle Algorithmen einen gemeinsamen Parameter, welches die Position ist, bei der das Stop-Loss modifiziert werden soll. Diese Position wird über die CPosition Klasse dargestellt, mit der wir schon vertraut sind. Zudem muss jede Art von Trailingstop eine Methode besitzen, durch welche das Stop-Loss einer Position modifiziert wird Diese Methode ist eine Art "Button", welcher den Trailing-Algorithmus Startet. Somit sollte der Name dieser Methode für alle Typen von Trailingstops gleich sein.

Wir haben nun zwei Gemeinsamkeiten für alle Typen von Trailingstops aufgezeigt, welche der Einfachheit halber in einer speziellen Basisklasse, die wir CTrailing nennen implementiert werden. Sie wird nun Methoden für das Festlegen der aktuellen Position und eine virtuelle Methode für die Modifizierung des Stop-Loss beinhalten, sowie eine spezielle virtuelle Copy-methode, deren Zweck wir später noch besprechen werden:

//+------------------------------------------------------------------+
//|                                                     Trailing.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Object.mqh>
#include "..\PositionMT5.mqh"
#include "..\Logs.mqh"
class CPosition;
//+------------------------------------------------------------------+
//| Die Basis-Klasse für Trailingstop                                |
//+------------------------------------------------------------------+
class CTrailing : public CObject
  {
protected:
   CPosition         *m_position;     // Die Position deren Stop-Loss modifiziert werden soll.
   CLog              *Log;
public:
                      CTrailing(void);
   void               SetPosition(CPosition *position);
   CPosition         *GetPosition(void);
   virtual bool       Modify(void);
   virtual CTrailing* Copy(void);
  };
//+------------------------------------------------------------------+
//| Constructor. Receives a logger module                            |
//+------------------------------------------------------------------+
CTrailing::CTrailing(void)
  {
   Log=CLog::GetLog();
  }
//+------------------------------------------------------------------+
//| Trailing-Stop Modifizierungs-Methode, welche in den abgeleiteten |
//| Klassen überschrieben werden soll                                |
//+------------------------------------------------------------------+
bool CTrailing::Modify(void)
  {
   return false;
  }
//+------------------------------------------------------------------+
//| Gibt eine Kopie der Instanz zurück                               |
//+------------------------------------------------------------------+  
CTrailing* CTrailing::Copy(void)
{
   return new CTrailing();
}
//+------------------------------------------------------------------+
//| Legt eine Position fest, deren Stop-Loss modifiziert werden soll |
//+------------------------------------------------------------------+
void CTrailing::SetPosition(CPosition *position)
  {
   m_position=position;
  }
//+-------------------------------------------------------------------+
//| Gibt eine Position zurück, deren Stop-Loss modifiziert werden soll|
//+-------------------------------------------------------------------+
CPosition *CTrailing::GetPosition(void)
  {
   return m_position;
  }
//+------------------------------------------------------------------+

Jeder Trailingstop wird von dieser Klasse abgeleitet. Die Basisklasse beinhaltet keine Parameter oder irgendwelche Stop-Loss Algorithmen Dieses hilft uns die maximale Flexibilität der Klassen zu erhalten. Obwohl die Trailingstop-Funktionen viele Parameter benötigen, benötigt die Modify-Methode keine dieser Parameter. Alle Parameter werden in den abgeleiteten Trailing-Klassen über spezielle Methoden gesetzt Somit sind mit dem Aufruf der Modifizierung bereits alle Parameter bekannt.

 

Die Interaktion der CTrailing-Klasse mit anderen Modulen der Strategie

Wir haben nun lediglich die Basis CTrai Klasse, aber dieses ist genug um sie in der Gesamtstruktur der Trading-Engine einzufügen. Wir fügen die Basisklasse in der CPosition-Klasse ein:

class CPosition
  {
public:
   CTrailing* Trailing;    // Trailing-Stop-Module
  };

Diese Anordnung sieht intuitiv und natürlich aus. In diesem Fall kann die Position über die gleiche Klasse kontrolliert werden:

void CMovingAverage::SupportBuy(const MarketEvent &event,CPosition *pos)
  {
   pos.Trailing.Modify();
  }

Dieses ist auch durch die standardisierte Modify-Methode möglich, d.h., es ist ebenso genau bekannt was im Falle einer Modifizierung aufgerufen werden muss. 

Aber die Integration des Trailingstop-Moduls endet hier noch nicht. Das oben aufgezeigte Beispiel benötigt immer noch ein Positionsmanagement auf dem Level der Anwender-Strategie. Wir müssen nun die BuySupport und SellSupport Methoden überschreiben und jede Position innerhalb der Logik Expert Advisors verwalten. Um das Positionsmanagement weiter zu vereinfachen, können wir einen Trailingstop-Modul direkt in der CStrategy-Klasse hinzufügen:

class CStrategy
  {
public:
   CTrailing* Trailing;   // Ein Trailing Stop Modul für alle Positionen
  };

Wir benötigen zudem noch eine CallSupport Methode, welche zu der CStrategy Klasse gehört.

//+------------------------------------------------------------------+
//| Ruft die Positionsmanagement Logik auf, vorausgesetzt, dass der  |
//| Status nicht gleich TRADE_WAIT ist.                              |
//+------------------------------------------------------------------+
void CStrategy::CallSupport(const MarketEvent &event)
  {
   m_trade_state=m_state.GetTradeState();
   if(m_trade_state == TRADE_WAIT)return;
   SpyEnvironment();
   for(int i=ActivePositions.Total()-1; i>=0; i--)
     {
      CPosition *pos=ActivePositions.At(i);
      if(pos.ExpertMagic()!=m_expert_magic)continue;
      if(pos.Symbol()!=ExpertSymbol())continue;
      if(CheckPointer(Trailing)!=POINTER_INVALID)
        {
         if(CheckPointer(Trailing)==POINTER_INVALID)
            pos.Trailing=Trailing.Copy();
         pos.Trailing.Modify();
         if(!pos.IsActive())
            continue;
        }
      if(pos.Direction()==POSITION_TYPE_BUY)
         SupportBuy(event,pos);
      else
         SupportSell(event,pos);
      if(m_trade_state==TRADE_STOP && pos.IsActive())
         ExitByStopRegim(pos);
     }
  }

Neue Funktionen sind in Gelb markiert Sie sind sehr einfach: Wenn standardmäßig ein Trailingstop platziert wird, aber die aktuelle Position keines besitzt, dann wird ein Trailing Stop für die aktuelle Position platziert, und anschließend wird die Stop-Loss-Order entsprechend der Trailingstop Logik modifiziert. Allerdings sollten wir noch ein sehr wichtiges Merkmal berücksichtigen. Es wird eine Instanz des Standard-Trailingstops zu jeder Position hinzugefügt, nicht das Standard-Trailingstop selbst. Diese Lösung hilft dabei, Verwirrungen innerhalb der Trailingstop-Logik zu vermeiden. Bedenken Sie, dass die selbe Instanz von einem Trailingstop verschiedene Positionen verwalten kann. Falls innerhalb der Klasse verschiedene Variablen für eine Position berechnet werden, sind diese beim nächsten Aufruf nicht länger gültig, da die übergebenen Positionen für die Verwaltung unterschiedlich sind. Dieses wird zu sehr unangenehmen Fehlern führen. Um dieses zu vermeiden, wird jeder Position ein individuelles Trailingstop hinzugefügt. Diese Instanz bleibt über die gesamte Lebensdauer der Position erhalten. Um ein individuelles trailingstop einer Position hinzuzufügen, ist es nötig die das Standard-Trailingstopt zu kopieren. Da die CStrategy Klasse nicht weiß, welche internen Daten und variablen kopiert werden müssen, wird der Kopiervorgang mit der abschließenden Trailing-Klasse durchgeführt. Sie sollte die virtuelle Copy()-Methode der Basis CTrailing-Klasse überschreiben und anschließend eine Referenz zurückgeben, welche auf eine Kopie in Form einer generischen CTrailing-Klasse verweist. Hier ist ein Beispiel für die Implementation einer Kopiermethode für das klassische Trailingstop CTrailingClassic:

//+------------------------------------------------------------------+
//| Gibt eine Kopie der Instanz zurück                               |
//+------------------------------------------------------------------+  
CTrailing *CTrailingClassic::Copy(void)
  {
   CTrailingClassic *tral=new CTrailingClassic();
   tral.SetDiffExtremum(m_diff_extremum);
   tral.SetStepModify(m_step_modify);
   tral.SetPosition(m_position);
   return tral;
  }

Die Methode erzeugt eine Instanz vom Typ CTrailingClassic, setzt die Parameter gleich der Parameter der aktuellen Instanz, und gibt dann einen Objekt in Form eines Pointers vom Typ CTrailing zurück.

Denken Sie an die folgende einfache Regel, wenn Sie benutzerdefinierte Trailing-Klassen entwickeln: 

Um ein standardmäßiges Trailingstop zu setzen, ist es notwendig, die Copy-Methode der Basis CTrailing Klasse zu überschreiben. Andernfalls wird die CStrategy Klasse die offenen Positionen nicht automatisch verwalten können. Falls Sie planen, dass Trailingstop nur in den BuySupport und SellSupport Methoden zu verwenden, dann ist es nicht notwendig, die virtuelle Copy-Methode zu überschreiben.

Die Notwendigkeit die Copy-Methode überschreiben zu müssen macht die Entwicklung ein wenig schwieriger, aber es macht das Verhalten des Moduls und seine Logik sicherer und schützt somit vor möglichen Datenfehlern.

Jetzt kann die CStrategy-Klasse mit dem übergebenen Trailing Stop Positionen verwalten. Wenn wir den CStrategy::Trailing Pointer mit irgendeinem Trailingstop-Algorithmus in einem benutzerdefinierten Konstruktor verbinden, wird es ein Standard-Trailingstop für alle Positionen die zu diesem Expert Advisor gehören. Es gibt also keinen Grund die BuySupport und SellSupport-Methoden für Strategien, die nur Trailing Stop für die Verwaltung von Positionen benötigen, zu überschreiben. Die Positionen werden automatisch auf dem Level der CStrategy Klasse verwaltet.

Beachten Sie das in dem CallSupport Code, der Aufruf von CTrailing::Modify von einer Überprüfung, ob die Position aktives oder nicht, gefolgt wird. Das bedeutet, dass auch wenn eine Position bereits geschlossen worden ist, der Prozess des Trailing Stops lediglich abgebrochen und mit der nächsten Position fortgefahren wird. Diese Eigenschaft führt zu einem interessanten Ergebnis:

Jeder Positionsmanagement-Algorithmus kann zur Zeit als ein Trailing Stop funktionieren. Die Modifizierung seiner Stop-Loss-Order ist nicht notwendig. Der Positionsmanagement-Algorithmus kann die Position unter bestimmten Bedingungen schließen. Dieses ist ein erwartetes Verhalten und die CStrategy arbeitet normal weiter.


Die praktische Verwendung eines Trailingstop Ein Beispiel für einen klassischen Trailingstop

Nun, nachdem die Basisklasse definiert ist, und wir der Strategie-Engine beigebracht haben, mit dieser Klasse zu interagieren, können wir eine spezifische Implementation von Trailingstop erzeugen Lassen Sie uns mit einem klassischen Trailingstop-Algorithmus beginnen Seine Arbeitsweise ist sehr einfach. Ein Trailing Stop bewegt die Stop-Loss-Order einer Position indem sie den Hoch-Kursen folgt (für eine Long-Position) und den Tief-Kursen (für eine Short-Position). Wenn der Preis zurückkommt, behält das Stop-Loss seinen aktuellen Level. Somit folgt der Stop-Loss dem aktuellen Preis mit einem bestimmten Abstand. Dieser Abstand wird über den zugehörigen Parameter bestimmt.

Zudem hat das Trailingstop noch einen weiteren Parameter. Dieser ist optional. Um eine zu häufige Korrektur des Stop-Loss Levels zu verhindern, werden wir noch eine weitere Limitierung hinzufügen: Der minimale Abstand eines neuen Stop-Loss Levels zu seinem alten Level sollte größer oder gleich StepModify Punkte sein. Der StepModify Wird wird als separater Parameter gesetzt. Dieser Parameter ist wichtig für das Handeln mit FORTS. Entsprechend den FORTS Handelsregeln, verlangt die Börse zusätzliche Gebühren, die sogenannten "Ineffizienten Transaktionen". Wenn es also viele Modifikationen des Stop-Loss Levels gibt, während es aber nur sehr wenige Trades gibt, dann verlangt die Börse zusätzliche Gebühren von dem Trader. Also sollte unser Algorithmus diese Funktion berücksichtigen.

Hier ist nun der Programmcode von unseren ersten Trailingstop. Dieser basiert auf der CTrailing Klasse und überschreibt die Modify-Methode  

//+------------------------------------------------------------------+
//|                                              TrailingClassic.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include "Trailing.mqh"
//+------------------------------------------------------------------+
//| Integrieren der Trailing Stop Parameter in der Liste der         |
//| Parameter des Expert Advisors                                    |
//+------------------------------------------------------------------+
#ifdef SHOW_TRAILING_CLASSIC_PARAMS
input double PointsModify=0.00200;
input double StepModify=0.00005;
#endif
//+------------------------------------------------------------------+
//| Ein klassischer Trailingstop                                     |
//+------------------------------------------------------------------+
class CTrailingClassic : public CTrailing
  {
private:
   double            m_diff_extremum;  // The distance in points from a reached extreme to the position's stop-loss
   double            m_step_modify;    // The minimum number of points to modify stop loss
   double            FindExtremum(CPosition *pos);
public:
                     CTrailingClassic(void);
   void              SetDiffExtremum(double points);
   double            GetDiffExtremum(void);
   void              SetStepModify(double points_step);
   double            GetStepModify(void);
   virtual bool      Modify(void);
   virtual CTrailing *Copy(void);
  };
//+------------------------------------------------------------------+
//| Konstruktor. Initializes default parameters                      |
//+------------------------------------------------------------------+
CTrailingClassic::CTrailingClassic(void) : m_diff_extremum(0.0),
                                           m_step_modify(0.0)
  {
#ifdef SHOW_TRAILING_CLASSIC_PARAMS
   m_diff_extremum=PointsModify;
   m_step_modify=StepModify;
#endif
  }
//+------------------------------------------------------------------+
//| Gibt eine Kopie der Instanz zurück                               |
//+------------------------------------------------------------------+  
CTrailing *CTrailingClassic::Copy(void)
  {
   CTrailingClassic *tral=new CTrailingClassic();
   tral.SetDiffExtremum(m_diff_extremum);
   tral.SetStepModify(m_step_modify);
   tral.SetPosition(m_position);
   return tral;
  }
//+------------------------------------------------------------------+
//| Sets the number of points from a reached extremum                |
//+------------------------------------------------------------------+
void CTrailingClassic::SetDiffExtremum(double points)
  {
   m_diff_extremum=points;
  }
//+------------------------------------------------------------------+
//| Setzt den Wert der minimalen Modifikation in Punkten             |
//+------------------------------------------------------------------+
void CTrailingClassic::SetStepModify(double points_step)
  {
   m_step_modify=points_step;
  }
//+------------------------------------------------------------------+
//| Gibt die Punkte von einem erreichten Extremwert zurück           |
//+------------------------------------------------------------------+
double CTrailingClassic::GetDiffExtremum(void)
  {
   return m_diff_extremum;
  }
//+------------------------------------------------------------------+
//| Gibt die minimale Modifikation in Punkten zurück                 |
//+------------------------------------------------------------------+
double CTrailingClassic::GetStepModify(void)
  {
   return m_step_modify;
  }
//+------------------------------------------------------------------+
//| Modifiziert das Trailingstop entsprechend der Logik eines        |
//| klassischen Trailing-Stop                                        |
//+------------------------------------------------------------------+
bool CTrailingClassic::Modify(void)
  {

   if(CheckPointer(m_position)==POINTER_INVALID)
     {
      string text="Invalid position for current trailing-stop. Set position with 'SetPosition' method";
      CMessage *msg=new CMessage(MESSAGE_WARNING,__FUNCTION__,text);
      Log.AddMessage(msg);
      return false;
     }
   if(m_diff_extremum<=0.0)
     {
      string text="Set points trailing-stop with 'SetDiffExtremum' method";
      CMessage *msg=new CMessage(MESSAGE_WARNING,__FUNCTION__,text);
      Log.AddMessage(msg);
      return false;
     }
   double extremum=FindExtremum(m_position);
   if(extremum == 0.0)return false;
   double n_sl = 0.0;
   if(m_position.Direction()==POSITION_TYPE_BUY)
      n_sl=extremum-m_diff_extremum;
   else
      n_sl=extremum+m_diff_extremum;
   if(n_sl!=m_position.StopLossValue())
      return m_position.StopLossValue(n_sl);
   return false;
  }
//+------------------------------------------------------------------+
//| Gibt den erreichten extremen Preis zurück während die Position   |
//| gehalten wird. Für eine Long-Position, gibt es Ideen höchsten        |
//| erreichten Preis zurück. Für eine Short-Position - Den tiefsten erreichten       |
//| Preis.                                                         |
//+------------------------------------------------------------------+
double CTrailingClassic::FindExtremum(CPosition *pos)
  {
   double prices[];
   if(pos.Direction()==POSITION_TYPE_BUY)
     {
      if(CopyHigh(pos.Symbol(),PERIOD_M1,pos.TimeOpen(),TimeCurrent(),prices)>1)
         return prices[ArrayMaximum(prices)];
     }
   else
     {
      if(CopyLow(pos.Symbol(),PERIOD_M1,pos.TimeOpen(),TimeCurrent(),prices)>1)
         return prices[ArrayMinimum(prices)];
     }
   return 0.0;
  }
//+------------------------------------------------------------------+

Der grundlegende Code dieser Klasse ist in den Methoden Modify und FindExtremum enthalten. Der er sucht mit der Methode FindExtremum nach dem Hoch- oder Tiefkursen(Je nach Art der Position). Dadurch wird auch nach einem Neustart der Strategie der Stop-Loss korrekt berechnet.

Unsere Trailingstop-Klasse enthält zusätzlich zurzeit noch unklare Programm-Konstruktionen in Form von SHOW_TRAILING_CLASSIC_PARAMS Marco und ein paar Eingangsvariablen 'input' Wir werden diese Konstruktionen in einem späteren separaten Abschnitt besprechen: "Hinzufügen von Trailing-Parametern zu den Expert Advisor Settings".

 

Hinzufügen von Trailing zu der CImpulse-Strategie

In dem vorangegangenen Artikel "Universeller Expert Advisor: Die Verwendung von wartenden (pending) Orders und Hedging-Support", haben wir zum ersten Mal die CImpulse Strategy vorgestellt. Es ist eine simple Handelsstrategie, die darauf basiert, dass bei starken Kursbewegungen in den Markt eingestiegen wird. Die vorgeschlagene Strategie verfügt über ein Positionsmanagement, basierend auf einem gleitenden Durchschnitt. Diese Strategie schließt eine Long-Position, wenn eine Bar unterhalb des gleitenden Durchschnitts öffnet. Diese Strategie schließt eine Short-Position, wenn eine Bar oberhalb des gleitenden Durchschnitts öffnet. Hier ist noch einmal der Programmcode, der in dem vorherigen Artikel beschrieben wurde, welcher diese Logik implementiert:

//+------------------------------------------------------------------+
//| Verwaltung einer Long-Position in Zusammenhang mit dem gleitenden Durchschnitt   |
//+------------------------------------------------------------------+
void CImpulse::SupportBuy(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      double target = Bid() - Bid()*(m_percent/100.0);
      if(target < Moving.OutValue(0))
         pos.StopLossValue(target);
      else
         pos.StopLossValue(0.0);
   }
   if(Bid() < Moving.OutValue(0))
      pos.CloseAtMarket();
}
//+------------------------------------------------------------------+
//| Verwalten einer Shortposition in Zusammenhang mit dem gleitenden Durchschnitt  |
//+------------------------------------------------------------------+
void CImpulse::SupportSell(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      double target = Ask() + Ask()*(m_percent/100.0);
      if(target > Moving.OutValue(0))
         pos.StopLossValue(target);
      else
         pos.StopLossValue(0.0);
   }
   if(Ask() > Moving.OutValue(0))
      pos.CloseAtMarket();
}

Lassen Sie uns dieses jetzt vereinfachen, indem wir ein klassisches Trailingstop anstelle der Regeln des Positionsmanagement verwenden. Wir werden nun die Methoden aus dem Code der Strategie löschen und ein klassisches Trailing Stop als Standard Trailing-Funktion in dem Konstruktor der Strategie hinzufügen. Lassen Sie uns die Strategie umbenennen in: CImpulseTrailingAuto:

//+------------------------------------------------------------------+
//| Initialisierung der Strategie und Konfiguration des              |
//| Trailing-Stops                                                   |
//+------------------------------------------------------------------+
CImpulseTrailing::CImpulseTrailing(void)
{
   CTrailingClassic* classic = new CTrailingClassic();
   classic.SetDiffExtremum(0.00100);
   Trailing = classic;
}

Nun, entsprechend der neuen Logik, wird eine Position, basierend auf einem Trailingstop, welcher mit einem Abstand von 0.001 Punkten von einem erreichten extremen Kurs arbeitet, geschlossen.

Der vollständige Quellcode in der CImpulse mit einem automatischen Trailingstop ist erhältlich in ImpulseTrailingAuto.mqh.

 

Hinzufügen der Trailing-Parameter zu den Expert Advisor settings

Die Arbeitsweise der von uns entwickelten Trailingstop-Operation ist sehr komfortabel. Aber wir müssen immer noch die Parameter des Trailingstop innerhalb der benutzerdefinierten Strategie konfigurieren. Wir müssen diese Prozedur vereinfachen: Zum Beispiel das Hinzufügen der Trailing-Stop-Parameter zu den Expert Advisor settings. Das Problem ist, dass wenn das Trailingstop nicht verwendet wird, die Parameter aber weiterhin innerhalb der Einstellungen des Expert-Advisors existieren, was zu Zweideutigkeit führen kann. Um dieses Problem zu verhindern, können wir eine conditional compilation verwenden. Lassen Sie uns die Trailingstop-Parameter und die speziellen conditional compilation macros SHOW_TRAILING_CLASSIC_PARAMS zu dem Modul des klassischen Trailing-Stops hinzufügen:

//+------------------------------------------------------------------+
//|                                              TrailingClassic.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include "Trailing.mqh"
//+------------------------------------------------------------------+
//| Integration der Trailing Stop Parameter in der Liste der         |
//| Parameter des Expert Advisors                                    |
//+------------------------------------------------------------------+
#ifdef SHOW_TRAILING_CLASSIC_PARAMS
input double PointsModify = 0.00100;
input double StepModify =   0.00005;
#endif
//+------------------------------------------------------------------+
//| Ein klassisches Trailing-Stop                                          |
//+------------------------------------------------------------------+
class CTrailingClassic : public CTrailing
  {
   ...
public:
                     CTrailingClassic(void);
   ...
  };
//+------------------------------------------------------------------+
//| Constructor. Initialisierung der Standard Parameter                      |
//+------------------------------------------------------------------+
CTrailingClassic::CTrailingClassic(void) : m_diff_extremum(0.0),
                                           m_step_modify(0.0)
  {
   #ifdef SHOW_TRAILING_CLASSIC_PARAMS
   m_diff_extremum = PointsModify;
   m_step_modify = StepModify;
   #endif
  }

Jetzt, wenn das SHOW_TRAILING_CLASSIC_PARAMS Makro definiert ist, werden die Trailing Parameter bei dem Kompilieren in den Expert-Adviser integriert:


Abbildung 1. Dynamisch verknüpfte Parameter PointsModify und StepModify.

Wenn das SHOW_TRAILING_CLASSIC_PARAMS Makro auskommentiert oder nicht verfügbar ist, dann verschwinden auch die Trailing-Stop-Settings in den EA Parametern:


Fig 2. Deaktivierte Parameter des Trailingstop

Das SHOW_TRAILING_CLASSIC_PARAMS Makro fügt die Trailing-Parameter zu den Einstellungen des EAs hinzu und konfiguriert zusätzlich CTrailingClassic, wodurch die konfigurierten Parameter automatisch während der Erzeugung hinzugefügt werden. Wenn die Strategie das Marco jetzt erzeugt, dann enthält es bereits die vom User angegebenen Parameter aus dem Expert Advisor Setup-Fenster. 

 

Ein gleitender Durchschnitt basierend auf einem Trailingstop

In dem vorangegangenen Abschnitt des Artikels, hat die CImpulse Strategie eine Position geschlossen, wenn ein Kuss oberhalb oder unterhalb eines gleitenden Durchschnitts lag. Dieser gleitende Durchschnitt wurde durch die CIndMovingAverage Indikator-Klasse repräsentiert. Die CIndMovingAverage Klasse ist der Trailingstop-Klasse sehr ähnlich. Sie berechnet die Werte des gleitenden Durchschnitts und erlaubt eine flexible Konfiguration der Indikatorparameter. Der einzige Unterschied besteht in der Abwesenheit eines Verwaltungs-Algorithmusses. Die CIndMovingAverage Klasse besitzt keine Modify() Methode. Auf der anderen Hand besitzt die CTrailing Klasse bereits alle notwendigen Methoden, aber sie besitzt Algorithmen für das Arbeiten mit dem gleitenden Durchschnitt. Die Kompositionsmethode macht es möglich, die Vorteile dieser beiden Klassen zu kombinieren, Und einen neuen Typ von Trailingstop basierend auf den Klassen: Ein Trailing Stop basierend auf dem gleitenden Durchschnitt zu erzeugen. Die Operation des Algorithmus ist sehr einfach: Sie setzt ein Stop Loss Level einer Position gleich dem Wert des gleitenden Durchschnitts. Lassen Sie uns eine zusätzliche Überprüfung der Modify-Methode hinzufügen: Wenn der aktuelle Kurs unterhalb (bei einem Kauf) oder oberhalb (bei einem Verkauf) des berechneten Stop Loss Levels ist, dann wird die Position zu dem aktuellen Marktpreis geschlossen. Es folgt nun die vollständige Klasse:

//+------------------------------------------------------------------+
//|                                               TrailingMoving.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include "Trailing.mqh"
#include "..\Indicators\MovingAverage.mqh"

//+------------------------------------------------------------------+
//| Trailing Stop basierend auf einem MovingAverage.  Setzen eines Stop-Loss       |
//| gleich dem MA-Level                                 |
//+------------------------------------------------------------------+
class CTrailingMoving : public CTrailing
{
public:
   virtual bool       Modify(void);
   CIndMovingAverage* Moving;
   virtual CTrailing* Copy(void);
};
//+------------------------------------------------------------------+
//| Setzen eines Stop-Loss gleich dem MA level                       |
//+------------------------------------------------------------------+
bool CTrailingMoving::Modify(void)
{
   if(CheckPointer(Moving) == POINTER_INVALID)
      return false;
   double value = Moving.OutValue(1);
   if(m_position.Direction() == POSITION_TYPE_BUY &&
      value > m_position.CurrentPrice())
      m_position.CloseAtMarket();
   else if(m_position.Direction() == POSITION_TYPE_SELL &&
      value < m_position.CurrentPrice())
      m_position.CloseAtMarket();
   else if(m_position.StopLossValue() != value)
      return m_position.StopLossValue(value);
   return false;
}
//+------------------------------------------------------------------+
//| Rückgabe einer exakten Kopie der CTrailingMoving Instanz         |
//+------------------------------------------------------------------+
CTrailing* CTrailingMoving::Copy(void)
{
   CTrailingMoving* mov = new CTrailingMoving();
   mov.Moving = Moving;
   return mov;
}

Die Modify-Funktion vergleicht den Level des gleitenden Durchschnitts mit dem aktuellen Stop-Level Wenn diese Level nicht gleich sind, dann platziert die Funktion für die Position einen neuen Stop-Level. Es wird der Wert des gleitenden Durchschnitts der letzten vollständigen Bar verwendet, da die sich die aktuelle Bar noch in dem Prozess der Formation befindet. Beachten Sie auch, dass der Moving-Average-Indikator als Pointer deklariert ist. Diese Eigenschaft erlaubt es dem Anwender, jedes Objekt des CIndMovingAverage Typs mit dem Trailing-Stop zu verbinden. 

Lassen Sie uns nun die Funktion dieser Klasse in den Strategietester testen. Hier ist ein kurzes Video über die Arbeitsweise:

 

 

Ein individuelles Trailingstop für eine Position

Wir haben jetzt die Mechanismen eines Trailingstops analysiert. Durch die Verwendung einer einheitlichen virtuellen Modify-Methode, kann die ABC Trading Engine ein Trailingstop für jede Position automatisch setzen und den zugehörige Berechnungsalgorithmus aufrufen. Normalerweise sollte dieses ausreichen, aber in einigen Fällen ist es eventuell nötig, die Positionen individuell zu verwalten. Das bedeutet, dass wir zu der einen Position ein Trailingstop hinzufügen wollen und ein anderes Trailingstop zu einer weiteren Position. Solche Trailing-Eigenschaften können nicht einheitlich auf Seiten der Trading-Engine implementiert werden, daher muss die Kontrolle von solchen Trailingstop in der Strategie implementiert werden. Dieses kann durch das Überschreiben der BuySupport und SellSupport-Methoden erreicht werden. Darüber hinaus ist es in so einem Fall nicht notwendig, das Standard-Trailing-Stop zu initialisieren, so wie wir es in dem Konstruktor der benutzerdefinierten Strategie gemacht haben.

Nehmen wir an, dass die Long-Positionen der CImpulse-Strategie über ein Trailingstop, basierend auf dem gleitenden Durchschnitt, verwaltet werden sollen. Den Short-Positionen soll ein klassisches Trailingstop hinzugefügt werden. Wir haben diese beiden Trailing-Typen vorher beschrieben. Lassen Sie uns nun die BuySupport und SellSupport-Methoden auf die folgende Weise überschreiben:

//+------------------------------------------------------------------+
//| Verwalten einer Long-Position mit dem gleitenden Durchschnitt    |
//+------------------------------------------------------------------+
void CImpulseTrailing::SupportBuy(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))
      return;
   if(pos.Trailing == NULL)
   {
      CTrailingMoving* trailing = new CTrailingMoving();
      trailing.Moving = GetPointer(this.Moving);
      pos.Trailing = trailing;
   }
   pos.Trailing.Modify();
}
//+------------------------------------------------------------------+
//| Verwalten einer Shortposition mit dem gleitenden Durchschnitt    |
//+------------------------------------------------------------------+
void CImpulseTrailing::SupportSell(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))
      return;
   if(pos.Trailing == NULL)
   {
      CTrailingClassic* trailing = new CTrailingClassic();
      trailing.SetDiffExtremum(0.00100);
      pos.Trailing = trailing;
   } 
   pos.Trailing.Modify();
}

Beachten Sie, dass für das Trailingstop, welches auf einem gleitenden Durchschnitt basiert, die CIndMovingAverage Klasse als Parameter gesetzt wird. Diese Klasse ist in der Strategie als ein 'Moving Object' erhältlich. In einer Programmzeile haben wir dem Trailingstop instruiert, welches Objekt für die Berechnung des Stop-Loss Levels verwendet werden soll. 

In der SupportSell Methode wird ein anderes Trailing für neue Positionen zugewiesen und dieses Trailingstop hat seine eigenen Parameter. Dieses Trailing verwendet einen Kanal mit einem Abstand von 0.00100 pips von dem Maximal-Kurs. 

Der vollständige Programmcode der CImpulse Strategie mit individuellen Trailing-Methoden für jeden Typ von Positionen ist erhältlich in derImpulseTrailingManual.mqh Datei.

 

Eine kurze Liste über Fehlerbehebungen der letzten Versionen

Die CStrategy Trading Engine hat sich seit ihrer ersten Publikation in dem ersten Teil dieser Artikel stark verändert. Wir haben neue Funktionen und Module für erweiterte Handelsmöglichkeiten hinzugefügt. Zudem sind einige Compiler Versionen mit unterschiedlichen Veränderungen seit der ersten Veröffentlichung erschienen. Einige dieser Veränderungen sind inkompatibel mit den älteren Versionen der CStrategy, was dazu geführt hat, dass wir die Trading-Engine modifizieren mussten. Diese Korrekturen und Erweiterungen für Ursachen das Auftreten von unvermeidlichen Fehlern. In diesem Abschnitt werden wir die Fehlerkorrekturen der neuesten Version der Trading Engine abdecken.

  • Es wurde dem Projekt ein Strategie-Bedienfeld hinzugefügt. Aufgrund eines Fehlers in einer vorherigen Version des Compilers, wurde das Bedienfeld in den Abschnitten 3 und 4 deaktiviert, weil es Kompatibilitätsprobleme gab. Nachdem der Compilerfehler behoben worden ist, wurde das Bedienfeld in den Teil fünf wieder hinzugefügt, aber es hat nicht einwandfrei funktioniert. In dem sechsten Teil der Trading-Engine, wurde die Funktion des Bedienfelds wieder vollständig hergestellt.
  • Das Bedienfeld besitzt aber einen Fehler: Anstelle "SellOnly" anzuzeigen, zeigt es "BuyOnly" doppelt an. Dieser Fehler wurde behoben.
  • In den vorherigen Versionen verursachte eine Veränderung des Handelsmodus auf dem Bedienfeld keine Veränderung des Handelsmodus in der Strategie. Dieser Fehler wurde in der sechsten Version behoben.
  • Es wurde ein neues Verhalten bei dem Wechsel der Modi hinzugefügt: in dem SellOnly-Mode, werden alle Buy-Positionen geschlossen und zusätzlich werden alle pending Buy-Orders, die zu der Strategie gehören, gelöscht. Dieselbe Funktionalität wurde BuyOnly hinzugefügt: alle pending Sell-Orders werden gelöscht. Wenn Sie den "Stop" Modus wählen, Werden alle Pending-Orders beider Richtungen gelöscht.

Bitte berichten Sie über alle Fehler, die Sie in der Trading-Engine finden. Alle erkannten Fehler werden behoben. 


Schlussfolgerung

In dem sechsten Teil dieses Artikels haben wir der Trading-Engine neue Funktionen hinzugefügt. Die Trading Engine verfügt nun über Trailingstops. Jedes Trailingstop ist eine spezielle Klasse, welche die standardisierte Modify()-Methode enthält, welche den Stop-Loss-Level modifiziert. Zudem besitzt Sie spezifische Daten und Parameter, welche den Algorithmus für die Bewegung des Trailingstop konfigurieren. Das trailingstop kann auf zwei Arten verwendet werden.

  • Die Trailingstop-Operation kann in der Trading-Engine einer Strategie implementiert werden oder es kann der Autopilot-Modus aktiviert werden.. In dem letzteren Fall, wird ein automatisch ein Standard-Trailingstop einer Position zugewiesen. In diesem Falle ist keine Kontrolle durch die benutzerdefinierte Strategie notwendig. 
  • Das Management des Trailingstop über die benutzerdefinierte Strategie. In diesem Fall verwaltet die benutzerdefinierte Strategie ihre eigenen Positionen unter Verwendung der Trailingstop-Funktionen. Dieser Modus erlaubt die Implementierung von komplexen Logiken — z.B. die Verwendung von unterschiedlichen Trading-Algorithmen für unterschiedliche Typen von Positionen.
Jedes Trailingstop besitzt die kompletten Informationen über die Position, welchen es verwaltet. Zudem kann die Methode für die Modifikation einer Position die Position zu jeder Zeit schließen. Diese Methode bietet ein hohes Maß an Flexibilität für den Trailing-Algorithmus. Formal kann jeder Positionskontrollalgorithmus als Trailingstop agieren. Zum Beispiel könnte anstelle der Veränderung des Stop-Loss-Levels, auch der Take-Profit-Level einer Order über den Algorithmus verändert werden. Solche Veränderungen Zerstören die Handelslogik nicht, da die Trading-Engine auch in diesem Falle weiterhin sauber arbeitet.

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/2411

Beigefügte Dateien |
Letzte Kommentare | Zur Diskussion im Händlerforum (2)
AK377MC
AK377MC | 9 Juni 2022 in 12:32

Dear Mr Sokolov,

Very interesting article, but unfortunately I couldn't instal a single one of your codes without  tons of compiller errors. I tried all 9 parts but in vain.

Hence my question: Is there a specific approach to install your code?

Thank You

Johann Kern
Johann Kern | 17 März 2023 in 05:08
Brevity is the soul of wit.
Acceptance, understanding, correct interpretation, there are hardly any more. Unfortunately
Grafische Interfaces II: Die Trennlinien und Context-Menüelemente (Kapitel 2) Grafische Interfaces II: Die Trennlinien und Context-Menüelemente (Kapitel 2)
In diesem Artikel erzeugen wir das Trendlinien-Element Es kann nicht nur als unabhängiges Interface-Element verwendet werden, sondern auch als ein Teil von vielen anderen Elementen. Anschließend haben wir alles, was für die Entwicklung der Kontextmenü Klasse benötigt wird. Diese Klasse werden wir in diesem Artikel im Detail besprechen. Zudem werden wir alle notwendigen Ergänzungen dieser Klasse hinzufügen, die für das Abspeichern von Pointern aller Elemente des grafischen Interfaces dieser Anwendung benötigt werden.
Grafische Interfaces II: Das Menu-Item-Element (Kapitel 1) Grafische Interfaces II: Das Menu-Item-Element (Kapitel 1)
In dem zweiten Teil dieser Serie, werden wir im Detail auf die Entwicklung von Interface Elementen, wie einen Hauptmenü und Kontextmenüs eingehen. Wir werden auch das Zeichnen und das Entwickeln einer speziellen Klasse für diese Elemente berücksichtigen. Wir werden ausführlich den Fragen der Verwaltung von Events und benutzerdefinierten Events nachgehen.
Grafische Interfaces II: Einrichtung des Eventhandlers für die Bibliothek (Kapitel 3) Grafische Interfaces II: Einrichtung des Eventhandlers für die Bibliothek (Kapitel 3)
Der vorherige Artikel beinhaltet die Implementation der Klassen für das Erzeugen der Bestandteile des Hauptmenüs. Nun ist es an der Zeit, dass wir uns die Eventhandler in den Basisklassen und in den Klassen für die Controls näher betrachten. Wir werden unsere Aufmerksamkeit auch auf das Verwalten des Status des Charts, in Abhängigkeit der Position des Mauszeigers, richten.
Spontane Änderung der Expert-Advisor-Parameter vom Bedienfeld aus Spontane Änderung der Expert-Advisor-Parameter vom Bedienfeld aus
Dieser Artikel zeigt anhand eines Beispiels die Implementierung eines Expert Advisors, dessen Parameter vom Bedienfeld aus kontrolliert werden können. Bei einer spontanen Änderung der Parameter schreibt der Expert Advisor die Werte vom Infofeld in eine Datei, um sie später von dieser Datei zu lesen und sie im Feld darzustellen. Dieser Artikel ist interessant für alle, die manuell oder semi-automatisch handeln wollen.