English 日本語
preview
Wiederverwendung von ungültig gemachten Orderblöcken als Mitigation Blocks (SMC)

Wiederverwendung von ungültig gemachten Orderblöcken als Mitigation Blocks (SMC)

MetaTrader 5Beispiele |
15 0
Hlomohang John Borotho
Hlomohang John Borotho

Inhaltsverzeichnis

  1. Einführung
  2. Verständnis des Mitigation Orderblocks
  3. Erste Schritte
  4. Backtest-Ergebnisse
  5. Schlussfolgerung


Einführung

Beim Handel mit Smart Money Concepts (SMC) stellen Orderblöcke Schlüsselbereiche dar, in denen institutionelle Händler Positionen akkumulieren oder verteilen, bevor sie den Preis in eine bestimmte Richtung treiben. Allerdings bleiben nicht alle Auftragsblocks gültig – einige werden verletzt, wenn sich die Marktbedingungen ändern. Wenn ein Auftragsblock nicht gehalten werden kann, verliert er nicht unbedingt seine Bedeutung, sondern verwandelt sich in einen potenziellen Entlastungsblock. Dieses Konzept konzentriert sich darauf, wie der Preis oft zu diesen ungültig gemachten Zonen zurückkehrt, um die verbleibenden Aufträge zu „entschärfen“, bevor er in Richtung des neuen Trends weitergeht, was den Händlern einen tieferen Einblick in die institutionellen Absichten und das Verhalten der Marktstruktur bietet.

Die Idee, ungültig gewordene Orderblöcke als Mitigationsblöcke wiederzuverwenden, ermöglicht es Händlern zu erkennen, wo intelligentes Geld nach einer Verschiebung des Bias wieder in den Markt einsteigen könnte. Indem sie untersuchen, wie der Preis mit diesen Bereichen interagiert, können Händler mit hoher Wahrscheinlichkeit Einstiege antizipieren, die mit dem neuen Richtungsfluss übereinstimmen. Das Verständnis dieser Umwandlung von einem Auftragsblock in einen Mitigationsblock verbessert die Präzision der zeitlichen Eingaben und das Risikomanagement.


Verständnis von Mitigation Orderblock:

Wenn der Preis steigt, hinterlässt er in der Regel eine Art Aufwärts-Orderblock in der Nähe oder am tiefen Umkehrpunkt, der den letzten Punkt darstellt, an dem die Käufer auftraten, bevor sie den Preis nach oben trieben. Wenn sich die Rallye fortsetzt, nähert sie sich oft einem vermeintlichen Widerstand, bei dem es sich um ein altes Hoch, einen früheren Orderblock oder eine Ausbruchsstruktur handeln könnte. Auf diesem Niveau suchen wir nach eindeutigen Anzeichen dafür, dass willige Verkäufer vorhanden sind, wie z. B. Dochte, die zurückgewiesen werden, oder Abwärtskerzen, die eine Verlagerung des Auftragsflusses anzeigen. Wenn der Markt anfängt, nach unten zu korrigieren und später wieder in die gleiche Widerstandszone steigt, muss als Nächstes beobachtet werden, ob der Markt die Bereitschaft zeigt, von diesem Niveau nach unten auszubrechen, was darauf hindeutet, dass der Verkaufsdruck beachtet wird und eine potenzielle Abwärtsbewegung folgen könnte.

Wenn der Markt in einen Abwärtstrend übergeht, bildet er in der Regel ein „M“-Muster, das falsche Hochs darstellt, die die Erschöpfung des Aufwärtsmomentums in der Nähe eines Widerstands signalisieren. Die zweite Spitze des „M“ führt in der Regel nicht zu einem neuen Hoch, was darauf hindeutet, dass die Käufer an Kraft verlieren und die Verkäufer beginnen, sich einzuschalten. Dieses Kursverhalten wird dann durch einen Bruch in der Marktstruktur bestätigt, der oft als Marktstrukturverschiebung bezeichnet wird, wenn der Markt unter ein wichtiges Tief fällt. Diese Verschiebung bestätigt, dass intelligentes Geld oder Institutionen sich tatsächlich so positionieren, dass sie die Preise nach unten treiben, wodurch der Markt von einem steigenden zu einem fallenden Umfeld übergeht.

In diesem Szenario konzentrieren wir uns auf den Bereich zwischen dem kurzfristigen Tief und dem kurzfristigen Hoch, um die Bereiche zu identifizieren, in denen zuvor Kaufaktivitäten stattgefunden haben. Der kurzfristige Kursanstieg lenkt die Aufmerksamkeit auf einen bestimmten institutionellen Bezugspunkt, den so genannten „Mitigation Block“. Dies ist der Fall, wenn der Kurs in einen oder innerhalb eines Abschwächungsblocks zurückgeht, was eine Gelegenheit für cleveres Geld bietet, wieder in den Markt einzusteigen und sich an der vorherrschenden Abwärtstrend zu orientieren. Der Grund für die Bedeutung dieser Zone liegt darin, dass der ursprüngliche Aufwärts-Auftragsblock nicht gehalten werden konnte – er wurde aufgrund des starken Verkaufsdrucks nicht beachtet, was darauf hindeutet, dass sich der institutionelle Auftragsfluss von der Akkumulation zur Distribution verlagert hat und dass der Preis nach der Abschwächung dieses Blocks wahrscheinlich weiter fallen wird.


