English Русский 日本語
preview
Modifizierter Grid-Hedge EA in MQL5 (Teil I): Erstellung eines einfachen Hedge EA

Modifizierter Grid-Hedge EA in MQL5 (Teil I): Erstellung eines einfachen Hedge EA

MetaTrader 5Handelssysteme | 26 Februar 2024, 10:28
204 4
Kailash Bai Mina
Kailash Bai Mina

Einführung

Möchten Sie in die Welt des Handels mit Expert Advisors (EAs) eintauchen, stoßen aber immer wieder auf diese Zeile - „Kein gefährliches Hedging/Grid/Martingale verwenden.“ Sie fragen sich vielleicht, was der ganze Wirbel um diese Strategien soll. Warum wird immer wieder behauptet, sie seien riskant, und was ist wirklich dran an diesen Behauptungen? Vielleicht denken Sie sogar: „Hey, können wir diese Strategien verbessern, um sie sicherer zu machen?“ Und warum beschäftigen sich die Händler überhaupt mit diesen Strategien? Was ist gut und was schlecht an ihnen? Wenn Ihnen diese Gedanken durch den Kopf gegangen sind, sind Sie hier genau richtig. Ihre Suche nach Antworten hat nun ein Ende.

Wir beginnen mit der Erstellung eines einfachen Hedge Expert Advisors. Betrachten Sie es als den ersten Schritt zu unserem größeren Projekt - einem Grid-Hedge Expert Advisor. Das wird ein cooler Mix aus klassischem Grid und Hedge-Strategien. Am Ende dieses Artikels werden Sie wissen, wie Sie eine grundlegende Hedge-Strategie entwickeln können, und Sie werden erfahren, ob diese Strategie so profitabel ist, wie manche behaupten.

Aber das ist noch nicht alles. In dieser Serie werden wir diese Strategien genauer unter die Lupe nehmen. Wir werden herausfinden, was funktioniert und was nicht, und wie wir es kombinieren können, um es noch besser zu machen. Unser Ziel? Um zu sehen, ob wir diese traditionellen Strategien nehmen, ihnen eine neue Wendung geben und sie nutzen können, um mit dem automatisierten Handel solide Gewinne zu erzielen. Bleiben Sie also bei mir und lassen Sie es uns gemeinsam herausfinden!

Hier ein kurzer Überblick über die Themen, die wir in diesem Artikel behandeln werden:

  1. Diskussion über die klassische Hedge-Strategie
  2. Automatisierung der klassischen Hedge-Strategie
  3. Backtesting unserer klassischen Hedge-Strategie
  4. Schlussfolgerung


Diskussion über die klassische Hedge-Strategie

Zuallererst sollten wir die Strategie erörtern, bevor wir weitermachen.

Zunächst eröffnen wir eine Kaufposition, sagen wir der Einfachheit halber, bei einem Kursniveau von 1000, setzen den Stop-Loss bei 950 und nehmen den Gewinn bei 1050 mit. Das heißt, wenn wir den Stop-Loss erreichen, verlieren wir 50 $, und wenn wir den Take-Profit erreichen, gewinnen wir 50 $. Jetzt haben wir die Gewinnmitnahme erreicht, die Strategie endet hier, und wir gehen mit einem Gewinn nach Hause. Aber wenn der Stopp-Loss erreicht wird, verlieren wir 50 $. Jetzt platzieren wir sofort eine Verkaufsposition bei 950 und setzen den Take-Profit auf 900 und den Stop-Loss auf 1000. Wenn diese neue Verkaufsposition den Take-Profit erreicht, ... gewinnen wir $50, aber der Haken ist, dass wir bereits $50 verloren haben, sodass unser Nettogewinn $0 ist, und falls sie den Stop-Loss erreicht, der auf dem Preisniveau 1000 liegt, verlieren wir wieder $50, sodass unser Gesamtverlust $100 ist, aber jetzt an diesem Punkt platzieren wir wieder eine Kaufposition mit dem gleichen Take-Profit und Stop-Loss wie die vorherige Kaufposition. Wenn diese neue Kaufposition den TP erreicht, erhalten wir 50 $ und unser gesamter Nettogewinn ist -$50-$50+$50 = -$50, d.h. ein Verlust von 50 $, und wenn sie den Stop-Loss erreicht, ist unsere Summe -$50-$50-$50=-$150, d.h. ein Gesamtverlust von 150 $.

Der Einfachheit halber lassen wir die Spreads und die Provisionen vorerst außer Acht.

Jetzt denken Sie vielleicht: „Was passiert hier, und wie kommen wir auf 100 %?“ Aber Sie übersehen einen wichtigen Punkt: die Losgröße. Was ist, wenn wir die Losgröße der aufeinanderfolgenden Positionen erhöhen? Lassen Sie uns also unsere Strategie überdenken.

Wir eröffnen eine Kaufposition von 0,01 Lot (Minimum möglich) bei 1000:

  • Wenn wir den Take-Profit (1050) erreichen, gehen wir mit einem Gewinn von 50 $ nach Hause und die Strategie endet hier.
  • Wenn wir den Stop-Loss (950) erreichen, verlieren wir 50 $.

Wenn wir den Stop-Loss erreichen, endet die Strategie nicht gemäß der obigen Regel. Gemäß unserer Strategie werden wir sofort eine Verkaufsposition von 0,02 (verdoppelter) Losgröße bei 950 platzieren:

  • Wenn wir den Take-Profit (900) erreichen, erhalten wir einen Gesamtnettogewinn von -$50+$100 = $50 und die Strategie endet hier.
  • Wenn wir den Stop-Loss (1000) erreichen, verlieren wir insgesamt $50+$100 = $150.

Wenn wir den Stop-Loss erreichen, endet die Strategie nicht gemäß der obigen Regel. Gemäß unserer Strategie werden wir sofort eine Kaufposition von 0,04 (wiederum verdoppelter) Losgröße bei 1000 platzieren:

  • Wenn wir den Take-Profit (1050) erreichen, erhalten wir einen Gesamtnettogewinn von -$50-$100+$200 = $50 und die Strategie endet hier.
  • Wenn wir den Stop-Loss (950) erreichen, verlieren wir insgesamt $50+$100+$150=$350.

Wenn wir den Stop-Loss erreichen, endet die Strategie nicht gemäß der obigen Regel. Gemäß unserer Strategie werden wir sofort eine Verkaufsposition von 0,08 (wiederum verdoppelter) Losgröße bei 950 platzieren:

  • Wenn wir den Take-Profit (900) erreichen, erhalten wir einen Gesamtnettogewinn von -$50-$100-$150+$400 = $50 und die Strategie endet hier.
  • Wenn wir den Stop-Loss (1000) erreichen, verlieren wir insgesamt $50+$100+$150+$200 = $500.

... 

Wie Sie vielleicht schon bemerkt haben, erhalten wir in jedem Fall einen Gewinn von 50 $, wenn die Strategie endet. Wenn nicht, wird die Strategie fortgesetzt. Diese Strategie wird fortgesetzt, bis wir entweder bei 900 oder 1050 den Take-Profit erreichen; der Preis wird schließlich einen dieser beiden Punkte erreichen, und wir werden einen garantierten Gewinn von 50 $ erhalten.

