English Русский 中文 Español 日本語 Português
preview
Implementierung eines Break-Even-Mechanismus in MQL5 (Teil 1): Basisklasse und Break-Even-Modus auf Basis fester Punkte

Implementierung eines Break-Even-Mechanismus in MQL5 (Teil 1): Basisklasse und Break-Even-Modus auf Basis fester Punkte

MetaTrader 5Beispiele |
18 0
Niquel Mendoza
Niquel Mendoza


Einführung

Das Verschieben eines Stop-Loss auf Break-Even ist eine Technik, die beim Handel eingesetzt wird, um offene Positionen sicherer zu verwalten. Dabei wird das Stop-Loss-Niveau auf den Eröffnungskurs des Handels verschoben, nachdem sich der Kurs eine bestimmte Anzahl von Punkten im Gewinn bewegt hat. Dies hilft, die Position zu schützen und Verluste bei einem unerwarteten Rücksetzer zu vermeiden oder zu begrenzen.

Bei der Anwendung eines Break-Even-Mechanismus gibt es zwei Hauptansätze:

  • Es stellt sicher, dass ein Trade nicht mit Verlust geschlossen wird, wenn der Kurs das Take-Profit-Ziel nicht erreicht;
  • Er wird verwendet, um Gewinne zu sichern, indem der Stop-Loss einige Punkte über dem Einstiegskurs gesetzt wird.

In dieser Artikelserie werden wir drei Varianten des Break-Even-Mechanismus entwickeln. Im ersten Teil erstellen wir nun die Basisklasse des Systems und programmieren den ersten einfachen Break-Even-Typ, der als Vorlage für künftige Erweiterungen dienen wird.


Was ist der Break-Even-Modus?

Bevor wir das Break-Even-Konzept in unser System integrieren, ist es wichtig, den grundlegenden Funktionsmechanismus zu verstehen.

