English 日本語
preview
Eine Schritt-für-Schritt-Anleitung zum Handel mit der Break of Structure (BoS)-Strategie

Eine Schritt-für-Schritt-Anleitung zum Handel mit der Break of Structure (BoS)-Strategie

MetaTrader 5Handel | 5 Juli 2024, 10:40
338 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In diesem Artikel werden wir den Break of Structure (BoS), einen Begriff, der eine signifikante Änderung des Trends oder der Richtung des Marktes, der Forex-Handelsstrategie, im Zusammenhang mit dem Smart Money Concept (SMC) und der Erstellung eines darauf basierenden Expert Advisors (EA) beschreibt.

Wir werden die Definition, die Typen, die Anwendungen der Handelsstrategien und die Entwicklung in MetaQuotes Language 5 (MQL5) für MetaTrader 5 (MT5) erkunden, während wir uns mit den Nuancen des Strukturbruchs beschäftigen. Der Begriff „Break of Structure“ ist ein nützliches Instrument für Händler, das sie erlernen können, um ihre Fähigkeit zur Vorhersage von Marktbewegungen zu verbessern, bessere Entscheidungen zu treffen und schließlich das Risikomanagement zu beherrschen. Mit Hilfe der folgenden Themen werden wir das oben genannte Ziel erreichen:

  1. Definition des Break of Structure (BoS)
  2. Beschreibung des Break of Structure (BoS)
  3. Arten von Break of Structure (BoS)
  4. Beschreibung der Handelsstrategie
  5. Entwurf der Handelsstrategie
  6. Implementierung in MetaQuotes Language 5 (MQL5)
  7. Ergebnisse im Strategietester
  8. Schlussfolgerung

Auf dieser Reise werden wir ausgiebig MetaQuotes Language 5 (MQL5) als unsere Basis-IDE-Codierungsumgebung verwenden und die Dateien auf dem MetaTrader 5 (MT5) Handels-Terminal ausführen. Daher ist es von größter Bedeutung, dass die oben genannten Versionen vorhanden sind. Fangen wir also an.


Definition des Break of Structure (BoS)

Der Strukturbruch (Break of Structure, BoS) ist ein Schlüsselkonzept in der technischen Analyse, das Smart Money Concepts (SMCs) nutzt, um signifikante Veränderungen in Markttrends oder -richtungen zu identifizieren. Er tritt in der Regel dann auf, wenn der Kurs sich entscheidend durch Swing-Tiefs oder Swing-Hochs bewegt, die durch frühere Kursbewegungen festgelegt wurden. Wenn die Kurse über die Swing-Hochs steigen oder unter die Swing-Tiefs fallen, durchbrechen sie einfach die zuvor gebildete Marktstruktur, daher auch der Name „Break“ (Durchbruch) der Struktur. Dies deutet in der Regel auf eine Änderung der Marktstimmung und der Trendrichtung hin und signalisiert eine Fortsetzung des bestehenden Trends oder den Beginn eines neuen Trends.


Beschreibung des Break of Structure (BoS)

Um einen Strukturbruch (BoS) effektiv zu beschreiben, müssen wir ihn zunächst von den anderen Elementen des Smart-Money-Konzepts abgrenzen, d. h. von der Marktstrukturverschiebung (MSS) und der Veränderung des Charakters (Change of Character, CHoCH).

  • Market Structure Shift (MSS)

Eine Marktstrukturverschiebung, den Sie vielleicht auch als Market Momentum Shift (MMS) kennen, tritt auf, wenn der Kurs das jüngste Hoch in einem Abwärtstrend oder umgekehrt das jüngste Hoch in einem Aufwärtstrend durchbricht, ohne zuvor das jüngste Swing-Tief bzw. Swing-Hoch zu durchbrechen. Dies bedeutet eine Trendumkehr aufgrund der Veränderung der Struktur, daher der Name „Verschiebung“ der Marktstruktur.

Marktstrukturverschiebung

  • Änderung des Charakters (CHoCH)

Eine Änderung des Charakters tritt hingegen ein, wenn der Kurs das jüngste Hoch in einem Abwärtstrend durchbricht, nachdem er zuvor das jüngste Swing-Tief durchbrochen hat, oder wenn der Kurs das jüngste Tief in einem Aufwärtstrend durchbricht, nachdem er zuvor das jüngste Swing-Hoch durchbrochen hat.

Change of Character

  • Bruch der Struktur (BoS)

Nachdem wir nun die wichtigsten Unterschiede zwischen den drei Hauptelementen des auf der Marktstruktur basierenden Ansatzes des Smart-Money-Konzepts kennen, wollen wir uns nun dem Hauptthema des Artikels widmen, nämlich seiner Auflösung. Aus der vorangegangenen Definition sollten Sie ersehen haben, dass ein Strukturbruch bedeutet, dass alte Höchst- oder Tiefststände durchbrochen werden, um neue Höchst- bzw. Tiefststände zu erreichen. Jeder Strukturbruch unterstützt den Markttrend nach oben, indem er ein neues höheres Hoch (HH) und ein neues höheres Tief (HL) bildet, oder nach unten, indem er ein neues niedrigeres Hoch (LH) und ein neues niedrigeres Tief (LL) bildet, was üblicherweise als Swing-Hoch und Swing-Tief des Preises beschrieben wird.

Break of Structure

Es gilt nur eine Regel: Der Ausbruch muss mit dem Schlusskurs der Kerze erfolgen. Das bedeutet, dass im Falle eines Ausbruchs des Swing-Hochs der Schlusskurs über dem Swing-Punkt liegen sollte, während im Falle eines Ausbruchs des Swing-Tiefs der Schlusskurs ebenfalls unter dem Swing-Punkt liegen sollte. Einfach ausgedrückt, gelten nur Ausbrüche an Kerzen- oder Balkenkörpern als gültige Strukturbrüche, was bedeutet, dass Brüche durch die Kerzenschatten oder -dochte als ungültige Strukturbrüche betrachtet werden.

  • Ungültige BoS-Setups:

Ungültige BoS

  • Gültige BoS-Setups:

Gültige BoS


Arten von Strukturbrüchen

Wie bereits gesagt, treten Strukturbrüche in Trendmärkten auf, d. h. sie treten entweder in Aufwärts- oder Abwärtstrends auf. Dies legt bereits nahe, dass es nur zwei Arten von Strukturbrüchen gibt.

  • Aufwärts Break of Structure

