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

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

MetaTrader 5Tester | 29 April 2024, 13:54
212 0
Kailash Bai Mina
Kailash Bai Mina

Einführung

Willkommen zum zweiten Teil unserer Artikelserie „Modifizierter Grid-Hedge EA in MQL5“. Beginnen wir mit einer Wiederholung dessen, was wir im ersten Teil behandelt haben. In Teil I haben wir uns mit der klassischen Hedge-Strategie beschäftigt, sie mit einem Expert Advisor (EA) automatisiert, Tests im Strategietester durchgeführt und einige erste Ergebnisse analysiert. Dies war der erste Schritt auf unserem Weg zur Entwicklung eines Modified Grid-Hedge EA – einer Mischung aus klassischen Hedge- und Grid- oder Rasterstrategien.

Ich hatte bereits erwähnt, dass sich Teil 2 auf die Optimierung der klassischen Hedge-Strategie konzentrieren würde. Aufgrund unerwarteter Verzögerungen werden wir uns jedoch vorerst auf die klassische Gitterstrategie konzentrieren.

In diesem Artikel befassen wir uns mit der klassischen Rasterstrategie, automatisieren sie mit einem EA in MQL5 und führen Tests im Strategie-Tester durch, um die Ergebnisse zu analysieren und nützliche Erkenntnisse über die Strategie zu gewinnen.

Lassen Sie uns also tief in den Artikel eintauchen und die Feinheiten der klassischen Rasterstrategie erkunden.

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

  1. Diskussion über die klassische Gitterstrategie
  2. Diskussion über die Automatisierung unserer Classic Grid Strategie
  3. Backtesting der klassischen Rasterstrategie
  4. Schlussfolgerung


Diskussion über die klassische Gitterstrategie 

Zuallererst sollten wir uns mit der Strategie selbst befassen, bevor wir fortfahren.

Wir beginnen unseren Ansatz mit einer Kaufposition, obwohl es genauso gut möglich ist, mit einer Verkaufsposition zu beginnen. Konzentrieren wir uns zunächst auf das Kaufszenario. Wir beginnen mit einem bestimmten Anfangskurs und verwenden der Einfachheit halber eine kleine Losgröße – sagen wir 0,01, die kleinstmögliche Losgröße. Von diesem Punkt an gibt es zwei mögliche Ergebnisse: Der Preis kann entweder steigen oder fallen. Wenn der Preis steigt, machen wir einen Gewinn, dessen Höhe vom Ausmaß des Anstiegs abhängt. Diesen Gewinn realisieren wir dann, wenn der Kurs das von uns festgelegte Take-Profit-Niveau erreicht. Die Herausforderung entsteht, wenn der Preis zu fallen beginnt – hier kommt unsere klassische Rasterstrategie ins Spiel, die eine Absicherung der Gewinne bietet.

Fällt der Kurs um einen bestimmten Betrag, sagen wir der Einfachheit halber 15 Pips, führen wir einen weiteren Kaufauftrag aus. Dieser Auftrag wird zum Anfangskurs abzüglich 15 Pips platziert, und hier erhöhen wir die Losgröße – der Einfachheit halber verdoppeln wir sie, indem wir einen Multiplikator der Losgröße von 2 anwenden. Wenn wir diesen neuen Kaufauftrag erteilen, stehen wir wieder vor zwei Szenarien: Der Preis kann steigen oder fallen. Ein Preisanstieg würde aufgrund der erhöhten Losgröße zu Gewinnen aus diesem neuen Auftrag führen. Außerdem bringt dieser Anstieg nicht nur Gewinne, sondern mindert auch den Verlust unseres ursprünglichen Kaufauftrags mit einer Losgröße von 0,01. Der Punkt, an dem unser Nettogewinn gleich Null ist, ist der Zeitpunkt, an dem der Preis das gewichtete durchschnittliche Preisniveau der beiden Aufträge erreicht. Dieser gewogene Durchschnitt kann wie folgt berechnet werden:

Bei dieser Berechnung steht X i für den Preis, zu dem Kaufaufträge erteilt werden, und w i für die entsprechenden Gewichte, die durch den Multiplikator der Losgröße bestimmt werden. Bis zu diesem Punkt kann das gewichtete durchschnittliche Preisniveau, W, wie folgt berechnet werden:

W = 1 × ( Anfangskurs ) + 2 × ( Anfangskurs - 15 Pips ) 3 ​

Hier ist X 1 der Anfangskurs, X 2 ist der Anfangskurs minus 15 Pips, w 1 ist gleich 1 und w 2 ist gleich 2.

Der Punkt, an dem der Nettogewinn Null erreicht, ist der Zeitpunkt, an dem sich das Preisniveau an dieses errechnete gewichtete Durchschnittspreisniveau anpasst. Die Demonstration dieses Konzepts sieht folgendermaßen aus:

Anmerkung: Der Einfachheit halber lassen wir die Spreads vorerst außer Acht.

Vereinfacht ausgedrückt, gehen wir davon aus, dass der Ausgangskurs x ist und ein Kaufauftrag mit einer Losgröße von 0,01 eröffnet wird. Wenn der Kurs dann um 15 Pips sinkt, erteilen wir einen neuen Kaufauftrag zu x - 15 und verdoppeln die Lotgröße auf 0,02. Das gewogene durchschnittliche Preisniveau zu diesem Zeitpunkt wird berechnet als ( 1 × x + 2 × ( x - 15 ) ) / 3, was x - 10 entspricht. / 3, was x - 10 entspricht. Dieser Wert liegt 10 Punkte unter dem ursprünglichen Kursniveau.

Wenn der Kurs x - 15 erreicht, führt der anfängliche Kaufauftrag von 0,01 zu einem Verlust von 1,5 $ (angenommen, es handelt sich um das Paar EURUSD, bei dem 1 $ 10 Pips entspricht). Wenn der Preis dann auf das gewichtete Durchschnittsniveau von x - 10 steigt, erhalten wir einen Verlust von 0,5 $ für den ursprünglichen Auftrag. Zusätzlich gewinnen wir durch den neuen Kaufauftrag von 0,02, da der Preis um 5 Pips vom Niveau von x - 15 gestiegen ist. Aufgrund der verdoppelten Losgröße verdoppelt sich auch unser Gewinn, der sich auf 1 $ beläuft. Unser gesamter Reingewinn beträgt also $0 (also -$1,5 + $0,5 + $1).

Steigt der Kurs über das gewichtete Durchschnittsniveau von x - 10 hinaus, erzielen wir weiterhin Gewinne, bis er das Take-Profit-Niveau erreicht.

Wenn der Preis nicht steigt, sondern sich weiter vom gewichteten Durchschnittspreisniveau entfernt, wenden wir die gleiche Strategie an. Sollte der Kurs um weitere 15 Pips unter x - 15 fallen, wird ein neuer Kaufauftrag bei x - 30 erteilt, wobei sich die Losgröße gegenüber dem vorherigen Auftrag erneut verdoppelt, jetzt auf 0,04 (2 mal 0,02). Das neue gewichtete durchschnittliche Kursniveau wird berechnet als ( 1 × x + 2 × ( x - 15 ) + 4 × ( x - 30 ) ) / 7, was vereinfacht zu x - 21,428 führt und damit 21,428 Punkte unter dem ursprünglichen Kursniveau liegt.