Im obigen Fall haben wir zuerst eine Kaufposition platziert, aber es ist nicht zwingend erforderlich, mit einer Kaufposition zu beginnen. Alternativ können wir die Strategie auch mit einer Verkaufsposition von 0,01 (in unserem Fall) beginnen.

Diese Alternative, mit einer Verkaufsposition zu beginnen, ist sehr wichtig, da wir die Strategie später modifizieren werden, um so viel Flexibilität wie möglich zu erhalten. So könnten wir beispielsweise einen Einstiegspunkt (in unserem Fall eine anfängliche Kaufposition) für den obigen Zyklus festlegen, aber diesen Einstiegspunkt nur auf eine Kaufposition zu beschränken, wäre problematisch, da wir den Einstiegspunkt so definieren könnten, dass es vorteilhaft wäre, zunächst eine Verkaufsposition zu platzieren.

Die Strategie, die mit einer Verkaufsposition beginnt, ist genau symmetrisch zu der obigen Strategie, die mit einer Kaufposition beginnt. Um dies zu verdeutlichen, wird unsere Strategie in etwa so aussehen:

  • Wenn wir den Take-Profit (900) erreichen, gehen wir mit einem Gewinn von 50 $ nach Hause und die Strategie endet hier.
  • Wenn wir den Stop-Loss (1000) erreichen, verlieren wir 50 $.

Wenn wir den Stop-Loss erreichen, endet die Strategie nicht gemäß der obigen Regel. Gemäß unserer Strategie werden wir sofort eine Kaufposition von 0,02 (verdoppelter) Losgröße bei 1000 platzieren:

  • Wenn wir den Take-Profit (1050) erreichen, erzielen wir einen Gesamtnettogewinn von -$50+$100 = $50 und die Strategie endet hier.
  • Wenn wir den Stop-Loss (950) erreichen, verlieren wir insgesamt $50+$100 = $150.

Wenn wir den Stop-Loss erreichen, endet die Strategie nicht gemäß der obigen Regel. Gemäß unserer Strategie werden wir sofort eine Verkaufsposition von 0,04 (wieder verdoppelt) Losgröße bei 950 platzieren:

  • Wenn wir den Take-Profit (900) erreichen, erhalten wir einen Gesamtnettogewinn von -$50-$100+$200 = $50 und die Strategie endet hier.
  • Wenn wir den Stop-Loss (1000) erreichen, verlieren wir insgesamt $50+$100+$150 = $350.

... und so weiter.

Wie wir sehen können, endet diese Strategie erst, wenn wir entweder das 900er- oder das 1050er-Kursniveau erreichen, was mit Sicherheit zu einem Gewinn von 50 $ führt. Wenn wir diese Preisniveaus nicht erreichen, wird die Strategie fortgesetzt, bis wir sie schließlich erreichen.

Anmerkung: Es ist nicht zwingend erforderlich, die Losgröße um den Faktor 2 zu erhöhen. Wir können ihn um einen beliebigen Faktor erhöhen, obwohl ein Multiplikator unter 2 in beiden Fällen keinen Gewinn garantiert. Wir haben uns der Einfachheit halber für 2 entschieden und können dies bei der späteren Optimierung der Strategie möglicherweise ändern.

Damit ist unsere Diskussion über die klassische Hedge-Strategie abgeschlossen.


Diskussion über die Automatisierung unserer klassischen Hedge-Strategie