Diese treten in Aufwärtstrends auf, die durch höhere Hochs (HH) und höhere Tiefs (HL) gekennzeichnet sind. Aus technischer Sicht ergibt sich ein Break of Structure daraus, dass der Kurs das jüngste höhere Hoch im Aufwärtstrend durchbricht und ein neues höheres Hoch bildet.

Aufwärts BoS

  • Abwärts Break of Structure

Hier kommt es in Abwärtstrends zu einem Abwärts-Strukturbruch, der sich aus niedrigeren Tiefs (LL) und niedrigeren Hochs (LH) zusammensetzt. Ein Break of Structure resultiert daraus, dass der Preis das jüngste untere Tief des Abwärtstrends durchbricht und ein neues unteres Tief bildet.

Abwärts BoS


Beschreibung der Handelsstrategie

Um mit dieser Strategie effektiv zu handeln, sind eine Reihe von Schritten erforderlich, aber keine Sorge. Wir werden sie Schritt für Schritt behandeln. 

Basis auf höheren Zeitrahmen (HTF) legen: Für eine umfassende Analyse sollten Sie erstens höhere Zeitrahmen für einen ausgewählten Vermögenswert untersuchen, da dies einen Gesamtüberblick über die Markttrends bietet. Dazu kann ein Vierstunden- oder Tages-Zeitrahmen gehören, da diese in der Regel die langfristige Entwicklung des Marktes erkennen lassen. Wir vermeiden es, einen niedrigeren Zeitrahmen zu verwenden, da dieser aufgrund von Manipulationen, Liquiditätsschwankungen und Zickzackkursen wie ein beschwipster Autofahrer viele Swing-Punkte enthält, was zu mehr unbedeutenden Brüchen führt.

Ermitteln Sie den zugrunde liegenden Markttrend: Zweitens müssen Sie den aktuellen Markttrend in Ihrem Chart erkennen. Aufwärtstrends bestehen aus Mustern höherer Hochs und höherer Tiefs im Kursgeschehen, während Abwärtstrends aus Mustern niedrigerer Tiefs und niedrigerer Hochs bestehen.

Identifizieren Sie Einstiegspunkte: Nachdem Sie den aktuellen Trend auf einem höheren Zeitrahmen identifiziert haben, können Sie in den Markt eindringen, wenn ein Swing-Hoch oder ein Swing-Tief durchbrochen wird, das mit dem Körper der durchbrechenden Kerze schließt. Je stärker die Kerze ist, desto beruhigender ist die Signalbestätigung.

Beispiel für einen Aufwärtstrend:

Beispiel für einen Aufwärtstrend

Beispiel für einen Abwärtstrend:

Beispiel für einen Abwärtstrend

Bei einem kleineren Zeitrahmen, z. B. einem Fünf-Minuten-Zeitrahmen, können Sie zusätzliche Bestätigungsstrategien wie Angebot und Nachfrage, technische Indikatoren wie den Relative Strength Index (RSI) und den MACD (Moving Average Convergence Divergence) oder japanische Kerzen-Muster wie Engulfing oder Inside-Bar-Muster verwenden.

Identifizieren der Ausstiegspunkte: Nach dem Markteintritt brauchen wir eine solide Strategie für den Marktaustritt, wobei wir unsere Risiken beherrschen müssen. Den Stop-Loss setzen wir an den vorherigen Swing-Punkt, vorausgesetzt, er liegt in der Nähe des Einstiegspunkts der Position und lässt uns eine sinnvolle Gewinnmöglichkeit. Wenn das nicht der Fall ist, verwenden wir ein Risiko-Ertrags-Verhältnis von festen Pips. Umgekehrt nehmen wir den Gewinn am nächsten Swing-Punkt mit, aber da es schwierig ist, den zukünftigen Swing-Punkt für das Take-Profit-Niveau zu bestimmen, verwenden wir das Risiko-Ertrags-Verhältnis als Anhaltspunkt für den Take-Profit.

Entwurf der Handelsstrategie

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

Aufwärts Break of Structure:

Aufwärts BoS Blaupause

Abwärts Break of Structure:

Abwärts BoS Blaupause


Implementierung in MetaQuotes Language 5 (MQL5) für MetaTrader 5 (MT5)

Nach dem Erlernen aller Theorien über die Break of Structure Handelsstrategie, lassen Sie uns dann die Theorie zu automatisieren und Handwerk ein Expert Advisor (EA) in MetaQuotes Language 5 (MQL5) für MetaTrader 5.

Um einen EA zu erstellen, klicken Sie auf Ihrem MetaTrader 5-Terminal auf die Registerkarte Tools und wählen Sie MetaQuotes Language Editor oder drücken Sie einfach F4 auf Ihrer Tastatur. Dadurch wird die MetaQuotes-Spracheditor-Umgebung geöffnet, die das Schreiben von Handelsrobotern, technischen Indikatoren, Skripten und Funktionsbibliotheken ermöglicht.

MetaQuotes öffnen

Sobald der MetaEditor geöffnet ist, klicken Sie auf Neu, markieren Sie in dem sich öffnenden Assistenten Expert Advisor (Vorlage) und klicken Sie auf Weiter.

Erstellen einer neuen EA-Datei

Angabe des Dateinamens

Geben Sie dann den gewünschten Namen der Expert Advisor-Datei ein, klicken Sie auf Weiter, klicken Sie auf Weiter und dann auf Fertig stellen. Nachdem wir all das getan haben, sind wir nun bereit, unsere Strategie für den Strukturbruch zu codieren und zu 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>
CTrade obj_Trade;

Die meisten unserer Aktivitäten werden in OnTick ausgeführt. Da es sich um eine reine Preisaktion handelt, brauchen wir OnInit für die Initialisierung des Indikator-Handles nicht zu verwenden. Der gesamte Code wird also nur im OnTick ausgeführt. Werfen wir zunächst einen Blick auf die Parameter, die die Funktion neben ihren Funktionen benötigt, denn sie sind das Herzstück dieses Codes:

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.