Bei einem Kursniveau von x - 30 droht dem anfänglichen Kaufauftrag von 0,01 ein Verlust von 3,0 $ (ausgehend vom Paar EURUSD, wo 1 $ 10 Pips entspricht). Der zweite Kaufauftrag zu 0,02 verursacht einen Verlust von 2 mal $1,5, also $3,0, was zu einem Gesamtverlust von $6,0 führt. Steigt der Kurs dann jedoch auf das gewichtete Durchschnittsniveau von x - 21,428, wird der Verlust aus dem ursprünglichen Kaufauftrag von 0,01 teilweise durch 0,8572 $ wieder ausgeglichen. Außerdem wird der Verlust bei dem zweiten Auftrag von 0,02 durch 2 mal $0,8572, also $1,7144, ausgeglichen. Zusätzlich profitieren wir von der neuen Kauforder von 0,04, da der Preis um 8,572 Pips vom Niveau von x - 30 gestiegen ist. Aufgrund der vervierfachten Losgröße (0,04) vervierfacht sich auch unser Gewinn, der sich auf das Vierfache von $0,8572, also $3,4288 beläuft. Der gesamte Nettogewinn beträgt also etwa 0 $ ( -$6 + $0,8572 + $1,7144 + $3,4288 = $0,0004). Aufgrund von Rundungen bei der Berechnung des gewichteten durchschnittlichen Preisniveaus ist diese Zahl nicht genau Null.

Sollte der Kurs vom gewichteten Durchschnittsniveau von x - 21,428 weiter steigen, werden wir weiterhin positive Gewinne bis zum Erreichen des Take-Profit-Niveaus anhäufen. Umgekehrt wiederholen wir den Vorgang, wenn der Preis wieder fällt, wobei wir die Losgröße bei jeder Iteration verdoppeln und das gewichtete durchschnittliche Preisniveau entsprechend anpassen. Dieser Ansatz stellt sicher, dass wir jedes Mal, wenn der Preis das Niveau des gewichteten Durchschnittspreises erreicht, einen Nettogewinn von etwa 0 $ erzielen, was schrittweise zu positiven Gewinnen führt.

Das gleiche Verfahren gilt für Verkaufsaufträge, wobei der Zyklus mit einem ersten Verkaufsauftrag beginnt. Schauen wir uns das mal an:

Wir beginnen mit einer Verkaufsposition zu einem bestimmten Anfangskurs, wobei wir der Einfachheit halber eine kleine Losgröße verwenden, beispielsweise 0,01 (die kleinstmögliche Größe). Von hier aus sind zwei Ergebnisse möglich: Der Preis kann entweder steigen oder fallen. Wenn der Kurs fällt, profitieren wir entsprechend dem Ausmaß des Rückgangs und realisieren diesen Gewinn, wenn der Kurs unser Take-Profit-Niveau erreicht. Schwierig wird es jedoch, wenn der Kurs zu steigen beginnt. Dann kommt unsere klassische Rasterstrategie zum Einsatz, um Gewinne zu sichern.

Angenommen, der Kurs steigt um einen bestimmten Betrag, sagen wir der Einfachheit halber 15 Pips. Daraufhin erteilen wir einen weiteren Verkaufsauftrag zum ursprünglichen Preis plus 15 Pips und verdoppeln der Einfachheit halber die Losgröße, sodass der Multiplikator der Losgröße 2 beträgt. Nachdem dieser neue Verkaufsauftrag erteilt wurde, ergeben sich zwei Szenarien: Der Kurs kann entweder weiter steigen oder fallen. Fällt der Kurs, profitieren wir aufgrund der erhöhten Losgröße von diesem neuen Verkaufsauftrag. Außerdem führt dieser Rückgang nicht nur zu Gewinnen, sondern verringert auch den Verlust bei unserem ursprünglichen Verkaufsauftrag mit der Losgröße 0,01. Wir erreichen einen Nettogewinn von Null, wenn der Preis das gewichtete durchschnittliche Preisniveau beider Aufträge erreicht, ein Konzept, das wir bereits bei der Diskussion der Kaufaufträge untersucht haben.

Die Formel für die Berechnung des gewichteten Durchschnitts bleibt dieselbe, wobei die jeweiligen Preise und Losgrößen der Aufträge berücksichtigt werden. Dieser strategische Ansatz stellt sicher, dass unsere Position auch bei schwankenden Märkten vor Verlusten geschützt bleibt und je nach Marktentwicklung Break-Even-Punkte oder Gewinne erzielt werden.

Nehmen wir der Einfachheit halber an, dass der Anfangskurs x ist und ein Verkaufsauftrag mit einer Losgröße von 0,01 erteilt wird. Steigt der Kurs dann um 15 Pips, erteilen wir einen neuen Verkaufsauftrag zu x + 15 und verdoppeln die Losgröße auf 0,02. Das gewichtete durchschnittliche Kursniveau in dieser Phase wird berechnet als ( 1 × x + 2 × ( x + 15 ) ) / 3, was vereinfacht x + 10 oder 10 Pips über dem Anfangskursniveau entspricht.

Wenn der Preis x + 15 erreicht, führt der ursprüngliche Verkaufsauftrag von 0,01 zu einem Verlust von 1,5 $ (unter der Annahme, dass das Paar EURUSD, bei dem 1 $ 10 Pips entspricht). Wenn der Kurs dann auf das gewichtete Durchschnittsniveau von x + 10 fällt, gleichen wir einen Verlust von $ 0,5 aus dem ursprünglichen Verkaufsauftrag aus. Darüber hinaus profitieren wir auch von der neuen Verkaufsorder von 0,02, da der Preis um 5 Pips vom Niveau von x + 15 gesunken ist. Aufgrund der verdoppelten Losgröße verdoppelt sich auch unser Gewinn und beträgt somit 1 $. Unser gesamter Nettogewinn beträgt also 0 $ (berechnet als -$1,5 + $0,5 + $1).

Sollte der Kurs ab dem gewichteten Durchschnittsniveau von x + 10 weiter sinken, werden wir bis zum Erreichen des Take-Profit-Niveaus positive Gewinne erzielen. Classic GridDiese Strategie gleicht die Verluste und Gewinne bei unterschiedlichen Preisbewegungen effektiv aus und sorgt für einen Nettogewinn oder ein Break-Even-Szenario bei unterschiedlichen Marktbedingungen.

Wenn der Preis im Gegensatz zum Rückgang weiter vom gewichteten Durchschnittspreisniveau aus steigt, wenden wir die gleiche Strategie wie zuvor an. Nehmen wir an, der Kurs übersteigt das Niveau x + 15 um weitere 15 Pips. Daraufhin erteilen wir einen neuen Verkaufsauftrag zu x + 30 und verdoppeln damit die Losgröße gegenüber dem vorherigen Auftrag, jetzt auf 0,04 (2 mal 0,02). Das revidierte gewichtete durchschnittliche Kursniveau wird dann berechnet als ( 1 × x + 2 × ( x + 15 ) + 4 × ( x + 30 ) ) / 7, was vereinfacht x + 21,428 oder 21,428 Pips über dem ursprünglichen Kursniveau entspricht.

Bei einem Kursniveau von x + 30 droht dem anfänglichen Verkaufsauftrag von 0,01 ein Verlust von 3,0 $ (ausgehend von dem Paar EURUSD, bei dem 1 $ 10 Pips entspricht). Der zweite Verkaufsauftrag zu 0,02 verursacht einen Verlust von 2 mal $1,5, also $3,0, was zu einem Gesamtverlust von $6,0 führt. Wenn der Kurs jedoch auf das gewichtete Durchschnittsniveau von x + 21,428 fällt, machen wir den Verlust aus dem ursprünglichen Verkaufsauftrag von 0,01 teilweise durch 0,8572 $ wieder wett. Außerdem wird der Verlust bei dem zweiten Auftrag von 0,02 durch 2 mal $0,8572, also $1,7144, ausgeglichen. Außerdem profitieren wir von der neuen Verkaufsorder von 0,04, da der Kurs um 8,572 Pips vom Niveau von x + 30 gesunken ist. Aufgrund der vervierfachten Losgröße (0,04) vervierfacht sich auch unser Gewinn, der sich auf das Vierfache von $0,8572, also $3,4288 beläuft. Der gesamte Nettogewinn beträgt also etwa 0 $ (berechnet als -$6 + $0,8572 + $1,7144 + $3,4288 = $0,0004). Aufgrund von Rundungen bei der Berechnung des gewichteten durchschnittlichen Preisniveaus ist diese Zahl nicht genau Null.