Die ersten Schritte

//+------------------------------------------------------------------+
//|                                            OB_&_MitigationOB.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 <Arrays\ArrayObj.mqh>

CTrade trade;
CArrayObj obList;

#define BullOB clrLime
#define BearOB clrRed
#define ViolatedBullOB clrBlue
#define ViolatedBearOB clrMagenta

//+------------------------------------------------------------------+
//|                           Input parameters                       |
//+------------------------------------------------------------------+
input double Lots = 0.11;
input int takeProfit = 3000;
input double stopLoss = 2000;
input int Time1Hstrt = 1;
input int Time1Hend = 10;

Wir beginnen mit der Definition des Fundaments und der Struktur des Expert Advisors (EA), der für die Identifizierung und Verwaltung von Orderblöcken und deren Abschwächung im Rahmen der Smart Money Concepts konzipiert ist. Anschließend werden zwei wichtige Bibliotheken hinzugefügt: Trade/Trade.mqh zur Abwicklung von Handelsoperationen wie der Auftragsausführung und Arrays/ArrayObj.mqh zur Verwaltung dynamischer Objektsammlungen, die in diesem Fall zur effizienten Speicherung von Auftragsblockdaten verwendet werden. Die Deklaration von CTrade trade initialisiert den Trading Handler, während CArrayObj obList einen Container für die Speicherung aller erkannten Orderblöcke zur Laufzeit erstellt.

Als Nächstes definieren wir Farbkonstanten für die visuelle Klarheit des Charts – grün (clrLime) für Aufwärts-Orderblöcke, rot (clrRed) für Abwärts-Orderblöcke und verschiedene Farben wie blau und magenta, um verletzte oder abgeschwächte Orderblöcke darzustellen. Diese visuellen Hinweise sind entscheidend für die Unterscheidung zwischen gültigen und ungültigen Zonen. Mit den folgenden Eingabeparametern können Händler das Verhalten des EA anpassen: Positionsgröße (Lots), Gewinn- und Verlustziele (takeProfit und stopLoss) sowie zeitbasierte Filter (Time1Hstrt und Time1Hend), um die Handelsausführung auf bestimmte Marktphasen zu beschränken. Mit dieser anfänglichen Einrichtung wird die visuelle, logische und operative Grundlage geschaffen, auf der der Rest des EA die Auftragsblöcke als Milderungszonen erkennen, klassifizieren und wiederverwenden wird.

//+------------------------------------------------------------------+
//|                         OrderBlock Class                         |
//+------------------------------------------------------------------+
class COrderBlock : public CObject
{
public:
   int direction;
   datetime time;
   double high;
   double low;
   bool violated;
   datetime violatedTime;
   bool traded;
   bool inTrade;
   string identifier;
   string tradeComment;

   COrderBlock()
   {
      traded = false;
      violated = false;
      inTrade = false;
      tradeComment = "";
   }

   void UpdateIdentifier()
   {
      identifier = IntegerToString(time) + IntegerToString(direction) + IntegerToString(violated);
   }

   void draw(datetime tmS, datetime tmE, color clr)
   {
      UpdateIdentifier();
      string objOB = "OB_REC_" + identifier;
      if(ObjectFind(0, objOB) == -1)
      {
         ObjectCreate(0, objOB, OBJ_RECTANGLE, 0, time, low, tmS, high);
         ObjectSetInteger(0, objOB, OBJPROP_FILL, true);
         ObjectSetInteger(0, objOB, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, objOB, OBJPROP_BACK, true);
      }

      string objtrade = "OB_TRADE_" + identifier;
      if(ObjectFind(0, objtrade) == -1)
      {
         ObjectCreate(0, objtrade, OBJ_RECTANGLE, 0, tmS, high, tmE, low);
         ObjectSetInteger(0, objtrade, OBJPROP_FILL, true);
         ObjectSetInteger(0, objtrade, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, objtrade, OBJPROP_BACK, true);
      }
   }

   void RemoveObjects()
   {
      UpdateIdentifier();
      string objOB = "OB_REC_" + identifier;
      string objtrade = "OB_TRADE_" + identifier;
      if(ObjectFind(0, objOB) >= 0) ObjectDelete(0, objOB);
      if(ObjectFind(0, objtrade) >= 0) ObjectDelete(0, objtrade);
   }
};

//+------------------------------------------------------------------+
//|                           Global variables                       |
//+------------------------------------------------------------------+
COrderBlock *activeMitigationOB = NULL;
int activeMitigationDirection = 0;