Nachdem wir nun gesehen haben, dass die Funktion OnTick bei jeder Änderung der Kursnotierungen aufgerufen wird, müssen wir eine Steuerlogik definieren, die wir später verwenden werden, um die Ausführung bestimmter Codeschnipsel zu steuern, sodass sie einmal pro Balken und nicht bei jedem Tick ausgeführt werden, um zumindest unnötige Codeläufe zu vermeiden und somit den Gerätespeicher zu schonen. Das ist notwendig, wenn man nach Hochs und Tiefs sucht. Wir brauchen nicht nach jedem Tick zu suchen und erhalten dennoch immer die gleichen Ergebnisse, vorausgesetzt, wir befinden uns immer noch auf derselben Kerze Hier ist die Logik:

   static bool isNewBar = false;
   int currBars = iBars(_Symbol,_Period);
   static int prevBars = currBars;
   if (prevBars == currBars){isNewBar = false;}
   else if (prevBars != currBars){isNewBar = true; prevBars = currBars;}

Zunächst deklarieren wir eine statische boolesche Variable namens „isNewBar“ und initialisieren sie mit dem Wert „false“. Der Zweck dieser Variable ist es, zu verfolgen, ob sich ein neuer Balken im Chart gebildet hat. Wir deklarieren die lokale Variable mit dem Schlüsselwort „static“, damit sie ihren Wert während der gesamten Lebensdauer der Funktion beibehalten kann. Das bedeutet, dass sie nicht dynamisch sein wird. Normalerweise ist unsere Variable immer „false“, es sei denn, wir ändern sie später in „true“, und wenn sie geändert wird, behält sie ihren Wert und wird beim nächsten Tick nicht aktualisiert, im Gegensatz zu einer dynamischen Variable, bei der sie immer auf den Initialisierungswert aktualisiert wird.

Dann deklarieren wir eine weitere 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.

Auch hier deklarieren wir eine weitere statische Integer-Variable „prevBars“, um die Gesamtzahl der vorherigen Balken im Diagramm zu speichern, wenn ein neuer Balken generiert wird, und initialisieren sie mit dem Wert der aktuellen Balken im Chart für den ersten Lauf der Funktion. Wir werden ihn verwenden, um die aktuelle Anzahl der Balken mit der vorherigen Anzahl der Balken zu vergleichen, um den Zeitpunkt einer neuen Balkenerzeugung im Chart zu bestimmen.

Schließlich wird mit einer bedingten Anweisung geprüft, ob die aktuelle Anzahl der Kerzen gleich der vorherigen Anzahl der Kerzen ist. Wenn sie gleich sind, bedeutet dies, dass sich kein neuer Balken gebildet hat, sodass die Variable „isNewBar“ falsch bleibt. Andernfalls, wenn die Zählung des aktuellen und des vorherigen Balkens nicht gleich ist, bedeutet dies, dass sich ein neuer Balken gebildet hat. In diesem Fall setzen wir die Variable „isNewBar“ auf „true“ und aktualisieren die Variable „prevBars“, damit sie mit der aktuellen Balkenanzahl übereinstimmt. Mit diesem Codeschnipsel können wir also verfolgen, ob sich ein neuer Balken gebildet hat, und das Ergebnis später verwenden, um sicherzustellen, dass wir eine Instanz nur einmal pro Balken ausführen.

Jetzt können wir nach Swing-Punkten in unserem Chart suchen. Wir benötigen eine Reihe von Scans für die Punkte. Wir wollen dies erreichen, indem wir einen bestimmten Balken auswählen und alle benachbarten Balken rechts und links, natürlich innerhalb der vordefinierten Spanne von Balken, scannen und feststellen, ob der aktuelle Balken der höchste innerhalb der Spanne ist, wenn es sich um ein Swing-Hoch handelt, oder der niedrigste, wenn es sich um ein Swing-Tief handelt. Definieren wir also zunächst die Variablen, die wir zum Speichern dieser Logik benötigen.

   const int length = 20;
   const int limit = 20;

Hier deklarieren wir zwei Integer-Variablen „length“ und „limit“. Die Länge gibt den Bereich der Balken an, der bei der Identifizierung der Hoch- und Tiefpunkte des Swings berücksichtigt werden soll, während das Limit den Index des aktuellen Balkens angibt, der zu diesem Zeitpunkt gescannt wird. Nehmen wir zum Beispiel an, dass wir einen Balken bei Index 10 ausgewählt haben, um zu prüfen, ob es sich um ein Swing-Hoch handelt. Anschließend wird eine Schleife über alle benachbarten Balken rechts und links gezogen, um festzustellen, ob es einen weiteren Balken gibt, der höher ist als der aktuelle Balken, der bei Index 10 liegt. Der linke Balken ist also der Balken vor dem aktuellen Balken und befindet sich somit am Index (Grenzwert, der gleich 10 ist, + 1) 11. Der gleiche Fall tritt ein, wenn man sich nach rechts bewegt.

Standardmäßig werden die Variablen mit 20 initialisiert. Außerdem sollten Sie bemerkt haben, dass wir sie als „const“ deklarieren, um sie konstant zu machen. Dadurch wird sichergestellt, dass ihr Wert während der gesamten Programmausführung konstant bleibt, was zu einer Konsistenz führt, die dazu beiträgt, den gleichen Analysebereich für Swing-Punkte über verschiedene Bars hinweg beizubehalten. Die Konstanthaltung der Werte hilft auch, eine versehentliche Änderung der Variablen während der Programmausführung zu verhindern.

Definieren wir nun kurz die anderen wichtigen Variablen des Programms. Wir müssen den aktuell analysierten Balken im Auge behalten und seine Beziehung zu den benachbarten Balken innerhalb des vordefinierten Bereichs bewerten. Wir erreichen dies durch die Deklaration der folgenden Variablen.

   int right_index, left_index;
   bool isSwingHigh = true, isSwingLow = true;
   static double swing_H = -1.0, swing_L = -1.0;
   int curr_bar = limit;