Sollte der Kurs vom gewichteten Durchschnittsniveau von x + 21,428 weiter fallen, werden wir bis zum Erreichen des Take-Profit-Niveaus positive Gewinne anhäufen. Steigt der Preis hingegen weiter an, wiederholen wir den Prozess, verdoppeln die Losgröße bei jeder Iteration und passen das gewichtete durchschnittliche Preisniveau entsprechend an. Dieser Ansatz stellt sicher, dass wir jedes Mal, wenn der Preis das Niveau des gewichteten Durchschnittspreises erreicht, einen Nettogewinn von etwa 0 $ erzielen, was schrittweise zu positiven Gewinnen führt.


Diskussion über die Automatisierung unserer Classic Grid Strategie

Nun wollen wir uns mit der Automatisierung dieser klassischen Rasterstrategie mithilfe eines Expert Advisors (EA) befassen.

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

input bool initialPositionBuy = true;
input double distance = 15;
input double takeProfit = 5;
input double initialLotSize = 0.01;
input double lotSizeMultiplier = 2;
  1. initialPositionBuy: Eine boolesche Variable, die den Typ der Ausgangsposition bestimmt – entweder eine Kauf- oder eine Verkaufsposition. Bei true ist die Ausgangsposition ein Kauf; andernfalls ist es ein Verkauf.
  2. distance: Der in Pips gemessene feste Abstand zwischen den einzelnen aufeinanderfolgenden Aufträgen.
  3. takeProfit: Der in Pips gemessene Abstand über dem Durchschnittspreis aller offenen Positionen, zu dem alle Positionen mit einem Gesamtnettogewinn geschlossen werden.
  4. initialLotSize: Die Losgröße der ersten Position.
  5. lotSizeMultiplier: Der Multiplikator, der auf die Losgröße für jede aufeinanderfolgende Position angewendet wird.

Dies sind die Eingabevariablen, die wir für verschiedene Zwecke wie die Optimierung unserer Strategie ändern werden. Nun werden wir einige weitere Variablen im globalen Raum definieren:

bool gridCycleRunning = false;
double lastPositionLotSize, lastPositionPrice, priceLevelsSumation, totalLotSizeSummation;

Diese Variablen werden für die folgenden Zwecke verwendet:

  1. gridCycleRunning: Dies ist eine boolesche Variable, die true ist, wenn der Zyklus läuft, und false, wenn er nicht läuft; standardmäßig ist sie false.
  2. lastPositionLotSize: Hierbei handelt es sich um eine Variable vom Typ double, die erstellt wurde, um die Losgröße der zuletzt geöffneten Position zu einem bestimmten Zeitpunkt im Zyklus zu speichern.
  3. lastPositionPrice: Dies ist eine Variable vom Typ double, die erstellt wurde, um das Niveau des offenen Preises der letzten Position zu einem bestimmten Zeitpunkt im Zyklus zu speichern.
  4. priceLevelsSumation: Dies ist die Summe aller offenen Preisniveaus aller Positionen, die später zur Berechnung des durchschnittlichen Preisniveaus verwendet wird.
  5. totalLotSizeSummation: Dies ist die Summe aller Losgrößen aller Positionen, die später zur Berechnung des durchschnittlichen Kursniveaus herangezogen wird.

Nachdem wir nun die wichtigsten Eingabevariablen festgelegt haben, fahren wir mit einer wichtigen Funktion fort, StartGridCycle(), , die die Initialisierung des Zyklus übernehmen wird.
//+------------------------------------------------------------------+
//| Hedge Cycle Intialization Function                               |
//+------------------------------------------------------------------+
void StartGridCycle()
   {
    double initialPrice = initialPositionBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);

    ENUM_ORDER_TYPE positionType = initialPositionBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
    m_trade.PositionOpen(_Symbol, positionType, initialLotSize, initialPrice, 0, 0);

    lastPositionLotSize = initialLotSize;
    lastPositionPrice = initialPrice;
    ObjectCreate(0, "Next Position Price", OBJ_HLINE, 0, 0, lastPositionPrice - distance * _Point * 10);
    ObjectSetInteger(0, "Next Position Price", OBJPROP_COLOR, clrRed);

    priceLevelsSumation = initialLotSize * lastPositionPrice;
    totalLotSizeSummation = initialLotSize;

    if(m_trade.ResultRetcode() == 10009)
        gridCycleRunning = true;
   }
//+------------------------------------------------------------------+

In der Funktion StartGridCycle() erstellen wir zunächst eine Variable vom Typ double, initialPrice, die je nach der Variablen initialPositionBuy entweder den Geld- oder Briefkurs speichert. Wenn initialPositionBuy wahr ist, speichern wir den Ask-Preis, wenn es falsch ist, speichern wir den Bid-Preis. Diese Unterscheidung ist wichtig, da eine Kaufposition nur zum Briefkurs eröffnet werden kann, während eine Verkaufsposition zum Geldkurs eröffnet werden muss.
ENUM_ORDER_TYPE positionType = initialPositionBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
CTrade m_trade;
m_trade.PositionOpen(_Symbol, positionType, initialLotSize, initialPrice, 0, 0);

Je nach dem Wert von initialPositionBuy wird nun entweder eine Kauf- oder eine Verkaufsposition eröffnet. Zu diesem Zweck erstellen wir eine Variable namens positionType vom Typ ENUM_ORDER_TYPE. ENUM_ORDER_TYPE ist ein vordefinierter, nutzerdefinierter Variablentyp in MQL5, der ganzzahlige Werte von 0 bis 8 annehmen kann, die jeweils einen bestimmten Auftragstyp repräsentieren, wie in der folgenden Tabelle definiert:

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

Innerhalb von ENUM_ORDER_TYPE entsprechen die Werte 0 und 1 dem ORDER_TYPE_BUY bzw. dem ORDER_TYPE_SELL . Für unsere Zwecke werden wir uns auf diese beiden konzentrieren. Die Verwendung von Identifikatoren anstelle ihrer ganzzahligen Werte ist von Vorteil, da Identifikatoren intuitiver und leichter zu merken sind.

Daher setzen wir positionType auf ORDER_TYPE_BUY, wenn initialPositionBuy wahr ist; andernfalls setzen wir es auf ORDER_TYPE_SELL .

Um fortzufahren, binden wir zunächst die Standard-Handelsbibliothek Trade.mqh in den globalen Raum ein. Dies geschieht mit der folgenden Anweisung:

#include <Trade/Trade.mqh>
CTrade m_trade;

Um fortzufahren, definieren wir auch eine Instanz der Klasse CTrade, die wir m_trade nennen. Diese Instanz wird für die Verwaltung von Handelsgeschäften verwendet. Um dann eine Position zu eröffnen, verwenden wir die Funktion PositionOpen von m_trade, die die tatsächliche Eröffnung von Kauf- oder Verkaufspositionen auf der Grundlage der von uns festgelegten Parameter, einschließlich des PositionType und anderer relevanter Handelseinstellungen, übernimmt.

m_trade.PositionOpen(_Symbol, positionType, initialLotSize, initialPrice, 0, 0);

Wenn wir die Funktion PositionOpen aus der Instanz m_trade verwenden, übergeben wir ihr mehrere Parameter, die für die Eröffnung einer Position wichtig sind:

  1. _Symbol: Der erste Parameter ist _Symbol, der sich auf das aktuelle Handelssymbol bezieht.

  2. positionType: Der zweite Parameter ist positionType, den wir zuvor basierend auf dem Wert von initialPositionBuy definiert haben. Damit wird festgelegt, ob es sich bei der eröffneten Position um einen Kauf oder Verkauf handelt.

  3. initialLotSize: Die Losgröße für die Position, wie durch unsere Eingabevariable initialLotSize definiert.

  4. initialPrice: Der Preis, zu dem die Position eröffnet wird. Dies wird durch die Variable initialPrice bestimmt, die je nach Art der zu eröffnenden Position entweder den Ask- oder Bid-Preis enthält.

  5. SL (Stop Loss) und TP (Take Profit): Die letzten beiden Parameter dienen der Einstellung von Stop Loss (SL) und Take Profit (TP). In dieser speziellen Strategie werden diese Parameter anfangs nicht festgelegt, da die Schließung der Aufträge durch die Logik der Strategie bestimmt wird, insbesondere wenn der Preis den Durchschnittspreis plus den takeProfit-Wert erreicht.

