English 日本語
preview
Entwicklung eines Expert Advisors (EA) auf Basis der Consolidation Range Breakout Strategie in MQL5

Entwicklung eines Expert Advisors (EA) auf Basis der Consolidation Range Breakout Strategie in MQL5

MetaTrader 5Handel | 6 September 2024, 09:52
34 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In diesem Artikel werden wir uns mit der Entwicklung eines Expert Advisors (EA) beschäftigen, der auf der Strategie Consolidation Range Breakout basiert und MetaQuotes Language 5 (MQL5), die Programmiersprache für MetaTrader 5 (MT5), verwendet. In der schnelllebigen Welt des Finanzhandels sind Strategien, die aus Marktmustern und -verhaltensweisen Kapital schlagen, entscheidend für den Erfolg. Eine solche Strategie ist der Consolidation Range Breakout, der sich darauf konzentriert, Perioden der Marktkonsolidierung zu erkennen und die darauf folgenden Ausbrüche zu handeln. Diese Strategie ist besonders effektiv, wenn es darum geht, signifikante Kursbewegungen zu erfassen, die auf einen Zeitraum mit geringer Volatilität folgen. Wir werden den Expert Advisor mit einer Consolidation-Range-Breakout-Strategie anhand der folgenden Themen erstellen:

  1. Überblick über die Strategie
  2. Blaupause der Strategie
  3. Implementierung in MetaQuotes Language 5 (MQL5)
  4. Backtest-Ergebnisse
  5. Schlussfolgerung

Am Ende dieses Artikels werden Sie ein umfassendes Verständnis dafür haben, wie man einen robusten EA auf der Grundlage der Consolidation Range Breakout-Strategie in MQL5 entwickelt und implementiert, sodass Sie über das Wissen verfügen, um Ihr Trading-Toolkit zu erweitern. Wir werden intensiv MetaQuotes Language 5 (MQL5) als Basis und die Integrierte Entwicklungsumgebung (IDE) für die Programmierung verwenden, um die Dateien auf dem Handelsterminal MetaTrader 5 (MT5) auszuführen. Daher ist es von größter Bedeutung, dass die oben genannten Versionen vorhanden sind. Es kann losgehen.


Überblick über die Strategie

Um die Strategie des Ausbruchs aus dem Konsolidierungsbereich leichter zu verstehen, sollten wir sie in einzelne Abschnitte unterteilen.

  • Erklärung zum Konsolidierungskreis

Eine Konsolidierungsspanne, die auch als Handelsspanne (trading range) bezeichnet wird, ist ein Zeitraum, in dem der Preis eines Finanzinstruments innerhalb einer bestimmten Spanne horizontal schwankt, ohne starke Auf- oder Abwärtsbewegungen zu zeigen. Dieser Zeitraum ist durch eine geringe Volatilität gekennzeichnet, wobei der Kurs zwischen einem klar definierten Unterstützungsniveau (untere Begrenzung) und einem Widerstandsniveau (obere Begrenzung) schwankt. Händler nutzen diese Phase häufig zur Vorhersage potenzieller Ausbruchspunkte, an denen sich der Kurs nach Beendigung der Konsolidierungsphase enorm in eine Richtung bewegen kann.

  • Wie die Strategie funktioniert

Die Consolidation Range Breakout-Strategie nutzt das vorhersehbare Verhalten von Kursen während Konsolidierungsphasen, um Ausbrüche zu identifizieren und zu handeln. Und so funktioniert es:

Identifizieren Sie das Konsolidierungsgebiet: Der erste Schritt besteht darin, einen Konsolidierungsbereich zu erkennen, indem man die jüngsten Kursbewegungen untersucht. Dabei werden die höchsten und niedrigsten Kurse über eine bestimmte Anzahl von Balken (Kerzen) ermittelt, um die obere und untere Grenze der Spanne zu definieren, die in der Regel als Widerstands- und Unterstützungsniveaus der Spanne dienen. Die Auswahl des Zeitrahmens ist hier nicht statisch, da jeder Chart für den Identifizierungsprozess verwendet werden kann, daher müssen Sie einen Chart-Zeitrahmen auswählen, der zu Ihrem Handelsstil passt.

Monitor für Ausbrüche: Sobald die Konsolidierungsspanne festgelegt ist, achtet die Strategie auf Kursbewegungen, die entweder die obere oder die untere Grenze der Spanne durchbrechen. Ein Ausbruch erfolgt, wenn der Kurs über dem Widerstandsniveau oder unter dem Unterstützungsniveau schließt. Andere Händler betrachten die gleiche Kerze, die die Spanne durchbricht, als Scalping, d.h. sobald der Preis unter oder über die extreme Spanne fällt, betrachten sie bereits einen Ausbruch.

Handeln Sie den Ausbruch: Sobald ein Ausbruch erkannt wird, leitet die Strategie einen Handel in Richtung des Ausbruchs ein. Bricht der Kurs über das Widerstandsniveau, wird ein Kaufauftrag erteilt. Umgekehrt wird ein Verkaufsauftrag erteilt, wenn der Kurs unter das Unterstützungsniveau fällt. Auch hier sind einige Händler der Ansicht, dass sie auf einen Rücksetzer warten, d. h. nach einem Ausbruch, um eine weitere Bestätigung zu erhalten, warten sie darauf, dass der Kurs wieder in die Spanne zurückkehrt, und sobald er wieder in dieselbe Richtung wie der ursprüngliche Kurs ausbricht, dringen sie in den Markt ein. Für die Verwendung wird die Option einer Rücksetzung nicht berücksichtigt. 

  • Umsetzung der Strategie

Die Umsetzung der Consolidation Range Breakout-Strategie erfolgt in mehreren Schritten:

Definieren der Bereichsparameter: Wir bestimmen die Anzahl der zu analysierenden Balken, um den Konsolidierungsbereich zu ermitteln, und legen die Kriterien fest, die einen Ausbruch darstellen. Es wird eine Spanne von Balken und eine Zielspanne für den Preis ermittelt und festgelegt. Damit eine Konsolidierungsspanne gültig ist, sind beispielsweise mindestens 10 Balken innerhalb einer Preisspanne von 700 Punkten oder 70 Pips erforderlich.

Entwickeln der Erkennungslogik: Wir schreiben einen Code, um historische Kursdaten zu scannen, den höchsten Höchststand und den niedrigsten Tiefststand innerhalb des angegebenen Bereichs zu ermitteln und diese Niveaus im Chart darzustellen. Der Code sollte klare Bedingungen enthalten, die erfüllt sein müssen, damit der Konsolidierungsbereich als gültig angesehen werden kann, und die getroffenen Annahmen müssen klar umrissen werden, um Unklarheiten zu vermeiden.

Überwachen der Preisdaten in Echtzeit: Wir überwachen kontinuierlich die eingehenden Kursdaten, um Ausbrüche zu erkennen, sobald sie auftreten. Bei jedem Tick muss eine Überwachung erfolgen, und wenn nicht notwendig, dann bei jeder neuen Kerzengeneration.

Ausführen des Handels: Wir implementieren eine Handelsausführungslogik, um Kauf- oder Verkaufsaufträge zu platzieren, wenn ein Ausbruch erkannt wird, einschließlich der Festlegung geeigneter Stop-Loss und Take-Profit zur Risikosteuerung.

Optimieren und testen: Wir testen die Strategie anhand historischer Daten, um die Parameter zu optimieren und ihre Wirksamkeit sicherzustellen, bevor wir sie im Live-Handel einsetzen. Auf diese Weise können wir die besten Parameter ermitteln und die wichtigsten Funktionen herausfinden, die verbessert oder gefiltert werden müssen, um das System zu verbessern und zu optimieren.