Zunächst deklarieren wir zwei Integer-Variablen „right_index“ und „left_index“, um die Indizes der benachbarten Kerzen zu verfolgen. Der rechte Index steht für den Index des Balkens rechts vom aktuellen Balken, während der linke Index für den Index des Balkens links vom aktuellen Balken steht, d.h. für den Balken, der für die Analyse ausgewählt wurde. Auch hier deklarieren wir zwei boolesche Variablen „isSwingHigh“ und „isSwingLow“, die als Flags dienen, um festzustellen, ob der aktuelle Balken ein potenzielles Swing-Hoch bzw. -Tief ist, und initialisieren sie auf true. Wenn nach der Analyse eine der beiden Flags erhalten bleibt, deutet dies auf das Vorhandensein eines Ausschlagspunktes hin. Außerdem deklarieren wir statische Doppelvariablen „swing_H“ und „swing_L“, die die Preisniveaus der Swing-Höchst- bzw. Tiefstwerte speichern werden. Wir initialisieren sie auf den Wert -1, um einfach anzuzeigen, dass noch kein Hoch- oder Tiefpunkt festgestellt wurde. Sie sind statisch, um sicherzustellen, dass die Swing-Punkte, sobald sie einmal festgelegt sind, unverändert bleiben und wir sie für eine spätere Bezugnahme speichern können, um später zu erkennen, wenn sie durch eine Änderung der Struktur gebrochen werden. Wir ändern sie auf -1, wenn wir einen Strukturbruch haben, oder sie werden durch neu erzeugte Schwingungspunkte ersetzt. Schließlich gibt es noch die Variable „curr_bar“, die den Ausgangspunkt für die Analyse bestimmt.

Bis zu diesem Punkt haben wir alle Variablen, die für das Programm wichtig sind, perfekt und ausreichend deklariert, und wir können mit unserer Analyseschleife beginnen. Um die Swing-Punkte zu analysieren und abzubilden, müssen wir dies nur einmal pro Kerze tun. Daher wird die Analyse für die Swing-Punkte nur einmal pro Kerze durchgeführt, und hier kommt unsere Variable „isNewBar“ ins Spiel.

   if (isNewBar){ ... }

Anschließend wird eine for-Schleife instanziiert, um die Höchst- und Tiefstwerte der Swings zu ermitteln.

      for (int j=1; j<=length; j++){
         right_index = curr_bar - j;
         left_index = curr_bar + j;
         if ( (high(curr_bar) <= high(right_index)) || (high(curr_bar) < high(left_index)) ){
            isSwingHigh = false;
         }
         if ( (low(curr_bar) >= low(right_index)) || (low(curr_bar) > low(left_index)) ){
            isSwingLow = false;
         }
      }

Wir deklarieren eine ganzzahlige Schleifenvariable „j“, um die Anzahl der Kerzen anzugeben, die beim Vergleich der aktuellen Kerze mit seinen Nachbarn berücksichtigt werden sollen. Wir berechnen dann den Index des Balkens rechts vom aktuellen Balken, indem wir „j“ vom aktuellen Balken abziehen. Mit der gleichen Logik erhalten wir den Index des benachbarten Balkens auf der linken Seite, indem wir „j“ zum aktuellen Balken hinzufügen. Wenn wir die Ergebnisse aus Gründen der Anschaulichkeit ausdrucken, erhalten wir das folgende Ergebnis:

Balken-Index

Die Druckanweisungen wurden durch die Verwendung der folgenden eingebauten Funktion erreicht:

         Print("Current Bar Index = ",curr_bar," ::: Right index: ",right_index,", Left index: ",left_index);

Bis hierhin ist klar, dass wir für den gewählten Balken-Index, in diesem Fall 20, alle benachbarten Balken links und rechts innerhalb der angegebenen Länge bewerten. Es ist offensichtlich, dass wir bei jeder Iteration rechts eine Zahl negieren und links eine Zahl addieren, was dazu führt, dass der rechte Index den Wert Null annimmt, was typischerweise den aktuellen Balken bedeutet, und der linke Index die vordefinierte Länge verdoppelt. Da wir nun sicher sind, dass wir die Balkenbewertung korrekt durchgeführt haben, fahren wir damit fort, das Vorhandensein der Swing-Punkte bei jeder Iteration zu bestimmen. 

Um festzustellen, ob es ein Swing-Hoch gibt, verwenden wir eine bedingte Anweisung, um zu prüfen, ob der Höchstkurs des aktuellen Balkens kleiner oder gleich dem Höchstkurs des Balkens am rechten Index oder kleiner als der Höchstkurs des Balkens am linken Index ist. Wenn eine der beiden Bedingungen erfüllt ist, bedeutet dies, dass der aktuelle Balken im Vergleich zu seinen Nachbarn kein höheres Hoch aufweist, sodass „isSwingHigh“ auf false gesetzt wird. Um festzustellen, ob ein Swing-Tief vorliegt, gilt die gleiche Logik, allerdings unter umgekehrten Bedingungen.

Wenn „isSwingHigh“ am Ende der Schleife immer noch wahr ist, deutet dies darauf hin, dass der aktuelle Balken ein höheres Hoch als die umliegenden Balken innerhalb des Längenbereichs aufweist und somit ein potenzielles Swing-Hoch markiert. Die gleiche Logik gilt auch für das Flag für Swing-Tief. Wenn das der Fall ist, füllen wir die Swing-Point-Variablen mit den entsprechenden Preisen und zeichnen die Swing-Punkte.

      if (isSwingHigh){
         swing_H = high(curr_bar);
         Print("UP @ BAR INDEX ",curr_bar," of High: ",high(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),high(curr_bar),77,clrBlue,-1);
      }
      if (isSwingLow){
         swing_L = low(curr_bar);
         Print("DOWN @ BAR INDEX ",curr_bar," of Low: ",low(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),low(curr_bar),77,clrRed,1);
      }

Nutzerdefinierte Funktionen werden verwendet, um die Höchstpreise der Swing-Hochpunkte und die Tiefstpreise der Swing-Tiefstpunkte zu ermitteln. Die Funktionen werden wie folgt deklariert:

double high(int index){return (iHigh(_Symbol,_Period,index));}
double low(int index){return (iLow(_Symbol,_Period,index));}
double close(int index){return (iClose(_Symbol,_Period,index));}
datetime time(int index){return (iTime(_Symbol,_Period,index));}

Die Funktion high nimmt einen einzigen Parameter oder ein Argument entgegen, der/das den Index des Balkens innerhalb der Kursdatenreihe darstellt, aus dem der Höchstkurs des angegebenen Balkens zum gegebenen Index abgerufen werden soll. Die gleiche Logik gilt für die Funktionen „low“, „close“ und „time“.

Um den Swing-Punkt auf dem Chart zur Visualisierung auf den jeweiligen Balken zu ziehen, verwenden wir die folgende nutzerdefinierte Funktion:

void drawSwingPoint(string objName,datetime time,double price,int arrCode,
   color clr,int direction){
   
   if (ObjectFind(0,objName) < 0){
      ObjectCreate(0,objName,OBJ_ARROW,0,time,price);
      ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrCode);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_FONTSIZE,10);
      if (direction > 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP);
      if (direction < 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
      
      string txt = " BoS";
      string objNameDescr = objName + txt;
      ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time,price);
      ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
   }
   ChartRedraw(0);
}

Die nutzerdefinierte Funktion „drawSwingPoint“ benötigt sechs Parameter, um ihre Wiederverwendbarkeit zu erleichtern. Die Funktionen der Parameter sind wie folgt:

  • objName: Eine Zeichenfolge, die den Namen des zu erstellenden grafischen Objekts angibt.
  • time: Ein datetime-Wert, der die Zeitkoordinate angibt, an der das Objekt platziert werden soll.
  • price: Ein double-Wert, der die Preiskoordinate angibt, an der das Objekt platziert werden soll.
  • arrCode: Eine ganze Zahl, die den Pfeilcode für das Pfeilobjekt angibt.
  • clr: Ein Farbwert (z. B. clrBlue, clrRed) für die grafischen Objekte.
  • direction: Eine ganze Zahl, die die Richtung (nach oben oder unten) für die Positionierung der Textbeschriftung angibt.

Die Funktion prüft zunächst, ob ein Objekt mit dem angegebenen objName bereits im Chart vorhanden ist. Wenn nicht, werden die Objekte erstellt. Die Erstellung des Objekts erfolgt mit Hilfe der eingebauten Funktion „ObjectCreate“, die die Angabe des zu zeichnenden Objekts, in diesem Fall des Pfeilobjekts mit der Bezeichnung „OBJ_ARROW“, sowie der Zeit und des Preises erfordert, die die Ordinaten des Objekterstellungspunkts bilden. Anschließend legen wir die Objekteigenschaften Pfeilcode, Farbe, Schriftgröße und Ankerpunkt fest. Für den Pfeilcode verfügt MQL5 über einige bereits vordefinierte Zeichen der Schriftart Wingdings, die direkt verwendet werden können. Hier ist eine Tabelle, in der die Zeichen angegeben sind:

Pfeil-Codes

Bis zu diesem Punkt zeichnen wir den angegebenen Pfeil nur wie folgt in das Chart ein:

Swing Point ohne Beschreibung

Wir können sehen, dass es uns gelungen ist, die Swing-Punkte mit dem angegebenen Pfeilcode zu zeichnen, in diesem Fall haben wir den Pfeilcode 77 verwendet, aber es gibt keine Beschreibung der Punkte. Um die entsprechende Beschreibung hinzuzufügen, verketten wir den Pfeil mit einem Text. Wir erstellen ein weiteres Textobjekt mit der Bezeichnung „OBJ_TEXT“ und legen auch die entsprechenden Eigenschaften fest. Die Textbeschriftung dient als beschreibende Anmerkung zum Swing-Punkt, indem sie zusätzlichen Kontext oder Informationen über den Swing-Punkt liefert und ihn für Händler und Analysten informativer macht. Wir wählen für den Text den Wert „BoS“, was bedeutet, dass es sich um einen Swing-Punkt handelt.

Die Variable „objNameDescr“ wird dann durch Verkettung des ursprünglichen „objName“ mit dem beschreibenden Text erstellt. Dieser kombinierte Name stellt sicher, dass der Pfeil und die zugehörige Textbeschriftung miteinander verknüpft sind. Dieses spezielle Codeschnipsel wird verwendet, um dies zu erreichen.

      string txt = " BoS";
      string objNameDescr = objName + txt;
      ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time,price);
      ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }

Dies ist das Ergebnis der Verkettung des Schwingungspunktes mit seiner Beschreibung.

Swing-Punkt mit Beschreibung

Der vollständige Code, der für die Analyse der Balken, die Identifizierung der Hoch- und Tiefpunkte der Schwünge, die Datendokumentation und die jeweilige Zuordnung der Objekte zu den Swing-Punkten im Chart verantwortlich ist, lautet wie folgt:

   if (isNewBar){
      for (int j=1; j<=length; j++){
         right_index = curr_bar - j;
         left_index = curr_bar + j;

         if ( (high(curr_bar) <= high(right_index)) || (high(curr_bar) < high(left_index)) ){
            isSwingHigh = false;
         }
         if ( (low(curr_bar) >= low(right_index)) || (low(curr_bar) > low(left_index)) ){
            isSwingLow = false;
         }
      }
      
      if (isSwingHigh){
         swing_H = high(curr_bar);
         Print("UP @ BAR INDEX ",curr_bar," of High: ",high(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),high(curr_bar),77,clrBlue,-1);
      }
      if (isSwingLow){
         swing_L = low(curr_bar);
         Print("DOWN @ BAR INDEX ",curr_bar," of Low: ",low(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),low(curr_bar),77,clrRed,1);
      }
   }

Als Nächstes identifizieren wir, wie im Theorieteil beschrieben, die Fälle, in denen die Swing-Punkte gebrochen werden, und wenn es einen solchen Fall gibt, visualisieren wir den Ausbruch bzw. die offenen 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);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);

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. 

Um festzustellen, ob es einen Kursanstieg und einen Durchbruch des Swing-Hoch-Niveaus gegeben hat, verwenden wir eine bedingte Aussage. Zunächst prüfen wir, ob ein Swing-Hoch vorhanden ist, und zwar nach der Logik, dass es größer als Null ist, weil wir nicht über ein Swing-Hoch ausbrechen können, das wir noch nicht haben. Wenn wir dann bereits ein Swing-Hoch haben, prüfen wir, ob Bid über dem Swing-Hoch liegt, um sicherzustellen, dass die Kaufposition zum Ask eröffnet wird und die Handelsstufen, d. h. Stop-Loss und Take-Profit, die mit dem Geldkurs verbunden sind, korrekt auf einen Punkt über dem Ausbruchsniveau abgebildet werden. Abschließend prüfen wir, ob der Schlusskurs des vorangegangenen Balkens über dem Swing-Hoch liegt, um sicherzustellen, dass wir einen gültigen Break haben, der die Anforderungen erfüllt. Wenn alle Bedingungen erfüllt sind, haben wir einen gültigen Strukturbruch (BoS), und wir drucken den Fall in das Journal.

   if (swing_H > 0 && Bid > swing_H && close(1) > swing_H){
      Print("BREAK UP NOW");
      ...
   }