Gehen wir nun zum nächsten Teil des Codes über:

lastPositionLotSize = initialLotSize;
lastPositionPrice = initialPrice;
ObjectCreate(0, "Next Position Price", OBJ_HLINE, 0, 0, lastPositionPrice - distance * _Point * 10);
ObjectSetInteger(0, "Next Position Price", OBJPROP_COLOR, clrRed);

  1. Einstellung der Variablen lastPositionLotSize und lastPositionPrice:

    • Wir haben die Variable lastPositionLotSize, die im globalen Bereich definiert ist, auf initialLotSize initialisiert. Diese Variable ist von entscheidender Bedeutung, da sie die Losgröße der zuletzt eröffneten Position festhält und die Berechnung der Losgröße für den nächsten Auftrag ermöglicht, indem sie mit dem eingegebenen Multiplikator multipliziert wird.
    • In ähnlicher Weise wird auch lastPositionPrice auf initialPrice gesetzt. Diese Variable, die ebenfalls im globalen Raum definiert ist, ist für die Bestimmung des Preisniveaus, zu dem nachfolgende Aufträge eröffnet werden sollen, von wesentlicher Bedeutung.

  2. Erstellen einer horizontalen Linie zur Visualisierung:

    • Um die visuelle Darstellung unserer Strategie im Chart zu verbessern, verwenden wir die Funktion ObjectCreate. Dieser Funktion werden die folgenden Parameter übergeben:
      • 0 für das aktuelle Chart
      • „Next Position Price“ als Name des Objekts
      • OBJ_HLINE als Objekttyp, der eine horizontale Linie angibt
      • Zusätzliche zweimal 0, einmal für das Unterfenster und die andere für den Zeitpunkt, da nur das Preisniveau für eine horizontale Linie benötigt wird
      • Das berechnete Preisniveau für den nächsten Auftrag, (lastPositionPrice - distance * _Point * 10), als letzter Parameter
    • Um die Farbe dieser horizontalen Linie auf Rot zu setzen, verwenden wir die Funktion ObjectSetInteger, wobei die Eigenschaft OBJPROP_COLOR auf clrRed gesetzt wird.

  3. Vorgehen bei dem Management der Durchschnittspreise in einem Rasterzyklus:

    • Nachdem diese ersten Schritte abgeschlossen sind, geht es nun darum, den Durchschnittspreis innerhalb des Rasterzyklus zu verwalten. Dabei wird der Durchschnittspreis dynamisch berechnet und angepasst, wenn neue Positionen eröffnet werden und sich der Markt entwickelt - eine Schlüsselkomponente der Grid-Handelsstrategie.

Fahren wir mit dem nächsten Teil der Code-Implementierung fort.

priceLevelsSumation = initialLotSize * lastPositionPrice;
totalLotSizeSummation = initialLotSize;

  1. Einstellung von priceLevelsSumation:

    • Wir haben priceLevelsSumation im globalen Raum definiert, um den gewichteten Durchschnittspreis aller offenen Positionen zu berechnen. Da es zunächst nur einen Auftrag gibt, setzen wir priceLevelsSumation gleich dem lastPositionPrice multipliziert mit dem entsprechenden Gewicht, das der Losgröße des Auftrags entspricht. Diese Einstellung bereitet die Variable darauf vor, weitere Preisniveaus zu akkumulieren, jeweils multipliziert mit den entsprechenden Losgrößen, wenn neue Positionen eröffnet werden.
  2. Initialisierung von totalLotSizeSummation;

    • Die Variable totalLotSizeSummation wird anfänglich gleich initialLotSize gesetzt. Dies ist im Zusammenhang mit der Formel für den gewichteten Durchschnitt sinnvoll, bei der wir durch die Gesamtgewichte dividieren müssen. Zu Beginn, bei nur einem Auftrag, entspricht das Gesamtgewicht der Losgröße dieses einzelnen Auftrags. Wenn Sie weitere Positionen eröffnen, fügen wir deren Gewichte (Losgrößen) zu dieser Summe hinzu und aktualisieren so dynamisch das Gesamtgewicht.

Lassen Sie uns nun mit dem letzten Teil der Funktion StartGridCycle() fortfahren:

if(m_trade.ResultRetcode() == 10009)
    gridCycleRunning = true;

Einstellung von hedgeCycleRunning:
  • Die Variable hedgeCycleRunning wird erst dann auf true gesetzt, wenn eine Position erfolgreich eröffnet wurde. Dies wird mit der Funktion ResultRetcode() der CTrade-Instanz mit dem Namen trade überprüft. Der Rückgabecode „10009“ zeigt an, dass der Auftrag erfolgreich abgeschickt wurde. (Anmerkung: Die verschiedenen Rückgabecodes können für unterschiedliche Ergebnisse von Handelsanfragen herangezogen werden).
  • Die Verwendung von hedgeCycleRunning ist für die Strategie von zentraler Bedeutung, da sie den Beginn des Rasterzyklus kennzeichnet. Die Bedeutung dieses Flags wird in den folgenden Teilen des Codes deutlicher werden.