Indem wir diese Schritte befolgen, können wir ein leistungsfähiges Instrument schaffen, um die Marktbedingungen mit der Strategie Consolidation Range Breakout auszunutzen. Im Folgenden finden Sie einige der Parameter, die für die Erstellung der Strategie erforderlich sind, sowie einen Überblick über die typischen Anforderungen.

ILLUSTRATION DES KONSOLIDIERUNGSAUSBRUCHS


Blaupause der Strategie

Um das von uns vermittelte Konzept leicht zu verstehen, sollten wir es in einer Blaupause visualisieren.

  • Consolidation Range Ausbruch nach oben:

AUSBRUCH AUS DER OBEREN EBENE

  • Consolidation Range Ausbruch nach unten:

AUSBRUCH DER UNTEREN EBENE


Implementierung in MetaQuotes Language 5 (MQL5)

Nachdem wir die grundlegenden Schritte und die Herangehensweise gelernt haben, die notwendig sind, um die Konsolidierungsbereich-Ausbruchsstrategie zu entwickeln, wollen wir die Theorie automatisieren und einen Expert Advisor (EA) in MetaQuotes Language 5 (MQL5) für MetaTrader 5 (MT5) entwickeln.

Um einen Expert Advisor (EA) zu erstellen, klicken Sie auf Ihrem MetaTrader 5-Terminal auf die Registerkarte Tools und aktivieren Sie MetaQuotes Language Editor oder drücken Sie einfach F4 auf Ihrer Tastatur. Alternativ können Sie auch auf das IDE-Symbol (Integrated Development Environment) in der Symbolleiste klicken. Dadurch wird die Umgebung des MetaQuotes-Editors geöffnet, die das Schreiben von Handelsrobotern, technischen Indikatoren, Skripten und Funktionsbibliotheken ermöglicht.

META-EDITOR ÖFFNEN

Sobald der MetaEditor geöffnet ist, navigieren Sie in der Symbolleiste zur Registerkarte „Datei“ und wählen Sie „Neue Datei“, oder drücken Sie einfach die Tastenkombination STRG + N, um ein neues Dokument zu erstellen. Alternativ können Sie auch auf das Symbol New auf der Registerkarte Werkzeuge klicken. Daraufhin erscheint ein Popup-Fenster des MQL-Assistenten.

NEUER EA

Markieren Sie in dem sich öffnenden Assistenten die Option Expert Advisor (Vorlage bzw. template) und klicken Sie auf Weiter (Next).

MQL WIZARD

Geben Sie in den allgemeinen Eigenschaften des Expertenberaters unter dem Abschnitt Name den Dateinamen Ihres Experten an. Beachten Sie, dass Sie den Backslash vor dem Namen des EA verwenden, um einen Ordner anzugeben oder zu erstellen, wenn er nicht existiert. Hier haben wir zum Beispiel standardmäßig „Experts\“. Das bedeutet, dass unser EA im Ordner Experts erstellt wird und wir ihn dort finden können. Die anderen Abschnitte sind ziemlich einfach, aber Sie können dem Link am Ende des Assistenten folgen, um zu erfahren, wie der Prozess genau abläuft.

EA-NAME

Nachdem Sie den gewünschten Expert Advisor-Dateinamen eingegeben haben, klicken Sie auf Weiter, dann auf Weiter und schließlich auf Fertig stellen. Nachdem wir all dies getan haben, können wir nun unsere Strategie programmieren.

Zunächst binden wir eine Handelsinstanz ein, indem wir #include am Anfang des Quellcodes verwenden. Dadurch erhalten wir Zugriff auf die Klasse CTrade, mit der wir ein Handelsobjekt erstellen werden. Dies ist von entscheidender Bedeutung, da wir sie zur Eröffnung von Geschäften benötigen.

#include <Trade/Trade.mqh> // Include the trade library
CTrade obj_Trade; // Create an instance of the CTrade class

Der Präprozessor wird die Zeile #include <Trade/Trade.mqh> durch den Inhalt der Datei Trade.mqh ersetzen. Die spitzen Klammern zeigen an, dass die Datei Trade.mqh aus dem Standardverzeichnis entnommen wird (normalerweise ist es das Terminal-Installationsverzeichnis\MQL5\Include). Das aktuelle Verzeichnis wird bei der Suche nicht berücksichtigt. Die Zeile kann an beliebiger Stelle im Programm platziert werden, aber in der Regel werden alle Einschlüsse am Anfang des Quellcodes platziert, um den Code besser zu strukturieren und die Referenz zu erleichtern. Die Deklaration des Objekts obj_Trade der Klasse CTrade ermöglicht uns dank der MQL5-Entwickler einen einfachen Zugriff auf die in dieser Klasse enthaltenen Methoden.

CTRADE CLASS

Da wir einen Bereich im Chart grafisch darstellen müssen, benötigen wir seinen Namen. Wir brauchen nur ein einziges Objekt für die Visualisierung und aktualisieren nach dem Plotten einfach seine Einstellungen, ohne es erneut zeichnen zu müssen. Um einen statischen Namen zu erhalten, der leicht abgerufen und sofort wiederverwendet werden kann, definieren wir ihn wie folgt.

#define rangeNAME "CONSOLIDATION RANGE" // Define the name of the consolidation range

Wir verwenden das Schlüsselwort #define, um ein Makro mit dem Namen „rangeNAME“ und dem Wert „CONSOLIDATION RANGE“ zu definieren, um den Namen unseres Konsolidierungsbereichs einfach zu speichern, anstatt den Namen bei jeder Erstellung der Ebene erneut eingeben zu müssen. Dies spart uns viel Zeit und verringert die Wahrscheinlichkeit, dass wir den Namen falsch angeben. Grundsätzlich werden Makros also zur Textersetzung während der Kompilierung verwendet.

Auch hier müssen wir die Koordinaten des Rechtecks speichern, das gezeichnet werden soll. Dabei handelt es sich um zweidimensionale (2D) Ordinaten des Formats (x, y), die zur eindeutigen Identifizierung der ersten und zweiten Position verwendet werden, die als x1,y1 bzw. x2,y2 dokumentiert sind. In einem Preischart wird die x-Achse durch die Datums- und Zeitskala dargestellt, während die y-Achse durch die Preisskala repräsentiert wird. Zum besseren Verständnis und zur Veranschaulichung wollen wir uns eine visuelle Darstellung ansehen.

ORDENTLICHE VERTRETUNG

Aus dem dargestellten Bild wird nun deutlich, warum wir die Koordinaten für das Plotten des Rechteckobjekts benötigen. Nachfolgend wird die Logik beschrieben, mit der sichergestellt wird, dass die Bereichskoordinaten gespeichert werden, ohne dass sie bei jeder Aktualisierung der Koordinaten deklariert werden müssen.

datetime TIME1_X1, TIME2_Y2; // Declare datetime variables to hold range start and end times
double PRICE1_Y1, PRICE2_Y2; // Declare double variables to hold range high and low prices

Hier deklarieren wir zwei Variablen vom Datentyp datetime. Die Methode, die wir hier verwenden, wird einzelne Datentyp-Deklaration genannt, die mehrere Variablen desselben Datentyps deklariert. Dies ist eine Abkürzung, um mehrere Variablen desselben Typs in einer einzigen Anweisung zu deklarieren. Es ist nützlich, da es die Prägnanz beibehält, indem es die Anzahl der Codezeilen reduziert und verwandte Variablen zusammenfasst, sodass es leichter zu verstehen ist, dass sie vom gleichen Typ sind und möglicherweise für ähnliche Zwecke verwendet werden, während die Konsistenz erhalten bleibt. Man könnte sie auch wie folgt schreiben:

datetime TIME1_X1;
datetime TIME2_Y2;