Anschließend definieren wir die Klasse COrderBlock, die als zentrale Datenstruktur für die Darstellung und Verwaltung von Orderblöcken innerhalb des EA dient. Diese Klasse erbt von CObject und ermöglicht die Speicherung und Bearbeitung von Instanzen in Objektarrays (CArrayObj). Jeder Auftragsblock speichert wichtige Attribute wie seine Richtung (auf oder abwärts), die Zeit, die Höchst- und Tiefstkurse und Zustände wie verletzt, gehandelt und im Handel. Zusätzliche Bezeichner wie „identifier“ und „tradeComment“ helfen dabei, jeden Orderblock im Chart eindeutig zu referenzieren und seine zugehörige Handelslogik zu verfolgen. Der Konstruktor initialisiert Standardzustände, um sicherzustellen, dass neu erkannte Orderblöcke als nicht gehandelt und nicht verletzt beginnen, um eine saubere Ausgangsbasis zu schaffen, bevor Chart- oder Handelsinteraktionen stattfinden.

Die Klasse enthält außerdem mehrere leistungsstarke Methoden, die sowohl die Visualisierung als auch das Lebenszyklusmanagement übernehmen. Die Methode UpdateIdentifier() generiert eine eindeutige Zeichenfolge auf der Grundlage von Zeit, Richtung und Verletzungsstatus, die für die Unterscheidung zwischen mehreren Auftragsblöcken unerlässlich ist. Die Funktion draw() ist für die Darstellung der Orderblockzonen und der zugehörigen Handelsregionen als farbige Rechtecke auf dem Chart verantwortlich, wobei sichergestellt wird, dass jeder Block visuell eindeutig ist und sich nicht überschneidet. In der Zwischenzeit bereinigt RemoveObjects() diese grafischen Objekte, wenn sie nicht mehr gültig sind, um die Übersichtlichkeit und Leistung des Charts zu erhalten. Schließlich werden zwei globale Variablen – activeMitigationOB und activeMitigationDirection – deklariert, um den derzeit aktiven Mitigation-Orderblock und seine Richtungsvorgabe zu verfolgen und eine Brücke zwischen Orderblock-Erkennung, Verletzung und Handelslogik später im EA zu schlagen.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   trade.SetExpertMagicNumber(76543);
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   for(int i = obList.Total()-1; i >= 0; i--)
   {
      COrderBlock* ob = obList.At(i);
      ob.RemoveObjects();
      delete ob;
   }
   obList.Clear();
}

In diesem Abschnitt werden die Initialisierungs- und Deinitialisierungsprozesse des Expert Advisors definiert, um eine ordnungsgemäße Verwaltung der Handels- und Grafikressourcen sicherzustellen. Die Funktion OnInit() weist eine eindeutige magische Nummer zu, um die vom System getätigten Handelsgeschäfte zu unterscheiden, was eine genaue Verfolgung und Verwaltung der offenen Positionen ermöglicht. Umgekehrt sorgt die Funktion OnDeinit() für eine saubere Beendigung, indem sie alle grafischen Ordnungsblockobjekte aus dem Chart entfernt und den zugewiesenen Speicher für jede Ordnungsblockinstanz freigibt. Diese Struktur verhindert Datenlecks oder grafische Unordnung und sorgt für eine stabile und effiziente Handelsumgebung beim Neustart oder Herunterfahren des Expert Advisors.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   if(isNewBar())
   {
      CheckForClosedTradesAndCleanup();
      CheckForViolations();
      getOrderB();
      CheckForTradeEntries();
   }
}

//+------------------------------------------------------------------+
//| Helpers to find bars and swings safely                           |
//+------------------------------------------------------------------+
int BarIndexFromTimeSafely(datetime t)
{
   int idx = iBarShift(_Symbol, _Period, t, true);
   if(idx < 0) idx = Bars(_Symbol, _Period) - 1;
   return(idx);
}

int FindHighestAfter(datetime t, int lookbackBars)
{
   int obIdx = BarIndexFromTimeSafely(t);
   int start = MathMax(1, obIdx - lookbackBars);
   int count = obIdx - start;
   if(count <= 0) return(-1);
   int idx = iHighest(_Symbol, _Period, MODE_HIGH, count, start);
   return(idx);
}

int FindLowestAfter(datetime t, int lookbackBars)
{
   int obIdx = BarIndexFromTimeSafely(t);
   int start = obIdx + 1;
   int maxPossible = Bars(_Symbol, _Period) - start;
   int count = MathMin(lookbackBars, maxPossible);
   if(count <= 0) return(-1);
   int idx = iLowest(_Symbol, _Period, MODE_LOW, count, start);
   return(idx);
}

Die Funktion OnTick() dient als Hauptausführungsschleife des Expert Advisors, die bei jedem Markttick Entscheidungen in Echtzeit trifft. Sie führt ihre Logik in erster Linie aus, sobald ein neuer Balken bestätigt wird, indem sie die Bedingung isNewBar() verwendet, um redundante Operationen innerhalb derselben Kerze zu vermeiden. Bei jedem neuen Balken führt es wichtige Aufgaben aus, wie z. B. das Bereinigen von abgeschlossenen Handelsgeschäften, die Überprüfung auf verletzte Orderblöcke, die Identifizierung neuer potenzieller Orderblöcke und die Suche nach gültigen Einstiegsmöglichkeiten. Dieser strukturierte Ansatz gewährleistet eine effiziente Ressourcennutzung und hält den logischen Ablauf der Strategie aufrecht, ohne das System zu überlasten.