Nachdem die Funktion StartGridCycle() ausgeführt wurde, die die Rasterstrategie einleitet, gehen wir nun zur Funktion OnTick() über. Wir unterteilen diese Funktion in 5 Segmente, die jeweils einen bestimmten Aspekt der Handelslogik behandeln:

  1. Start des Rasterzyklus
  2. Handhabung von Kaufpositionen
  3. Handhabung von Verkaufspositionen
  4. Die Funktion für das Ende des Rasterzyklus
  5. Handhabung des Schließens von Positionen
  1. Start des Rasterzyklus:
    double price = initialPositionBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
      
      if(!gridCycleRunning)
         {
          StartGridCycle();
         }
    1. Deklaration der Variablen „price“:

      • Es wird eine neue Variable mit dem Namen „price“ deklariert. Ihr Wert wird durch das Flag initialPositionBuy bestimmt:
        • Wenn initialPositionBuy wahr ist, wird dem Preis der aktuelle Ask-Preis zugewiesen.
        • Wenn initialPositionBuy falsch ist, wird dem Preis der aktuelle Bid-Preis zugewiesen.
    2. Bedingte Ausführung auf der Grundlage von gridCycleRunning:

      • Der nächste Schritt umfasst eine bedingte Prüfung der Variable gridCycleRunning:
        • Wenn gridCycleRunning false ist, bedeutet dies, dass der Rasterzyklus noch nicht begonnen oder der vorherige Zyklus abgeschlossen wurde. In diesem Fall führen wir die Funktion StartGridCycle() aus, die bereits ausführlich erläutert wurde. Diese Funktion initialisiert den Rasterzyklus, indem sie die erste Position öffnet und die erforderlichen Parameter einstellt.
        • Wenn gridCycleRunning true ist, bedeutet dies, dass der Rasterzyklus bereits aktiv ist. In diesem Szenario beschließen wir, erst einmal nichts zu tun. Diese Entscheidung ermöglicht es dem bestehenden Rasterzyklus, auf der Grundlage seiner etablierten Logik weiterzuarbeiten, ohne einen neuen Zyklus einzuleiten oder den aktuellen zu stören.

    Dieser Ansatz steuert effizient die Einleitung und Fortsetzung des Rasterzyklus und stellt sicher, dass die Handelsstrategie den vorgesehenen Betriebsablauf einhält. Fahren wir mit den nächsten Schritten bei der Umsetzung der Rasterstrategie fort.


  2. Handhabung von Kaufpositionen
    if(initialPositionBuy && price <= lastPositionPrice - distance * _Point * 10 && gridCycleRunning)
         {
          double newPositionLotSize = NormalizeDouble(lastPositionLotSize * lotSizeMultiplier, 2);
          m_trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, newPositionLotSize, price, 0, 0);
      
          lastPositionLotSize *= lotSizeMultiplier;
          lastPositionPrice = price;
          ObjectCreate(0, "Next Position Price", OBJ_HLINE, 0, 0, lastPositionPrice - distance * _Point * 10);
          ObjectSetInteger(0, "Next Position Price", OBJPROP_COLOR, clrRed);
      
          priceLevelsSumation += newPositionLotSize * lastPositionPrice;
          totalLotSizeSummation += newPositionLotSize;
          ObjectCreate(0, "Average Price", OBJ_HLINE, 0, 0, priceLevelsSumation / totalLotSizeSummation);
          ObjectSetInteger(0, "Average Price", OBJPROP_COLOR, clrGreen);
         }

    Hier haben wir eine weitere if-Anweisung, die 3 Bedingungen hat:

    1. Bedingung für initialPositionBuy:

      • Das Codesegment wird nur ausgeführt, wenn die Variable initialPositionBuy wahr ist. Diese Bedingung stellt sicher, dass die Logik für die Behandlung von Kaufpositionen von derjenigen für Verkaufspositionen getrennt ist.

    2. Bedingung für den Preis:

      • Die zweite Bedingung prüft, ob die Variable price kleiner oder gleich lastPositionPrice - distance * _Point * 10 ist. Diese Bedingung ist entscheidend für die Entscheidung, wann eine neue Kaufposition eröffnet werden sollte. Hier wird die Subtraktionsoperation (-) verwendet, die mit der in Ihrer Rasterstrategie für Kaufpositionen definierten Richtungslogik übereinstimmt.

    3. Bedingung für gridCycleRunning:

      • Die dritte Bedingung setzt voraus, dass die Variable gridCycleRunning wahr ist, was bedeutet, dass der Rasterzyklus derzeit aktiv ist. Dies ist wichtig, um sicherzustellen, dass neue Positionen nur im Rahmen eines laufenden Handelszyklus eröffnet werden.

    Wenn alle drei Bedingungen erfüllt sind, eröffnet der EA eine neue Kaufposition. Zuvor wird jedoch die Losgröße für die neue Position berechnet:

    • Eine neue Variable vom Typ double, newPositionLotSize, wird deklariert und gleich auf lastPositionLotSize multipliziert mit lotSizeMultiplier gesetzt.
    • Die sich ergebende Losgröße wird dann auf zwei Dezimalstellen normalisiert, um die Losgröße gültig zu machen, da die Losgröße ein strenges Vielfaches von 0,01 sein muss.

    Auf diese Weise wird sichergestellt, dass neue Positionen in angemessener Größe eröffnet werden, wobei die Regeln der Rasterstrategie eingehalten werden. Anschließend verwendet der EA die Funktion PositionOpen() der CTrade-Klasseninstanz namens m_trade (die zuvor im globalen Bereich deklariert wurde), um die Kaufposition mit der berechneten Losgröße zu eröffnen.

    Der nächste Schritt besteht darin, die Variable lastPositionLotSize zu aktualisieren, um die Logik fortzusetzen. Dies ist entscheidend für die Genauigkeit der Losgrößen bei nachfolgenden Aufträgen:

    • Die letztePositionLotSize wird gleich sich selbst multipliziert mit dem LotSizeMultiplier gesetzt. Entscheidend ist, dass diese Multiplikation keine Normalisierung auf zwei Dezimalstellen beinhaltet.
    • Der Grund für die Vermeidung einer Normalisierung wird anhand eines Szenarios veranschaulicht, in dem der LotSizeMultiplier 1,5 und die initialLotSize 0,01 beträgt. Multipliziert man 0,01 mit 1,5, erhält man 0,015, was, auf zwei Dezimalstellen normiert, auf 0,01 zurückgerundet wird. Dadurch entsteht eine Schleife, in der die Losgröße trotz der Multiplikation konstant bei 0,01 bleibt.
    • Um dieses Problem zu vermeiden und sicherzustellen, dass die Losgrößen wie vorgesehen zunehmen, wird lastPositionLotSize mit dem unnormalisierten Produkt aus sich selbst und dem lotSizeMultiplier aktualisiert. Dieser Schritt ist entscheidend für das korrekte Funktionieren der Gitterstrategie, insbesondere bei gebrochenen Multiplikatoren.

    Mit diesem Update kontrolliert der EA die Losgrößen für neue Positionen genau und passt sie an, sodass die beabsichtigte Progression der Rasterstrategie erhalten bleibt.

    Der nächste Schritt besteht darin, den lastPositionPrice zu aktualisieren und das nächste Positionsniveau im Chart zu visualisieren:

    1. Aktualisierung von lastPositionPrice:
      • Die Variable lastPositionPrice wird gleich dem Preis aktualisiert, der durch die Bedingung initialPositionBuy bestimmt wird. Da die erste Bedingung der if-Anweisung den Eintritt nur gewährleistet, wenn initialPositionBuy wahr ist, entspricht der Preis in diesem Zusammenhang dem Ask-Preis.

    2. Visualisierung der nächsten Positionsebene:
      • Im Chart wird eine horizontale Linie mit der Bezeichnung „Next Position Price“ (nächster Positionspreis) eingezeichnet. Diese Linie stellt das Preisniveau dar, zu dem der nächste Auftrag eröffnet wird.
      • Da initialPositionBuy wahr ist, was bedeutet, dass es sich bei der nächsten Position um einen Kauf handelt, wird das Preisniveau für diese Zeile auf lastPositionPrice (der gerade aktualisiert wurde) minus den Abstand (wie in den Eingaben angegeben) multipliziert mit _Point und dann weiter mit 10 multipliziert.
      • Diese horizontale Linie wird mit der Funktion ObjectCreate() erstellt, und ihre Farbe wird mit zusätzlichen Objekteigenschaftsfunktionen zur einfachen Visualisierung auf clrRed gesetzt. Mit dieser visuellen Hilfe lässt sich das Preisniveau für den nächsten potenziellen Kaufauftrag leicht ermitteln.

    Durch die Aktualisierung von lastPositionPrice und die visuelle Markierung des nächsten Order-Levels bereitet sich der EA effektiv auf die nachfolgenden Schritte im Rasterzyklus vor und stellt sicher, dass jede neue Position mit den Kriterien der Strategie übereinstimmt und visuell auf dem Chart nachvollziehbar ist.

    Lassen Sie uns nun die Berechnung des durchschnittlichen Preisniveaus verfeinern, wobei wir uns auf den gewichteten Durchschnitt konzentrieren:

    1. Aktualisierung von priceLevelsSummation und totalLotSizeSummation:

      • Um den gewichteten Durchschnittspreis zu aktualisieren, addieren wir das Produkt aus lastPositionPrice und newPositionLotSize zu priceLevelsSummation.
      • Für totalLotSizeSummation wird einfach der Wert von newPositionLotSize addiert.
      • Diese Aktualisierungen sind wichtig, um den Überblick über die kumulativen Kursniveaus und die Gesamtlosgrößen aller Positionen zu behalten.

    2. Berechnung des gewichteten durchschnittlichen Preisniveaus:

      • Das durchschnittliche Preisniveau, das in diesem Zusammenhang ein gewichteter Durchschnitt ist, wird berechnet, indem priceLevelsSummation durch totalLotSizeSummation dividiert wird.
      • Diese Berechnung spiegelt den Durchschnittspreis aller offenen Positionen unter Berücksichtigung der jeweiligen Losgrößen genau wider.

    3. Visualisierung des gewichteten durchschnittlichen Preisniveaus:

      • Eine weitere horizontale Linie wird mit der Funktion ObjectCreate() auf dem berechneten Durchschnittspreisniveau im Chart erstellt.
      • Die Farbe dieser Linie wird auf clrGreen gesetzt, um sie von der anderen horizontalen Linie zu unterscheiden, die den nächsten Positionspreis anzeigt.
      • Es ist wichtig zu beachten, dass für diese Berechnung der normalisierte Wert von lastPositionLotSize multipliziert mit lotSizeMultiplier verwendet wird. Dadurch wird sichergestellt, dass die tatsächliche Losgröße der neu eröffneten Position berücksichtigt wird, was einen genauen gewichteten Durchschnitt ergibt.

    Durch diese Schritte behält der EA nicht nur das durchschnittliche Preisniveau aller offenen Positionen im Auge, sondern stellt es auch visuell auf dem Chart dar. Dies ermöglicht eine einfache Überwachung und Entscheidungsfindung auf der Grundlage des aktuellen Zustands des Rasterzyklus und der Marktbedingungen.


  3. Handhabung von Verkaufspositionen:
    if(!initialPositionBuy && price >= lastPositionPrice + distance * _Point * 10 && gridCycleRunning)
         {
          double newPositionLotSize = NormalizeDouble(lastPositionLotSize * lotSizeMultiplier, 2);
          m_trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, newPositionLotSize, price, 0, 0);
      
          lastPositionLotSize *= lotSizeMultiplier;
          lastPositionPrice = price;
          ObjectCreate(0, "Next Position Price", OBJ_HLINE, 0, 0, lastPositionPrice + distance * _Point * 10);
          ObjectSetInteger(0, "Next Position Price", OBJPROP_COLOR, clrRed);
      
          priceLevelsSumation += newPositionLotSize * lastPositionPrice;
          totalLotSizeSummation += newPositionLotSize;
          ObjectCreate(0, "Average Price", OBJ_HLINE, 0, 0, priceLevelsSumation / totalLotSizeSummation);
          ObjectSetInteger(0, "Average Price", OBJPROP_COLOR, clrGreen);
         }

    Hier haben wir eine weitere if-Anweisung, die wiederum 3 Bedingungen hat:

    1. Bedingung für initialPositionBuy:

      • Dieser Teil des Codes wird nur ausgeführt, wenn initialPositionBuy falsch ist. Diese Bedingung stellt sicher, dass die Logik speziell für Verkaufspositionen und nicht für Kaufpositionen gilt.

    2. Bedingung für den Preis:

      • Die zweite Bedingung prüft, ob der Preis größer oder gleich lastPositionPrice + distance * _Point * 10 ist. Dies ist wichtig, um den richtigen Zeitpunkt für die Eröffnung einer neuen Verkaufsposition zu bestimmen. In diesem Fall wird die Additionsoperation (+) verwendet, die mit der Richtungslogik Ihrer Strategie für Verkaufspositionen übereinstimmt.

    3. Bedingung für gridCycleRunning:

      • Die dritte Bedingung erfordert, dass gridCycleRunning wahr ist, was bedeutet, dass der Rasterzyklus aktiv läuft. Dadurch wird sichergestellt, dass neue Positionen nur im Rahmen des laufenden Handelszyklus eröffnet werden.

    Wenn alle drei Bedingungen erfüllt sind, eröffnet der EA eine neue Verkaufsposition:

    • Eine Variable vom Typ double, newPositionLotSize, wird deklariert und gleich auf lastPositionLotSize multipliziert mit lotSizeMultiplier gesetzt.
    • Diese neue Losgröße wird dann auf zwei Dezimalstellen normalisiert, um die Losgröße gültig zu machen, da die Losgröße ein strenges Vielfaches von 0,01 sein muss.
    • Schließlich verwendet der EA die Funktion PositionOpen() aus der CTrade-Klasseninstanz m_trade (die zuvor im globalen Bereich deklariert wurde), um die Verkaufsposition mit der ermittelten Losgröße zu eröffnen.

    Auf diese Weise wird sichergestellt, dass neue Verkaufspositionen in angemessenen Abständen und in angemessener Größe eröffnet werden, wobei die Regeln der Rasterstrategie eingehalten werden und die Progression der Strategie erhalten bleibt.

    Im nächsten Schritt, der Behandlung von Verkaufspositionen, konzentrieren wir uns auf die korrekte Aktualisierung der Variable lastPositionLotSize:

    • Die Variable lastPositionLotSize wird so aktualisiert, dass sie gleich sich selbst multipliziert mit dem LotSizeMultiplier ist. Dieser Schritt ist entscheidend für die Beibehaltung der Losgrößenprogression im Rasterzyklus.
    • Wichtig ist, dass diese Multiplikation keine Normalisierung auf zwei Dezimalstellen beinhaltet. Diese Entscheidung ist wichtig, um ein mögliches Problem mit gebrochenen Multiplikatoren zu vermeiden.
    • Zur Veranschaulichung: Bei einem lotSizeMultiplier von 1,5 und einer initialLotSize von 0,01 würde die Multiplikation 0,015 ergeben. Eine Normalisierung auf zwei Dezimalstellen würde den Wert auf 0,01 abrunden. Die Wiederholung dieses Prozesses würde immer wieder zu einer Losgröße von 0,01 führen, wodurch eine Schleife entsteht und die Absicht der Strategie untergraben wird.
    • Um dieses Problem zu umgehen, wird lastPositionLotSize auf das nicht-normalisierte Produkt aus sich selbst und dem LotSizeMultiplier gesetzt. Auf diese Weise wird sichergestellt, dass die Losgrößen angemessen ansteigen, vor allem wenn es sich um Multiplikatoren handelt, die zu Bruchteilen von Losgrößen führen.

    Durch die Aktualisierung der lastPositionLotSize ohne Normalisierung verfolgt der EA effektiv die Losgrößen für neue Verkaufspositionen und passt sie an, um sicherzustellen, dass die Rasterstrategie wie vorgesehen funktioniert.

    1. Aktualisierung von lastPositionPrice:

      • Die Variable lastPositionPrice wird auf den Preis aktualisiert, der je nach dem Wert von initialPositionBuy entweder der Ask- oder der Bid-Preis ist.
      • Da wir diesen Teil des Codes in diesem Fall nur eingeben, wenn initialPositionBuy falsch ist (gemäß der ersten Bedingung), wird lastPositionPrice auf den Bid-Kurs gesetzt.

    2. Zeichnen einer horizontalen Linie für die nächste Position:

      • Im Chart wird eine horizontale Linie mit der Bezeichnung „Next Position Price“ eingezeichnet. Diese Linie zeigt das Preisniveau an, auf dem der nächste Auftrag (in diesem Fall ein Verkaufsauftrag) eröffnet wird.
      • Das Preisniveau für diese horizontale Linie wird auf lastPositionPrice (das aktualisiert wurde) plus die in den Eingaben angegebene Entfernung gesetzt, mit _Point multipliziert und dann weiter mit 10 multipliziert. Mit dieser Berechnung wird die geeignete Höhe für den nächsten Verkaufsauftrag ermittelt.
      • Die Linie wird mit der Funktion ObjectCreate() erstellt, einer vordefinierten Funktion in MQL5 zum Zeichnen von Objekten in den Charts.
      • Die Farbe dieser Linie ist auf clrRot eingestellt, was ihre Sichtbarkeit erhöht und sie im Chart leicht erkennbar macht.

    Durch die entsprechende Einstellung von lastPositionPrice und die visuelle Darstellung des Niveaus der nächsten Order bereitet sich der EA effektiv auf nachfolgende Verkaufsorders vor und stellt sicher, dass diese mit den Regeln der Rasterstrategie übereinstimmen und auf dem Chart leicht zu erkennen sind.

    Bei der Berechnung des durchschnittlichen Preisniveaus, insbesondere des gewichteten Durchschnitts für Verkaufspositionen, geht es um Folgendes:

    1. Aktualisierung von priceLevelsSummation und totalLotSizeSummation:

      • Der Wert von lastPositionPrice multipliziert mit newPositionLotSize wird zu priceLevelsSummation addiert. In diesem Schritt werden die Kursniveaus aller offenen Positionen kumuliert und nach ihrer jeweiligen Losgröße gewichtet.
      • Der Wert von newPositionLotSize wird zu totalLotSizeSummation addiert. Mit dieser Variablen werden die kumulativen Losgrößen aller Positionen erfasst.

    2. Berechnung des gewichteten durchschnittlichen Preisniveaus:

      • Das durchschnittliche Preisniveau ergibt sich aus der Division von priceLevelsSummation durch totalLotSizeSummation. Diese Berechnung ergibt den gewichteten Durchschnittspreis aller offenen Positionen.

    3. Visualisierung des gewichteten durchschnittlichen Preisniveaus:

      • Mit der Funktion ObjectCreate() wird eine horizontale Linie auf dem Niveau des gewichteten Durchschnittspreises erstellt. Diese visuelle Darstellung hilft bei der Überwachung des Durchschnittspreises aller Positionen.
      • Die Farbe dieser Linie ist auf clrGreen eingestellt, sodass sie leicht von anderen Linien im Chart zu unterscheiden ist.
      • Es ist wichtig zu beachten, dass für diese Berechnungen der normalisierte Wert von lastPositionLotSize multipliziert mit lotSizeMultiplier verwendet wird. Dadurch wird sichergestellt, dass die tatsächlichen, realen Losgrößen der eröffneten Positionen berücksichtigt werden, was eine genaue Berechnung des gewichteten Durchschnitts ermöglicht.

    Diese Methode zur Berechnung und Visualisierung des gewichteten durchschnittlichen Preisniveaus ist für die effektive Verwaltung von Verkaufspositionen im Rasterzyklus von entscheidender Bedeutung und ermöglicht eine fundierte Entscheidungsfindung auf der Grundlage des aktuellen Markt- und Positionsstatus.


  4. Die Funktion für das Ende des Rasterzyklus:
    //+------------------------------------------------------------------+
    //| Stop Function for a particular Grid Cycle                        |
    //+------------------------------------------------------------------+
    void StopGridCycle()
       {
        gridCycleRunning = false;
        ObjectDelete(0, "Next Position Price");
        ObjectDelete(0, "Average Price");
        for(int i = PositionsTotal() - 1; i >= 0; i--)
           {
            ulong ticket = PositionGetTicket(i);
            if(PositionSelectByTicket(ticket))
               {
                m_trade.PositionClose(ticket);
               }
           }
       }
    //+------------------------------------------------------------------+

    1. Einstellung von gridCycleRunning auf False:

      • Die erste Aktion in dieser Funktion besteht darin, der booleschen Variable gridCycleRunning false zuzuweisen. Diese Änderung bedeutet, dass der Rasterzyklus nicht mehr aktiv ist und sich gerade schließt.

    2. Löschen von Chartobjekten:

      • Anschließend verwenden wir die vordefinierte Funktion ObjectDelete(), um zwei bestimmte Objekte aus dem Chart zu entfernen: „Next Position Price“ und „Average Price“. Mit diesem Schritt wird das Chart von diesen Markierungen befreit, was darauf hinweist, dass der Zyklus abgeschlossen ist und diese Kursniveaus nicht mehr relevant sind.

    3. Alle Positionen schließen:

      • Die Funktion führt dann eine Schleife durch alle offenen Positionen durch.
      • Jede Position wird einzeln über ihre Ticketnummer ausgewählt.
      • Sobald eine Position ausgewählt ist, wird sie mit der Funktion PositionClose() der Instanz m_trade geschlossen. Die Ticketnummer der Position wird als Parameter an diese Funktion übergeben.
      • Dieser systematische Ansatz stellt sicher, dass alle im Rahmen des Rasterzyklus eröffneten Positionen geschlossen werden, wodurch die Handelstätigkeit für diesen bestimmten Zyklus effektiv beendet wird.

    Wenn wir diese Schritte befolgen, schließt die Funktion für das Ende des Rasterzyklus methodisch und effizient alle offenen Positionen, setzt die Handelsumgebung zurück und bereitet den EA auf einen neuen Grid Cycle vor.


  5. Handhabung des Schließens von Positionen

    if(gridCycleRunning)
       {
        if(initialPositionBuy && price >= (priceLevelsSumation / totalLotSizeSummation) + takeProfit * _Point * 10)
            StopGridCycle();
    
        if(!initialPositionBuy && price <= (priceLevelsSumation / totalLotSizeSummation) - takeProfit * _Point * 10)
            StopGridCycle();
       }

    Im letzten Abschnitt, „Handhabung des Schließens von Positionen“, wird der Prozess der Schließung von Aufträgen durch eine if-Anweisung gesteuert, die von bestimmten Bedingungen abhängig ist:

    1. Die wichtigste Bedingung für die Durchführung einer Aktion ist, dass gridCycleRunning wahr sein muss, was auf einen aktiven Rasterzyklus hinweist.
    2. In diesem Zusammenhang gibt es zwei weitere bedingte Prüfungen, die auf dem Wert von initialPositionBuy basieren:
      • Wenn initialPositionBuy wahr ist und der aktuelle Preis takeProfit Pips (definiert in den Eingabevariablen) über dem gewichteten durchschnittlichen Preisniveau liegt, dann wird die Funktion StopGridCycle() ausgeführt.
      • Umgekehrt wird die Funktion StopGridCycle() ausgeführt, wenn initialPositionBuy falsch ist und der aktuelle Kurs takeProfit Pips unter dem gewichteten durchschnittlichen Kursniveau liegt.

    Diese Bedingungen sorgen dafür, dass der Rasterzyklus gestoppt wird und die Positionen auf der Grundlage der festgelegten Take-Profit-Kriterien in Bezug auf das gewichtete durchschnittliche Preisniveau geschlossen werden, womit der Automatisierungsprozess für die klassische Rasterstrategie abgeschlossen ist.