Die Variable „TIME1_X1“ enthält den Zeitwert der ersten Koordinate auf der x-Achse, während „TIME2_Y2“ den Zeitwert der zweiten Koordinate ebenfalls auf der x-Achse enthält. Analog dazu geben wir die Preiskoordinaten wie folgt an:

double PRICE1_Y1, PRICE2_Y2; // Declare double variables to hold range high and low prices

Wir müssen den Markt bei jedem neuen Balken ständig abtasten, um festzustellen, ob sich aufgrund der geringen Volatilität ein Konsolidierungsbereich bildet. Daher werden zwei Variablen benötigt, um die Flags zu speichern, wenn der Bereich existiert und wenn der Preis innerhalb des Bereichs liegt.

bool isRangeExist = false; // Flag to check if the range exists
bool isInRange = false; // Flag to check if we are currently within the range

Hier definieren wir zwei boolesche Variablen namens „isRangeExist“ und „isInRange“ und initialisieren sie. Die Variable „isRangeExist“ dient als Flag, das anzeigt, ob ein Konsolidierungsbereich identifiziert und im Chart dargestellt wurde. Wir initialisieren ihn auf false, da zu Beginn kein Bereich festgelegt wurde. Auch hier wird die Variable „isInRange“, die ebenfalls auf „false“ initialisiert ist, verwendet, um festzustellen, ob der aktuelle Marktpreis innerhalb des festgelegten Konsolidierungsbereichs liegt. Diese Flags sind für die Logik des Expert Advisors von entscheidender Bedeutung, da sie dazu beitragen, den Status des Bereichserkennungs- und Ausbruchsüberwachungsprozesses zu steuern und sicherzustellen, dass nur dann Maßnahmen ergriffen werden, wenn die entsprechenden Bedingungen erfüllt sind.

Auf globaler Ebene müssen wir noch die Mindestanzahl der zu berücksichtigenden Kerzen sowie die Bereichsgröße in Punkten festlegen. Wie schon im theoretischen Teil haben wir festgestellt, dass diese Parameter entscheidend für die Gültigkeit des Konsolidierungsbereichs sind und sicherstellen, dass wir sinnvolle Konsolidierungsbereiche haben. 

int rangeBars = 10; // Number of bars to consider for the range
int rangeSizePoints = 400; // Maximum range size in points

Auch hier deklarieren und initialisieren wir zwei Integer-Variablen, „rangeBars“ und „rangeSizePoints“. Die Variable „rangeBars“ ist auf 10 gesetzt, um die Anzahl der Balken (oder Kerzen) anzugeben, die wir zur Bestimmung des Konsolidierungsbereichs analysieren. Das bedeutet, dass wir auf die letzten 10 Balken zurückblicken, um das höchste Hoch und das niedrigste Tief zu finden, um unsere Spanne zu definieren. Die Variable „rangeSizePoints“ ist auf 400 gesetzt, was die maximal zulässige Größe des Konsolidierungsbereichs in Punkten angibt. Wenn die Spanne zwischen dem höchsten und dem niedrigsten Kurs innerhalb dieser 10 Balken 400 Punkte überschreitet, wird sie nicht als gültige Konsolidierungsspanne betrachtet. Diese Parameter sind für die Festlegung der Kriterien für die Spanne und die Ermittlung aussagekräftiger Konsolidierungszeiträume in den Preisdaten von wesentlicher Bedeutung. 

Da wir Positionen eröffnen werden, legen wir schließlich Stop-Loss und Take-Profit fest. 

double sl_points = 500.0; // Stop loss points
double tp_points = 500.0; // Take profit points

Das ist alles, was wir im globalen Bereich brauchen. Sie werden sich vielleicht fragen, was ein globaler Geltungsbereich ist. Ein globaler Bereich bezieht sich einfach auf einen Bereich eines Programms, in dem Variablen, Funktionen und andere Elemente über den gesamten Code hinweg zugänglich sind, außerhalb von Funktionen oder Blöcken. Wenn eine Variable oder Funktion im globalen Bereich deklariert wird, kann jeder Teil des Programms auf sie zugreifen und sie verändern. 

Alle unsere Aktivitäten werden beim OnTick-Ereignishandler ausgeführt. Dies wird eine reine Preisaktion sein und wir werden uns stark auf diesen Event-Handler verlassen. Werfen wir also einen Blick auf die Parameter, die die Funktion benötigt, denn sie sind das Herzstück dieses Codes.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
//---
   
}

Wie wir bereits gesehen haben, handelt es sich um eine einfache, aber wichtige Funktion, die keine Argumente benötigt und nichts zurückgibt. Es handelt sich lediglich um eine Funktion, die keinen Wert zurückgibt. Diese Funktion wird in Expert Advisors verwendet und wird ausgeführt, wenn es einen neuen Tick gibt, d. h. eine Änderung der Preisnotierungen für den jeweiligen Rohstoff.

Da wir nun gesehen haben, dass die Funktion OnTick bei jeder Änderung der Kursnotierungen generiert wird, müssen wir eine Kontrolllogik definieren, die es uns ermöglicht, den Code einmal pro Balken und nicht bei jedem Tick auszuführen, um zumindest unnötige Codeläufe zu vermeiden und somit den Gerätespeicher zu schonen. Dies wird bei der Suche nach Konsolidierungsbereichen notwendig sein. Wir brauchen nicht bei jedem Tick nach den Setups zu suchen, sondern erhalten immer die gleichen Ergebnisse, vorausgesetzt, wir befinden uns immer noch auf demselben Kerzen. Hier ist die Logik:

   int currBars = iBars(_Symbol,_Period); // Get the current number of bars
   static int prevBars = currBars; // Static variable to store the previous number of bars
   static bool isNewBar = false; // Static flag to check if a new bar has appeared
   if (prevBars == currBars){isNewBar = false;} // Check if the number of bars has not changed
   else {isNewBar = true; prevBars = currBars;} // If the number of bars has changed, set isNewBar to true and update prevBars

Zunächst deklarieren wir eine Integer-Variable „currBars“, die die berechnete Anzahl der aktuellen Balken auf dem Chart für das angegebene Handelssymbol und die Periode bzw. den Zeitrahmen speichert, wie Sie vielleicht schon gehört haben. Dies wird durch die Verwendung der Funktion iBars erreicht, die nur zwei Argumente benötigt, nämlich das Symbol und den Punkt. Zweitens deklarieren wir eine statische Integer-Variable „prevBars“ und initialisieren sie mit der aktuellen Anzahl der Balken. Mit dem Schlüsselwort static wird sichergestellt, dass die Variable „prevBars“ ihren Wert zwischen den Funktionsaufrufen beibehält, d. h. die Anzahl der Balken des vorherigen Ticks wird gespeichert. Drittens deklarieren wir eine statische boolesche Variable „isNewBar“ und initialisieren sie ebenfalls auf false. Anhand dieser Variable können wir feststellen, ob ein neuer Balken erschienen ist. Als Nächstes wird eine bedingte Anweisung verwendet, um zu prüfen, ob die aktuelle Anzahl der Balken gleich der vorherigen Anzahl der Balken ist. Wenn sie gleich sind, bedeutet das, dass sich kein neuer Balken gebildet hat, und wir setzen das Flag für die Erzeugung eines neuen Balkens auf false. Andernfalls, wenn die vorherigen Balken nicht mit den aktuellen Balken übereinstimmen, bedeutet dies, dass sich die Anzahl der Balken erhöht hat, was darauf hinweist, dass ein neuer Balken erschienen ist. Daher haben wir das Flag für die Erzeugung neuer Balken auf true gesetzt und den Wert der vorherigen Balken auf die aktuellen Balken aktualisiert.