Um das Setup des Ausbruchs zu visualisieren, müssen wir einen Pfeil zeichnen, der sich vom Swing-Hoch bis zu der Kerze erstreckt, bei der der Break eintritt. Das bedeutet, dass wir zwei Koordinaten für die beiden Punkte des zu erstellenden Pfeils benötigen, in der Regel den Anfang des Pfeils, der an den Swing-Punkt angehängt werden soll, und das Ende des Pfeils, das die Kerze ist, an der der Ausbruch stattfindet. Dies lässt sich viel einfacher in einem Bild wie unten darstellen:

Punkt-Koordinaten

Die beiden Koordinaten, die wir benötigen, sind die Zeit, die als X dargestellt wird und auf der x-Achse liegt, und der Preis, der als Y dargestellt wird und auf der y-Achse liegt. Um die zweiten Koordinaten zu erhalten, d. h. die Kerze, bei der der Strukturbruch auftritt, verwenden wir den aktuellen Balkenindex, der normalerweise 0 ist. Die Ermittlung des Index des Balkens, der den Hochpunkt des Swings enthält, ist jedoch etwas kompliziert. Erinnern Sie sich, dass wir nur den Preis der Swing-Hoch-Kerze gespeichert haben. Wir könnten auch den Balkenindex während der gleichen Zeit speichern, in der wir den Preis speichern, aber das wäre völlig sinnlos, da danach neue Balken erzeugt werden. Das bedeutet nicht, dass wir den Index des Balkens, der den Swing-Punkt enthält, nicht finden können. Wir können die Höchstkurse der vorangegangenen Bars durchgehen und einen finden, der unserem Swing-Hochpunkt entspricht. Im Folgenden wird beschrieben, wie dies erreicht wird.

      int swing_H_index = 0;
      for (int i=0; i<=length*2+1000; i++){
         double high_sel = high(i);
         if (high_sel == swing_H){
            swing_H_index = i;
            Print("BREAK HIGH @ BAR ",swing_H_index);
            break;
         }
      }

Zunächst deklarieren wir eine Integer-Variable „swing_H_index“, die unseren Swing-Hoch-Index enthalten wird, und initialisieren sie mit Null. Dann verwenden wir die for-Schleife, um eine Schleife über das Doppelte aller vordefinierten Balken plus einen zusätzlichen Balkenbereich von 1000 zu bilden, d. h. eine beliebige Anzahl von Balken, für die der Swing-Punkt gefunden werden konnte, und vergleichen Sie den Höchststand des ausgewählten Balkens mit dem gespeicherten Swing-Hochpunkt. Wenn wir also eine Übereinstimmung finden, speichern wir den Index und brechen die Schleife vorzeitig ab, da wir bereits unseren Index für den Hochschwungbalken gefunden haben. 

Mit Hilfe des Indexes für den Hochschwung des Balkens können wir nun die Eigenschaften des Balkens abrufen. In diesem Fall sind wir nur an der Zeit interessiert, in der die x-Koordinaten des Pfeilanfangspunkts markiert sind. Wir verwenden eine nutzerdefinierte Funktion, die sich nicht wesentlich von der vorherigen Funktion unterscheidet, die wir für die Zuordnung des Pfeilcodes verwendet haben.

void drawBreakLevel(string objName,datetime time1,double price1,
   datetime time2,double price2,color clr,int direction){
   if (ObjectFind(0,objName) < 0){
      ObjectCreate(0,objName,OBJ_ARROWED_LINE,0,time1,price1,time2,price2);
      ObjectSetInteger(0,objName,OBJPROP_TIME,0,time1);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,0,price1);
      ObjectSetInteger(0,objName,OBJPROP_TIME,1,time2);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,1,price2);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_WIDTH,2);
      
      string txt = " Break   ";
      string objNameDescr = objName + txt;
      ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time2,price2);
      ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_RIGHT_UPPER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_RIGHT_LOWER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
   }
   ChartRedraw(0);
}

Hier sind die Unterschiede in der Funktion gegenüber der vorherigen.

  1. Wir deklarieren den Funktionsnamen als „drawBreakLevel“.
  2. Das Objekt, das wir erstellen, ist eine gepfeilte Linie mit der Bezeichnung „OBJ_ARROWED_LINE“.
  3. Unsere Linie aus Pfeilen enthält zwei Koordinaten: Zeit 1 und Preis 1 für die erste und Zeit 2 und Preis 2 für die zweite Koordinate.
  4. Der verkettete Text lautet „Break“ und signalisiert, dass ein Strukturbruch (BoS) aufgetreten ist.

Anschließend wird die Funktion verwendet, um die Linie mit Pfeilen mit dem Break-Level im Chart zu zeichnen. Für die Zeit 2 der zweiten Koordinate addieren wir einfach 1, was uns zum Balken vor dem aktuellen Balken bringt, um genau zu sein. Dann setzen wir den Wert der Swing-Hoch-Variable auf -1 zurück, um zu signalisieren, dass wir die Struktur bereits durchbrochen haben und das Setup nicht mehr existiert. Auf diese Weise lässt sich vermeiden, dass der Durchbruch des Swing-Hochs in den vorangegangenen Ticks gesucht wird, da wir den Punkt des Swing-Hochs bereits durchbrochen haben. Wir warten also nur darauf, dass sich ein weiteres Swing-Hoch bildet, und die Variable wird wieder gefüllt und die Schleife geht weiter.

      drawBreakLevel(TimeToString(time(0)),time(swing_H_index),high(swing_H_index),
      time(0+1),high(swing_H_index),clrBlue,-1);
      
      swing_H = -1.0;

Schließlich eröffnen wir eine Kaufposition, sobald wir den Durchbruch des Swing-Hochs erreicht haben. 

      //--- Open Buy
      obj_Trade.Buy(0.01,_Symbol,Ask,Bid-500*7*_Point,Bid+500*_Point,"BoS Break Up BUY");
      
      return;