Zur Unterstützung dieser Hauptoperationen wurden die Hilfsfunktionen – BarIndexFromTimeSafely(), FindHighestAfter() und FindLowestAfter() – entwickelt, um den Datenabruf und die Schwunganalyse zuverlässig zu verwalten. BarIndexFromTimeSafely() stellt sicher, dass das Programm auch bei unregelmäßigen Marktbedingungen oder fehlenden Daten den korrekten Balkenindex zu einem bestimmten Zeitpunkt finden kann. Die Funktionen FindHighestAfter() und FindLowestAfter() identifizieren wichtige hohe und tiefe Umkehrpunkte innerhalb eines definierten Rückblickzeitraums, die für die Erkennung der Marktstruktur und die Validierung des Orderblockverhaltens entscheidend sind. Zusammen bilden diese Funktionen die Grundlage für eine präzise und adaptive Auftragsblockanalyse.

//+------------------------------------------------------------------+
//| Check for violations of existing orderblocks                     |
//+------------------------------------------------------------------+
void CheckForViolations()
{
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   for(int i = 0; i < obList.Total(); i++)
   {
      COrderBlock* ob = obList.At(i);
      if(ob.violated || ob.traded) continue;

      if(ob.direction == 1 && currentBid < ob.low)
      {
         if(activeMitigationOB != NULL)
         {
            activeMitigationOB = NULL;
            activeMitigationDirection = 0;
         }
         
         ob.violated = true;
         ob.violatedTime = iTime(_Symbol, PERIOD_CURRENT, 0);
         ob.traded = false;
         ob.UpdateIdentifier();
         activeMitigationOB = ob;
         activeMitigationDirection = -1;
         Print("Bullish OB violated and becomes BEARISH mitigation: ", TimeToString(ob.time));
      }
      else if(ob.direction == -1 && currentAsk > ob.high)
      {
         if(activeMitigationOB != NULL)
         {
            activeMitigationOB = NULL;
            activeMitigationDirection = 0;
         }
         
         ob.violated = true;
         ob.violatedTime = iTime(_Symbol, PERIOD_CURRENT, 0);
         ob.traded = false;
         ob.UpdateIdentifier();
         activeMitigationOB = ob;
         activeMitigationDirection = 1;
         Print("Bearish OB violated and becomes BULLISH mitigation: ", TimeToString(ob.time));
      }
   }
}

Die Funktion CheckForViolations() ist für die kontinuierliche Überwachung aller bestehenden Orderblöcke zuständig, um festzustellen, ob diese durch das aktuelle Marktpreisgeschehen verletzt wurden. Er ruft die aktuellen Geld- und Briefkurse ab und durchläuft dann jeden gespeicherten Auftragsblock, wobei er diejenigen überspringt, die bereits als gehandelt oder verletzt markiert wurden.

Bei Aufwärts-Orderblöcken liegt ein Verstoß vor, wenn der Geldkurs (Ask) unter das Tief des Blocks fällt, während bei Abwärts-Orderblöcken ein Verstoß vorliegt, wenn der Briefkurs (Bid) über das Hoch des Blocks steigt. Sobald eine Verletzung festgestellt wird, aktualisiert das System den Status des Auftragsblocks, markiert den Verletzungszeitpunkt und setzt alle aktiven Mitigation-Block-Referenzen zurück, bevor der verletzte Block als neuer aktiver Mitigation-Auftragsblock mit einer entgegengesetzten Richtungsvorspannung neu zugewiesen wird.

Dieser dynamische Übergang von einem Marktzustand in einen anderen ermöglicht es dem EA, sich an das veränderte Preisverhalten anzupassen und zuvor starke Zonen als neue Abschwächungsbereiche zu behandeln, wodurch die Reaktionsfähigkeit der Strategie auf Veränderungen im Auftragsfluss effektiv verfeinert wird.

//+------------------------------------------------------------------+
//| Check for trade entries in orderblocks                           |
//+------------------------------------------------------------------+
void CheckForTradeEntries()
{
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   for(int i = 0; i < obList.Total(); i++)
   {
      COrderBlock* ob = obList.At(i);
      if(ob.traded) continue;

      bool priceInZone = false;
      bool isBullishTrade = false;
      bool isMitigation = ob.violated;

      if(!ob.violated)
      {
         if(ob.direction == 1 && currentAsk < ob.high && currentAsk > ob.low)
         {
            priceInZone = true;
            isBullishTrade = true;
         }
         else if(ob.direction == -1 && currentBid > ob.low && currentBid < ob.high)
         {
            priceInZone = true;
            isBullishTrade = false;
         }
      }
      else
      {
         if(ob.direction == 1 && currentBid > ob.low && currentBid < ob.high)
         {
            priceInZone = true;
            isBullishTrade = false;
         }
         else if(ob.direction == -1 && currentAsk < ob.high && currentAsk > ob.low)
         {
            priceInZone = true;
            isBullishTrade = true;
         }
      }

      if(priceInZone)
      {
         if(isMitigation)
         {
            if(activeMitigationOB == NULL || activeMitigationOB != ob)
            {
               continue;
            }
         }

         if(isBullishTrade)
         {
            ExecuteBuyTrade(ob);
         }
         else
         {
            ExecuteSellTrade(ob);
         }
         ob.traded = true;
         break;
      }
   }
}