Backtesting der klassischen Rasterstrategie

Nachdem die Automatisierung unserer Classic Grid Strategy nun abgeschlossen ist, ist es an der Zeit, ihre Leistung in einem realen Szenario zu bewerten.

Für den Backtest werden die folgenden Eingabeparameter verwendet:

  • initialPositionBuy : true
  • distance : 15 Pips
  • takeProfit : 5 Pips
  • initialLotSize : 0.01
  • lotSizeMultiplier : 2.0

Der Test wird für das Währungspaar EURUSD durchgeführt und erstreckt sich vom 1. November 2023 bis zum 22. Dezember 2023. Die gewählte Hebelwirkung ist 1:500, mit einer Starteinlage von $10.000. Was den Zeitrahmen anbelangt, so ist dieser für unsere Strategie irrelevant, sodass eine beliebige Auswahl ausreicht, ohne die Ergebnisse zu beeinträchtigen. Obwohl dieser Test einen relativ kurzen Zeitraum von knapp zwei Monaten abdeckt, soll er als repräsentative Stichprobe für mögliche Ergebnisse über längere Zeiträume dienen.

Schauen wir uns nun die Ergebnisse dieses Backtests an:


Diese Ergebnisse sehen recht gut aus. Bei der Überprüfung der Backtest-Ergebnisse lassen sich interessante Trends erkennen, die durch die blauen und grünen Linien im Chart dargestellt werden. Schauen wir uns an, was die einzelnen Linien bedeuten und wie ihre Bewegungen die Leistung der Strategie widerspiegeln:

  1. Verstehen von den blauen und den grünen Linien:

    • Die blaue Linie stellt den Kontostand (balance) dar, während die grüne Linie das Kapital (equity) angibt.
    • Es ist ein auffälliges Muster zu beobachten, bei dem der Saldo zunimmt, das Kapital abnimmt und schließlich beide an demselben Punkt konvergieren.

  2. Erklärung der Schwankungen des Saldos:

    • Die Strategie sieht vor, dass alle Aufträge eines Zyklus gleichzeitig abgeschlossen werden, was im Idealfall zu einem direkten Anstieg des Saldos führen sollte. Das Schaubild zeigt jedoch ein Muster von Anstieg und Rückgang des Saldos, das einer weiteren Erklärung bedarf.
    • Diese Schwankung ist auf die leichte Verzögerung (Sekundenbruchteile) beim Abschluss von Aufträgen zurückzuführen. Auch wenn die Aufträge fast gleichzeitig abgeschlossen werden, zeigt das Chart die inkrementellen Aktualisierungen des Saldos.
    • Anfänglich steigt der Saldo, da profitable Positionen zuerst geschlossen werden. Die Positionen werden in einer Schleife geschlossen, beginnend mit der letzten (profitabelsten) Position, wie durch die Funktion PositionsTotal() angezeigt. Daher kann die Aufwärts- und die kurze Abwärtsbewegung der Saldenlinie vernachlässigt werden, denn der Schwerpunkt liegt auf dem Nettoaufwärtstrend.

  3. Bewegung der Kapitallinie:

    • So wie der Saldo sinkt das Kapital zunächst und steigt dann an. Das liegt daran, dass die profitablen Positionen zuerst geschlossen werden, wodurch das Kapital vorübergehend sinkt, bevor es sich wieder erholt.
    • Die Bewegung der grünen Aktienlinie folgt einer ähnlichen Logik wie die der blauen Saldenlinie, was zu der gleichen Konsequenz eines insgesamt positiven Trends führt.