Zunächst müssen wir den Plan besprechen, wie wir bei der Erstellung eines Expert Advisors vorgehen wollen. Es gibt eine ganze Reihe von Möglichkeiten, dies zu tun. Zwei wichtige Ansätze sind die folgenden:

  • Ansatz #1: Festlegung von vier Niveaus (Variablen) gemäß der Strategie und Platzierung von Positionen, sobald der Preis diese Linien erneut gemäß unserer Strategie überschreitet.
  • Ansatz #2: Verwendung von schwebenden Aufträgen und Erkennung, wann dieser schwebende Auftrag ausgeführt wird und Platzierung weiterer schwebender Aufträge, wenn dies geschieht.

    Beide Ansätze sind nahezu gleichwertig, und man kann darüber streiten, welcher von beiden etwas besser ist, aber ich werde nur auf Ansatz Nr. 1 eingehen, da er einfacher zu programmieren und zu verstehen ist.


    Automatisierung der klassischen Hedge-Strategie

    Zunächst werden wir einige Variablen im globalen Bereich deklarieren:

    input bool initialPositionBuy = true;
    input double buyTP = 15;
    input double sellTP = 15;
    input double buySellDiff = 15;
    input double initialLotSize = 0.01;
    input double lotSizeMultiplier = 2;

    1. isPositionBuy ist eine boolsche Variable, die entscheidet, welcher Positionstyp als Nächstes platziert wird, d.h. eine Kauf- oder Verkaufsposition.
    2. buyTP ist der Abstand zwischen A und B, d.h. der Take-Profit von Kaufpositionen (in Pips), wobei A und B später definiert werden.
    3. sellTP ist der Abstand zwischen C und D, d.h. der Take-Profit der Verkaufspositionen (in Pips), wobei C und D später definiert werden.
    4. buySellDiff ist der Abstand zwischen B und C, d.h. zwischen Kauf- und Verkaufskursniveau (in Pips).
    5. intialLotSize ist die Losgröße der ersten Position.
    6. lotSizeMultiplier ist der Multiplikator der Losgröße für eine aufeinanderfolgende Position.
    A, B, C, D sind grundsätzlich Preisniveaus in abnehmender Reihenfolge von oben nach unten.

    Anmerkung: Diese Variablen werden später zur Optimierung der Strategie verwendet.

    Wir setzen zum Beispiel buyTP, sellTP und buySellDiff gleich 15 Pips, aber wir werden diese später ändern und sehen, welche Werte uns optimalen Gewinn und Drawdown geben.

    Dies sind die Eingabevariablen, die später für die Optimierung verwendet werden.

    Jetzt erstellen wir einige weitere Variablen im globalen Raum:

    double A, B, C, D;
    bool isPositionBuy;
    bool hedgeCycleRunning = false;
    double lastPositionLotSize;

    1. Wir haben zunächst 4 Ebenen mit den Namen A, B, C, D als Doppelvariablen definiert:
      • A: Dies ist das Kursniveau der Gewinnmitnahme für alle Kaufpositionen.
      • B: Dies stellt das Eröffnungskursniveau für alle Kaufpositionen und den Stop-Loss für alle Verkaufspositionen dar.
      • C: Dies stellt das Eröffnungskursniveau für alle Verkaufspositionen und den Stop-Loss für alle Kaufpositionen dar.
      • D: Dies ist das Preisniveau der Gewinnmitnahme für alle Verkaufspositionen.
    2. isPositionBuy: Dies ist eine boolsche Variable, die 2 Werte annehmen kann, nämlich true und false, wobei true bedeutet, dass die Ausgangsposition ein Kauf ist und false, dass die Ausgangsposition ein Verkauf ist.
    3. hedgeCycleRunning: Dies ist eine boolsche Variable, die wiederum 2 Werte annehmen kann, nämlich true und false, wobei true bedeutet, dass ein Hedge-Zyklus läuft, d.h. dass der ursprüngliche Auftrag eröffnet wurde, aber die oben definierten Preisniveaus A oder D noch nicht erreicht wurden, während false bedeutet, dass das Preisniveau entweder A oder D erreicht wurde und ein neuer Zyklus gestartet wird, was wir später sehen werden. Außerdem wird diese Variable standardmäßig auf false gesetzt.
    4. lastPositionLotSize: Wie der Name schon sagt, enthält diese Variable vom Typ Double immer die Losgröße der zuletzt geöffneten Order. Falls der Zyklus noch nicht gestartet wurde, nimmt sie den Wert der Eingangsvariablen initialLotSize an, den wir später festlegen werden.

    Jetzt erstellen wir die folgende Funktion: 
    //+------------------------------------------------------------------+
    //| Hedge Cycle Intialization Function                               |
    //+------------------------------------------------------------------+
    void StartHedgeCycle()
       {
        isPositionBuy = initialPositionBuy;
        double initialPrice = isPositionBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
        A = isPositionBuy ? initialPrice + buyTP * _Point * 10 : initialPrice + (buySellDiff + buyTP) * _Point * 10;
        B = isPositionBuy ? initialPrice : initialPrice + buySellDiff * _Point * 10;
        C = isPositionBuy ? initialPrice - buySellDiff * _Point * 10 : initialPrice;
        D = isPositionBuy ? initialPrice - (buySellDiff + sellTP) * _Point * 10 : initialPrice - sellTP * _Point * 10;
    
        ObjectCreate(0, "A", OBJ_HLINE, 0, 0, A);
        ObjectSetInteger(0, "A", OBJPROP_COLOR, clrGreen);
        ObjectCreate(0, "B", OBJ_HLINE, 0, 0, B);
        ObjectSetInteger(0, "B", OBJPROP_COLOR, clrGreen);
        ObjectCreate(0, "C", OBJ_HLINE, 0, 0, C);
        ObjectSetInteger(0, "C", OBJPROP_COLOR, clrGreen);
        ObjectCreate(0, "D", OBJ_HLINE, 0, 0, D);
        ObjectSetInteger(0, "D", OBJPROP_COLOR, clrGreen);
    
        ENUM_ORDER_TYPE positionType = isPositionBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
        double SL = isPositionBuy ? C : B;
        double TP = isPositionBuy ? A : D;
        CTrade trade;
        trade.PositionOpen(_Symbol, positionType, initialLotSize, initialPrice, SL, TP);
        
        lastPositionLotSize = initialLotSize;
        if(trade.ResultRetcode() == 10009) hedgeCycleRunning = true;
        isPositionBuy = isPositionBuy ? false : true;
       }
    //+------------------------------------------------------------------+

    Die Funktion ist vom Typ void, d. h. sie muss nichts zurückgeben. Die Funktion funktioniert wie folgt:

    Zunächst setzen wir die Variable isPositionBuy (bool) gleich der Eingangsvariablen initialPositionBuy, die uns sagt, welcher Positionstyp zu Beginn eines jeden Zyklus platziert werden soll. Sie werden sich vielleicht fragen, warum wir zwei Variablen brauchen, wenn sie beide gleich sind, aber beachten Sie, dass sich isPositionBuy ändert (letzte Zeile des obigen Codeblocks). Der Wert initialPositionBuy ist jedoch immer fest und wird nicht verändert.

    Dann definieren wir eine neue Variable (Typ double) mit dem Namen initialPrice, die wir mit Hilfe eines ternären Operators gleich Ask oder Bid setzen. Wenn isPositionBuy wahr ist, entspricht initialPrice dem Briefkurs zu diesem Zeitpunkt, andernfalls dem Geldkurs.

    Dann definieren wir die Variablen (Typ double), die wir zuvor kurz besprochen haben, d.h. die Variablen A, B, C, D, indem wir den ternären Operator wie folgt verwenden:

    1. Wenn isPositionBuy wahr ist:
      • A ist gleich der Summe von initialPrice und buyTP (Eingangsvariable), wobei buyTP mit dem Faktor (_Point*10) multipliziert wird, wobei _Point die vordefinierte Funktion „Point()“ ist.
      • B ist gleich initialPrice.
      • C ist gleich initialPrice minus buySellDiff (Eingangsvariable), wobei buySellDiff mit dem Faktor (_Point*10) multipliziert wird.
      • D ist gleich initialPrice minus Summe aus buySellDiff und sellTP, die mit dem Faktor (_Point*10) multipliziert wird.

    2. Wenn isPositionBuy falsch ist:
      • A ist gleich der Summe aus initialPrice und (buySellDiff + buyTP), die mit dem Faktor (_Point*10) multipliziert wird.
      • B entspricht der Summe aus initialPrice und buySellDiff , wobei buySellDiff mit einem Faktor von (_Point*10) multipliziert wird.
      • C ist gleich initialPrice.
      • D ist gleich initialPrice minus sellTP, wobei sellTP mit dem Faktor (_Point*10) multipliziert wird.

    Zur Visualisierung zeichnen wir nun mit ObjectCreate einige Linien in das Diagramm, die die Preisniveaus A, B, C und D darstellen, und setzen ihre Farbeigenschaft mit ObjectSetInteger auf clrGreen (Sie können auch eine andere Farbe verwenden).

    Nun müssen wir den ersten Auftrag eröffnen, der je nach der Variable isPositionBuy als Kauf oder Verkauf ausgeführt werden kann. Zu diesem Zweck definieren wir drei Variablen: positionType, SL, TP.

    1. positionType: Der Typ dieser Variablen ist ENUM_ORDER _TYPE , ein vordefinierter nutzerdefinierter Variablentyp, der gemäß der folgenden Tabelle ganzzahlige Werte von 0 bis 8 annehmen kann:

      Ganzzahlige Werte Kennung
       0 ORDER_TYPE_BUY
       1 ORDER_TYPE_SELL
       2 ORDER_TYPE_BUY_LIMIT
       3 ORDER_TYPE_SELL_LIMIT
       4 ORDER_TYPE_BUY_STOP
       5 ORDER_TYPE_SELL_STOP
       6 ORDER_TYPE_BUY_STOP_LIMIT
       7 ORDER_TYPE_SELL_STOP_LIMIT
       8 ORDER_TYPE_CLOSE_BY

      Wie Sie sehen können, steht 0 für ORDER_TYPE_BUY und 1 für ORDER_TYPE_SELL, wir brauchen also nur diese beiden. Wir werden Bezeichner und keine ganzen Zahlen verwenden, da diese schwer zu merken sind.

    2. SL: Wenn isPositionBuy wahr ist, entspricht SL dem Preisniveau C, andernfalls ist es gleich B.

    3. TP: Wenn isPositionBuy wahr ist, entspricht TP dem Preisniveau A, andernfalls ist es gleich D.

    Mit diesen 3 Variablen müssen wir eine Position wie folgt festlegen:

    Zunächst importieren wir die Standard-Handelsbibliothek mit #include:

    #include <Trade/Trade.mqh>
    

    Jetzt, kurz vor dem Öffnen der Position, erstellen wir eine Instanz der CTrade-Klasse mit:

    CTrade trade;
    
    trade.PositionOpen(_Symbol, positionType, initialLotSize, initialPrice, SL, TP);

    Und mit dieser Instanz platzieren wir eine Position mit der Funktion PositionOpen in dieser Instanz mit folgenden Parametern:

    1. _Symbol gibt das aktuelle Symbol an, an das der Expert Advisor gebunden ist.
    2. positionType ist die Variable ENUM_ORDER_TYPE, die wir zuvor definiert haben.
    3. Die anfängliche Losgröße ist die Eingabevariable.
    4. initialPrice ist der Eröffnungskurs der Order, der entweder Ask (für Kaufpositionen) oder Bid (für Verkaufspositionen) ist.
    5. Schließlich geben wir SL- und TP-Kursniveaus an.

    Damit wird eine Kauf- oder Verkaufsposition platziert. Nachdem die Position platziert wurde, speichern wir ihre Losgröße in der im globalen Bereich definierten Variablen namens lastPositionLotSize, sodass wir dieses Los und den Multiplikator aus der Eingabe verwenden können, um die Losgröße einer weiteren Position zu berechnen.

    Damit bleiben uns noch 2 Dinge zu tun:

    if(trade.ResultRetcode() == 10009) hedgeCycleRunning = true;
    isPositionBuy = isPositionBuy ? false : true;

    Hier setzen wir den Wert von hedgeCycleRunning nur dann auf true, wenn eine Position erfolgreich platziert wurde. Dies wird durch die Funktion ResultRetcode() in der CTrade-Instanz namens trade bestimmt, die „10009“ zurückgibt, was eine erfolgreiche Platzierung anzeigt (Sie können alle diese Rückgabewerte hier sehen). Der Grund für die Verwendung von hedgeCycleRunning wird durch weiteren Code erläutert.

    Ein letzter Punkt ist, dass wir den ternären Operator verwenden, um den Wert von isPositionBuy zu ändern. Wenn es falsch war, wird es wahr und umgekehrt. Wir tun dies, weil unsere Strategie vorsieht, dass nach der Eröffnung einer Anfangsposition ein Verkauf nach einem Kauf und ein Kauf nach einem Verkauf platziert wird, d. h. es wird abgewechselt.

    Damit ist die Besprechung der grundlegend wichtigen Funktion StartHedgeCycle() abgeschlossen, da wir diese Funktion immer wieder verwenden werden.

    Lassen Sie uns nun mit unserem letzten Stück Code fortfahren.

    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick()
       {
        double _Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double _Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    
        if(!hedgeCycleRunning)
           {
            StartHedgeCycle();
           }
    
        if(_Bid <= C && !isPositionBuy)
           {
            double newPositionLotSize = NormalizeDouble(lastPositionLotSize * lotSizeMultiplier, 2);
            trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, newPositionLotSize, _Bid, B, D);
            lastPositionLotSize = newPositionLotSize;
            isPositionBuy = isPositionBuy ? false : true;
           }
        
        if(_Ask >= B && isPositionBuy)
           {
            double newPositionLotSize = NormalizeDouble(lastPositionLotSize * lotSizeMultiplier, 2);
            trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, newPositionLotSize, _Ask, C, A);
            lastPositionLotSize = newPositionLotSize;
            isPositionBuy = isPositionBuy ? false : true;
           }
        
        if(_Bid >= A || _Ask <= D)
           {
            hedgeCycleRunning = false;
           }
       }
    //+------------------------------------------------------------------+

    Die ersten beiden Zeilen sind selbsterklärend, sie definieren lediglich _Ask und _Bid (Doppelvariablen), die Ask und Bid zu diesem Zeitpunkt speichern.

    Dann verwenden wir eine if-Anweisung, um den Hedge-Zyklus mit der Funktion StartHedgeCycle() zu starten, wenn die Variable hedgeCycleRunning false ist. Wir wissen bereits, was die Funktion StartHedgeCycle() tut, aber um es zusammenzufassen, tut sie Folgendes:

    1. Definieren Sie die Preisniveaus A, B, C und D.
    2. Zeichnen Sie zur Veranschaulichung horizontale grüne Linien auf den Kursniveaus A, B, C, D.
    3. Eröffnen Sie eine Position.
    4. Speichern Sie die Losgröße dieser Position in der Variablen lastPositionLotSize, die im globalen Bereich definiert ist, damit sie überall verwendet werden kann.
    5. Setzen Sie hedgeCycleRunning auf true, da es vorher false war, und genau deshalb haben wir die Funktion StartHedgeCycle() ausgeführt.
    6. Schließlich ändern Sie die Variable isPositionBuy von true auf false und false auf true, wie in unserer Strategie vorgesehen.

    Wir führen StartHedgeCycle() nur einmal aus, weil wir es ausgeführt haben, wenn hedgeCycleRunning false war, und am Ende der Funktion ändern wir es auf false. Daher wird StartHedgeCycle() nicht mehr ausgeführt, es sei denn, wir setzen hedgeCycleRunning wieder auf false.

    Überspringen wir die nächsten beiden if-Anweisungen vorerst und kommen wir später darauf zurück. Schauen wir uns die letzte if-Anweisung an:

    if(_Bid >= A || _Ask <= D)
       {
        hedgeCycleRunning = false;
       }

    Dieser sorgt für den Neustart des Zyklus. Wie bereits erwähnt, wird der Zyklus neu gestartet, wenn hedgeCycleRunning auf true gesetzt wird, und alles, was wir zuvor besprochen haben, wird erneut passieren. Außerdem habe ich dafür gesorgt, dass bei einem Neustart des Zyklus alle Positionen aus dem vorherigen Zyklus mit einem Take-Profit geschlossen werden (unabhängig davon, ob es sich um eine Kauf- oder Verkaufsposition handelt).

    Wir haben also den Beginn des Zyklus, das Ende des Zyklus und den Neustart gehandhabt, aber uns fehlt immer noch der wichtigste Teil, nämlich die Handhabung der Auftragseröffnung, wenn der Preis das Niveau B von unten oder das Niveau C von oben erreicht. Der Positionstyp muss ebenfalls alternativ sein, wobei Kaufen nur auf B-Niveau und Verkaufen nur auf C-Niveau eröffnet wird.

    Wir haben den entsprechenden Code übersprungen, also lassen Sie uns darauf zurückkommen.

    if(_Bid <= C && !isPositionBuy)
       {
        double newPositionLotSize = NormalizeDouble(lastPositionLotSize * lotSizeMultiplier, 2);
        CTrade trade;
        trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, newPositionLotSize, _Bid, B, D);
        lastPositionLotSize = lastPositionLotSize * lotSizeMultiplier;
        isPositionBuy = isPositionBuy ? false : true;
       }
    
    if(_Ask >= B && isPositionBuy)
       {
        double newPositionLotSize = NormalizeDouble(lastPositionLotSize * lotSizeMultiplier, 2);
        CTrade trade;
        trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, newPositionLotSize, _Ask, C, A);
        lastPositionLotSize = lastPositionLotSize * lotSizeMultiplier;
        isPositionBuy = isPositionBuy ? false : true;
       }

     Diese beiden if-Anweisungen behandeln also die Aufträge, die zwischen dem Zyklus eröffnet werden (zwischen der Anfangsposition und dem noch nicht beendeten Zyklus).

    1. Erste IF-Anweisung: Damit wird ein Verkaufsauftrag eröffnet. Wenn die Variable Bid (die den Geldkurs zu diesem Zeitpunkt enthält) unter oder gleich dem C-Level ist und isPositionBuy falsch ist, deklarieren wir eine Variable des Typs double namens newPositionLotSize. Dieser wird gleich lastPositionLotSize multipliziert mit dem LotSizeMultiplier gesetzt, und dann wird der Double-Wert mit einer vordefinierten Funktion namens NormalizeDouble auf 2 Dezimalstellen normalisiert.

      Dann platzieren wir eine Verkaufsposition, indem wir die vordefinierte Funktion PositionOpen() aus der CTrade-Instanz namens trade verwenden und die newPositionLotSize als Parameter übergeben. Schließlich setzen wir lastPositionLotSize auf diese neue Losgröße (ohne Normalisierung), sodass wir sie in weiteren Positionen multiplizieren können, und schließlich wechseln wir die isPositionBuy von true zu false oder false zu true. 

    2. Zweite IF-Anweisung: Damit wird ein Kaufauftrag eröffnet. Wenn der Ask-Kurs (Variable, die den Ask-Kurs zu diesem Zeitpunkt enthält) gleich oder über dem B-Level ist und isPositionBuy wahr ist, dann definieren wir eine Doppelvariable namens newPositionLotSize. Wir setzen newPositionLotSize gleich lastPositionLotSize multipliziert mit lotSizeMultiplier und normalisieren den Double-Wert auf zwei Dezimalstellen mit der vordefinierten Funktion NormalizeDouble, wie zuvor.

      Dann platzieren wir eine Kaufposition, indem wir die vordefinierte Funktion PositionOpen() aus der CTrade-Instanz namens trade verwenden und die newPositionLotSize als Parameter angeben. Schließlich setzen wir lastPositionLotSize auf diese neue Losgröße (ohne Normalisierung), damit wir sie in weiteren Positionen multiplizieren können. Schließlich wechseln wir bei isPositionBuy von true zu false oder false zu true.

    Es gibt 2 sehr wichtige Punkte, die hier zu beachten sind:

    • In der ersten IF-Anweisung haben wir den „Bid“-Kurs verwendet und angegeben, dass eine Position eröffnet wird, wenn „Bid“ unter oder gleich „C“ ist und „isBuyPosition“ falsch ist. Warum haben wir hier „Bid“ verwendet?

      Angenommen, wir verwenden Ask, dann besteht die Möglichkeit, dass die vorherige Kaufposition geschlossen wird, die neue Verkaufsposition aber nicht eröffnet wird. Das liegt daran, dass wir wissen, dass der Kauf bei Ask eröffnet und bei Bid schließt, sodass die Möglichkeit besteht, dass der Kauf geschlossen wird, wenn Bid die C-Preislinie von oben kreuzt oder ihr entspricht. Dadurch wird der Kaufauftrag um den Stop-Loss geschlossen, den wir bei der Eröffnung der Position festgelegt haben, aber der Verkauf ist noch nicht eröffnet. Wenn also der Ask und Bid gleichzeitig steigen, dann wurde unsere Strategie nicht eingehalten. Das ist der Grund, warum wir Bid anstelle von Ask verwenden.

      In der zweiten IF-Anweisung haben wir symmetrisch den Ask-Kurs verwendet und angegeben, dass wir eine Position eröffnen würden, wenn Ask über oder gleich B ist und isBuyPosition wahr ist. Warum haben wir Ask hier verwendet?

      Angenommen, wir verwenden Bid, dann besteht die Möglichkeit, dass die vorherige Verkaufsposition geschlossen wird, die neue Kaufposition aber nicht eröffnet wird. Wir wissen, dass der Verkauf zum Bid eröffnet und zum Ask geschlossen wird. Es besteht also die Möglichkeit, dass der Verkauf geschlossen wird, wenn der Briefkurs die Linie des Preisniveaus B von unten kreuzt oder gleich ist, wodurch der Verkaufsauftrag durch den Stop-Loss geschlossen wird, den wir bei der Eröffnung der Position festgelegt haben. Der Kauf ist jedoch noch nicht eröffnet. Wenn also der Preis von Ask und Bid sinkt, dann wurde unsere Strategie nicht umgesetzt. Aus diesem Grund haben wir Ask statt Bid verwendet.

      Der springende Punkt ist also, dass, wenn eine Kauf-/Verkaufsposition geschlossen wird, sofort eine nachfolgende Verkaufs-/Kaufposition eröffnet werden muss, damit die Strategie korrekt verfolgt werden kann.

    • In den beiden IF-Anweisungen haben wir erwähnt, dass wir den Wert von lastPositionLotSize mit (lastPositionLotSize * lotSizeMultiplier) und nicht mit newPositionLotSize gleichsetzen, was dem normalisierten Wert von (lastPositionLotSize * lotSizeMultiplier) bis zu zwei Dezimalstellen unter Verwendung der vordefinierten Funktion NormalizeDouble() entspricht.
      NormalizeDouble(lastPositionLotSize * lotSizeMultiplier, 2)
      Und warum haben wir das getan? Eigentlich, wenn wir dies dem normalisierten Wert gleichsetzen, wird unsere Strategie korrekt befolgt, Nehmen wir zum Beispiel an, wir legen die anfängliche Losgröße 0,01 und Multiplikator 1,5 fest, dann wird die erste Losgröße natürlich 0,01 betragen und als nächstes 0,01*1,5 = 015. Natürlich können wir diese Losgröße nicht mehr eröffnen, wir können nur Vielfache von 0,01 zulassen, 0,01 erfüllt das, 0,015 nicht. Das ist genau der Grund, warum wir die Losgröße bis auf 2 Dezimalstellen normalisieren, also 0.01 werden geöffnet. Jetzt haben wir 2 Optionen, für den Wert von lastPositionLotSize, entweder 0,01 (0,010) oder 0,015. Nehmen wir an, wir wählen 0,01 (0,010) dann, wenn wir das nächste Mal eine Position platzieren wollen, würden wir auf 0,01*1,5 = 0,015 kommen und nach der Normalisierung wird das zu 0,01 - und so geht es weiter. Wir haben also den Multiplikator 1,5 verwendet und mit einer Losgröße von 0,01 begonnen, aber die Losgröße hat sich nie erhöht, und wir sind in einer Schleife stecken geblieben, in der alle Positionen mit einer Losgröße von 0,01 platziert werden, was bedeutet, dass wir lastPositionLotSize nicht mit 0,01 (0,010) gleichsetzen dürfen, also wählen wir stattdessen die andere Option 0,015, d. h. wir wählen den Wert vor der Normalisierung.

      Genau aus diesem Grund setzen wir lastPositionLotSize gleich (lastPositionLotSize * lotSizeMultiplier) und nicht NormalizeDouble(lastPositionLotSize * lotSizeMultiplier, 2).

    Schließlich sieht unser gesamter Code wie folgt aus:

    #include <Trade/Trade.mqh>
    
    input bool initialPositionBuy = true;
    input double buyTP = 15;
    input double sellTP = 15;
    input double buySellDiff = 15;
    input double initialLotSize = 0.01;
    input double lotSizeMultiplier = 2;
    
    
    
    double A, B, C, D;
    bool isPositionBuy;
    bool hedgeCycleRunning = false;
    double lastPositionLotSize;
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
       {
        return(INIT_SUCCEEDED);
       }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
       {
        ObjectDelete(0, "A");
        ObjectDelete(0, "B");
        ObjectDelete(0, "C");
        ObjectDelete(0, "D");
       }
    
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick()
       {
        double _Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double _Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    
        if(!hedgeCycleRunning)
           {
            StartHedgeCycle();
           }
    
        if(_Bid <= C && !isPositionBuy)
           {
            double newPositionLotSize = NormalizeDouble(lastPositionLotSize * lotSizeMultiplier, 2);
            CTrade trade;
            trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, newPositionLotSize, _Bid, B, D);
            lastPositionLotSize = lastPositionLotSize * lotSizeMultiplier;
            isPositionBuy = isPositionBuy ? false : true;
           }
        
        if(_Ask >= B && isPositionBuy)
           {
            double newPositionLotSize = NormalizeDouble(lastPositionLotSize * lotSizeMultiplier, 2);
            CTrade trade;
            trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, newPositionLotSize, _Ask, C, A);
            lastPositionLotSize = lastPositionLotSize * lotSizeMultiplier;
            isPositionBuy = isPositionBuy ? false : true;
           }
        
    if(_Bid >= A || _Ask <= D)
       {
        hedgeCycleRunning = false;
       }
       }
    //+------------------------------------------------------------------+
    
    //+------------------------------------------------------------------+
    //| Hedge Cycle Intialization Function                               |
    //+------------------------------------------------------------------+
    void StartHedgeCycle()
       {
        isPositionBuy = initialPositionBuy;
        double initialPrice = isPositionBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
        A = isPositionBuy ? initialPrice + buyTP * _Point * 10 : initialPrice + (buySellDiff + buyTP) * _Point * 10;
        B = isPositionBuy ? initialPrice : initialPrice + buySellDiff * _Point * 10;
        C = isPositionBuy ? initialPrice - buySellDiff * _Point * 10 : initialPrice;
        D = isPositionBuy ? initialPrice - (buySellDiff + sellTP) * _Point * 10 : initialPrice - sellTP * _Point * 10;
    
        ObjectCreate(0, "A", OBJ_HLINE, 0, 0, A);
        ObjectSetInteger(0, "A", OBJPROP_COLOR, clrGreen);
        ObjectCreate(0, "B", OBJ_HLINE, 0, 0, B);
        ObjectSetInteger(0, "B", OBJPROP_COLOR, clrGreen);
        ObjectCreate(0, "C", OBJ_HLINE, 0, 0, C);
        ObjectSetInteger(0, "C", OBJPROP_COLOR, clrGreen);
        ObjectCreate(0, "D", OBJ_HLINE, 0, 0, D);
        ObjectSetInteger(0, "D", OBJPROP_COLOR, clrGreen);
    
        ENUM_ORDER_TYPE positionType = isPositionBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
        double SL = isPositionBuy ? C : B;
        double TP = isPositionBuy ? A : D;
        CTrade trade;
        trade.PositionOpen(_Symbol, positionType, initialLotSize, initialPrice, SL, TP);
        
        lastPositionLotSize = initialLotSize;
        if(trade.ResultRetcode() == 10009) hedgeCycleRunning = true;
        isPositionBuy = isPositionBuy ? false : true;
       }
    //+------------------------------------------------------------------+
    

    Damit ist unsere Diskussion über die Automatisierung unserer klassischen Hedge-Strategie abgeschlossen.


    Backtesting unserer klassischen Hedge-Strategie

    Nachdem wir nun den Expert Advisor erstellt haben, der unsere Strategie automatisch verfolgt, ist es nur logisch, ihn zu testen und die Ergebnisse zu sehen.

    Ich werde die folgenden Eingabeparameter verwenden, um unsere Strategie zu testen:

    1. initialBuyPosition: true
    2. buyTP: 15
    3. sellTP: 15
    4. buySellDiff: 15
    5. initialLotSize: 0.01
    6. lotSizeMultiplier: 2.0

    Ich werde ihn mit EURUSD vom 1. Januar 2023 bis zum 06. Dezember 2023 mit einem Hebel von 1:500 und einem Ersteinlage von $10.000 testen. Wenn Sie sich über den Zeitrahmen wundern, er ist irrelevant für unsere Strategie, er kann beliebige gewählt werden (Es wird überhaupt nicht unsere Ergebnisse beeinflussen), Lassen Sie uns die Ergebnisse unten sehen:


    Wenn man sich das Diagramm ansieht, könnte man meinen, dass dies eine rentable Strategie ist, aber sehen wir uns andere Daten an und diskutieren wir einige Punkte im Diagramm:

    Wie Sie sehen können, hatten wir einen Nettogewinn von 1470,62 $, wobei der Bruttogewinn 13.153,68 $ und der Bruttoverlust 11683,06 $ betrug.

    Werfen wir auch einen Blick auf den Saldo und den Drawdown des Kapitals:

    Absoluter Drawdown des Saldos $1170.10
    Maximaler Drawdown des Saldos $1563.12 (15.04%)
    Relativer Drawdown des Saldos 15.04% ($1563.13)
    Absoluter Drawdown des Kapitals $2388.66
    Maximaler Drawdown des Kapitals $2781.97 (26.77%)
    Relativer Drawdown des Kapitals 26.77% ($2781.97)

    Lassen Sie uns diese Begriffe verstehen:

    1. Absoluter Drawdown des Saldos: Dies ist die Differenz zwischen dem Anfangskapital, das in unserem Fall 10.000 $ beträgt, und dem minimalen Saldo, dem niedrigsten Punkt der Saldenkurve.
    2. Maximaler Drawdown des Saldos: Dies ist die Differenz zwischen dem höchsten und dem niedrigsten Punkt der Saldenkurve (Saldenwerte).
    3. Relativer Drawdown des Saldos: Dies ist der Prozentsatz des maximalen Drawdown des Saldos vom höchsten Punkt des Saldos (Spitzensaldo).

    Die Erklärungen für das Kapital sind vergleichbar:

    1. Absoluter Drawdown des Kapitals: Dies ist die Differenz zwischen dem Anfangskapital, das in unserem Fall 10.000 $ beträgt, und dem kleinsten Kapitalwert.
    2. Maximaler Drawdown des Kapitals: Dies ist die Differenz zwischen dem höchsten und dem niedrigsten Kapitalwert.
    3. Relativer Drawdown des Kapitals: Dies ist der prozentuale Anteil des Kapitaldrawdowns vom höchsten Wert der Kapitalkurve.

    Nachstehend finden Sie die Formeln für alle 6 Statistiken:


    Bei der Analyse der obigen Daten wäre der Drawdown des Saldos die geringste unserer Sorgen, da er durch den Drawdown des Kapitals abgedeckt ist. In gewissem Sinne können wir sagen, dass der Drawdown des Saldos eine Untergruppe des Drawdown des Kapitals ist. Hingegen sind Drawdown des Kapitals unser größtes Problem, wenn wir unserer Strategie folgen, weil wir die Losgröße bei jeder Bestellung um 2 verdoppeln, was zu einem exponentiellen Wachstum der Losgröße führt. Das wird durch die folgende Tabelle veranschaulicht:

    Zahl der eröffneten Position Losgröße der nächsten Position (noch nicht eröffnet) Erforderliche Marge für die nächste Position (EURUSD) 
    0 0.01 $2.16 
    1 0.02 $4.32 
    2 0.04 $8.64
    3 0.08 $17.28
    4 0.16 $34.56
    5 0.32 $69.12
    6 0.64 $138.24
    7 1.28 $276.48
    8 2.56 $552.96
    9 5.12 $1105.92
    10 10.24 $2211.84
    11 20.48 $4423.68
    12 40.96 $8847.36
    13 80.92 $17694.72
    14 163.84 $35389.44

    In unserer Untersuchung verwenden wir derzeit EURUSD als Handelspaar. Es ist wichtig zu wissen, dass die erforderliche Marge für eine Losgröße von 0,01 $ bei 2,16 $ liegt, obwohl sich diese Zahl ändern kann.

    Bei näherer Betrachtung lässt sich ein bemerkenswerter Trend feststellen: Die erforderliche Marge für nachfolgende Positionen steigt exponentiell an. Nach dem 12. Auftrag sind wir zum Beispiel auf einen finanziellen Engpass gestoßen. Die erforderliche Marge steigt auf 17.694,44 $, eine Zahl, die weit außerhalb unserer Reichweite liegt, wenn man bedenkt, dass unsere ursprüngliche Investition 10.000 $ betrug. In diesem Szenario sind unsere Stop-Losses noch nicht einmal berücksichtigt.

    Lassen Sie uns das weiter aufschlüsseln. Wenn wir Stop-Losses einbeziehen, die auf 15 Pips pro Handel gesetzt wurden, und nachdem wir die ersten 12 Geschäfte verloren haben, würde unsere kumulative Losgröße bei erstaunlichen 81,91 liegen (die Summe der Serien: 0.01+0.02+0.04+...+20.48+40.96). Dies entspricht einem Gesamtverlust von 12.286,5 $, berechnet mit dem EURUSD-Wert von 1 $ pro 10 Pips bei einer Losgröße von 0,01. Es ist eine einfache Rechnung: (81.91/0.01) * 1.5 = $12,286.5. Der Verlust übersteigt nicht nur unser Anfangskapital, sondern macht es auch unmöglich, 12 Positionen in einem Zyklus mit einer Investition von 10.000 $ in EURUSD zu halten.

    Betrachten wir nun ein etwas anderes Szenario: Können wir mit unseren 10.000 $ in EURUSD insgesamt 11 Positionen halten?

    Stellen Sie sich vor, wir haben insgesamt 10 Positionen erreicht. Das bedeutet, dass wir bereits bei 9 Positionen Stop-Losses erreicht haben und dabei sind, die 10. zu verlieren. Wenn wir planen, eine 11. Position zu eröffnen, würde die Gesamtlosgröße für die 10 Positionen 10,23 betragen, was zu einem Verlust von 1.534,5 $ führen würde. Dieser wird auf die gleiche Weise wie zuvor berechnet, unter Berücksichtigung des EURUSD-Kurses und der Losgröße. Die erforderliche Marge für die nächste Position würde 4.423,68 $ betragen. Wenn wir diese Beträge zusammenzählen, kommen wir auf 5.958,18 Dollar, was deutlich unter dem Schwellenwert von 10.000 Dollar liegt. Es ist also möglich, insgesamt 10 Stellen zu erhalten und eine elfte zu eröffnen.

    Es stellt sich jedoch die Frage: Ist es möglich, mit den gleichen 10.000 Dollar insgesamt 12 Stellen zu besetzen?

    Nehmen wir an, wir haben die Grenze von 11 Stellen erreicht. Hier haben wir bereits bei 10 Positionen Verluste erlitten und stehen kurz davor, die 11. zu verlieren. Die Gesamtlosgröße für diese 11 Positionen beträgt 20,47, was zu einem Verlust von 3.070,5 $ führt. Rechnet man die erforderliche Marge für den 12. Platz hinzu, die sich auf satte 8.847,36 $ beläuft, steigen unsere Gesamtausgaben auf 11.917,86 $ und übersteigen damit unsere ursprüngliche Investition. Es ist daher klar, dass die Eröffnung einer 12. Position, wenn bereits 11 im Spiel sind, finanziell nicht tragbar ist. Wir hätten bereits 3.070,5 $ verloren, sodass wir nur noch 6.929,5 $ hätten.

    Aus den Backtest-Statistiken geht hervor, dass die Strategie selbst bei einer Investition von 10.000 $ in einer relativ stabilen Währung wie dem EURUSD bedenklich nahe am Zusammenbruch ist. Die maximalen aufeinanderfolgenden Verluste liegen bei 10, was bedeutet, dass wir nur ein paar Pips von der katastrophalen 11 entfernt sind. Sollte auch die 11. Position ihren Stop-Loss erreichen, würde die Strategie aus dem Ruder laufen und zu erheblichen Verlusten führen.

    In unserem Bericht wird die absoluten Drawdown mit $2.388,66 angegeben. Hätten wir den Stop-Loss der 11. Position erreicht, wären unsere Verluste auf 3.070,5 $ angewachsen. Damit wären wir 681,84 $ (3.070,5 $ - 2.388,66 $) von einem totalen Fehlschlag der Strategie entfernt gewesen.

    Es gibt jedoch einen entscheidenden Faktor, den wir bisher übersehen haben - die Spanne. Diese Variable kann sich erheblich auf die Gewinne auswirken, wie zwei konkrete Fälle in unserem Bericht zeigen, die in der folgenden Abbildung hervorgehoben sind.

    Beachten Sie die roten Ellipsen auf dem Bild. In diesen Fällen konnten wir trotz des Gewinns des Handels (wobei Gewinn gleichbedeutend ist mit der Sicherung des höchsten Lots im letzten Handel) keinen Gewinn erzielen. Diese Anomalie wird auf die Ausbreitung zurückgeführt. Ihr variabler Charakter macht unsere Strategie noch komplizierter und erfordert eine eingehendere Analyse im nächsten Teil dieser Serie.

    Wir müssen auch die Grenzen der klassischen Hedge-Strategie berücksichtigen. Ein erheblicher Nachteil ist die beträchtliche Haltekapazität, die erforderlich ist, um zahlreiche Aufträge aufrechtzuerhalten, wenn das Take-Profit-Niveau nicht frühzeitig erreicht wird. Diese Strategie kann nur dann Gewinne sichern, wenn der Multiplikator der Losgröße 2 oder größer ist (im Fall buyTP = sellTP = buySellDiff, ohne Berücksichtigung des Spreads). Liegt sie unter 2, besteht die Gefahr, dass mit zunehmender Anzahl von Aufträgen negative Gewinne erzielt werden. Diese Dynamik und die Optimierung der klassischen Hedge-Strategie zur Erzielung maximaler Erträge werden wir im nächsten Teil unserer Serie untersuchen.


    Schlussfolgerung

    Wir haben also in diesem ersten Teil unserer Serie eine recht komplexe Strategie besprochen und sie auch mit Hilfe des Expert Advisors in MQL5 automatisiert. Es kann eine gewinnbringende Strategie sein, obwohl man eine hohe Haltekapazität haben muss, um Positionen in höheren Losgrößen zu platzieren, was für einen Anleger nicht immer machbar und auch sehr riskant ist, da es passieren kann, dass er einen großen Drawdown erleidet. Um diese Einschränkungen zu überwinden, müssen wir die Strategie optimieren, was im nächsten Teil dieser Serie geschehen wird.

    Bis jetzt haben wir willkürlich festgelegte Werte für lotSizeMultiplier, initialLotSize, buySellDiff, sellTP, buyTP verwendet, aber wir können diese Strategie optimieren und optimale Werte für diese Eingangsparameter finden, die uns die maximal möglichen Erträge liefern. Wir werden auch herausfinden, ob es vorteilhaft ist, zunächst mit einer Kauf- oder Verkaufsposition zu beginnen, was auch von den verschiedenen Währungen und Marktbedingungen abhängen kann. Wir werden also im nächsten Teil dieser Serie viele nützliche Dinge behandeln, also bleiben Sie dran. 

    Vielen Dank, dass Sie sich die Zeit genommen haben, meine Artikel zu lesen. Ich hoffe, Sie finden sie sowohl informativ als auch hilfreich für Ihre Bemühungen. Wenn Sie Ideen oder Vorschläge für meinen nächsten Beitrag haben, zögern Sie bitte nicht, sie mir mitzuteilen.

    Viel Spaß beim Coding! Viel Spaß beim Handeln!


    Übersetzt aus dem Englischen von MetaQuotes Ltd.
    Originalartikel: https://www.mql5.com/en/articles/13845

    Beigefügte Dateien |
    Letzte Kommentare | Zur Diskussion im Händlerforum (4)
    DidMa
    DidMa | 9 Dez. 2023 in 04:01
    Ausgezeichneter Artikel, vielen Dank. Ich kann nicht für den zweiten Teil warten, wie ich bin selbst ein Programmierer und über ein Hedging-System, aber andere Strategie zu codieren. Vielen Dank für Ihre Arbeit und das Teilen!
    Kailash Bai Mina
    Kailash Bai Mina | 9 Dez. 2023 in 15:25
    DidMa #:
    Ausgezeichneter Artikel, vielen Dank. Ich kann nicht für den zweiten Teil warten, wie ich bin selbst ein Programmierer und über ein Hedging-System, aber andere Strategie zu codieren. Vielen Dank für Ihre Arbeit und das Teilen!

    Ich freue mich, dass mein Artikel Ihnen geholfen hat und Sie sich auf den nächsten Teil freuen. Ich arbeite daran und werde ihn so bald wie möglich veröffentlichen.

    Mit freundlichen Grüßen.

    Lionel Niquet
    Lionel Niquet | 24 Jan. 2024 in 03:44

    Sehr schöner Ansatz. Klar und prägnant.

    Mit freundlichen Grüßen.

    greatrufai1
    greatrufai1 | 20 Feb. 2024 in 01:17
    Hervorragende Arbeit @Kailash Bai Mina. Ich habe versucht, eine automatisierte Strategie zu verwenden, die die Marktstruktur analysiert, um die Ausgangsposition zu bestimmen. Ich würde sie gerne mit Ihnen hier teilen, aber ich will nicht vor mir selbst da sein. Wann werden Sie darüber berichten, bitteeeee??????
    Wie man einen einfachen Multi-Currency Expert Advisor mit MQL5 erstellt (Teil 5):  Die Bollinger Bänder mit dem Keltner-Kanal — Indikatoren Signal Wie man einen einfachen Multi-Currency Expert Advisor mit MQL5 erstellt (Teil 5): Die Bollinger Bänder mit dem Keltner-Kanal — Indikatoren Signal
    Der Multi-Currency Expert Advisor in diesem Artikel ist ein Expert Advisor oder Handelsroboter, der handeln kann (z.B. Aufträge eröffnen, schließen und verwalten, Trailing Stop Loss und Trailing Profit) für mehr als ein Symbolpaar aus nur einem Symbolchart. In diesem Artikel werden wir Signale von zwei Indikatoren verwenden, in diesem Fall Bollinger Bänder® und dem Keltner Kanal.
    Kombinatorisch symmetrische Kreuzvalidierung in MQL5 Kombinatorisch symmetrische Kreuzvalidierung in MQL5
    In diesem Artikel stellen wir die Implementierung der kombinatorisch symmetrischen Kreuzvalidierung in reinem MQL5 vor, um den Grad der Überanpassung nach der Optimierung einer Strategie unter Verwendung des langsamen vollständigen Algorithmus des Strategietesters zu messen.
    Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil 3): Verhaltensmuster 1 Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil 3): Verhaltensmuster 1
    Ein neuer Artikel aus der Reihe der Artikel über Entwurfmuster. Wir werden einen Blick auf einen seiner Typen werfen, nämlich den Verhaltensmuster, um zu verstehen, wie wir Kommunikationsmethoden zwischen erstellten Objekten effektiv aufbauen können. Durch die Vervollständigung dieser Verhaltensmuster werden wir in der Lage sein zu verstehen, wie wir eine wiederverwendbare, erweiterbare und getestete Software erstellen und aufbauen können.
    Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil 2): Strukturelle Muster Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil 2): Strukturelle Muster
    In diesem Artikel werden wir unsere Artikel über Entwurfsmuster fortsetzen, nachdem wir gelernt haben, wie wichtig dieses Thema für uns als Entwickler ist, um erweiterbare, zuverlässige Anwendungen nicht nur mit der Programmiersprache MQL5, sondern auch mit anderen zu entwickeln. Wir werden eine andere Art von Entwurfsmustern kennenlernen, nämlich die strukturellen, um zu lernen, wie man Systeme entwirft, indem man das, was wir als Klassen haben, zur Bildung größerer Strukturen verwendet.