Die Funktion CheckForTradeEntries() handhabt die Kernlogik, die bestimmt, wann und wo der Expert Advisor auf der Grundlage der Preisinteraktion mit aktiven Orderblöcken oder Mitigation-Blöcken Handelsgeschäfte ausführen soll. Zunächst werden die aktuellen Geld- und Briefkurse abgerufen und jeder gespeicherte Auftragsblock durchlaufen, wobei diejenigen, die bereits für den Handel verwendet werden, übersprungen werden. Die Logik prüft dann, ob der aktuelle Preis innerhalb einer gültigen Orderblockzone gehandelt wird, und identifiziert die Handelsrichtung auf der Grundlage der Art des Blocks – auf- oder abwärts. Bei unverletzten Orderblöcken werden Käufe ausgelöst, wenn der Briefkurs in die Aufwärts-Zone eintaucht, während Verkäufe entstehen, wenn der Geldkurs sich in die Abwärts-Zone bewegt.

Beim Handel mit verletzten Orderblöcken (die jetzt als Mitigation Blocks fungieren) wird die Handelslogik umgekehrt, um die entgegengesetzte Reaktion in der Marktstruktur widerzuspiegeln. Das System stellt sicher, dass nur der aktuell aktive Mitigation Block Handelsgeschäfte auslösen kann, um unnötige Wiederholungen oder Konflikte zu vermeiden. Sobald die Bedingungen erfüllt sind, führt es entweder einen Kauf- oder einen Verkaufshandel unter Verwendung der jeweiligen Auftragsblockparameter aus und markiert den Block als „gehandelt“, um doppelte Positionen zu vermeiden. Diese zweischichtige Logik, die sowohl gültige als auch abgeschwächte Orderblöcke verarbeitet, bietet eine dynamische und anpassungsfähige Struktur für den Smart-Money-Handel, die es dem Algorithmus ermöglicht, intelligent auf die sich verändernden Marktbedingungen zu reagieren.

//+------------------------------------------------------------------+
//| Execute a buy trade                                              |
//+------------------------------------------------------------------+
void ExecuteBuyTrade(COrderBlock* ob)
{
   double entry = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   int swingHighIdx = FindHighestAfter(ob.time, 20);
   double tp = (swingHighIdx>0) ? getHigh(swingHighIdx) : entry + takeProfit * _Point;
   double sl = NormalizeDouble(ob.low - stopLoss * _Point, _Digits);

   string comment = "Bull_OB_" + ob.identifier;
   ob.tradeComment = comment;

   if(trade.Buy(Lots, _Symbol, entry, sl, tp, comment))
   {
      color obColor = ob.violated ? ViolatedBullOB : BullOB;
      ob.draw(ob.time, iTime(_Symbol, PERIOD_CURRENT, 0), obColor);
      ob.inTrade = true;
      Print("Buy trade executed. Type: ", ob.violated ? "Mitigation" : "Regular", 
            " OB Time: ", TimeToString(ob.time), " Entry: ", entry, " SL: ", sl, " TP: ", tp);
   }
}

//+------------------------------------------------------------------+
//| Execute a sell trade                                             |
//+------------------------------------------------------------------+
void ExecuteSellTrade(COrderBlock* ob)
{
   double entry = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   int swingLowIdx = FindLowestAfter(ob.time, 20);
   double tp = (swingLowIdx>0) ? getLow(swingLowIdx) : entry - takeProfit * _Point;
   double sl = NormalizeDouble(ob.high + stopLoss * _Point, _Digits);

   string comment = "Bear_OB_" + ob.identifier;
   ob.tradeComment = comment;

   if(trade.Sell(Lots, _Symbol, entry, sl, tp, comment))
   {
      color obColor = ob.violated ? ViolatedBearOB : BearOB;
      ob.draw(ob.time, iTime(_Symbol, PERIOD_CURRENT, 0), obColor);
      ob.inTrade = true;
      Print("Sell trade executed. Type: ", ob.violated ? "Mitigation" : "Regular", 
            " OB Time: ", TimeToString(ob.time), " Entry: ", entry, " SL: ", sl, " TP: ", tp);
   }
}