Wir verwenden unser Objekt „obj_Trade“ und den Punktoperator, um Zugriff auf alle in der Klasse enthaltenen Methoden zu erhalten. In diesem Fall brauchen wir nur zu kaufen und verwenden daher die Methode „Kaufen“, wobei wir das Volumen, die Handelsstufen und den Handelskommentar angeben. Schließlich kehren wir einfach zurück, da alles fertig ist und wir keinen weiteren Code mehr ausführen müssen. Wenn Sie jedoch weiteren Code haben, sollten Sie die Verwendung des Return-Operators vermeiden, da er die aktuelle Funktionsausführung beendet und die Kontrolle an das aufrufende Programm zurückgibt. Der vollständige Code, der dafür sorgt, dass wir die Brüche in der Struktur finden, die Pfeillinien zeichnen und Kaufpositionen eröffnen, lautet wie folgt:

   if (swing_H > 0 && Bid > swing_H && close(1) > swing_H){
      Print("BREAK UP NOW");
      int swing_H_index = 0;
      for (int i=0; i<=length*2+1000; i++){
         double high_sel = high(i);
         if (high_sel == swing_H){
            swing_H_index = i;
            Print("BREAK HIGH @ BAR ",swing_H_index);
            break;
         }
      }
      drawBreakLevel(TimeToString(time(0)),time(swing_H_index),high(swing_H_index),
      time(0+1),high(swing_H_index),clrBlue,-1);
      
      swing_H = -1.0;
      
      //--- Open Buy
      obj_Trade.Buy(0.01,_Symbol,Ask,Bid-500*7*_Point,Bid+500*_Point,"BoS Break Up BUY");
      
      return;
   }

Für den Durchbruch unter das Swing-Tief, das gleichzeitige Zeichnen der Durchbruchslinien mit Pfeilen und die Eröffnung von Verkaufspositionen gilt dieselbe Logik, nur unter umgekehrten Bedingungen. Der vollständige Code lautet wie folgt:

   else if (swing_L > 0 && Ask < swing_L && close(1) < swing_L){
      Print("BREAK DOWN NOW");
      int swing_L_index = 0;
      for (int i=0; i<=length*2+1000; i++){
         double low_sel = low(i);
         if (low_sel == swing_L){
            swing_L_index = i;
            Print("BREAK LOW @ BAR ",swing_L_index);
            break;
         }
      }
      drawBreakLevel(TimeToString(time(0)),time(swing_L_index),low(swing_L_index),
      time(0+1),low(swing_L_index),clrRed,1);

      swing_L = -1.0;
      
      //--- Open Sell
      obj_Trade.Sell(0.01,_Symbol,Bid,Ask+500*7*_Point,Ask-500*_Point,"BoS Break Down SELL");

      return;
   }

Hier ist die Darstellung des Meilensteins.

Meilenstein

Im Folgenden finden Sie den vollständigen Code, der benötigt wird, um eine Break of Structure (BoS) Forex-Handelsstrategie in MQL5 zu erstellen, die die Breaks identifiziert und die Positionen entsprechend öffnet.

//+------------------------------------------------------------------+
//|                                                          BOS.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>
CTrade obj_Trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){return(INIT_SUCCEEDED);}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   
   static bool isNewBar = false;
   int currBars = iBars(_Symbol,_Period);
   static int prevBars = currBars;
   if (prevBars == currBars){isNewBar = false;}
   else if (prevBars != currBars){isNewBar = true; prevBars = currBars;}
   
   const int length = 5;
   const int limit = 5;

   int right_index, left_index;
   bool isSwingHigh = true, isSwingLow = true;
   static double swing_H = -1.0, swing_L = -1.0;
   int curr_bar = limit;
   
   if (isNewBar){
      for (int j=1; j<=length; j++){
         right_index = curr_bar - j;
         left_index = curr_bar + j;
         //Print("Current Bar Index = ",curr_bar," ::: Right index: ",right_index,", Left index: ",left_index);
         //Print("curr_bar(",curr_bar,") right_index = ",right_index,", left_index = ",left_index);
         // If high of the current bar curr_bar is <= high of the bar at right_index (to the left),
         //or if it’s < high of the bar at left_index (to the right), then isSwingHigh is set to false
         //This means that the current bar curr_bar does not have a higher high compared
         //to its neighbors, and therefore, it’s not a swing high
         if ( (high(curr_bar) <= high(right_index)) || (high(curr_bar) < high(left_index)) ){
            isSwingHigh = false;
         }
         if ( (low(curr_bar) >= low(right_index)) || (low(curr_bar) > low(left_index)) ){
            isSwingLow = false;
         }
      }
      //By the end of the loop, if isSwingHigh is still true, it suggests that 
      //current bar curr_bar has a higher high than the surrounding bars within
      //length range, marking a potential swing high.
      
      if (isSwingHigh){
         swing_H = high(curr_bar);
         Print("UP @ BAR INDEX ",curr_bar," of High: ",high(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),high(curr_bar),77,clrBlue,-1);
      }
      if (isSwingLow){
         swing_L = low(curr_bar);
         Print("DOWN @ BAR INDEX ",curr_bar," of Low: ",low(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),low(curr_bar),77,clrRed,1);
      }
   }
   
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);

   if (swing_H > 0 && Bid > swing_H && close(1) > swing_H){
      Print("BREAK UP NOW");
      int swing_H_index = 0;
      for (int i=0; i<=length*2+1000; i++){
         double high_sel = high(i);
         if (high_sel == swing_H){
            swing_H_index = i;
            Print("BREAK HIGH @ BAR ",swing_H_index);
            break;
         }
      }
      drawBreakLevel(TimeToString(time(0)),time(swing_H_index),high(swing_H_index),
      time(0+1),high(swing_H_index),clrBlue,-1);
      
      swing_H = -1.0;
      
      //--- Open Buy
      obj_Trade.Buy(0.01,_Symbol,Ask,Bid-500*7*_Point,Bid+500*_Point,"BoS Break Up BUY");
      
      return;
   }
   else if (swing_L > 0 && Ask < swing_L && close(1) < swing_L){
      Print("BREAK DOWN NOW");
      int swing_L_index = 0;
      for (int i=0; i<=length*2+1000; i++){
         double low_sel = low(i);
         if (low_sel == swing_L){
            swing_L_index = i;
            Print("BREAK LOW @ BAR ",swing_L_index);
            break;
         }
      }
      drawBreakLevel(TimeToString(time(0)),time(swing_L_index),low(swing_L_index),
      time(0+1),low(swing_L_index),clrRed,1);

      swing_L = -1.0;
      
      //--- Open Sell
      obj_Trade.Sell(0.01,_Symbol,Bid,Ask+500*7*_Point,Ask-500*_Point,"BoS Break Down SELL");

      return;
   }
   
}
//+------------------------------------------------------------------+