Vereinfacht ausgedrückt bedeutet der Break-Even-Modus, dass das Stop-Loss-Niveau um eine bestimmte Anzahl von Punkten (zusätzliche Punkte) verschoben wird, nachdem sich der Kurs um eine bestimmte Strecke vom Eröffnungskurs des Handels entfernt hat.


    be1

    Abbildung 1. Eine Verkaufsposition vor Änderung des Stop-Loss-Niveaus

    Die erste Abbildung zeigt, dass der Stop-Loss anfänglich auf dem Niveau des Signals platziert wird, das durch den Order-Blocks-Indikator erzeugt wird (ein Indikator, der in früheren Artikeln einer anderen Serie entwickelt wurde).

     be2

    Abbildung 2. Eine Verkaufsposition nach Änderung des Stop-Loss-Niveaus

    Abbildung 2 zeigt, wie das System nach nach Bildung einer neuen Kerze das Stop-Loss-Niveau automatisch auf einen bestimmten Abstand verschiebt, der den festgelegten 150 Punkten entspricht. Dieses Verhalten spiegelt den oben beschriebenen Aktivierungsprozess wider.

    Im Folgenden betrachten wir, was nach der Aktivierung des Break-Even-Modus passiert.

    be3


    Abbildung 3: Verkaufsposition nach Schließung auf Break-Even-Niveau

    Wie in Abbildung 3 zu sehen ist, schloss die Position am Break-Even-Niveau mit einem kleinen Gewinn aufgrund der zuvor konfigurierten Anpassung. In diesem speziellen Fall wurde ein Totalverlust (Stop Loss) vermieden, da sich der Kurs nicht wesentlich vom Einstiegspunkt entfernt hat.

    Dies ist einer der Vorteile des Break-Even-Mechanismus: Er ermöglicht einen potenziellen Gewinn oder hilft, Totalverluste zu vermeiden.
    Es ist jedoch zu beachten, dass die Sicherung des Gewinns davon abhängt, wie viele zusätzliche Punkte für das Break-Even-Niveau festgelegt werden.

    Auf der anderen Seite hat diese Methode aber auch einige Nachteile. Wenn der Stop-Loss nicht verschoben worden wäre, hätte der Trade manchmal sein Take-Profit-Ziel erreichen können. Diese Situation hängt weitgehend von der angewandten Strategie ab.

    In unserem Fall, wenn wir mit Order-Blocks-Signalen arbeiten, ist es im Allgemeinen ratsam, den Break-Even-Modus anzuwenden, da es Fälle gibt, in denen Order-Blöcke nicht funktionieren, sodass der Schutz offener Positionen von entscheidender Bedeutung ist.

    Break-Even-Modus auf der Grundlage der ATR

    Eine weitere Managementoption besteht darin, den Stop Loss dynamisch auf der Grundlage der ATR (Average True Range) auf den Break-Even zu verschieben.

    Diese Methode ähnelt der vorhergehenden, aber statt einer festen Anzahl von Punkten wird ein Multiplikator-Parameter verwendet. Mit dem Multiplikator können wir den optimalen Abstand für die Anpassung des Stop-Loss unter Berücksichtigung der aktuellen Marktvolatilität berechnen. Dieser Ansatz erweist sich als flexibler, wenn es um hochvolatile Vermögenswerte wie Gold geht.

    Wenn zum Beispiel ein festes Break-Even-Level von 150 Punkten (entspricht 1,50 $ in Gold) festgelegt wird, kann der Goldpreis in der Zeit vor der Veröffentlichung von Wirtschaftsnachrichten leicht um 3,00 $ oder mehr steigen. In solchen Situationen hilft die dynamische Anpassung auf Basis der ATR, sich besser an das Marktverhalten anzupassen und zu verhindern, dass normale Schwankungen den Trade vorzeitig beenden.

    Die ATR-basierte Methode verwendet also keine festen Abstände, sondern passt sich dynamisch an die aktuellen Marktbedingungen an.

    Break-Even-Modus auf der Grundlage des Risiko-Ertrags-Verhältnisses (RRR)

    Schließlich kann der Stop-Loss anhand des Risiko-Ertrags-Verhältnisses auf das Break-Even-Niveau nachgezogen werden. Die RRR stellt das Verhältnis zwischen dem eingegangenen Risiko und dem erwarteten Gewinn aus einem Trade dar. Er wird berechnet, indem der Take-Profit-Wert durch den Stop-Loss-Wert geteilt wird.

    Das Verständnis der RRR ist wichtig für ein besseres Positionsmanagement:

    • Wenn die RRR höher ist, sinkt in der Regel der Prozentsatz der erfolgreichen Abschlüsse, da der Kurs eine größere Strecke zurücklegen muss, um das Ziel zu erreichen;
    • Wenn das RRR niedriger ist, steigt die Wahrscheinlichkeit, den Take Profit zu erreichen, aber die möglichen Verluste können höher sein, wenn sich der Markt gegen uns entwickelt.

      Die Logik hinter der Anwendung des Break-Even-Modus auf der Grundlage der RRR ist einfach. Angenommen, eine Position wird mit einem Take Profit eröffnet, der doppelt so groß ist wie der Stop Loss, also ein RRR von 1:2.

      Mit dieser Methode kann der Stop-Loss so konfiguriert werden, dass er sich auf den Einstiegskurs bewegt, wenn der Trade ein Verhältnis von 1:1 im Verhältnis zum Anfangsrisiko erreicht. Das bedeutet, dass der Trade automatisch abgesichert ist, sobald der Markt genug zugelegt hat, um das eingegangene Risiko auszugleichen.

      Zum Beispiel:

      • Bei Erreichen eines Verhältnisses von 1:1 kann der Stop Loss verschoben werden, um die Position zu schützen;
      • Er kann auch so konfiguriert werden, dass er je nach Art der verwendeten Strategie weiter fortschreitet, z. B. im Verhältnis 1:2, 1:3 usw.

        Dieser Ansatz ermöglicht die Absicherung von Trades, nachdem der Markt eine ausreichende Bewegung in eine günstige Richtung gezeigt hat, basierend auf einem bestimmten Risiko-Ertrags-Verhältnis.


        Erforderliche Strukturen und Enumerationen

        In diesem Abschnitt implementieren wir die Grundlagen des Break-Even-Mechanismus beginnen. Bevor wir die Hauptklasse erstellen, werden wir die notwendigen Strukturen und Aufzählungen definieren. Das Verfahren wird klar, einfach und unkompliziert sein. Wir werden nicht alle Aufzählungen verwenden, die wir für das Risikomanagement erstellt haben, da wir nur einige von ihnen benötigen, um die Break-Even-Funktion zu implementieren. Fangen wir an.

        Wir werden eine einfache Struktur erstellen, um die wichtigsten Informationen zu speichern, die es uns ermöglichen, den Break-Even-Modus anzuwenden. Diese Struktur muss enthalten:

        • Ticket eines Trades
        • price_to_beat, d.h. der Preis, den der Markt überschreiten muss, um den Stop Loss auf den breakeven_price zu verschieben,
        • die zum neuen Niveau wird, auf dem der Stop Loss gesetzt wird, wenn die Bedingung erfüllt ist,
        • Positionstyp für eine korrekte Bestimmung der Niveaus.

          Die Struktur wird folgendermaßen aussehen:

          struct position_be
           {
            ulong              ticket;           //Position Ticket
            double             breakeven_price;  //Be price
            double             price_to_beat;    //Price to exceed to reach break even
            ENUM_POSITION_TYPE type;             //Position type
           };

          Enumeration der Break-Even-Typen

          Wir haben eine Enumeration erstellt, die es Ihnen ermöglicht, eine der drei bereits erwähnten Arten der Break-Even-Aktivierung auszuwählen, um die Verwaltung jedes Trades zu optimieren:

          enum ENUM_BREAKEVEN_TYPE
           {
            BREAKEVEN_TYPE_RR = 0,           //By RR
            BREAKEVEN_TYPE_FIXED_POINTS = 1, //By FixedPoints
            BREAKEVEN_TYPE_ATR = 2           //By Atr
           };

          Enumeration für Break-Even-Modus

          Nicht alle Handelsstrategien funktionieren auf die gleiche Weise. Deshalb haben wir eine Enumeration erstellt, mit der wir eindeutig festlegen können, wie der Break-Even-Modus auf offene Trades angewendet wird.

          enum ENUM_BREAKEVEN_MODE
           {
            BREAKEVEN_MODE_AUTOMATIC,
            BREAKEVEN_MODE_MANUAL
           };

          • Im Modus BREAKEVEN_MODE_AUTOMATIC werden alle neuen Trades automatisch mit OnTradeTransaction verfolgt. Der Händler muss nichts zusätzlich tun. Das System erkennt, schützt und verwaltet sofort neue Positionen.
          • Der Modus BREAKEVEN_MODE_MANUAL bietet maximale Flexibilität bei der Konfiguration. Der Händler wählt aus, welche Tickets durch den Break-Even-Mechanismus geschützt werden und ab wann die Überprüfung der Bedingungen beginnt. Dieser Modus ist ideal für Strategien, die eine manuelle Auswahl erfordern, wie z. B. diskretionäres Scalping oder hochpräzise Handels-Setups.


          Erstellen der Basisklasse CBreakEvenBase

          Bevor wir mit der Definition der Basisklasse beginnen, werden wir das in früheren Artikeln entwickelte Risikomanagementsystem einbeziehen.

          #include <Risk_Management.mqh>

          Geschützte Variablen von CBreakEvenBase

          Nachstehend finden wir die Definition der Klasse und ihrer internen Variablen. Für jede Variable wird auch eine Erläuterung gegeben.

          //+------------------------------------------------------------------+
          //| Main class to apply break even                                   |
          //+------------------------------------------------------------------+
          class CBreakEvenBase
           {
          protected:
            CTrade             obj_trade;     //CTrade object
            MqlTick            tick;          //tick structure
            string             symbol;        //current symbol
            double             point_value;   //value of the set symbol point
            position_be        PositionsBe[];  //array of positions of type Positions
            ulong              magic;         //magic number of positions to make break even
            bool               pause;         //Boolean variable to activate the pause of the review, this is used to prevent the array from going out of range
            int                num_params;    //Number of parameters the class needs
            ENUM_BREAKEVEN_MODE breakeven_mode; //Break even mode, manual or automatic
            bool               allow_extra_logs;
            .
            .
           };

          Die Variable, die Aufträge sendet und ändert. 

          CTrade             obj_trade;    //CTrade object

          Die MqlTick-Struktur, die Informationen über den letzten aufgezeichneten Tick speichert. 

          MqlTick            tick;         //tick structure

          Der Name des zu analysierenden Instruments. 

          string             symbol;       //current symbol

          Der Wert eines Punktes für das ausgewählte Symbol. 

          double             point_value;  //value of the set symbol point

          Das Array der Positionen, auf die der Break-Even-Modus angewendet wird. 

          position_be        PositionsBe[]; //array of positions of type Positions

          Die eindeutige Kennung für Trades bzw. Positionen (Magic Number). 

          ulong              magic;        //magic number of positions to make break even

          Ein Indikator, der die Prüfung vorübergehend unterbricht. 

          bool               pause;         //Boolean variable to activate the pause of the review, this is used to prevent the array from going out of range

          Die Anzahl der von der Klasse benötigten Parameter.

          int                num_params;    //Number of parameters the class needs

          Die Art der Anwendung des Break-Even (manuell oder automatisch).

          ENUM_BREAKEVEN_MODE breakeven_mode; //Break even mode, manual or automatic

          Die Möglichkeit, zusätzliche Protokolleinträge zu erstellen.

          bool               allow_extra_logs;

          Die Methoden von CBreakEvenBase

          In diesem Abschnitt beginnen wir mit der Definition der Funktionen, die von den verschiedenen später implementierten Klassen geerbt werden sollen.

          Konstruktor

          Der Konstruktor ist ein wichtiger Bestandteil jeder Klasse. In der Klasse CBreakEvenBase besteht seine Aufgabe darin, die internen Variablen zu initialisieren und ihnen die erforderlichen Werte zuzuweisen, damit die Klasse korrekt funktioniert.

          Dieser Konstruktor akzeptiert drei Parameter: das Symbol, von dem die Geld- und Briefdaten übernommen werden; die Magie, die die Identifizierung spezifischer Aufträge ermöglicht; und den Modus, der die gewählte Art des Break-Even-Managements angibt (automatisch oder manuell).

          CBreakEvenBase(string symbol_, ulong magic_, ENUM_BREAKEVEN_MODE mode_);

          Definition des Konstruktors

          Wenn der Konstruktor ausgeführt wird, werden die Variablen pause und allow_extra_logs mit dem Wert false initialisiert. Dann wird geprüft, ob der empfangene Moduswert gültig ist. Ist dies nicht der Fall, wird eine Fehlermeldung ausgegeben, und ExpertRemove wird aufgerufen, um den Expert Advisor aus dem Chart zu entfernen, sodass der Code nicht mit einer falschen Einstellung weiter ausgeführt werden kann.

          //+------------------------------------------------------------------+
          //| Constructor                                                       |
          //+------------------------------------------------------------------+
          CBreakEvenBase::CBreakEvenBase(string symbol_, ulong magic_, ENUM_BREAKEVEN_MODE mode_)
            : pause(false), allow_extra_logs(false)
           {
            if(magic_ != NOT_MAGIC_NUMBER)
              obj_trade.SetExpertMagicNumber(magic_);
          
            if(mode_ != BREAKEVEN_MODE_MANUAL && mode_ != BREAKEVEN_MODE_AUTOMATIC)
             {
              printf("%s:: Error critico el modo del break even %s, es invalido", __FUNCTION__, EnumToString(mode_));
              ExpertRemove();
             }
          
            this.symbol = symbol_;
            this.num_params = 0;
            this.magic = magic_;
            this.breakeven_mode = mode_;
            this.point_value = SymbolInfoDouble(symbol_, SYMBOL_POINT);
           }
          

          Die entsprechenden Werte werden auch den Parametern symbol, magic und breakeven_mode zugewiesen, wobei die als Argumente erhaltenen Werte verwendet werden.

          Der Punktwert des Symbols wird in der Variablen point_value gespeichert, und die Anzahl der Parameter wird mit dem Wert 0 initialisiert. Jede Klasse, die von CBreakEvenBase geerbt wird, definiert ihre eigene Anzahl von Parametern in ihrem eigenen Konstruktor.

          Wenn die Variable magic_ gleich NOT_MAGIC_NUMBER ist, wird die Funktion der Klasse CTrade, die für die Einstellung der magischen Zahl vorgesehen ist, nicht ausgeführt. 

          Anmerkung: NOT_MAGIC_NUMBER ist eine Definition, die in der Include-Datei des Risikomanagements erstellt wird. Mit dieser Definition wird angegeben, dass die Magic Number nicht verwendet wird. Wenn also der Break-Even-Mechanismus verwendet wird, wird er auf alle Trades angewendet (nur wenn der Break-Even-Modus automatisch ist).

          Destructor

          Der Destruktor wird verwendet, um den Speicher freizugeben, der von dem Array verwendet wird, in dem die verwalteten Positionen gespeichert sind. Diese Funktion wird automatisch ausgeführt, wenn das Objekt zerstört wird.

          //+------------------------------------------------------------------+
          //| Destructor                                                       |
          //+------------------------------------------------------------------+
          CBreakEvenBase::~CBreakEvenBase()
           {
            ArrayFree(PositionsBe);
           }


          Gemeinsame Klassenfunktionen implementieren

          Als Nächstes werden wir die Funktionen definieren, die Teil der Basisklasse CBreakEvenBase sind. Diese Funktionen werden von den abgeleiteten Klassen, die später implementiert werden, verwendet und erweitert.

          Zu Beginn deklarieren wir eine Funktion, die die Anzahl der von der Klasse benötigten Parameter zurückgibt. Diese Funktion gibt einfach den Wert der geschützten Variablen num_params zurück. Außerdem ist sie als const gekennzeichnet, um zu verhindern, dass der Zustand des Objekts verändert wird, und als final, um zu verhindern, dass sie von untergeordneten Klassen überschrieben wird.

          virtual inline int GetNumParams() const final { return num_params; }

          Als Nächstes deklarieren wir die Funktion Add, mit der eine neue position_be-Struktur zum Array PositionsBe hinzugefügt wird. Diese Funktion erhält die notwendigen Handelsdaten, wie z.B. das Ticket, den Eröffnungskurs, den Stop Loss und den Positionstyp.

          virtual bool       Add(ulong post_ticket, double open_price, double sl_price, ENUM_POSITION_TYPE position_type) = 0;

          Jede Klasse, die von CBreakEvenBase erbt, muss ihre eigene Version dieser Funktion implementieren, da sie als rein und virtuell deklariert ist.

          Die Hauptfunktion dieser Klasse besteht darin, den Break-Even zu gewährleisten. Sie ist für die Anwendung der Break-Even-Anpassung auf alle im Array PositionsBe gespeicherten Positionen verantwortlich. Zu diesem Zweck wird das Array in einer for-Schleife durchlaufen.

          Bevor die Anpassung vorgenommen wird, wird geprüft, ob die Größe des Feldes größer als Null ist und ob die Variable pause falsch ist. Wenn diese Bedingungen nicht erfüllt sind, wird die Funktion sofort beendet.

          Da die Struktur position_be bereits sowohl das Zielniveau als auch den Preis enthält, zu dem die Anpassung ausgelöst werden soll, muss nur noch geprüft werden, ob die Marktbedingung erfüllt ist:

          • Wenn es sich bei der Position um einen Kauf handelt, wird geprüft, ob der Briefkurs größer oder gleich dem price_to_beat-Wert ist;
          • Wenn es sich um einen Verkauf handelt, wird geprüft, ob der Geldkurs kleiner oder gleich demselben Wert ist.

          Nach Bestätigung der Bedingung wird die Position mit dem entsprechenden Ticket identifiziert und der aktuelle Take-Profit-Wert berechnet. Anschließend wird die Änderung mit der Methode PositionModify der Klasse CTrade durchgeführt. Schließlich wird diese Position zum Entfernen aus dem Array markiert.

          //+------------------------------------------------------------------+
          //| Function to make break even                                      |
          //+------------------------------------------------------------------+
          void CBreakEvenBase::BreakEven(void)
           {
            if(this.PositionsBe.Size() < 1 || pause)
              return;
          
            SymbolInfoTick(this.symbol, tick);
          
            int indices_to_remove[];
          
            for(int i = 0 ; i < ArraySize(this.PositionsBe) ; i++)
             {
              if((this.PositionsBe[i].type == POSITION_TYPE_BUY && tick.ask >= this.PositionsBe[i].price_to_beat) ||
                 (this.PositionsBe[i].type == POSITION_TYPE_SELL && tick.bid <= this.PositionsBe[i].price_to_beat))
              {
                  if(!PositionSelectByTicket(this.PositionsBe[i].ticket))
                  {
                   printf("%s:: Error al seleccionar el ticket %I64u",__FUNCTION__,this.PositionsBe[i].ticket);
                   ExtraFunctions::AddArrayNoVerification(indices_to_remove, i);
                   continue;
                  }
                  
                  double position_tp = PositionGetDouble(POSITION_TP);
                  obj_trade.PositionModify(this.PositionsBe[i].ticket, this.PositionsBe[i].breakeven_price, position_tp);
                  ExtraFunctions::AddArrayNoVerification(indices_to_remove, i);
              }
             }
          
            ExtraFunctions::RemoveMultipleIndexes(this.PositionsBe, indices_to_remove);
           }

          Um dem Array PositionsBe eine Struktur hinzuzufügen, wenn ein neuer Trade eröffnet wird, definieren wir eine Funktion, die innerhalb des OnTradeTransaction-Ereignisses aufgerufen wird. Diese Funktion heißt OnTradeTransactionEvent und wird jedes Mal ausgeführt, wenn eine Handelstransaktion stattfindet.

          virtual void       OnTradeTransactionEvent(const MqlTradeTransaction& trans) final;

          Bevor eine Position zum Array PositionsBe hinzugefügt wird, muss das entsprechende Ticket mit der Funktion HistoryDealSelect ausgewählt werden. Anschließend wird anhand des Einstiegswerts die Art der Eröffnung eines Trades bestimmt.

          Entspricht der Eintrag einem Markteintritt, d.h. DEAL_ENTRY_IN, wird geprüft, ob die Position vollständig geöffnet ist, ob der Break-Even-Modus automatisch ist - BREAKEVEN_MODE_AUTOMATIC - und ob die Magic-Number der Position mit der internen magischen Variable übereinstimmt oder ob diese den Wert NOT_MAGIC_NUMBER hat.

          Wenn alle Bedingungen erfüllt sind, wird die Position mit der Funktion Hinzufügen zum Array PositionsBe hinzugefügt. Wenn allow_extra_logs aktiviert ist, wird eine Meldung über die Positionserfassung gedruckt.

          Entspricht der Eintrag einem Ausgang, d. h. DEAL_ENTRY_OUT, und ist die Position vollständig geschlossen, wird das entsprechende Ticket aus dem Array PositionsBe entfernt. Um mögliche Konflikte während dieser Entfernung zu vermeiden, wird die Variable pause vorübergehend auf true gesetzt.

          Der Code der Funktion ist wie folgt aufgebaut:

          //+------------------------------------------------------------------+
          //| OnTradeTransactionEvent                                          |
          //+------------------------------------------------------------------+
          void CBreakEvenBase::OnTradeTransactionEvent(const MqlTradeTransaction &trans)
           {
            if(trans.type != TRADE_TRANSACTION_DEAL_ADD)
              return;
          
            HistoryDealSelect(trans.deal);
            ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal, DEAL_ENTRY);
            bool pos = PositionSelectByTicket(trans.position);
          
            if(breakeven_mode == BREAKEVEN_MODE_AUTOMATIC)
             {
              ulong position_magic = (ulong)HistoryDealGetInteger(trans.deal, DEAL_MAGIC);
          
              if(entry == DEAL_ENTRY_IN && pos && (this.magic == position_magic || this.magic == NOT_MAGIC_NUMBER))
               {
                if(Add(trans.position, PositionGetDouble(POSITION_PRICE_OPEN), PositionGetDouble(POSITION_SL), (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE)))
                  if(this.allow_extra_logs)
                    printf("%s:: Se a añadido el ticket %I64u del array de posiciones", __FUNCTION__, trans.position);
                return;
               }
             }
          
            if(entry == DEAL_ENTRY_OUT && pos == false)
             {
              this.pause = true;
          
              if(ExtraFunctions::RemoveIndexFromAnArrayOfPositions(PositionsBe, trans.position))
                if(this.allow_extra_logs)
                  printf("%s:: Se a eliminado el ticket %I64u del array de posiciones", __FUNCTION__, trans.position);
          
              this.pause = false;
             }
           }

          Einige abgeleitete Klassen erfordern die Konfiguration verschiedener Parameter. Für eine einfache Anpassung werden beispielsweise zwei ganzzahlige Werte benötigt, während andere Methoden, wie die ATR-basierte Methode, Daten wie den Zeitraum, den Zeitrahmen und den Multiplikator erfordern.

          Da es nicht möglich ist, mehrere Versionen einer einzigen virtuellen Funktion mit unterschiedlichen Parametern in der Basisklasse zu definieren, wird vorgeschlagen, ein Array vom Typ MqlParam zu verwenden, um die erforderliche Konfiguration zu speichern. Auf diese Weise kann jede vererbende Klasse die empfangenen Werte intern interpretieren und zuweisen.

          virtual void       Set(MqlParam &params[]) = 0;

          Schließlich wurde eine Funktion hinzugefügt, die es ermöglicht, zusätzliche Informationsmeldungen zu aktivieren oder zu deaktivieren. Dies ist nützlich, wenn wir Aktionen wie das Hinzufügen oder Entfernen von Tickets während der Programmausführung protokollieren müssen.

          virtual void       SetExtraLogs(bool allow_extra_logs_) final { this.allow_extra_logs = allow_extra_logs_; }


          Die Entwicklung der ersten Klasse CBreakEvenSimple

          Interne Variablen

          Nun, da die Basisklasse fertig ist, werden wir mit der Entwicklung der Klasse CBreakEvenSimple beginnen. Diese Klasse wendet die Break-Even-Methode mit einer festen Anzahl von Punkten an.

          Hierfür werden zwei Variablen deklariert:

          int                extra_points_be, points_be;

          • Die Variable extra_points_be gibt an, wie weit der Break-Even-Level nach der Aktivierung zusätzlich verschoben wird,
          • Die Variable points_be legt fest, um wie viele Punkte sich der Preis im Gewinn bewegen muss, bevor die Anpassung durchgeführt wird.

          Konstruktor

          Im Konstruktor müssen die Anfangswerte festgelegt werden. Der Konstruktor der Basisklasse wird ebenfalls aufgerufen, um die geerbten Parameter zuzuweisen. Innerhalb des Konstruktorkörpers werden die internen Variablen auf Null initialisiert und die Anzahl der von der Klasse benötigten Parameter definiert, die in diesem Fall zwei sind.

                               CBreakEvenSimple(string symbol_, ulong magic_, ENUM_BREAKEVEN_MODE mode_)
          :                CBreakEvenBase(symbol_, magic_, mode_) { this.extra_points_be = 0; this.points_be = 0; this.num_params = 2;}

          Funktion zur Einstellung von Parametern

          Um den internen Variablen extra_points_be und points_be Werte zuzuweisen, wird ein Array vom Typ MqlParam verwendet. Diese Struktur ist Teil der MQL5-Sprache und wird typischerweise beim Hinzufügen von Indikatoren mit der Funktion ChartIndicatorAdd() verwendet.

          In diesem Fall wird sie verwendet, um Werte an die Klasse zu übergeben. Die Set-Funktion wird in der Basisklasse überschrieben. Hierfür wird das Schlüsselwort „override“ verwendet.

          void               Set(MqlParam &params[]) override;

          Innerhalb der Funktion wird geprüft, ob die Größe des Arrays params nicht kleiner als zwei ist. Wenn diese Bedingung nicht erfüllt ist, wird eine Fehlermeldung angezeigt. Andernfalls wird die Funktion SetSimple aufgerufen, um die entsprechenden Werte zuzuweisen.

          //+------------------------------------------------------------------+
          //| Set attributes of CBreakEvenSimple class with MqlParam array     |
          //+------------------------------------------------------------------+
          void CBreakEvenSimple::Set(MqlParam &params[])
           {
            if(params.Size() < 2)
             {
              printf("%s:: Error setting simple break-even, the size of the params array %I32u to less than 2", __FUNCTION__, params.Size());
              return;
             }
          
            SetSimple(int(params[0].integer_value), int(params[1].integer_value));
           }

          Die Funktion SetSimple

          Die Funktion SetSimple ermöglicht es, die Werte für points_be und extra_points_be direkt zu setzen, ohne ein Parameter-Array zu verwenden. In diesem Fall werden die Ganzzahlwerte direkt beim Aufruf der Funktion übernommen.

          //+------------------------------------------------------------------+
          //| Function to set member variables without using MalParams         |
          //+------------------------------------------------------------------+
          void CBreakEvenSimple::SetSimple(int points_be_, int extra_points_be_)
           {
            if(points_be_ <= 0)
             {
              printf("%s:: Error when setting the break even value for fixed points, be points %I32d are invalid.", __FUNCTION__, extra_points_be_);
              ExpertRemove();
              return;
             }
          
            if(extra_points_be_ < 0)
             {
              printf("%s:: Error when setting the break even value for fixed points, extra points %I32d are invalid.", __FUNCTION__, extra_points_be_);
              ExpertRemove();
              return;
             }
          
            if(extra_points_be_ >= points_be_)
             {
              printf("%s:: Warning: The break even points (breakeven_price) is greater than the breakeven points (price_to_beat)\nTherefore the value of the extra breakeven points will be modified 0.",
                     __FUNCTION__);
              this.points_be = points_be_; //0
              this.extra_points_be = 0; //1
              return;
             }
          
            this.points_be = points_be_; //0
            this.extra_points_be = extra_points_be_; //1
           }

          Diese Methode kann nützlich sein, wenn die Werte direkt im Code definiert werden oder wenn es in bestimmten Fällen notwendig ist, die Verwendung des Typs MqlParam zu vermeiden.

          Funktion zum Hinzufügen von Positionsdaten zum Array PositionsBe

          Um die Klasse CBreakEvenSimple weiterzuentwickeln, muss die Funktion Add überschrieben werden. Diese Funktion ermöglicht die Berechnung und Registrierung der Hauptwerte der Struktur position_be, die dann im Array PositionsBe gespeichert werden.

          Der Parameter open_price wird als Grundlage für die Berechnung von zwei Werten verwendet: breakeven_price und price_to_beat. Diese Variablen definieren das Niveau, auf das der Stop Loss verschoben wird, und das Niveau, das der Kurs erreichen muss, um die Anpassung auszulösen.

          Die folgenden Formeln werden zur Berechnung der Werte von breakeven_price verwendet:

          • Für Kaufpositionen:

          break_even_price = open_price + (point_value * extra_points_be)

          • Für Verkaufspositionen:

          break_even_price = open_price - (point_value * extra_points_be)

          Für die Berechnung von price_to_beat werden hingegen dieselben Formeln verwendet, aber die Variable extra_points_be wird durch points_be ersetzt.

          Nach der Ermittlung dieser Werte wird die Struktur position_be mit den erforderlichen Daten gefüllt. Diese Struktur wird dann mithilfe der Funktion AddArrayNoVerification, die bereits im vorangegangenen Abschnitt über das Risikomanagement verwendet wurde, dem Array PositionsBe hinzugefügt.

          Der Implementierungscode sieht wie folgt aus:

          //+----------------------------------------------------------------------------------------------+
          //| Create a new structure and add it to the main array using the 'AddToArrayBe' function        |
          //+----------------------------------------------------------------------------------------------+
          bool CBreakEvenSimple::Add(ulong post_ticket, double open_price, double sl_price, ENUM_POSITION_TYPE position_type)
           {
            position_be new_pos;
            new_pos.breakeven_price =  position_type == POSITION_TYPE_BUY ? open_price + (point_value * extra_points_be) : open_price - (point_value * extra_points_be);
            new_pos.type =  position_type;
            new_pos.price_to_beat = position_type == POSITION_TYPE_BUY ? open_price + (point_value * points_be) : open_price - (point_value * points_be) ;
            new_pos.ticket = post_ticket;
            ExtraFunctions::AddArrayNoVerification(this.PositionsBe,new_pos); 
            return true;
           }

          Vollständiger Klassencode:

          //+------------------------------------------------------------------+
          //| class CBreakEvenSimple                                           |
          //+------------------------------------------------------------------+
          class CBreakEvenSimple : public CBreakEvenBase
           {
          private:
            int                extra_points_be, points_be;
          
          public:
                               CBreakEvenSimple(string symbol_, ulong magic_, ENUM_BREAKEVEN_MODE mode_)
              :                CBreakEvenBase(symbol_, magic_, mode_) { this.extra_points_be = 0; this.points_be = 0; this.num_params = 2;}
          
          
            bool               Add(ulong post_ticket, double open_price, double sl_price, ENUM_POSITION_TYPE position_type) override;
            void               Set(MqlParam &params[]) override;
            void               SetSimple(int points_be_, int extra_points_be_);
           };
          
          //+----------------------------------------------------------------------------------------------+
          //| Create a new structure and add it to the main array using the 'AddToArrayBe' function        |
          //+----------------------------------------------------------------------------------------------+
          bool CBreakEvenSimple::Add(ulong post_ticket, double open_price, double sl_price, ENUM_POSITION_TYPE position_type)
           {
            position_be new_pos;
            new_pos.breakeven_price =  position_type == POSITION_TYPE_BUY ? open_price + (point_value * extra_points_be) : open_price - (point_value * extra_points_be);
            new_pos.type =  position_type;
            new_pos.price_to_beat = position_type == POSITION_TYPE_BUY ? open_price + (point_value * points_be) : open_price - (point_value * points_be) ;
            new_pos.ticket = post_ticket;
            ExtraFunctions::AddArrayNoVerification(this.PositionsBe, new_pos);
            return true;
           }
          
          //+------------------------------------------------------------------+
          //| Set attributes of CBreakEvenSimple class with MqlParam array     |
          //+------------------------------------------------------------------+
          void CBreakEvenSimple::Set(MqlParam &params[])
           {
            if(params.Size() < 2)
             {
              printf("%s:: Error setting simple break-even, the size of the params array %I32u to less than 2", __FUNCTION__, params.Size());
              return;
             }
          
            SetSimple(int(params[0].integer_value), int(params[1].integer_value));
           }
          
          //+------------------------------------------------------------------+
          //| Function to set member variables without using MalParams         |
          //+------------------------------------------------------------------+
          void CBreakEvenSimple::SetSimple(int points_be_, int extra_points_be_)
           {
            if(points_be_ <= 0)
             {
              printf("%s:: Error when setting the break even value for fixed points, be points %I32d are invalid.", __FUNCTION__, extra_points_be_);
              ExpertRemove();
              return;
             }
          
            if(extra_points_be_ < 0)
             {
              printf("%s:: Error when setting the break even value for fixed points, extra points %I32d are invalid.", __FUNCTION__, extra_points_be_);
              ExpertRemove();
              return;
             }
          
            if(extra_points_be_ >= points_be_)
             {
              printf("%s:: Warning: The break even points (breakeven_price) is greater than the breakeven points (price_to_beat)\nTherefore the value of the extra breakeven points will be modified 0.",
                     __FUNCTION__);
              this.points_be = points_be_; //0
              this.extra_points_be = 0; //1
              return;
             }
          
            this.points_be = points_be_; //0
            this.extra_points_be = extra_points_be_; //1
           }
          //+------------------------------------------------------------------+

          Damit ist die grundlegende Implementierung der Break-Even-Methode auf Basis fester Punktabstände abgeschlossen. Im nächsten Abschnitt wird die Funktionalität dieser Klasse anhand der Order Blocks, die im letzten Artikel über Risikomanagement für Expert Advisor entwickelt wurden, getestet.


          Testen des Break-Even-Modus

          In diesem Abschnitt werden wir den Order Blocks Expert Advisor anpassen, um die Anwendung des Fixed-Points-Break-Even-Modus zu integrieren. Ziel ist es, die Funktionsweise des Systems zu überprüfen und das Verhalten der Positionen bei einer solchen aktiven Verwaltung zu bewerten.

          Zunächst ändern wir die Parameter des Expert Advisors und fügen einen neuen speziellen Abschnitt hinzu:

          sinput group "-----| Break Even |----"

          In diesem Abschnitt werden alle Parameter im Zusammenhang mit dem Break-Even-Mechanismus aufgeführt. Da bisher nur die Variante des festen Punktabstands entwickelt wurde, wird ein allgemeiner bool-Parameter hinzugefügt, um sie zu aktivieren oder zu deaktivieren.

          input bool use_be = true;                         // Enable Break Even usage

          Anschließend wird ein Unterabschnitt für die spezifischen Parameter dieser Methode definiert.

          sinput group "- BreakEven based on Fixed Points -"
          input int be_fixed_points_to_put_be = 200;         // Points traveled needed to activate Break Even
          input int be_fixed_points_extra = 100;             // Extra adjustment points when activating Break Even
          

          Um die Funktionalität zu integrieren, wird ein Objekt vom Typ CBreakEvenSimple erstellt. Künftig wird dies durch ein flexibleres Objekt vom Typ Manager ersetzt werden, das die Verwaltung verschiedener Varianten über eine einzige Schnittstelle ermöglicht.

          CBreakEvenSimple break_even(_Symbol, Magic, BREAKEVEN_MODE_AUTOMATIC);

          In der Funktion OnInit werden, wenn der Parameter use_be aktiviert ist, die mit der Funktion SetSimple konfigurierten Werte zugewiesen.

          //---
            if(use_be)
              break_even.SetSimple(be_fixed_points_to_put_be, be_fixed_points_extra);
          

          Damit das Break-Even-Management korrekt funktioniert, muss die Funktion OnTradeTransactionEvent des break_even-Objekts innerhalb der Funktion OnTradeTransaction ausgeführt werden. Er ist für die Eintragung oder Entfernung von Trades in das entsprechende Array zuständig.

          //+------------------------------------------------------------------+
          //| TradeTransaction function                                        |
          //+------------------------------------------------------------------+
          void OnTradeTransaction(const MqlTradeTransaction& trans,
                                  const MqlTradeRequest& request,
                                  const MqlTradeResult& result)
           {
            risk.OnTradeTransactionEvent(trans);
            break_even.OnTradeTransactionEvent(trans);
           }
          

          Schließlich wird die Break-Even-Funktion innerhalb von OnTick nur dann aufgerufen, wenn die Verwendung dieses Mechanismus vom Benutzer aktiviert wurde:

          if(use_be) break_even.BreakEven();   

          Dank dieser Änderungen kann der Expert Advisor nun automatisch den Break-Even-Mechanismus anwenden, wenn die festgelegten Bedingungen erfüllt sind. 

          Backtest

          Der Backtest des Expert Advisors mit den Order Blocks und dem Break-Even-Modus wurde ohne Einschränkungen beim Kapitalmanagement durchgeführt. Es wurden keine Gewinn- oder Verlustgrenzen festgelegt. Die Trades konnten nur geschlossen werden, wenn das Stop-Loss- oder Take-Profit-Niveau erreicht wurde. Es wurde ein Risiko-Ertrags-Verhältnis von 1:2 verwendet, wobei der Take Profit doppelt so hoch ist wie der Stop Loss.

          1. Allgemeine Einstellungen:

              Abbildung 4. Allgemeine Einstellungen für den Backtest

          2. Backtest-Chart der Strategie, die ohne den Break-Even-Mechanismus arbeitet.

          Abbildung 5. Backtest-Chart ohne Verwendung des Break-Even-Mechanismus

          Abbildung 5 zeigt das Verhalten des Expert Advisors ohne Anwendung des Break-Even-Mechanismus. Bei diesem Test verringerte sich der ursprüngliche Saldo von 15.000 $ auf 8.700 $, was zu einem Verlust von etwa 7.000 $ führte. Dieses Ergebnis dient als Maßstab für die Bewertung der Auswirkungen der Aktivierung oder Deaktivierung dieser Funktion.

          3. Backtest-Chart der Strategie, die mit aktiviertem Break-Even-Mechanismus arbeitet.

          Abbildung 6. Backtest-Chart mit aktiviertem Break-Even-Mechanismus

          Der zweite Test wurde mit aktiviertem Break-Even-Modus durchgeführt. Der Parameter be_fixed_points_extra wurde auf 100 Punkte und der Parameter be_fixed_points_to_put_be auf 600 Punkte gesetzt.

          Bei diesem Test zeigte das Diagramm einen langsameren Verlauf. Bei einer Reihe von Verlust-Trades war der Rückgang des Kontostands geringer. Der Saldo sank von 10.900 $ auf 7.900 $, was einem Verlust von 3.000 $ entspricht. Diese Differenz von 3.000 $ im Vergleich zum ersten Szenario zeigt, dass die Anwendung des Break-Even-Mechanismus dazu beitragen kann, die Auswirkungen von Verlustserien zu verringern.

          Es wurden jedoch auch Einschränkungen festgestellt. In beiden Tests begannen die Trades mit Verlusten bis etwa März 2024. Ab dem 01.03.2024 waren Veränderungen zu beobachten: eine Reihe von profitablen Trades erschien. Im ersten Backtest lag der Saldo bei über 16.000 $, während er im zweiten Backtest, bei dem der Break-Even-Modus aktiviert war, nur 10.900 $ erreichte.

          Eine mögliche Erklärung sind frühzeitige Schließungen, die durch den Break-Even-Modus verursacht werden. Wenn der Kurs nach der Eröffnung einer Position zurückgeht, aber das Stop-Loss-Niveau nicht erreicht, kann er in Ermangelung eines Break-Even-Mechanismus in die erwartete Richtung umkehren und mit Gewinn schließen. Umgekehrt würde die Position bei aktiviertem Break-Even-Modus bei Null schließen, wodurch die Möglichkeit, diesen Gewinn zu erzielen, wegfallen würde.

          Dies deutet darauf hin, dass im ersten Szenario viele Trades bis zum Erreichen des Take Profits zwischenzeitlich im Minus lagen. Aus diesem Grund kann das Ergebnis bei der Anwendung des Break-Even-Modus mit festen Punktabständen in Szenarien, in denen es häufig zu Kursrückgängen kommt, eingeschränkt sein.

          Bei hoher Volatilität kann diese Konfiguration schnell aktiviert werden, wodurch das Kapital geschützt wird, gleichzeitig aber auch die Trades vorzeitig geschlossen werden. In Zeiten geringerer Aktivität wird er möglicherweise gar nicht ausgelöst. Daher ist die Verwendung des fixierten Break-Even-Modus besser für Händler geeignet, die dem Schutz des Kontostands Vorrang vor der Erzielung eines höheren Gewinns pro Handel einräumen.


          Schlussfolgerung

          In diesem Artikel haben wir das Break-Even-Konzept, seine Umsetzung in MQL5 und einige mögliche Varianten analysiert. Alles, was entwickelt wurde, wurde auf den Order Blocks Expert Advisor angewendet, der im letzten Artikel zum Risikomanagement erstellt wurde.

          Zum Abschluss der Analyse wurde ein Vergleich des Systemverhaltens mit und ohne aktivierten Break-Even-Modus durchgeführt. Es wurde festgestellt, dass bei einer Reihe von gewinnbringenden Trades das Wachstum des Kontostandes durch den Einsatz dieser Methode eingeschränkt werden könnte. Umgekehrt reduziert der Break-Even-Modus in Verlustphasen das Risiko großer Verluste. Diese Dualität deutet auf ein gewisses Gleichgewicht zwischen Risiko und Ergebnis hin, auch wenn die tatsächliche Wirksamkeit von den statistischen Ergebnissen der zugrunde liegenden Strategie ohne äußere Zwänge (wie Gewinn- oder Verlustgrenzen) abhängt.

          Wir sind auch zu dem Schluss gekommen, dass der Break-Even-Modus besser für Händler geeignet ist, die keinen aggressiven Handel anstreben, sondern eher eine stetige Entwicklung der Ergebnisse mit einer größeren Kontrolle über das Beenden von Trades bevorzugen.

          In diesem Artikel verwendete/aktualisierte Dateien:

          Dateiname Typ Beschreibung 
           Risk_Management.mqh  .mqh (Header-Datei) Enthält die Risikomanagementklasse, die im letzten Artikel der Risikomanagementreihe entwickelt wurde.
           Order_Block_Indicador_New_Part_2.mq5 .mq5 (Indikator) Enthält den Code für den Order Block Indikator.
           Order Block EA MetaTrader 5.mq5  .mq5 (Expert Advisor) Der Code für den Order Block-Expert Advisor mit integriertem Break-Even-Modus.
           OB_SET_WITHOUT_BREAKEVEN.set .set (Konfigurationsdatei) Einstellungen für den ersten Backtest, ohne den Break-Even-Mechanismus.
           OB_SET_WITH_BREAKEVEN.set .set (Konfigurationsdatei) Einstellungen für den zweiten Backtest mit aktiviertem Break-Even-Modus.
           PositionManagement.mqh .mqh (Header-Datei) Die mqh-Datei, die den Code für den Break-Even-Mechanismus enthält.


          Übersetzt aus dem Spanischen von MetaQuotes Ltd.
          Originalartikel: https://www.mql5.com/es/articles/17957

          Beigefügte Dateien |
          MQL5.zip (210.17 KB)
          Marktsimulation (Teil 20): Erste Schritte mit SQL (III) Marktsimulation (Teil 20): Erste Schritte mit SQL (III)
          Obwohl wir Operationen mit einer Datenbank mit etwa 10 Datensätzen durchführen können, lässt sich das Thema deutlich besser verstehen, wenn wir mit einer Datei arbeiten, die mehr als 15 Tausend Datensätze enthält. Das heißt, wenn wir versuchen würden, eine solche Datenbank manuell zu erstellen, wäre dies ein enormer Aufwand. Es ist jedoch selbst zu Lernzwecken schwierig, eine solche Datenbank zum Download zu finden. Aber in Wirklichkeit müssen wir nicht darauf zurückgreifen – wir können MetaTrader 5 verwenden, um eine Datenbank für uns zu erstellen. Im heutigen Artikel werden wir uns ansehen, wie man das macht.
          Das Hilbert-Schmidt-Unabhängigkeitskriterium (HSIC) Das Hilbert-Schmidt-Unabhängigkeitskriterium (HSIC)
          Der Artikel behandelt den nichtparametrischen statistischen Test HSIC (Hilbert-Schmidt Independence Criterion), mit dem sich lineare und nichtlineare Abhängigkeiten in Daten ermitteln lassen. Es werden zwei Implementierungen zur Berechnung von HSIC in der Sprache MQL5 vorgestellt: der exakte Permutationstest und die Gamma-Approximation. Die Leistungsfähigkeit der Methode wird an synthetischen Daten demonstriert, die eine nichtlineare Beziehung zwischen Merkmalen und der Zielvariablen modellieren.
          Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
          In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
          Selbstlernender Expert Advisor mit einem neuronalen Netz auf Basis einer Markov-Zustandsübergangsmatrix Selbstlernender Expert Advisor mit einem neuronalen Netz auf Basis einer Markov-Zustandsübergangsmatrix
          Selbstlernende EA mit einem neuronalen Netz auf der Grundlage einer Zustandsmatrix. Wir kombinieren Markov-Ketten mit einem mehrschichtigen neuronalen Netz MLP, das mit der ALGLIB MQL5-Bibliothek entwickelt wurde. Wie können Markov-Ketten und neuronale Netze für Prognosen im Devisenhandel kombiniert werden?