Nun müssen wir für jeden generierten Balken, für den wir keinen Konsolidierungsbereich haben, die vordefinierten Balken nach einem potenziellen Zeitraum mit niedriger Volatilität durchsuchen. 

   if (isRangeExist == false && isNewBar){ // If no range exists and a new bar has appeared

      ...

   }

Wir prüfen, ob „isRangeExist“ falsch ist, was bedeutet, dass noch kein Bereich festgelegt wurde, und „isNewBar“ wahr ist, was bedeutet, dass ein neuer Balken erschienen ist. Dadurch wird sichergestellt, dass wir nur dann fortfahren, wenn noch kein Konsolidierungsbereich identifiziert wurde und sich ein neuer Balken gebildet hat.

Um die Koordinaten des ersten Punkts unseres Rechteckobjekts zu bestimmen, das auf dem Chart eingezeichnet werden soll, benötigen wir die Extremum-Resistance-Levels, d.h. den Zeitpunkt des letzten Balkens in unserem vordefinierten Balken-Scan-Bereich und den Preis des höchsten Balkens innerhalb des Bereichs.

      TIME1_X1 = iTime(_Symbol,_Period,rangeBars); // Get the start time of the range
      int highestHigh_BarIndex = iHighest(_Symbol,_Period,MODE_HIGH,rangeBars,1); // Get the bar index with the highest high in the range
      PRICE1_Y1 = iHigh(_Symbol,_Period,highestHigh_BarIndex); // Get the highest high price in the range

Zunächst legen wir die Startzeit des Bereichs fest, indem wir die Funktion iTime verwenden, die die Eröffnungszeit eines bestimmten Balkens für das angegebene Symbol und den angegebenen Zeitraum zurückgibt. Die Funktion benötigt drei Eingabeparameter oder Argumente, wobei _Symbol das Handelssymbol ist (z. B. „AUDUSD“), _Period der Zeitrahmen (z. B. PERIOD_M1 für 1-Minuten-Balken) und „rangeBars“ der Index des Balkens ist, der die angegebene Anzahl von Perioden zurückliegt. Das Ergebnis wird in „TIME1_X1“ gespeichert und markiert den Startzeitpunkt unseres Konsolidierungskreises.

Als Nächstes wird der Balken mit dem höchsten Kurs innerhalb des angegebenen Bereichs mit Hilfe der Funktion iHighest ermittelt, die den Index des Balkens mit dem höchsten Kurs innerhalb einer bestimmten Anzahl von Balken zurückgibt. Die Funktion benötigt fünf Argumente. Wir müssen nicht noch einmal erklären, was die ersten beiden Parameter bewirken, da wir dies bereits getan haben. Der dritte Parameter, „MODE_HIGH“, gibt an, dass wir nach dem höchsten Preis suchen. Das vierte Feld „rangeBars“ gibt die Anzahl der Balken an, die bei der Scan-Analyse berücksichtigt werden sollen, und schließlich bedeutet 1, dass wir mit der Suche ab dem Balken vor dem aktuellen Balken beginnen, der gerade gebildet wird. Technisch gesehen hat der sich gerade bildende Balken den Index 0 und der davor liegende Balken den Index 1. Der resultierende Index wird in der Integer-Variablen „highestHigh_BarIndex“ gespeichert.

Schließlich ermitteln wir den höchsten Preis dieses Balkens mit der Funktion iHigh, die den höchsten Preis eines bestimmten Balkens zurückgibt. Die Funktion benötigt drei Eingabeparameter, wobei die ersten beiden besonders einfach sind. Das dritte Argument, „highestHigh_BarIndex“, ist der Index des im vorherigen Schritt ermittelten Balkens. Der Höchstpreis wird in „PRICE1_Y1“ gespeichert. Diese Variablen ermöglichen es uns, den Startpunkt und den höchsten Punkt der Konsolidierungsspanne zu definieren, die für die Darstellung der Spanne und die spätere Erkennung von Ausbrüchen entscheidend sind.

Um die zweiten Koordinaten zu erhalten, wird ein ähnlicher Ansatz wie bei der Bestimmung der Ordinaten des ersten Punktes verwendet. 

      TIME2_Y2 = iTime(_Symbol,_Period,0); // Get the current time
      int lowestLow_BarIndex = iLowest(_Symbol,_Period,MODE_LOW,rangeBars,1); // Get the bar index with the lowest low in the range
      PRICE2_Y2 = iLow(_Symbol,_Period,lowestLow_BarIndex); // Get the lowest low price in the range

Der Unterschied im Code besteht darin, dass unsere Zeit zunächst mit dem aktuellen Balken verknüpft ist, der den Index 0 hat. Zweitens: Um den Index des niedrigsten Balken innerhalb des vordefinierten Balkenbereichs zu erhalten, verwenden wir die Funktion iLowest und geben mit „MODE_LOW“ an, dass wir nach dem Tiefstpreis suchen. Schließlich wird die Funktion iLow verwendet, um den Preis für den tiefsten Balken zu ermitteln. Kurz gesagt, hier ist eine Visualisierung der erforderlichen Koordinaten, wenn wir ein beliebiges Chart nehmen würden.

KOORDINATEN-VISUALISIERUNG

Nachdem wir nun die Punkte des Konsolidierungsbereichs haben, müssen wir ihre Gültigkeit überprüfen, um sicherzustellen, dass die Bedingungen für einen gültigen Bereich erfüllt sind, wie bereits im einleitenden Teil erwähnt, bevor sie im Chart berücksichtigt und gezeichnet werden.

      isInRange = (PRICE1_Y1 - PRICE2_Y2)/_Point <= rangeSizePoints; // Check if the range size is within the allowed points

Wir berechnen die Differenz zwischen dem höchsten Höchstpreis (PREIS1_Y1) und dem niedrigsten Tiefstpreis (PREIS2_Y2) innerhalb der Spanne und wandeln die Differenz dann in Punkte um, indem wir sie durch die Größe eines Punktes, _Point, teilen. Beispielsweise könnte der höchste Preiswert 0,66777 und der niedrigste Preiswert 0,66773 betragen. Ihre mathematische Differenz beträgt 0,66777 - 0,66773 = 0,00004. Der Punktwert des angenommenen Symbols würde 0,00001 betragen. Dividiert man nun das Ergebnis durch den Punktwert, ergibt sich 0,00004/0,00001 = 4 Punkte. Dieser Wert wird dann mit „rangeSizePoints“ verglichen, der die maximal zulässige Bereichsgröße in Punkten angibt.

Schließlich prüfen wir, ob ein gültiger Bereich identifiziert wurde, und wenn ja, zeichnen wir ihn in das Chart ein und informieren über die erfolgreiche Erstellung.

      if (isInRange){ // If the range size is valid
         plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // Plot the consolidation range
         isRangeExist = true; // Set the range exist flag to true
         Print("RANGE PLOTTED"); // Print a message indicating the range is plotted
      }

Hier wird geprüft, ob das ermittelte Konsolidierungsgebiet gültig ist, indem die Variable „isInRange“ ausgewertet wird. Wenn das Variablenflag wahr ist, was bedeutet, dass die Größe des Bereichs innerhalb akzeptabler Grenzen liegt, wird der Konsolidierungsbereich im Chart dargestellt. Für die Darstellung rufen wir die Funktion „plotConsolidationRange“ mit den Eingabeparametern „rangeNAME“, „TIME1_X1“, „PRICE1_Y1“, „TIME2_Y2“ und „PRICE2_Y2“ auf, die eine visuelle Darstellung des Bereichs erzeugt. Nachdem der Bereich erfolgreich gezeichnet wurde, setzen wir das Flag „isRangeExist“ auf true, um anzuzeigen, dass ein gültiger Bereich identifiziert und gezeichnet wurde. Zusätzlich wird die Meldung „RANGE PLOTTED“ zu Protokollierungszwecken auf dem Terminal ausgegeben, um zu bestätigen, dass das Konsolidierungsgebiet erfolgreich visualisiert wurde.