Zusammenfassend lässt sich sagen, dass trotz der geringfügigen Schwankungen in der Grafik, die auf die Reihenfolge der Auftragsabschlüsse und leichte Verzögerungen zurückzuführen sind, der übergeordnete Trend auf ein erfolgreiches Ergebnis der Strategie hindeutet, was durch die endgültige Konvergenz und die Aufwärtsbewegung sowohl der Salden- als auch der Kapitallinie belegt wird.

Die Backtest-Ergebnisse zeigen die Rentabilität der klassischen Rasterstrategie. Es ist jedoch wichtig, sich über eine wesentliche Einschränkung dieser Strategie im Klaren zu sein: das Erfordernis einer hohen Haltekapazität.

Diese Strategie erfordert oft einen erheblichen Kapitaleinsatz, um die Rückschläge zu verkraften, die auftreten, bevor die Rentabilität erreicht ist. Die Fähigkeit, Positionen über ungünstige Marktbewegungen hinweg zu halten, ohne dass es zu Nachschussforderungen kommt oder man gezwungen ist, Positionen mit Verlust zu schließen, ist entscheidend.

Diese Einschränkung wird in den folgenden Teilen unserer Serie, in denen wir Optimierungstechniken für diese Strategien erforschen werden, eine zentrale Rolle spielen. Ziel ist es, ihre Effizienz zu steigern und die erforderliche Haltekapazität zu verringern, sodass die Strategie für Händler mit unterschiedlichem Kapital zugänglicher und weniger riskant wird.