Die Funktionen ExecuteBuyTrade() und ExecuteSellTrade() sorgen für die präzise Ausführung von Handelsgeschäften, sobald ein Orderblock oder Mitigation Block die Eingabebedingungen erfüllt. Bei einem Kaufszenario verwendet das System den aktuellen Briefkurs als Einstiegsniveau und berechnet das Take-Profit-Ziel auf der Grundlage des nächsten festgestellten hohen Umkehrpunkts. Wird kein gültiger hoher Umkehrpunkt gefunden, wird standardmäßig ein fester Pip-Abstand verwendet, der durch die takeProfit-Eingabe definiert ist. Der Stop-Loss wird knapp unter dem Tiefpunkt des Auftragsblocks positioniert, sodass ein risikobewusstes Setup gewährleistet ist. Die Funktion versieht den Handel dann mit einem beschreibenden Kommentar zur Nachverfolgung und aktualisiert das Chart visuell mit der richtigen Farbe – grün für einen gültigen Aufwärts-Block oder blau für einen abgeschwächten. Diese Struktur ermöglicht sowohl in der Logik als auch in der Chart-Darstellung eine klare Unterscheidung zwischen regulären und mitigation-basierten Handelsgeschäften.

Die Verkaufslogik folgt einem gespiegelten Prozess, wobei der aktuelle Geldkurs als Einstiegsniveau verwendet wird und der Take-Profit durch den nächstgelegenen tiefen Umkehrpunkt oder einen festen Fallback-Wert bestimmt wird. Der Stop-Loss wird knapp über dem Höchststand des Auftragsblocks platziert, sodass eine konsistente Risiko-Ertrags-Symmetrie bei Kauf-Setups gewährleistet ist. Zur Identifizierung wird ein eindeutiger Handelskommentar erstellt, und die entsprechende Farbe für fallend oder schwach fallend (rot bzw. magenta) wird auf dem Chart angewendet, um aktive Handelszonen hervorzuheben. Beide Funktionen schließen mit dem Setzen des Flags „inTrade“ auf true ab, wodurch sichergestellt wird, dass jeder Orderblock nur eine Position auslöst. Dieses modulare Design trennt effektiv die Ausführungslogik von Käufen und Verkäufen und ermöglicht ein strukturiertes, visuell verfolgtes und regelbasiertes Handelsverhalten, das mit Smart-Money-Konzepten übereinstimmt.