double high(int index){return (iHigh(_Symbol,_Period,index));}
double low(int index){return (iLow(_Symbol,_Period,index));}
double close(int index){return (iClose(_Symbol,_Period,index));}
datetime time(int index){return (iTime(_Symbol,_Period,index));}

void drawSwingPoint(string objName,datetime time,double price,int arrCode,
   color clr,int direction){
   
   if (ObjectFind(0,objName) < 0){
      ObjectCreate(0,objName,OBJ_ARROW,0,time,price);
      ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrCode);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_FONTSIZE,10);
      if (direction > 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP);
      if (direction < 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
      
      string txt = " BoS";
      string objNameDescr = objName + txt;
      ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time,price);
      ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
   }
   ChartRedraw(0);
}

void drawBreakLevel(string objName,datetime time1,double price1,
   datetime time2,double price2,color clr,int direction){
   if (ObjectFind(0,objName) < 0){
      ObjectCreate(0,objName,OBJ_ARROWED_LINE,0,time1,price1,time2,price2);
      ObjectSetInteger(0,objName,OBJPROP_TIME,0,time1);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,0,price1);
      ObjectSetInteger(0,objName,OBJPROP_TIME,1,time2);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,1,price2);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_WIDTH,2);
      
      string txt = " Break   ";
      string objNameDescr = objName + txt;
      ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time2,price2);
      ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_RIGHT_UPPER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_RIGHT_LOWER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
   }
   ChartRedraw(0);
}

Prost auf uns! Jetzt haben wir ein Smart-Money-Konzept-Handelssystem entwickelt, das auf der Break of Structure (BoS) Forex-Handelsstrategie basiert, um nicht nur Handelssignale zu generieren, sondern auch Marktpositionen auf der Grundlage der generierten Signale zu eröffnen.


Ergebnisse im Strategietester

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

  • Salden-/Kapitalkurve:

Grafik

  • Backtest-Ergebnisse:

Ergebnisse


Schlussfolgerung

Zusammenfassend lässt sich sagen, dass die Automatisierung der Strategie des „Break of Structure“ (BoS) gar nicht so komplex 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 wird in dem Artikel der theoretische Teil hervorgehoben, der berücksichtigt und klar verstanden werden muss, um eine BoS-Forex-Handelsstrategie zu erstellen. Dazu gehören neben dem Bauplan auch seine Definition, Beschreibung und Typen. Darüber hinaus hebt der Kodierungsaspekt der Strategie die Schritte hervor, die unternommen werden, um die Kerzen zu analysieren, die Swing-Punkte zu identifizieren, ihre Brü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 BoS-Strategie, was eine schnellere Ausführung und die Skalierbarkeit der Strategie erleichtert.

Haftungsausschluss: Die in diesem Artikel dargestellten Informationen dienen nur zu Lehrzwecken. Es soll lediglich Einblicke in die Erstellung eines Break of Structure (BoS) Expert Advisors (EA) auf Basis des Smart Money Concept Ansatzes geben und somit als Grundlage für die Erstellung eines besseren Expert Advisors unter Berücksichtigung von mehr Optimierung und Datenextraktion verwendet werden. Die dargestellten Informationen garantieren keinen Handelserfolg.

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 Smart Money Concept (SMC)-Ansatzes und insbesondere der Break of Structure (BoS)-Strategie.


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

MQL5-Assistent-Techniken, die Sie kennen sollten (Teil 22): Conditional GANs MQL5-Assistent-Techniken, die Sie kennen sollten (Teil 22): Conditional GANs
Generative Adversarial Networks (GAN) sind eine Kombination von neuronalen Netzen, die sich gegenseitig trainieren, um genauere Ergebnisse zu erzielen. Wir nehmen den bedingten Typ dieser Netze an, da wir eine mögliche Anwendung bei der Vorhersage von Finanzzeitreihen innerhalb einer Klasse von Expertensignalen anstreben.
Integration von Hidden-Markov-Modellen in MetaTrader 5 Integration von Hidden-Markov-Modellen in MetaTrader 5
In diesem Artikel zeigen wir, wie mit Python trainierte Hidden Markov Modelle in MetaTrader 5 Anwendungen integriert werden können. Hidden-Markov-Modelle sind ein leistungsfähiges statistisches Instrument zur Modellierung von Zeitreihendaten, bei denen das modellierte System durch nicht beobachtbare (verborgene) Zustände gekennzeichnet ist. Eine grundlegende Prämisse von HMMs ist, dass die Wahrscheinlichkeit, sich zu einem bestimmten Zeitpunkt in einem bestimmten Zustand zu befinden, vom Zustand des Prozesses im vorherigen Zeitfenster abhängt.
DoEasy. Steuerung (Teil 33): Vertikale Bildlaufleiste DoEasy. Steuerung (Teil 33): Vertikale Bildlaufleiste
In diesem Artikel werden wir die Entwicklung der grafischen Elemente der DoEasy-Bibliothek fortsetzen und das vertikale Scrollen von Formularobjekt-Steuerelementen sowie einige nützliche Funktionen und Methoden hinzufügen, die in Zukunft benötigt werden.
Datenwissenschaft und maschinelles Lernen (Teil 23): Warum schneiden LightGBM und XGBoost besser ab als viele KI-Modelle? Datenwissenschaft und maschinelles Lernen (Teil 23): Warum schneiden LightGBM und XGBoost besser ab als viele KI-Modelle?
Diese fortschrittlichen gradient-boosted Entscheidungsbaumtechniken bieten eine überragende Leistung und Flexibilität, wodurch sie sich ideal für die Finanzmodellierung und den algorithmischen Handel eignen. Erfahren Sie, wie Sie diese Tools nutzen können, um Ihre Handelsstrategien zu optimieren, die Vorhersagegenauigkeit zu verbessern und sich einen Wettbewerbsvorteil auf den Finanzmärkten zu verschaffen.