Der Codeschnipsel für die Funktion, die für das Plotten oder die Aktualisierung des Konsolidierungskreises zuständig ist, lautet wie folgt:

//+------------------------------------------------------------------+
//| Function to plot the consolidation range                         |
//| rangeName - name of the range object                             |
//| time1_x1 - start time of the range                               |
//| price1_y1 - high price of the range                              |
//| time2_x2 - end time of the range                                 |
//| price2_y2 - low price of the range                               |
//+------------------------------------------------------------------+
void plotConsolidationRange(string rangeName,datetime time1_x1,double price1_y1,
   datetime time2_x2,double price2_y2){
   if (ObjectFind(0,rangeName) < 0){ // If the range object does not exist
      ObjectCreate(0,rangeName,OBJ_RECTANGLE,0,time1_x1,price1_y1,time2_x2,price2_y2); // Create the range object
      ObjectSetInteger(0,rangeName,OBJPROP_COLOR,clrBlue); // Set the color of the range
      ObjectSetInteger(0,rangeName,OBJPROP_FILL,true); // Enable fill for the range
      ObjectSetInteger(0,rangeName,OBJPROP_WIDTH,5); // Set the width of the range
   }
   else { // If the range object exists
      ObjectSetInteger(0,rangeName,OBJPROP_TIME,0,time1_x1); // Update the start time of the range
      ObjectSetDouble(0,rangeName,OBJPROP_PRICE,0,price1_y1); // Update the high price of the range
      ObjectSetInteger(0,rangeName,OBJPROP_TIME,1,time2_x2); // Update the end time of the range
      ObjectSetDouble(0,rangeName,OBJPROP_PRICE,1,price2_y2); // Update the low price of the range
   }
   ChartRedraw(0); // Redraw the chart to reflect changes
}

Zunächst definieren wir eine void Funktion namens „plotConsolidationRange“ und übergeben ihr 5 Parameter oder Argumente, nämlich den Bereichsnamen, die 2-Koordinaten des ersten Punktes und die 2-Koordinaten des zweiten Punktes. Anschließend wird mit einer bedingten Anweisung geprüft, ob das Objekt existiert, indem die Funktion ObjectFind verwendet wird, die eine negative Ganzzahl zurückgibt, falls das Objekt nicht gefunden wird. Ist dies der Fall, wird das Objekt mit der Bezeichnung OBJ_RECTANGLE zur aktuellen Zeit und zu den angegebenen Preisen für die erste und zweite Koordinate erstellt. Anschließend werden die Farbe, die Flächenfüllung und die Breite festgelegt. Wenn das Objekt gefunden wird, aktualisieren wir einfach die Zeit und die Preise auf die angegebenen Werte und zeichnen das Chart neu, damit die aktuellen Änderungen übernommen werden. Der Modifikatorwert 0 wird verwendet, um auf die erste Koordinate zu zeigen, und 1 für die zweite Koordinate.

Das ist alles, was wir brauchen, um den identifizierten Konsolidierungsbereich auf dem Chart darzustellen. Der vollständige Quellcode, der dafür verantwortlich ist, lautet wie folgt:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
//---
   
   int currBars = iBars(_Symbol,_Period); // Get the current number of bars
   static int prevBars = currBars; // Static variable to store the previous number of bars
   static bool isNewBar = false; // Static flag to check if a new bar has appeared
   if (prevBars == currBars){isNewBar = false;} // Check if the number of bars has not changed
   else {isNewBar = true; prevBars = currBars;} // If the number of bars has changed, set isNewBar to true and update prevBars
   
   if (isRangeExist == false && isNewBar){ // If no range exists and a new bar has appeared
      TIME1_X1 = iTime(_Symbol,_Period,rangeBars); // Get the start time of the range
      int highestHigh_BarIndex = iHighest(_Symbol,_Period,MODE_HIGH,rangeBars,1); // Get the bar index with the highest high in the range
      PRICE1_Y1 = iHigh(_Symbol,_Period,highestHigh_BarIndex); // Get the highest high price in the range
      
      TIME2_Y2 = iTime(_Symbol,_Period,0); // Get the current time
      int lowestLow_BarIndex = iLowest(_Symbol,_Period,MODE_LOW,rangeBars,1); // Get the bar index with the lowest low in the range
      PRICE2_Y2 = iLow(_Symbol,_Period,lowestLow_BarIndex); // Get the lowest low price in the range
      
      isInRange = (PRICE1_Y1 - PRICE2_Y2)/_Point <= rangeSizePoints; // Check if the range size is within the allowed points
      
      if (isInRange){ // If the range size is valid
         plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // Plot the consolidation range
         isRangeExist = true; // Set the range exist flag to true
         Print("RANGE PLOTTED"); // Print a message indicating the range is plotted
      }
   }

}

Nach der Kompilierung erhalten wir folgende Ergebnisse:

EINGEZEICHNETER STATISCHER FÜLLBEREICH

Sie können sehen, dass wir den Bereich innerhalb der vordefinierten Punkte aufzeichnen und die Plotinstanz im Journal informieren. Wenn der Bereich nicht ausgefüllt werden soll, müssen Sie lediglich das Flag der Eigenschaft Füllen auf false setzen. Dadurch wird die Linieneigenschaft des Rechtecks gezeichnet, und die Breite wird aktiviert und wie definiert angewendet. Die Logik ist wie folgt:

      ObjectSetInteger(0,rangeName,OBJPROP_FILL,false); // Disable fill for the range

Daraus ergibt sich die folgende Spanne:

UNGEFÜLLTER BEREICH

Für den Artikel werden wir einen gefüllten Bereich verwenden. Jetzt, da wir sicher sind, dass wir einen Bereich festlegen können, müssen wir fortfahren und eine Logik entwickeln, die entweder auf Bereichsdurchbrüche und offene Positionen bzw. Bereichsausweitungen achtet und die Bereichskoordinaten auf die neuen Werte aktualisiert.

Als Nächstes identifizieren wir, wie im Theorieteil beschrieben, die Ausbruchsfälle, und wenn es einen solchen gibt, eröffnen wir entsprechende Marktpositionen. Da dies bei jedem Tick geschehen muss, verzichten wir auf die Einschränkung der neuen Balken. Zunächst geben wir Ask und Bid an, die wir zur Eröffnung der Positionen verwenden, sobald die jeweiligen Bedingungen erfüllt sind. Beachten Sie, dass dies auch bei jedem Tick geschehen muss, damit wir die neuesten Preisnotierungen erhalten.

   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); // Get and normalize the current Ask price
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); // Get and normalize the current Bid price

Hier deklarieren wir die Variablen vom Typ double für die Speicherung der aktuellen Preise und normalisieren sie auf die Ziffern der Symbolwährung, indem wir die Gleitkommazahl runden, um die Genauigkeit zu erhalten. 

Da wir bei jedem Tick nach Ausbrüchen suchen, benötigen wir eine Logik, die sicherstellt, dass wir das Programm nur belasten, wenn ein Bereich existiert und wir uns in diesem Bereich befinden. Die meiste Zeit wird sich der Markt in einer mittleren bis hohen Volatilität befinden, sodass die Häufigkeit des Auftretens der Konsolidierungsspanne begrenzt sein wird, und wenn die Spanne identifiziert wurde und innerhalb der Chartnähe liegt, wird der Preis schließlich aus der Spanne ausbrechen. Wenn eine dieser Bedingungen erfüllt ist, ist es nicht notwendig, auf Ausbrüche oder weitere Bereichsaktualisierungen zu achten. Wir warten einfach ab, bis ein anderer Bereich sich gebildet hat. 

   if (isRangeExist && isInRange){ // If the range exists and we are in range

      ...

   }