//+------------------------------------------------------------------+
//| Detect new orderblocks                                           |
//+------------------------------------------------------------------+
void getOrderB()
{
   MqlDateTime structTime;
   TimeCurrent(structTime);
   static int prevDay = 0;

   if(structTime.hour >= Time1Hstrt && structTime.hour < Time1Hend)
   {
      if(prevDay != structTime.day)
      {
         prevDay = structTime.day;

         for(int i = 3; i < 100; i++)
         {
            if(i + 3 >= Bars(_Symbol, _Period)) continue;

            if(getOpen(i+2) > getClose(i+2) &&
               getClose(i+1) > getOpen(i+1) &&
               getClose(i) > getOpen(i))
            {
               COrderBlock* ob = new COrderBlock();
               ob.direction = 1;
               ob.time = getTime(i+2);
               ob.high = getHigh(i+2);
               ob.low = getLow(i+2);
               ob.violated = false;
               ob.traded = false;
               ob.UpdateIdentifier();
               obList.Add(ob);
               Print("Bullish Order Block detected at: ", TimeToString(ob.time));
            }
            else if(getOpen(i+2) < getClose(i+2) &&
                    getClose(i+1) < getOpen(i+1) &&
                    getClose(i) < getOpen(i))
            {
               COrderBlock* ob = new COrderBlock();
               ob.direction = -1;
               ob.time = getTime(i+2);
               ob.high = getHigh(i+2);
               ob.low = getLow(i+2);
               ob.violated = false;
               ob.traded = false;
               ob.UpdateIdentifier();
               obList.Add(ob);
               Print("Bearish Order Block detected at: ", TimeToString(ob.time));
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Check for closed trades and cleanup objects after closure        |
//+------------------------------------------------------------------+
void CheckForClosedTradesAndCleanup()
{
   for(int i = obList.Total()-1; i >= 0; i--)
   {
      COrderBlock* ob = obList.At(i);
      if(ob.inTrade)
      {
         if(!PositionWithCommentExists(ob.tradeComment))
         {
            Print("Trade closed for OB: ", ob.identifier, " -> cleaning up drawings");
            ob.RemoveObjects();
            ob.inTrade = false;
            ob.traded = false;
            ob.tradeComment = "";

            if(activeMitigationOB == ob)
            {
               activeMitigationOB = NULL;
               activeMitigationDirection = 0;
               Print("Active mitigation OB cleared after trade closure");
            }

            obList.Delete(i);
            delete ob;
         }
      }
   }
}

Die Funktion getOrderB() ist für die Erkennung frischer Auftragsblöcke auf der Grundlage einer einfachen Drei-Kerzen-Muster-Logik zuständig. Es wird nur innerhalb eines bestimmten Zeitfensters gescannt, das durch die Nutzereingaben Time1Hstrt und Time1Hend definiert wird, um sicherzustellen, dass die Erkennung während der kontrollierten Handelszeiten erfolgt. Die Funktion prüft zunächst, ob Aufwärts-Orderblocks vorliegen, d. h. ob auf eine Abwärtskerze zwei aufeinanderfolgende Aufwärtskerzen folgen, was auf eine potenzielle Akkumulation und einen Stimmungsumschwung am Markt hindeutet. Wenn dieses Muster erkannt wird, wird ein neues COrderBlock-Objekt erstellt, seine Attribute (z. B. Richtung, Zeit, Hoch und Tief) werden initialisiert, und es wird der globalen Liste hinzugefügt.

Ebenso wird ein Abwärts-Auftragsblock erkannt, wenn auf eine Aufwärtskerze zwei aufeinanderfolgende Abwärtskerzen folgen, was auf eine mögliche Verteilung und einen bevorstehenden Kursrückgang hindeutet. Diese modulare und zeitabhängige Erkennung stellt sicher, dass nur wichtige Blöcke pro Tag registriert werden, wodurch das Durcheinander reduziert und der Fokus auf die Bereiche gelegt wird, in denen Handlungsbedarf besteht.

Die Funktion CheckForClosedTradesAndCleanup() ergänzt den Erkennungsprozess durch die Aufrechterhaltung der Chart-Sauberkeit und der Systemintegrität. Sobald ein mit einem Auftragsblock verbundenes Handelsgeschäft abgeschlossen ist, prüft die Funktion den Status, indem sie das Vorhandensein einer Position mit dem Kommentar des Handelsgeschäfts des Blocks überprüft. Wenn das Handelsgeschäft nicht mehr existiert, werden alle zugehörigen visuellen Elemente (Rechtecke und Hervorhebungen) aus dem Chart entfernt, um Verwechslungen mit aktiven Setups zu vermeiden. Außerdem wird der interne Status des Auftragsblocks zurückgesetzt, die Mitigationsreferenz gelöscht, falls möglich, und das Auftragsblockobjekt aus der globalen Liste gelöscht, um Speicher freizugeben. Dieser Bereinigungszyklus hält das System dynamisch und effizient und stellt sicher, dass nur relevante und aktive Auftragsblöcke verfolgt werden, während veraltete Blöcke sicher aus dem Speicher und der Chartumgebung entfernt werden.

//+------------------------------------------------------------------+
//| Check if position with specific comment exists                   |
//+------------------------------------------------------------------+
bool PositionWithCommentExists(string comment)
{
   if(StringLen(comment) == 0) return(false);
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0 && PositionSelectByTicket(ticket))
      {
         string c = PositionGetString(POSITION_COMMENT);
         if(c == comment) return(true);
      }
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Helper functions                                                 |
//+------------------------------------------------------------------+
bool isNewBar()
{
   static datetime lastBar;
   datetime currentBar = iTime(_Symbol, _Period, 0);
   if(lastBar != currentBar)
   {
      lastBar = currentBar;
      return true;
   }
   return false;
}

double getHigh(int index) { return iHigh(_Symbol, _Period, index); }
double getLow(int index) { return iLow(_Symbol, _Period, index); }
double getOpen(int index) { return iOpen(_Symbol, _Period, index); }
double getClose(int index) { return iClose(_Symbol, _Period, index); }
datetime getTime(int index) { return iTime(_Symbol, _Period, index); }

Die Funktion PositionWithCommentExists() spielt eine entscheidende Rolle bei der Aufrechterhaltung der Integrität des Handelsstatus, indem sie überprüft, ob eine Position mit einem bestimmten identifizierenden Kommentar noch existiert. Da jeder Auftragsblockhandel mit einem eindeutigen Kommentar versehen ist, durchläuft diese Funktion alle offenen Positionen, ruft deren Kommentare ab und vergleicht sie mit dem als Eingabe angegebenen Kommentar. Wenn eine Übereinstimmung gefunden wird, wird true zurückgegeben, was bedeutet, dass der Handel noch aktiv ist; andernfalls wird false zurückgegeben. Dieser Überprüfungsprozess ist für Funktionen wie die Bereinigung von Handelsgeschäften und die Schadensbegrenzungslogik von entscheidender Bedeutung, da er sicherstellt, dass Aktionen wie das Entfernen von Objekten oder das Zurücksetzen von Zuständen erst dann erfolgen, wenn das zugehörige Handelsgeschäft wirklich abgeschlossen ist. Durch die Verwendung dieser Methode vermeidet der EA eine doppelte Verarbeitung und erhält ein genaues Abbild der aktuellen und der abgeschlossenen Handelsgeschäfte.

Die folgenden Hilfsfunktionen dienen als unterstützende Dienstprogramme für den Datenabruf und die Verwaltung von Leisten. Die Funktion isNewBar() erkennt, wann sich ein neuer Balken gebildet hat, sodass wichtige Operationen wie die Erkennung von Orderblöcken oder die Überprüfung von Verstößen nur einmal pro Balken ausgeführt werden, wodurch eine redundante Verarbeitung bei jedem Tick vermieden wird. In der Zwischenzeit fungieren die anderen Hilfsfunktionen – getHigh(), getLow(), getOpen(), getClose() und getTime() – als bequeme Abstraktionen zum Abrufen bestimmter Kerzenattribute aus dem Chart. Dadurch wird die Lesbarkeit des Codes vereinfacht und die Konsistenz der Datenverarbeitung im gesamten Expert Advisor sichergestellt. Zusammen bilden diese kleinen, aber wichtigen Werkzeuge die Grundlage dafür, dass komplexere Routinen wie die Identifizierung von Auftragsblöcken und die Handelsausführung reibungslos und effizient funktionieren.


Backtest-Ergebnisse

Die Backtests wurde für den 1H-Zeitrahmen über ein etwa zweimonatiges Testfenster (02. Juni 2025 bis 29. Juli 2025) mit den Standardeinstellungen bewertet.



Schlussfolgerung

Zusammenfassend lässt sich sagen, dass dieser Expert Advisor (EA) Smart-Money-Konzepte (SMC) wie Orderblöcke und Mitigation-Orderblöcke in ein strukturiertes, regelbasiertes Handelssystem integriert. Es beginnt mit der Identifizierung gültiger Auf- und Abwärts-Orderblöcke anhand der Preisstruktur und der Kerzenformationen und überwacht dann Verstöße, um ungültige Orderblöcke in Mitigationszonen umzuwandeln. Durch seine Erkennungs-, Validierungs- und Ausführungsmechanismen verwaltet der EA den Handel dynamisch – er führt Kauf- oder Verkaufspositionen aus, wenn der Preis wieder in die Schlüsselbereiche eintritt. Es verwaltet auch alle visuellen Darstellungen von Orderblöcken, indem es Rechtecke aufzieht und entfernt, wenn Handelsgeschäfte geöffnet oder geschlossen werden, und so sicherstellt, dass der Chart immer die wichtigsten institutionellen Zonen widerspiegelt.

Zusammenfassend lässt sich sagen, dass dieses System einen methodischen Ansatz zur Interpretation der Marktstruktur und des institutionellen Preisverhaltens innerhalb von MQL5 darstellt. Durch die Automatisierung der Erkennung von Orderblöcken, ihrer Übergänge in Abschwächungszonen und des anschließenden Handelsmanagements erfasst der EA die Essenz dessen, wie Smart Money ungültige Zonen für Liquidität und Fortsetzungs-Setups wiederverwendet. Die Einbeziehung von Bereinigungsroutinen, Hilfsfunktionen und strengen Validierungsbedingungen erhöht die Zuverlässigkeit und Realitätsnähe. Insgesamt verbindet dieses Projekt technische Ausführung mit konzeptionellem Marktstrukturhandel und bietet einen vollständigen, selbstverwaltenden Rahmen, der widerspiegelt, wie professionelle Händler die Dynamik des Auftragsflusses interpretieren und darauf reagieren.

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

Beigefügte Dateien |
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.
Entwicklung des Price Action Analysis Toolkit (Teil 47): Verfolgen von Forex-Sitzungen und Ausbrüchen in MetaTrader 5 Entwicklung des Price Action Analysis Toolkit (Teil 47): Verfolgen von Forex-Sitzungen und Ausbrüchen in MetaTrader 5
Globale Marktsitzungen prägen den Rhythmus des Handelstages, und die Kenntnis ihrer Überschneidungen ist entscheidend für das Timing von Ein- und Ausstiegen. In diesem Artikel werden wir einen interaktiven EA für Handelssitzungen erstellen, der diese globalen Stunden direkt auf Ihrem Chart zum Leben erweckt. Der EA zeichnet automatisch farbcodierte Rechtecke für die Sitzungen in Asien, Tokio, London und New York, die in Echtzeit aktualisiert werden, sobald der jeweilige Markt eröffnet oder geschlossen wird. Sie verfügt über Schaltflächen auf dem Chart, ein dynamisches Informationspanel und eine Laufschrift, die Status- und Ausbruchsmeldungen live überträgt. Dieser bei verschiedenen Brokern getestete EA kombiniert Präzision mit Stil und hilft Händlern, Volatilitätsübergänge zu erkennen, sitzungsübergreifende Ausbrüche zu identifizieren und visuell mit dem Puls des globalen Marktes verbunden zu bleiben.
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.
MQL5-Handelswerkzeuge (Teil 9): Entwicklung eines Ersteinrichtungsassistenten für Expert Advisors mit scrollbarem Leitfaden MQL5-Handelswerkzeuge (Teil 9): Entwicklung eines Ersteinrichtungsassistenten für Expert Advisors mit scrollbarem Leitfaden
In diesem Artikel entwickeln wir einen MQL5-Erstanwender-Setup-Assistenten für Expert Advisors mit einem scrollbaren Leitfaden mit interaktivem Dashboard, dynamischer Textformatierung und visuellen Steuerelementen wie Schaltflächen und Kontrollkästchen, die es dem Anwender ermöglichen, Anweisungen zu navigieren und Handelsparameter effizient zu konfigurieren. Die Nutzer des Programms erhalten einen Einblick in die Funktionsweise des Programms und in die ersten Schritte, die sie unternehmen müssen, ähnlich wie bei einem Orientierungsmodell.