Schlussfolgerung

In dieser Folge haben wir uns mit den Feinheiten der klassischen Rasterstrategie befasst und sie erfolgreich mit einem Expert Advisor (EA) in MQL5 automatisiert. Wir haben auch einige erste Ergebnisse dieser Strategie untersucht und ihr Potenzial sowie verbesserungswürdige Bereiche aufgezeigt.

Der Weg zur Optimierung solcher Strategien ist jedoch noch lange nicht zu Ende. Zukünftige Teile dieser Serie werden sich auf die Feinabstimmung der Strategie konzentrieren, insbesondere auf die optimalen Werte für Eingabeparameter wie distance, takeProfit, initialLotSize und lotSizeMultiplier, um die Renditen zu maximieren und die Drawdowns zu minimieren. Ein interessanter Aspekt unserer bevorstehenden Untersuchung wird die Frage sein, ob es sinnvoll ist, mit einer Kauf- oder Verkaufsposition zu beginnen, was je nach Währung und Marktbedingungen unterschiedlich sein kann.

Freuen Sie sich auf unsere nächsten Artikel, in denen wir weitere wertvolle Erkenntnisse und Techniken aufdecken werden. Ich danke Ihnen, dass Sie sich die Zeit genommen haben, meine Artikel zu lesen, und hoffe, dass sie Ihnen sowohl Wissen als auch praktische Hilfe bei Ihren Handels- und Codierungsbemühungen bieten. Wenn Sie spezielle Themen oder Ideen haben, die Sie im nächsten Teil dieser Reihe behandelt sehen möchten, sind Ihre Vorschläge immer willkommen.

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


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

Beigefügte Dateien |
Entwicklung eines MQTT-Clients für Metatrader 5: ein TDD-Ansatz - Teil 5 Entwicklung eines MQTT-Clients für Metatrader 5: ein TDD-Ansatz - Teil 5
Dieser Artikel ist der fünfte Teil einer Serie, die unsere Entwicklungsschritte für einen nativen MQL5-Client für das MQTT 5.0-Protokoll beschreibt. In diesem Teil beschreiben wir die Struktur von PUBLISH-Paketen, wie wir ihre Publish Flags setzen, Topic Name(s) Strings kodieren und Packet Identifier(s) setzen, falls erforderlich.
Einführung in MQL5 (Teil 2): Navigieren zwischen vordefinierten Variablen, gebräuchlichen Funktionen und Kontrollflussanweisungen Einführung in MQL5 (Teil 2): Navigieren zwischen vordefinierten Variablen, gebräuchlichen Funktionen und Kontrollflussanweisungen
Begeben wir uns mit Teil zwei unserer MQL5-Serie auf eine aufschlussreiche Reise. Diese Artikel sind nicht einfach nur Anleitungen, sie sind die Tore zu einem verzauberten Reich, in dem Programmieranfänger und Zauberer gleichermaßen zu Hause sind. Was macht diese Reise wirklich magisch? Teil zwei unserer MQL5-Serie zeichnet sich durch seine erfrischende Einfachheit aus, die komplexe Konzepte für alle zugänglich macht. Beantworten Sie Ihre Fragen interaktiv und sorgen Sie so für eine bereichernde und individuelle Lernerfahrung. Lassen Sie uns eine Gemeinschaft aufbauen, in der das Verständnis von MQL5 für jeden ein Abenteuer ist. Willkommen in der Welt der Verzauberung!
Deep Learning, Vorhersage und Aufträge mit Python, dem MetaTrader5 Python-Paket und ONNX-Modelldatei Deep Learning, Vorhersage und Aufträge mit Python, dem MetaTrader5 Python-Paket und ONNX-Modelldatei
Im Rahmen des Projekts wird Python für Deep Learning-basierte Prognosen auf den Finanzmärkten eingesetzt. Wir werden die Feinheiten des Testens der Leistung des Modells anhand von Schlüsselkennzahlen wie dem mittleren absoluten Fehler (MAE), dem mittleren quadratischen Fehler (MSE) und dem R-Quadrat (R2) erkunden und lernen, wie man alles in eine ausführbare Datei verpackt. Wir werden auch eine ONNX-Modelldatei mit seinem EA erstellen.
Algorithmischer Handel mit MetaTrader 5 und R für Einsteiger Algorithmischer Handel mit MetaTrader 5 und R für Einsteiger
Begeben wir uns auf eine fesselnde Entdeckungsreise, bei der Finanzanalyse und algorithmischer Handel aufeinandertreffen, während wir die Kunst der nahtlosen Verbindung von R und MetaTrader 5 enträtseln. Dieser Artikel ist Ihr Leitfaden für den Brückenschlag zwischen den analytischen Finessen von R und den beeindruckenden Handelsmöglichkeiten von MetaTrader 5.