Hier wird geprüft, ob der Konsolidierungsbereich existiert „isRangeExist“ und ob der aktuelle Preis innerhalb dieses Bereichs liegt „isInRange“. Wenn beide Bedingungen erfüllt sind, berechnen wir die potenziellen Ausbruchskurse.

      double R_HighBreak_Prc = (PRICE2_Y2+rangeSizePoints*_Point); // Calculate the high breakout price
      double R_LowBreak_Prc = (PRICE1_Y1-rangeSizePoints*_Point); // Calculate the low breakout price

Um den höchsten Ausbruchskurs zu ermitteln, addieren wir die maximal zulässige Schwankungsbreite in Punkten zum niedrigsten Tiefstkurs. Diese Berechnung erfolgt durch Multiplikation der Bereichsgrößenpunkte mit der Punktgröße und Addition des Ergebnisses zur Variablen „PRICE2_Y2“, wobei der Endwert in einer Variablen vom Typ Double mit der Bezeichnung „R_HighBreak_Prc“ gespeichert wird. Nehmen wir zum Beispiel an, dass unser niedrigster Tiefstkurs 0,66773 ist, dann haben wir eine Bereichsgröße von 400 Punkten und einen Punktwert von 0,00001. Multipliziert man 400 mit 0,00001, erhält man (400 * 0,00001) 0,00400. Addiert man diesen Wert zum Preis, erhält man (0,66773 + 0,00400) 0,67173. Dieses endgültige Ergebnis ist der Wert, der im hohen Ausbruchspreis gespeichert wird, und wir werden diesen Wert zum Vergleich mit dem Marktpreis verwenden, um einen Break zu definieren, wenn der Angebotspreis über diesem Preis liegt. Um den unteren Ausbruchskurs zu bestimmen, ziehen wir die maximal zulässige Schwankungsbreite in Punkten vom höchsten Höchstkurs ab. Dazu werden die Punkte der Bereichsgröße mit der Punktgröße multipliziert und das Ergebnis vom höchsten Höchstkurs abgezogen, wobei der Endwert in der Variablen „R_LowBreak_Prc“ gespeichert wird.

Dann prüfen wir, ob ein Ausbruch erfolgt, und wenn ja, eröffnen wir die entsprechenden Positionen. Zunächst wollen wir uns mit der Situation befassen, in der der Marktpreis über den definierten hohen Ausbruchspreis bricht und damit eine Kaufgelegenheit signalisiert.

      if (Ask > R_HighBreak_Prc){ // If the Ask price breaks the high breakout price
         Print("BUY NOW, ASK = ",Ask,", L = ",PRICE2_Y2,", H BREAK = ",R_HighBreak_Prc); // Print a message to buy
         isInRange = false; isRangeExist = false; // Reset range flags
         if (PositionsTotal() > 0){return;} // Exit the function
         obj_Trade.Buy(0.01,_Symbol,Ask,Bid-sl_points*_Point,Bid+tp_points*_Point);
         return; // Exit the function
      }

Zunächst prüfen wir, ob der aktuelle Briefkurs größer ist als der hohe Ausbruchskurs. Wenn diese Bedingung erfüllt ist, deutet dies darauf hin, dass der Marktpreis die obere Grenze der Konsolidierungsspanne überschritten hat. Als Antwort wird eine Nachricht auf dem Terminal ausgedruckt, die das Ereignis protokolliert und den Kontext für das Kaufsignal liefert, indem der aktuelle Briefkurs, der niedrigste Tiefstkurs der Spanne und der höchste Ausbruchskurs angegeben werden. Anschließend setzen wir die Flags „isInRange“ und „isRangeExist“ auf „false“ zurück, was bedeutet, dass der aktuelle Konsolidierungskreis nicht mehr gültig ist und weitere Entscheidungen auf der Grundlage dieses Kreises verhindert werden. Als Nächstes prüfen wir mit der Funktion PositionsTotal, ob es bereits Positionen gibt, und beenden die Funktion vorzeitig, um zu vermeiden, dass mehrere Positionen gleichzeitig eröffnet werden. Wenn es keine bestehenden Positionen gibt, platzieren wir eine Kauforder mit der Methode „Buy“ des Objektes CTrade „obj_Trade“ und geben das Handelsvolumen, das Symbol, den Eröffnungskurs als Ask-Kurs, den Stop-Loss und den Take-Profit an. Schließlich beenden wir die Funktion, um den Handelsinitiierungsprozess abzuschließen und sicherzustellen, dass innerhalb dieses Ticks kein weiterer Code ausgeführt wird. 

Für den Fall, dass der Marktpreis unter den definierten niedrigen Ausbruchspreis fällt und damit eine Verkaufsmöglichkeit signalisiert, wird eine ähnliche Kontrolllogik angewendet, wie im folgenden Codeausschnitt dargestellt. 

      else if (Bid < R_LowBreak_Prc){ // If the Bid price breaks the low breakout price
         Print("SELL NOW"); // Print a message to sell
         isInRange = false; isRangeExist = false; // Reset range flags
         if (PositionsTotal() > 0){return;} // Exit the function
         obj_Trade.Sell(0.01,_Symbol,Bid,Ask+sl_points*_Point,Ask-tp_points*_Point);
         return; // Exit the function
      }

Nach dem Kompilieren ergibt sich folgendes Bild.

BESTÄTIGUNG DER KAUFPOSITION

Aus dem Bild ist ersichtlich, dass wir eine Kaufposition eröffnen, sobald wir aus der Handelsspanne, in diesem Fall aus dem Höchstkurs, ausbrechen, und zwar mit einem Stop-Loss und einem Take-Profit. Die Einstiegsbedingungen und Handelsniveaus sind völlig dynamisch, und Sie können ein Niveau wählen, das Sie für geeignet halten oder das Ihrem Handelsstil entspricht. So könnten Sie z. B. Niveaus innerhalb des Bereichs der Extremwerte oder des Risiko-Ertrags-Verhältnisses haben. Zur Bestätigung können Sie sehen, dass die Preisforderung 0,68313 und der Tiefstkurs 0,67911 beträgt, sodass der Höchstkurs (0,67911 + 0,00400) 0,68311 beträgt. Mathematisch gesehen liegt die aktuelle Preisforderung bei 0,68313 und damit über dem berechneten Höchstpreis von 0,68311, womit unsere Bedingungen für einen Ausbruch aus der hohen Handelsspanne erfüllt sind, was dazu führt, dass eine Kaufposition zum aktuellen Preisforderung eröffnet wird. 

Derzeit ist der Bereich statisch und bewegt sich nicht. Das heißt, das Rechteck ist fest. Sie sehen, dass das Bereichsobjekt nicht aktualisiert wird, auch wenn wir den Bereich korrekt festlegen. Wir müssen also die Spanne aktualisieren, wenn der Preis den definierten Objektbereichspreis überschreitet. Um dem Rechteck Leben einzuhauchen, sollten wir uns eine Logik überlegen, die die Erweiterung des Bereichs immer um die erzeugten Balken aktualisiert. Betrachten wir zunächst das Szenario, in dem der aktuelle Briefkurs den zuvor verzeichneten Höchstkurs innerhalb der Konsolidierungsspanne übersteigt. Wenn diese Bedingung erfüllt ist, bedeutet dies, dass die obere Grenze der Konsolidierungsspanne aktualisiert werden muss, um den neuen Höchstpreis widerzuspiegeln. Dies wird mit dem folgenden Codeschnipsel erreicht.

      if (Ask > PRICE1_Y1){ // If the Ask price is higher than the current high price
         PRICE1_Y1 = Ask; // Update the high price to the Ask price
         TIME2_Y2 = iTime(_Symbol,_Period,0); // Update the end time to the current time
         Print("UPDATED RANGE PRICE1_Y1 TO ASK, NEEDS REPLOT"); // Print a message indicating the range needs to be replotted
         plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // Replot the consolidation range
      }

Liegt der Angebotspreis über dem zuvor aufgezeichneten Höchstpreis, setzen wir den „PRICE1_Y1“ auf den aktuellen Briefkurs. Gleichzeitig aktualisieren wir die Endzeit des Bereichs „TIME2_Y2“ auf die aktuelle Zeit, die wir mit der Funktion iTime erhalten, indem wir den Index des Zielbalkens als aktuellen Balken, 0, übergeben. Um den Überblick über diese Anpassungen zu behalten und für Klarheit zu sorgen, drucken wir eine Meldung auf dem Terminal aus, die besagt, dass der Bereich aktualisiert wurde und neu gezeichnet werden muss. Anschließend rufen wir die Funktion „plotConsolidationRange“ mit den aktualisierten Parametern auf, einschließlich des neuen Höchstkurses und der aktuellen Zeit, um die Änderungen im Chart visuell darzustellen.

Ein ähnlicher Ansatz gilt für den Fall, dass der aktuelle Geldkurs unter den zuvor erfassten niedrigsten Kurs innerhalb der Konsolidierungsspanne fällt, was ein Hinweis darauf ist, dass die untere Grenze der Konsolidierungsspanne aktualisiert werden muss, um den neuen niedrigsten Kurs widerzuspiegeln.

      else if (Bid < PRICE2_Y2){ // If the Bid price is lower than the current low price
         PRICE2_Y2 = Bid; // Update the low price to the Bid price
         TIME2_Y2 = iTime(_Symbol,_Period,0); // Update the end time to the current time
         Print("UPDATED RANGE PRICE2_Y2 TO BID, NEEDS REPLOT"); // Print a message indicating the range needs to be replotted
         plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // Replot the consolidation range
      }

Um diese Änderungen nachzuvollziehen, nehmen wir einen Fall, in dem wir keine Aktualisierungen haben, und einen, in dem wir die Aktualisierungen in einem Graphics Interchange Format (GIF) haben, um einen einfachen Vergleich zu ermöglichen.

Vor der Aktualisierung:

VOR DEM UPDATE

Nach der Aktualisierung:

NACH DER AKTUALISIERUNG

Schließlich könnte es noch ein Szenario geben, in dem weder der Briefkurs den Höchstkurs übersteigt noch der Geldkurs unter den Tiefstkurs fällt. Wenn dies der Fall ist, müssen wir den Konsolidierungsbereich noch erweitern, um den letzten abgeschlossenen Balken einzuschließen. Nachfolgend finden Sie ein Codefragment, das diese Aufgabe übernimmt.

      else{
         if (isNewBar){ // If a new bar has appeared
            TIME2_Y2 = iTime(_Symbol,_Period,1); // Update the end time to the previous bar time
            Print("EXTEND THE RANGE TO PREV BAR TIME"); // Print a message indicating the range is extended
            plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // Replot the consolidation range
         }
      }

Mit dem Flag „isNewBar“ wird überprüft, ob ein neuer Balken erschienen ist. Wenn tatsächlich ein neuer Balken erschienen ist, aktualisieren wir die Endzeit des Konsolidierungsbereichs „TIME2_Y2“ auf die Zeit des vorhergehenden Balkens, die wir mit der Funktion iTime erhalten, wobei wir den Index des Zielbalkens mit 1 angeben, d.h. den Balken, der dem aktuellen Balken vorausgeht. Um die Übersichtlichkeit zu gewährleisten und diese Anpassung zu verfolgen, geben wir eine Meldung an das Terminal aus, die besagt, dass die Endzeit des Bereichs auf die Zeit des vorhergehenden Balkens erweitert wurde. Anschließend rufen wir die Funktion „plotConsolidationRange“ mit den aktualisierten Parametern, einschließlich der neuen Endzeit, auf, um die Änderungen im Chart visuell darzustellen.

Hier ist eine Illustration des Meilensteins „In-Range-Update“.

IN-RANGE UPDATE

Der vollständige Quellcode, der für die Erstellung des auf der Consolidation Range Breakout-Strategie basierenden Expert Advisor (EA) in MQL5 verantwortlich ist, wird im Folgenden bereitgestellt:

//+------------------------------------------------------------------+
//|                                 CONSOLIDATION RANGE BREAKOUT.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"


#include <Trade/Trade.mqh> // Include the trade library
CTrade obj_Trade; // Create an instance of the CTrade class

#define rangeNAME "CONSOLIDATION RANGE" // Define the name of the consolidation range

datetime TIME1_X1, TIME2_Y2; // Declare datetime variables to hold range start and end times
double PRICE1_Y1, PRICE2_Y2; // Declare double variables to hold range high and low prices

bool isRangeExist = false; // Flag to check if the range exists
bool isInRange = false; // Flag to check if we are currently within the range

int rangeBars = 10; // Number of bars to consider for the range
int rangeSizePoints = 400; // Maximum range size in points

double sl_points = 500.0; // Stop loss points
double tp_points = 500.0; // Take profit points

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
//---
   // Initialization code here (we don't initialize anything)
//---
   return(INIT_SUCCEEDED); // Return initialization success
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
//---
   // Deinitialization code here (we don't deinitialize anything)
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
//---
   
   int currBars = iBars(_Symbol,_Period); // Get the current number of bars
   static int prevBars = currBars; // Static variable to store the previous number of bars
   static bool isNewBar = false; // Static flag to check if a new bar has appeared
   if (prevBars == currBars){isNewBar = false;} // Check if the number of bars has not changed
   else {isNewBar = true; prevBars = currBars;} // If the number of bars has changed, set isNewBar to true and update prevBars
   
   if (isRangeExist == false && isNewBar){ // If no range exists and a new bar has appeared
      TIME1_X1 = iTime(_Symbol,_Period,rangeBars); // Get the start time of the range
      int highestHigh_BarIndex = iHighest(_Symbol,_Period,MODE_HIGH,rangeBars,1); // Get the bar index with the highest high in the range
      PRICE1_Y1 = iHigh(_Symbol,_Period,highestHigh_BarIndex); // Get the highest high price in the range
      
      TIME2_Y2 = iTime(_Symbol,_Period,0); // Get the current time
      int lowestLow_BarIndex = iLowest(_Symbol,_Period,MODE_LOW,rangeBars,1); // Get the bar index with the lowest low in the range
      PRICE2_Y2 = iLow(_Symbol,_Period,lowestLow_BarIndex); // Get the lowest low price in the range
      
      isInRange = (PRICE1_Y1 - PRICE2_Y2)/_Point <= rangeSizePoints; // Check if the range size is within the allowed points
      
      if (isInRange){ // If the range size is valid
         plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // Plot the consolidation range
         isRangeExist = true; // Set the range exist flag to true
         Print("RANGE PLOTTED"); // Print a message indicating the range is plotted
      }
   }
   
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); // Get and normalize the current Ask price
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); // Get and normalize the current Bid price
   
   if (isRangeExist && isInRange){ // If the range exists and we are in range
      double R_HighBreak_Prc = (PRICE2_Y2+rangeSizePoints*_Point); // Calculate the high breakout price
      double R_LowBreak_Prc = (PRICE1_Y1-rangeSizePoints*_Point); // Calculate the low breakout price
      if (Ask > R_HighBreak_Prc){ // If the Ask price breaks the high breakout price
         Print("BUY NOW, ASK = ",Ask,", L = ",PRICE2_Y2,", H BREAK = ",R_HighBreak_Prc); // Print a message to buy
         isInRange = false; isRangeExist = false; // Reset range flags
         if (PositionsTotal() > 0){return;} // Exit the function
         obj_Trade.Buy(0.01,_Symbol,Ask,Bid-sl_points*_Point,Bid+tp_points*_Point);
         return; // Exit the function
      }
      else if (Bid < R_LowBreak_Prc){ // If the Bid price breaks the low breakout price
         Print("SELL NOW"); // Print a message to sell
         isInRange = false; isRangeExist = false; // Reset range flags
         if (PositionsTotal() > 0){return;} // Exit the function
         obj_Trade.Sell(0.01,_Symbol,Bid,Ask+sl_points*_Point,Ask-tp_points*_Point);
         return; // Exit the function
      }
      
      if (Ask > PRICE1_Y1){ // If the Ask price is higher than the current high price
         PRICE1_Y1 = Ask; // Update the high price to the Ask price
         TIME2_Y2 = iTime(_Symbol,_Period,0); // Update the end time to the current time
         Print("UPDATED RANGE PRICE1_Y1 TO ASK, NEEDS REPLOT"); // Print a message indicating the range needs to be replotted
         plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // Replot the consolidation range
      }
      else if (Bid < PRICE2_Y2){ // If the Bid price is lower than the current low price
         PRICE2_Y2 = Bid; // Update the low price to the Bid price
         TIME2_Y2 = iTime(_Symbol,_Period,0); // Update the end time to the current time
         Print("UPDATED RANGE PRICE2_Y2 TO BID, NEEDS REPLOT"); // Print a message indicating the range needs to be replotted
         plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // Replot the consolidation range
      }
      else{
         if (isNewBar){ // If a new bar has appeared
            TIME2_Y2 = iTime(_Symbol,_Period,1); // Update the end time to the previous bar time
            Print("EXTEND THE RANGE TO PREV BAR TIME"); // Print a message indicating the range is extended
            plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // Replot the consolidation range
         }
      }
      
   }
   
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Function to plot the consolidation range                         |
//| rangeName - name of the range object                             |
//| time1_x1 - start time of the range                               |
//| price1_y1 - high price of the range                              |
//| time2_x2 - end time of the range                                 |
//| price2_y2 - low price of the range                               |
//+------------------------------------------------------------------+
void plotConsolidationRange(string rangeName,datetime time1_x1,double price1_y1,
   datetime time2_x2,double price2_y2){
   if (ObjectFind(0,rangeName) < 0){ // If the range object does not exist
      ObjectCreate(0,rangeName,OBJ_RECTANGLE,0,time1_x1,price1_y1,time2_x2,price2_y2); // Create the range object
      ObjectSetInteger(0,rangeName,OBJPROP_COLOR,clrBlue); // Set the color of the range
      ObjectSetInteger(0,rangeName,OBJPROP_FILL,true); // Enable fill for the range
      ObjectSetInteger(0,rangeName,OBJPROP_WIDTH,5); // Set the width of the range
   }
   else { // If the range object exists
      ObjectSetInteger(0,rangeName,OBJPROP_TIME,0,time1_x1); // Update the start time of the range
      ObjectSetDouble(0,rangeName,OBJPROP_PRICE,0,price1_y1); // Update the high price of the range
      ObjectSetInteger(0,rangeName,OBJPROP_TIME,1,time2_x2); // Update the end time of the range
      ObjectSetDouble(0,rangeName,OBJPROP_PRICE,1,price2_y2); // Update the low price of the range
   }
   ChartRedraw(0); // Redraw the chart to reflect changes
}


Backtest-Ergebnisse

Nach einem Test mit dem Strategietester sind die Ergebnisse wie folgt.

  • Salden-/Kapitalkurve:

GRAPH

  • Backtest-Ergebnisse:

ERGEBNISSE

  • Handelsbuchungen nach Perioden:

HANDELSBUCHUNGEN NACH ZEITRÄUMEN

Schlussfolgerung

Zusammenfassend können wir sagen, dass die Automatisierung der Consolidation-Breakout-Strategie gar nicht so kompliziert ist, wie man denkt, wenn man sich die nötigen Gedanken macht. Technisch gesehen erfordert ihre Erstellung lediglich ein klares Verständnis der Strategie und der tatsächlichen Anforderungen bzw. der Ziele, die erfüllt werden müssen, um eine gültige Strategie einzurichten. 

Insgesamt hebt der Artikel den theoretischen Teil hervor, der berücksichtigt und klar verstanden werden muss, um eine Consolidation Range Breakout Forex-Handelsstrategie zu erstellen. Dazu gehören neben dem Entwurf auch die Definition und die Übersicht. Darüber hinaus hebt der Kodierungsaspekt der Strategie die Schritte hervor, die unternommen werden, um die Kerzen zu analysieren, Perioden mit niedriger Volatilität zu identifizieren, die Unterstützungs- und Widerstandsniveaus für die Perioden zu ermitteln, ihre Ausbrüche zu verfolgen, ihre Ergebnisse zu visualisieren und Handelspositionen auf der Grundlage der erzeugten Signale zu eröffnen. Langfristig ermöglicht dies die Automatisierung der Consolidation Range Breakout-Strategie, was eine schnellere Ausführung und die Skalierbarkeit der Strategie erleichtert.

Haftungsausschluss: Die in diesem Artikel dargestellten Informationen dienen nur zu Lehrzwecken. Er soll lediglich Einblicke in die Erstellung eines Consolidation Range Breakout Expert Advisors (EA) auf Basis des Price-Action-Ansatzes geben und somit als Grundlage für die Erstellung eines besseren Expert Advisors mit mehr Optimierung und Datenextraktion dienen. Die dargestellten Informationen garantieren keine positiven Handelsergebnisse.

Wir hoffen, dass Sie den Artikel hilfreich, unterhaltsam und leicht verständlich fanden, sodass Sie das präsentierte Wissen bei der Entwicklung zukünftiger Expertenberater nutzen können. Aus technischer Sicht erleichtert dies die Analyse des Marktes auf der Grundlage des Price-Action-Ansatzes und insbesondere der Consolidation-Range-Breakout-Strategie. Viel Vergnügen!


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

Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
Einführung in MQL5 (Teil 8): Leitfaden für Einsteiger zur Erstellung von Expert Advisors (II) Einführung in MQL5 (Teil 8): Leitfaden für Einsteiger zur Erstellung von Expert Advisors (II)
Dieser Artikel behandelt häufige Anfängerfragen aus MQL5-Foren und zeigt praktische Lösungen auf. Lernen Sie, grundlegende Aufgaben wie Kaufen und Verkaufen, die Kursabfrage der Kerzen und die Verwaltung automatisierter Handelsaspekte wie Handelslimits, Handelszeiträume und Gewinn-/Verlustschwellen durchzuführen. Erhalten Sie eine schrittweise Anleitung, um Ihr Verständnis und Ihre Implementierung dieser Konzepte in MQL5 zu verbessern.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Portfolio-Optimierung in Python und MQL5 Portfolio-Optimierung in Python und MQL5
Dieser Artikel befasst sich mit fortgeschrittenen Portfolio-Optimierungstechniken unter Verwendung von Python und MQL5 mit MetaTrader 5. Es wird gezeigt, wie Algorithmen für die Datenanalyse, die Vermögensallokation und die Generierung von Handelssignalen entwickelt werden können, wobei die Bedeutung datengestützter Entscheidungsfindung im modernen Finanzmanagement und bei der Risikominderung hervorgehoben wird.