English 日本語
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 35): Erstellung eines Blockausbruch-Handelssystems

Automatisieren von Handelsstrategien in MQL5 (Teil 35): Erstellung eines Blockausbruch-Handelssystems

MetaTrader 5Handel |
43 1
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In unserem vorherigen Artikel (Teil 34) haben wir das System Trendline Breakout in MetaQuotes Language 5 (MQL5) entwickelt, das Unterstützungs- und Widerstandstrendlinien mit Hilfe von Umkehrpunkten identifiziert, die durch die R-Quadrat-Anpassungsgüte validiert wurden, um Breakout-Trades mit dynamischen Chartvisualisierungen auszuführen. In Teil 35 erstellen wir das Handelssystem der Ausbruchsblöcke, das Konsolidierungsbereiche erkennt, Ausbruchsblöcke mit Umkehrpunkten validiert und Retests mit anpassbaren Risikoparametern und visuellem Feedback durchführt. Wir werden die folgenden Themen behandeln:

  1. Verstehen der Blockausbruch-Strategie
  2. Implementation in MQL5
  3. Backtests
  4. Schlussfolgerung

Am Ende haben Sie eine funktionierende MQL5-Strategie für den Handel mit Blockausbruch-Retests, die Sie anpassen können – legen wir los!


Verstehen der Blockausbruch-Strategie

Die Strategie der Ausbruchsblöcke ist eine Handelsstrategie, die Konsolidierungsbereiche identifiziert, in denen sich der Preis innerhalb einer engen Spanne bewegt, gefolgt von einem Ausbruch und einer impulsiven Bewegung, wobei Auftragsblöcke gebildet werden, die, wenn sie für ungültig erklärt werden, zu Blockausbrüchen für einen potenziellen Retest-Handel werden. Es nutzt die Rückkehr des Kurses zu diesen Blöcken nach einer signifikanten Abwärtsbewegung, um mit definierten Stop-Loss- und Take-Profit-Levels in Richtung des Ausbruchs zu handeln, ergänzt durch visuelle Chartelemente. Sehen Sie sich unten die verschiedenen Blockausbrüche an, denen wir begegnen könnten.

Abwärts-Ausbruchsblock:

ABWÄRTS-AUSBRUCHSBLOCK

Aufwärts-Ausbruchsblock:

AUFWÄRTS-AUSBRUCHSBLOCK

Unser Plan ist es, Konsolidierungsbereiche durch die Analyse einer bestimmten Anzahl von Balken zu erkennen, Ausbrüche zu identifizieren, wenn der Preis den Bereich verlässt, und impulsive Bewegungen mit Hilfe eines auf einem Multiplikator basierenden Schwellenwertes zu bestätigen. Wir werden eine Logik implementieren, um Blockausbrüche mit Umkehrpunkten zu validieren, Handel bei Retests mit anpassbaren Parametern auszuführen und Blöcke mit dynamischen Kennzeichnungen und Pfeilen zu visualisieren und so ein System zur Identifizierung und zum Handel von Blockausbruch-Chancen zu schaffen. Kurz gesagt, hier ist eine visuelle Darstellung unserer Ziele.

SYSTEM DER BLOCKAUSBRÜCHE


Implementation in MQL5

Um das Programm in MQL5 zu erstellen, öffnen Sie MetaEditor, gehen zum Navigator, suchen den Ordner Experts, klicken auf die Registerkarte „Neu“ und folgen den Anweisungen, um die Datei zu erstellen. Sobald das erstellt ist, müssen wir in der Programmierumgebung einige Eingabeparameter und globale Variablen deklarieren, die wir im gesamten Programm verwenden werden.

//+------------------------------------------------------------------+
//|                                    Breaker Block Strategy EA.mq5 |
//|                           Copyright 2025, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Forex Algo-Trader, Allan"
#property link "https://t.me/Forex_Algo_Trader"
#property version "1.00"
#property strict

#include <Trade/Trade.mqh>                         //--- Include Trade library for position management
CTrade obj_Trade;                                  //--- Instantiate trade object for order operations

Wir beginnen die Implementierung, indem wir die Handelsbibliothek mit „#include <Trade/Trade.mqh>“ einbinden, die integrierte Funktionen für die Verwaltung von Handelsoperationen bietet. Anschließend initialisieren wir das Handelsobjekt „obj_Trade“ mit der Klasse CTrade, sodass der Expert Advisor Kauf- und Verkaufsaufträge programmatisch ausführen kann. So wird sichergestellt, dass die Ausführung von Handelsgeschäften effizient und ohne manuelle Eingriffe erfolgt. Dann können wir einige Eingaben machen, sodass der Nutzer das Verhalten über die Nutzerschnittstelle (UI) ändern und steuern kann.

//+------------------------------------------------------------------+
//| Input Parameters                                                 |
//+------------------------------------------------------------------+
input double tradeLotSize = 0.01;                  // Trade size for each position in lots
input bool   enableTrading = true;                 // Toggle to enable or disable automated trading
input bool   enableTrailingStop = true;            // Toggle to enable or disable trailing stop
input double trailingStopPoints = 30;              // Distance in points for trailing stop adjustment
input double minProfitToTrail = 50;                // Minimum profit in points before trailing starts
input int    uniqueMagicNumber = 12345;            // Unique identifier for EA trades
input int    consolidationBars = 7;                // Number of bars to check for consolidation range
input double maxConsolidationSpread = 50;          // Maximum allowed spread in points for consolidation
input int    barsToWaitAfterBreakout = 3;          // Bars to wait after breakout before impulse check
input double impulseMultiplier = 1.0;              // Multiplier for detecting impulsive price moves
input double stopLossDistance = 1500;              // Stop loss distance in points from entry
input double takeProfitDistance = 1500;            // Take profit distance in points from entry
input double moveAwayDistance = 50;                // Distance in points for price to move away post-invalidation
input color  bullishColor = clrGreen;              // Base color for bullish order/breaker blocks
input color  bearishColor = clrRed;                // Base color for bearish order/breaker blocks
input color  labelTextColor = clrBlack;            // Color for text labels on blocks
input bool   enableSwingValidation = true;         // Enable validation of swing points for block invalidation
input bool   showSwingPoints = true;               // Show swing point labels if validation enabled
input color  swingLabelColor = clrWhite;           // Color for swing point labels
input int    swingFontSize = 10;                   // Font size for swing point labels

Hier definieren wir Eingabeparameter, um das Verhalten des Programms zu konfigurieren: „tradeLotSize“ legt die Positionsgröße fest, während „enableTrading“ und „enableTrailingStop“ die Ausführung und den Trailing-Stop steuern, wobei „trailingStopPoints“ und „minProfitToTrail“ die Stop-Logik verfeinern. „uniqueMagicNumber“ identifiziert Trades, und Konsolidierung wird mit „consolidationBars“ und „maxConsolidationSpread“ erkannt. Der Rest der Eingaben ist selbsterklärend. Wir haben Kommentare hinzugefügt, um ihnen das Verständnis zu erleichtern. Zum Schluss müssen wir noch einige globale Variablen definieren, die wir für die gesamte Systemsteuerung verwenden werden.

//+------------------------------------------------------------------+
//| Structure for price and index                                    |
//+------------------------------------------------------------------+
struct PriceAndIndex {                             //--- Define structure for price and index data
   double price;                                   //--- Store price value (high or low)
   int    index;                                   //--- Store bar index of price
};

//+------------------------------------------------------------------+
//| Global variables for market state tracking                       |
//+------------------------------------------------------------------+
PriceAndIndex rangeHighestHigh = {0, 0};           //--- Track highest high in consolidation range
PriceAndIndex rangeLowestLow = {0, 0};             //--- Track lowest low in consolidation range
bool   isBreakoutDetected = false;                 //--- Flag for breakout detection
double lastImpulseLow = 0.0;                       //--- Store low price after breakout for impulse
double lastImpulseHigh = 0.0;                      //--- Store high price after breakout for impulse
int    breakoutBarNumber = -1;                     //--- Store bar index of breakout
datetime breakoutTimestamp = 0;                    //--- Store timestamp of breakout
string   blockNames[];                             //--- Store names of block objects
datetime blockEndTimes[];                          //--- Store end times of blocks
bool     invalidatedStatus[];                      //--- Track invalidation status of blocks
string   blockTypes[];                             //--- Track block types (OB/BB, bullish/bearish)
bool     movedAwayStatus[];                        //--- Track if price moved away after invalidation
bool     retestedStatus[];                         //--- Track if block was retested
string   blockLabels[];                            //--- Store label object names for blocks
datetime creationTimes[];                          //--- Store creation times of blocks
datetime invalidationTimes[];                      //--- Store invalidation times of blocks
double   invalidationSwings[];                     //--- Store swing high/low at invalidation
bool     isBullishImpulse = false;                 //--- Flag for bullish impulsive move
bool     isBearishImpulse = false;                 //--- Flag for bearish impulsive move
#define OB_Prefix "OB REC "                        //--- Define prefix for order block names

Hier legen wir die Datenstrukturen und die Zustandsverfolgung für das System fest. Zunächst definieren wir die Struktur „PriceAndIndex“, um einen Preiswert (Hoch oder Tief) und den entsprechenden Balkenindex zu speichern, was eine genaue Verfolgung der Grenzen der Konsolidierungsspanne ermöglicht. Dann initialisieren wir die globalen Variablen: „rangeHighestHigh“ und „rangeLowestLow“ als „PriceAndIndex“-Instanzen, um das höchste Hoch und das tiefste Tief des Konsolidierungsbereichs zu speichern, „isBreakoutDetected“ als false, um Ausbruchsereignisse zu kennzeichnen, „lastImpulseLow“ und „lastImpulseHigh“ als 0.0, um die Preise nach dem Ausbruch für Impulsüberprüfungen aufzuzeichnen, „breakoutBarNumber“ als -1 und „breakoutTimestamp“ als 0, um das Ausbruchs-Timing zu verfolgen, und die Arrays „blockNames“, „blockEndTimes“, „invalidatedStatus“, „blockTypes“, „movedAwayStatus“, „retestedStatus“, „blockLabels“, „creationTimes“, „invalidationTimes“ und „invalidationSwings“ zur Verwaltung von Blockobjekten, ihrem Ablauf, ihrer Ungültigkeit, ihren Typen (Order oder Blockausbruch, Auf- oder Abwärts), ihrem Retest-Status und den zugehörigen Umkehrpunkten.

Schließlich definieren wir „OB_Prefix“ als „OB REC“, um eine einheitliche Kennzeichnung von Auftragsblockobjekten zu gewährleisten. Definieren wir nun einige Hilfsfunktionen, die wir benötigen, um die Farbe von ungültig gewordenen Auftragsblöcken abzudunkeln und Ereignisbehandler zu behandeln.

//+------------------------------------------------------------------+
//| Darken color by factor                                           |
//+------------------------------------------------------------------+
color DarkenColor(color colorValue, double factor = 0.8) {
   int red = int((colorValue & 0xFF) * factor);          //--- Extract and darken red component
   int green = int(((colorValue >> 8) & 0xFF) * factor); //--- Extract and darken green component
   int blue = int(((colorValue >> 16) & 0xFF) * factor); //--- Extract and darken blue component
   return (color)(red | (green << 8) | (blue << 16));    //--- Combine components into color
}

//+------------------------------------------------------------------+
//| Price data accessors                                             |
//+------------------------------------------------------------------+
double high(int index) {
   return iHigh(_Symbol, _Period, index);         //--- Return high price for specified index
}
double low(int index) {
   return iLow(_Symbol, _Period, index);          //--- Return low price for specified index
}
double close(int index) {
   return iClose(_Symbol, _Period, index);        //--- Return close price for specified index
}
datetime time(int index) {
   return iTime(_Symbol, _Period, index);         //--- Return time for specified index
}


//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   obj_Trade.SetExpertMagicNumber(uniqueMagicNumber); //--- Set magic number for trade identification
   return(INIT_SUCCEEDED);                            //--- Return initialization success
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   ObjectsDeleteAll(0, OB_Prefix);                //--- Remove all objects with OB prefix
   ChartRedraw(0);                                //--- Redraw chart to clear objects
}

Wir arbeiten weiter an der Implementierung von Nutzungs- und Lebenszyklusmanagementfunktionen für unser System. Zunächst entwickeln wir die Funktion „DarkenColor“, die einen Farbwert und einen optionalen Faktor (Standardwert 0.8), extrahiert die Rot-, Grün- und Blaukomponenten mit bitweisen Operationen („colorValue & 0xFF“, „(colorValue >> 8) & 0xFF“, „(colorValue >> 16) & 0xFF“), verdunkelt sie durch Multiplikation mit dem Faktor und kombiniert sie mit bitweisen Verschiebungen („red | (green << 8) | (blue << 16)“), um eine verdunkelte Farbe zur visuellen Unterscheidung von ungültigen Blöcken zu erhalten.

Anschließend erstellen wir die Zugriffsfunktionen „high“, „low“, „close“ und „time“, die Hoch (iHigh), Tief („iLow“), Schlusspreis („iClose“) und den Zeitstempel (iTime) für einen bestimmten Balken-Index für das aktuelle Symbol und den aktuellen Zeitraum zurückgeben und so das Abrufen von Kursdaten vereinfachen. Als Nächstes rufen wir in der Funktion OnInit „SetExpertMagicNumber“ mit „obj_Trade“ für „uniqueMagicNumber“ auf, um Handelsgeschäfte zur Identifizierung zu markieren und INIT_SUCCEEDED bei einer erfolgreichen Initialisierung zurückzugeben. Schließlich werden in der Funktion OnDeinit mit ObjectsDeleteAll alle Chartobjekte mit „OB_Prefix“ entfernt und ChartRedraw aufgerufen, um das Chart zu aktualisieren und eine saubere Ressourcenbereinigung sicherzustellen. Wir können nun zur Hauptereignisbehandlung durch OnTick übergehen, um unsere wesentliche Steuerungslogik zu implementieren.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   static bool isNewBar = false;                  //--- Track new bar status
   int currentBarCount = iBars(_Symbol, _Period); //--- Get current bar count
   static int previousBarCount = currentBarCount; //--- Store previous bar count
   if (previousBarCount == currentBarCount) {     //--- Check if no new bar
      isNewBar = false;                           //--- Set no new bar
   } else {                                       //--- New bar detected
      isNewBar = true;                            //--- Set new bar flag
      previousBarCount = currentBarCount;         //--- Update previous bar count
   }
   if (!isNewBar) return;                         //--- Exit if not new bar
   int startBarIndex = 1;                         //--- Set start index for analysis
   int chartScale = (int)ChartGetInteger(0, CHART_SCALE); //--- Get chart zoom scale
   int dynamicFontSize = 8 + (chartScale * 2);    //--- Calculate dynamic font size
   if (!isBreakoutDetected) {                     //--- Check if no breakout detected
      if (rangeHighestHigh.price == 0 && rangeLowestLow.price == 0) { //--- Check if range not set
         bool isConsolidated = true;              //--- Assume consolidation
         for (int i = startBarIndex; i < startBarIndex + consolidationBars - 1; i++) { //--- Iterate consolidation bars
            if (MathAbs(high(i) - high(i + 1)) > maxConsolidationSpread * Point() || MathAbs(low(i) - low(i + 1)) > maxConsolidationSpread * Point()) { //--- Check spread
               isConsolidated = false;            //--- Mark as not consolidated
               break;                             //--- Exit loop
            }
         }
         if (isConsolidated) {                    //--- Confirm consolidation
            rangeHighestHigh.price = high(startBarIndex); //--- Set initial high
            rangeHighestHigh.index = startBarIndex; //--- Set high index
            for (int i = startBarIndex + 1; i < startBarIndex + consolidationBars; i++) { //--- Find highest high
               if (high(i) > rangeHighestHigh.price) { //--- Check higher high
                  rangeHighestHigh.price = high(i); //--- Update highest high
                  rangeHighestHigh.index = i;    //--- Update high index
               }
            }
            rangeLowestLow.price = low(startBarIndex); //--- Set initial low
            rangeLowestLow.index = startBarIndex; //--- Set low index
            for (int i = startBarIndex + 1; i < startBarIndex + consolidationBars; i++) { //--- Find lowest low
               if (low(i) < rangeLowestLow.price) { //--- Check lower low
                  rangeLowestLow.price = low(i);  //--- Update lowest low
                  rangeLowestLow.index = i;       //--- Update low index
               }
            }
            Print("Consolidation range established: Highest High = ", rangeHighestHigh.price, " at index ", rangeHighestHigh.index, " and Lowest Low = ", rangeLowestLow.price, " at index ", rangeLowestLow.index); //--- Log range
         }
      } else {                                    //--- Range already set
         double currentHigh = high(1);            //--- Get current high
         double currentLow = low(1);              //--- Get current low
         if (currentHigh <= rangeHighestHigh.price && currentLow >= rangeLowestLow.price) { //--- Check within range
            Print("Range extended: High = ", currentHigh, ", Low = ", currentLow); //--- Log range extension
         } else {                                 //--- Outside range
            Print("No extension: Bar outside range."); //--- Log no extension
         }
      }
   }
}

Wir beginnen mit der Definition einer Logik, um zunächst die Konsolidierungskreise zu ermitteln. In der Funktion OnTick werden neue Balken verfolgt, indem die aktuelle Balkenanzahl von iBars mit einem statischen „previousBarCount“ verglichen wird, „isNewBar“ auf true gesetzt wird und „previousBarCount“ aktualisiert wird, wenn ein neuer Balken erkannt wird, oder false, wenn nicht. Anschließend wird die Zoom-Skala des Charts mit ChartGetInteger unter Verwendung von CHART_SCALE abgerufen und eine „dynamicFontSize“ als 8 plus das Doppelte der Skala für die adaptive Größenanpassung der Kennzeichnungen berechnet. Wenn kein Ausbruch erkannt wird („isBreakoutDetected“ ist falsch) und kein Bereich festgelegt ist („rangeHighestHigh.price“ und „rangeLowestLow.price“ gleich 0), wird auf eine Konsolidierung geprüft, indem die „consolidationBars“ ab „startBarIndex“ 1 durchlaufen werden und sichergestellt wird, dass sich die Höchst- und Tiefstwerte benachbarter Bars um weniger als „maxConsolidationSpread * Point()“ unterscheiden, wobei MathAbs verwendet wird.

Im Falle einer Konsolidierung werden die Werte „rangeHighestHigh.price“ und „rangeLowestLow.price“ mit Hilfe von „high“ und „low“ auf den Höchst- und Tiefstwert von „startBarIndex“ gesetzt, dann werden die „consolidationBars“ durchlaufen, um sie mit ihren Indizes auf den Höchst- und Tiefstwert zu aktualisieren, und der Bereich wird mit der Funktion Print protokolliert. Wenn ein Bereich existiert, wird geprüft, ob das Hoch und das Tief („high(1)“, „low(1)“) des aktuellen Balkens innerhalb des Bereichs „rangeHighestHigh.price“ und „rangeLowestLow.price“ liegen, wobei die Erweiterung protokolliert wird, wenn sie wahr ist, oder keine Erweiterung, wenn sie außerhalb liegt. Wir können nun die Preise verwenden, um die Auftragsblöcke zu erkennen, zu visualisieren und zu verwalten, bevor wir sie für weitere Analysen verwenden, denn wir müssen sie zuerst ungültig machen, bevor sie zu Unterbrecherblöcken werden.

if (rangeHighestHigh.price > 0 && rangeLowestLow.price > 0) { //--- Check if range defined
   double currentClosePrice = close(1);        //--- Get current close price
   if (currentClosePrice > rangeHighestHigh.price) { //--- Check upward breakout
      Print("Upward breakout at ", currentClosePrice, " > ", rangeHighestHigh.price); //--- Log breakout
      isBreakoutDetected = true;               //--- Set breakout flag
   } else if (currentClosePrice < rangeLowestLow.price) { //--- Check downward breakout
      Print("Downward breakout at ", currentClosePrice, " < ", rangeLowestLow.price); //--- Log breakout
      isBreakoutDetected = true;               //--- Set breakout flag
   }
}
if (isBreakoutDetected) {                      //--- Process breakout
   Print("Breakout detected. Resetting for the next range."); //--- Log reset
   breakoutBarNumber = 1;                      //--- Set breakout bar index
   breakoutTimestamp = TimeCurrent();          //--- Set breakout timestamp
   lastImpulseHigh = rangeHighestHigh.price;   //--- Store high for impulse check
   lastImpulseLow = rangeLowestLow.price;      //--- Store low for impulse check
   isBreakoutDetected = false;                 //--- Reset breakout flag
   rangeHighestHigh.price = 0;                 //--- Clear highest high
   rangeHighestHigh.index = 0;                 //--- Clear high index
   rangeLowestLow.price = 0;                   //--- Clear lowest low
   rangeLowestLow.index = 0;                   //--- Clear low index
}
if (breakoutBarNumber >= 0 && TimeCurrent() > breakoutTimestamp + barsToWaitAfterBreakout * PeriodSeconds()) { //--- Check impulse window
   double impulseRange = lastImpulseHigh - lastImpulseLow; //--- Calculate impulse range
   double impulseThresholdPrice = impulseRange * impulseMultiplier; //--- Calculate impulse threshold
   isBullishImpulse = false;                   //--- Reset bullish impulse flag
   isBearishImpulse = false;                   //--- Reset bearish impulse flag
   for (int i = 1; i <= barsToWaitAfterBreakout; i++) { //--- Check bars for impulse
      double closePrice = close(i);            //--- Get close price
      if (closePrice >= lastImpulseHigh + impulseThresholdPrice) { //--- Check bullish impulse
         isBullishImpulse = true;              //--- Set bullish impulse flag
         Print("Impulsive upward move: ", closePrice, " >= ", lastImpulseHigh + impulseThresholdPrice); //--- Log bullish impulse
         break;                                //--- Exit loop
      } else if (closePrice <= lastImpulseLow - impulseThresholdPrice) { //--- Check bearish impulse
         isBearishImpulse = true;              //--- Set bearish impulse flag
         Print("Impulsive downward move: ", closePrice, " <= ", lastImpulseLow - impulseThresholdPrice); //--- Log bearish impulse
         break;                                //--- Exit loop
      }
   }
   if (!isBullishImpulse && !isBearishImpulse) { //--- Check no impulse
      Print("No impulsive movement detected."); //--- Log no impulse
   }
   bool isOrderBlockValid = isBearishImpulse || isBullishImpulse; //--- Validate order block
   if (isOrderBlockValid) {                    //--- Process valid order block
      datetime blockStartTime = iTime(_Symbol, _Period, consolidationBars + barsToWaitAfterBreakout + 1); //--- Set block start time
      double blockTopPrice = lastImpulseHigh;  //--- Set block top price
      int visibleBarsOnChart = (int)ChartGetInteger(0, CHART_VISIBLE_BARS); //--- Get visible bars
      datetime blockEndTime = blockStartTime + (visibleBarsOnChart / 1) * PeriodSeconds(); //--- Set block end time
      double blockBottomPrice = lastImpulseLow; //--- Set block bottom price
      string blockName = OB_Prefix + "(" + TimeToString(blockStartTime) + ")"; //--- Generate block name
      color blockColor = isBullishImpulse ? bullishColor : bearishColor; //--- Set block color
      string blockLabel = isBullishImpulse ? "Bullish Order Block" : "Bearish Order Block"; //--- Set block label
      string blockType = isBullishImpulse ? "OB-bullish" : "OB-bearish"; //--- Set block type
      if (ObjectFind(0, blockName) < 0) {      //--- Check if block exists
         ObjectCreate(0, blockName, OBJ_RECTANGLE, 0, blockStartTime, blockTopPrice, blockEndTime, blockBottomPrice); //--- Create block rectangle
         ObjectSetInteger(0, blockName, OBJPROP_TIME, 0, blockStartTime); //--- Set start time
         ObjectSetDouble(0, blockName, OBJPROP_PRICE, 0, blockTopPrice); //--- Set top price
         ObjectSetInteger(0, blockName, OBJPROP_TIME, 1, blockEndTime); //--- Set end time
         ObjectSetDouble(0, blockName, OBJPROP_PRICE, 1, blockBottomPrice); //--- Set bottom price
         ObjectSetInteger(0, blockName, OBJPROP_FILL, true); //--- Enable fill
         ObjectSetInteger(0, blockName, OBJPROP_COLOR, blockColor); //--- Set block color
         ObjectSetInteger(0, blockName, OBJPROP_BACK, false); //--- Set to foreground
         datetime labelTime = blockStartTime + (blockEndTime - blockStartTime) / 2; //--- Calculate label time
         double labelPrice = (blockTopPrice + blockBottomPrice) / 2; //--- Calculate label price
         string labelObjectName = blockName + " Label"; //--- Generate label name
         ObjectCreate(0, labelObjectName, OBJ_TEXT, 0, labelTime, labelPrice); //--- Create label
         ObjectSetString(0, labelObjectName, OBJPROP_TEXT, blockLabel); //--- Set label text
         ObjectSetInteger(0, labelObjectName, OBJPROP_COLOR, labelTextColor); //--- Set label color
         ObjectSetInteger(0, labelObjectName, OBJPROP_FONTSIZE, dynamicFontSize); //--- Set label font size
         ObjectSetInteger(0, labelObjectName, OBJPROP_ANCHOR, ANCHOR_CENTER); //--- Set label anchor
         ChartRedraw(0);                       //--- Redraw chart
         ArrayResize(blockNames, ArraySize(blockNames) + 1); //--- Resize block names array
         blockNames[ArraySize(blockNames) - 1] = blockName; //--- Add block name
         ArrayResize(blockEndTimes, ArraySize(blockEndTimes) + 1); //--- Resize end times array
         blockEndTimes[ArraySize(blockEndTimes) - 1] = blockEndTime; //--- Add end time
         ArrayResize(invalidatedStatus, ArraySize(invalidatedStatus) + 1); //--- Resize invalidated status
         invalidatedStatus[ArraySize(invalidatedStatus) - 1] = false; //--- Set invalidated status
         ArrayResize(blockTypes, ArraySize(blockTypes) + 1); //--- Resize block types array
         blockTypes[ArraySize(blockTypes) - 1] = blockType; //--- Add block type
         ArrayResize(movedAwayStatus, ArraySize(movedAwayStatus) + 1); //--- Resize moved away status
         movedAwayStatus[ArraySize(movedAwayStatus) - 1] = false; //--- Set moved away status
         ArrayResize(retestedStatus, ArraySize(retestedStatus) + 1); //--- Resize retested status
         retestedStatus[ArraySize(retestedStatus) - 1] = false; //--- Set retested status
         ArrayResize(blockLabels, ArraySize(blockLabels) + 1); //--- Resize block labels array
         blockLabels[ArraySize(blockLabels) - 1] = labelObjectName; //--- Add label name
         ArrayResize(creationTimes, ArraySize(creationTimes) + 1); //--- Resize creation times
         creationTimes[ArraySize(creationTimes) - 1] = time(1); //--- Set creation time
         ArrayResize(invalidationTimes, ArraySize(invalidationTimes) + 1); //--- Resize invalidation times
         invalidationTimes[ArraySize(invalidationTimes) - 1] = 0; //--- Set invalidation time
         ArrayResize(invalidationSwings, ArraySize(invalidationSwings) + 1); //--- Resize invalidation swings
         invalidationSwings[ArraySize(invalidationSwings) - 1] = 0.0; //--- Set invalidation swing
         Print("Order Block created: ", blockName); //--- Log block creation
      }
   }
   breakoutBarNumber = -1;                     //--- Reset breakout bar
   breakoutTimestamp = 0;                      //--- Reset breakout timestamp
   lastImpulseHigh = 0;                        //--- Reset impulse high
   lastImpulseLow = 0;                         //--- Reset impulse low
   isBullishImpulse = false;                   //--- Reset bullish impulse
   isBearishImpulse = false;                   //--- Reset bearish impulse
}

Hier implementieren wir die Logik zur Erkennung von Unterbrechungen und zur Erstellung von Auftragsblöcken. Zunächst prüfen wir, ob ein Konsolidierungsbereich definiert ist („rangeHighestHigh.price“ und „rangeLowestLow.price“ > 0), indem wir den Schlusskurs des aktuellen Balkens mit „close(1)“ abrufen; wenn er über „rangeHighestHigh.price“ überschreitet, wird ein Ausbruch nach oben protokolliert und „isBreakoutDetected“ auf true gesetzt; liegt er unter „rangeLowestLow.price“, wird ein Ausbruch nach unten protokolliert und das Flag gesetzt. Wenn „isBreakoutDetected“ wahr ist, protokollieren wir den Breakout, setzen „breakoutBarNumber“ auf 1 und „breakoutTimestamp“ auf TimeCurrent, speichern „lastImpulseHigh“ und „lastImpulseLow“ und setzen die Bereichsvariablen und das Breakout-Flag zurück.

Wenn „breakoutBarNumber“ nicht negativ ist und die aktuelle Zeit den Wert von „breakoutTimestamp + barsToWaitAfterBreakout * PeriodSeconds“ übersteigt, berechnen wir den „impulseRange“ („lastImpulseHigh – lastImpulseLow“) und den Schwellenwert („impulseRange * impulseMultiplier“), wobei wir Bars innerhalb von „barsToWaitAfterBreakout“ auf einen Schlusskurs prüfen, der über „lastImpulseHigh + impulseThresholdPrice“ (Einstellung „isBullishImpulse“) oder unter „lastImpulseLow – impulseThresholdPrice“ (Einstellung „isBearishImpulse“) liegt.

Wenn kein Impuls erkannt wird, protokollieren wir das; wenn doch und gültig („isBearishImpulse“ oder „isBullishImpulse“), erstellen wir einen Orderblock mit ObjectCreate (OBJ_RECTANGLE) mit „blockStartTime“ aus iTime, Top/Bottom-Preisen aus „lastImpulseHigh“/“lastImpulseLow“ und Endzeit basierend auf „ChartGetInteger(CHART_VISIBLE_BARS)“, wobei Eigenschaften wie „OBJPROP_FILL“ und „OBJPROP_COLOR“ („bullishColor“ oder „bearishColor“), fügen eine zentrierte Kennzeichnung mit „blockLabel“ über „OBJ_TEXT“ hinzu und aktualisieren die Arrays „blockNames“, „blockEndTimes“, „invalidatedStatus“, „blockTypes“, „movedAwayStatus“, „retestedStatus“, „blockLabels“, „creationTimes“, und „invalidationSwings“. Schließlich setzen wir die Breakout-Variablen zurück. So entsteht ein System zur Erkennung von Ausbrüchen und zur Visualisierung von Auftragsblöcken. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

ERSTES ERKENNUNG VON ANFÄNGLICHEN AUFTRAGSBLÖCKEN

Da wir nun die anfänglichen Orderblöcke erkennen können, müssen wir eine Logik definieren, damit wir sie bei einem entsprechenden Preisausbruch als ungültig markieren und die Ungültigkeit anhand von Preisaktionsschwungpunkten bestätigen. Wir werden ihre Farbe zur Unterscheidung verdunkeln.

for (int j = ArraySize(blockNames) - 1; j >= 0; j--) { //--- Iterate through blocks
   string currentBlockName = blockNames[j];    //--- Get current block name
   bool doesBlockExist = false;                //--- Initialize block existence flag
   double blockHigh = ObjectGetDouble(0, currentBlockName, OBJPROP_PRICE, 0); //--- Get block high
   double blockLow = ObjectGetDouble(0, currentBlockName, OBJPROP_PRICE, 1); //--- Get block low
   datetime blockStartTime = (datetime)ObjectGetInteger(0, currentBlockName, OBJPROP_TIME, 0); //--- Get block start
   datetime blockEndTime = (datetime)ObjectGetInteger(0, currentBlockName, OBJPROP_TIME, 1); //--- Get block end
   color blockCurrentColor = (color)ObjectGetInteger(0, currentBlockName, OBJPROP_COLOR); //--- Get block color
   if (time(1) < blockEndTime) {               //--- Check if block still valid
      doesBlockExist = true;                   //--- Set block exists
   }
   if (StringFind(blockTypes[j], "OB-") == 0 && !invalidatedStatus[j]) { //--- Check valid order block
      bool invalidated = false;                //--- Initialize invalidation flag
      string newBlockType = "";                //--- Initialize new block type
      color invalidatedColor = clrNONE;        //--- Initialize invalidated color
      string newLabel = "";                    //--- Initialize new label
      bool isForBullishBB = false;             //--- Initialize bullish breaker block flag
      double breakPrice = 0.0;                 //--- Initialize break price
      int arrowCode = 0;                       //--- Initialize arrow code
      int anchor = 0;                          //--- Initialize anchor
      if (blockTypes[j] == "OB-bearish" && close(1) > blockHigh) { //--- Check bearish block invalidation
         isForBullishBB = true;                //--- Set bullish breaker block
         breakPrice = blockHigh;               //--- Set break price
         arrowCode = 233;                      //--- Set upward arrow
         anchor = ANCHOR_BOTTOM;               //--- Set bottom anchor
         newBlockType = "Invalidated-bearish"; //--- Set invalidated type
         invalidatedColor = DarkenColor(bearishColor); //--- Darken bearish color
         newLabel = "Invalidated Bearish Order Block"; //--- Set invalidated label
      } else if (blockTypes[j] == "OB-bullish" && close(1) < blockLow) { //--- Check bullish block invalidation
         isForBullishBB = false;               //--- Set bearish breaker block
         breakPrice = blockLow;                //--- Set break price
         arrowCode = 234;                      //--- Set downward arrow
         anchor = ANCHOR_TOP;                  //--- Set top anchor
         newBlockType = "Invalidated-bullish"; //--- Set invalidated type
         invalidatedColor = DarkenColor(bullishColor); //--- Darken bullish color
         newLabel = "Invalidated Bullish Order Block"; //--- Set invalidated label
      } else {                                 //--- No invalidation
         continue;                             //--- Skip to next block
      }
      bool validSwingForInvalidation = true;   //--- Assume valid swing
      int swingShift = -1;                     //--- Initialize swing shift
      double swingPrice = 0.0;                 //--- Initialize swing price
      if (enableSwingValidation) {             //--- Check swing validation
         int creationShift = iBarShift(_Symbol, _Period, creationTimes[j], false); //--- Get creation bar shift
         if (creationShift > 1) {              //--- Ensure enough bars
            double extreme = isForBullishBB ? blockLow : blockHigh; //--- Set extreme price
            bool isBearishOB = isForBullishBB; //--- Set bearish OB flag
            if (isBearishOB) {                 //--- Handle bearish OB
               double minLow = extreme;        //--- Initialize minimum low
               for (int k = creationShift - 1; k > 1; k--) { //--- Find lower low
                  if (low(k) < minLow) {       //--- Check lower low
                     minLow = low(k);          //--- Update minimum low
                     swingShift = k;           //--- Update swing shift
                  }
               }
               validSwingForInvalidation = minLow < extreme; //--- Validate swing
               swingPrice = minLow;            //--- Set swing price
            } else {                           //--- Handle bullish OB
               double maxHigh = extreme;       //--- Initialize maximum high
               for (int k = creationShift - 1; k > 1; k--) { //--- Find higher high
                  if (high(k) > maxHigh) {     //--- Check higher high
                     maxHigh = high(k);        //--- Update maximum high
                     swingShift = k;           //--- Update swing shift
                  }
               }
               validSwingForInvalidation = maxHigh > extreme; //--- Validate swing
               swingPrice = maxHigh;           //--- Set swing price
            }
         } else {                              //--- Insufficient bars
            validSwingForInvalidation = false; //--- Invalidate swing
         }
      }
      if (validSwingForInvalidation) {        //--- Confirm swing validation
         invalidated = true;                  //--- Set invalidated flag
      }
      if (invalidated) {                      //--- Process invalidation
         ObjectSetInteger(0, currentBlockName, OBJPROP_COLOR, invalidatedColor); //--- Update block color
         ObjectDelete(0, blockLabels[j]);     //--- Delete old label
         datetime labelTime = blockStartTime + (blockEndTime - blockStartTime) / 2; //--- Calculate new label time
         double labelPrice = (blockHigh + blockLow) / 2; //--- Calculate new label price
         string newLabelObjectName = currentBlockName + " Label"; //--- Generate new label name
         ObjectCreate(0, newLabelObjectName, OBJ_TEXT, 0, labelTime, labelPrice); //--- Create new label
         ObjectSetString(0, newLabelObjectName, OBJPROP_TEXT, newLabel); //--- Set label text
         ObjectSetInteger(0, newLabelObjectName, OBJPROP_COLOR, labelTextColor); //--- Set label color
         ObjectSetInteger(0, newLabelObjectName, OBJPROP_FONTSIZE, dynamicFontSize); //--- Set label font size
         ObjectSetInteger(0, newLabelObjectName, OBJPROP_ANCHOR, ANCHOR_CENTER); //--- Set label anchor
         string arrowName = currentBlockName + "_break_arrow"; //--- Generate arrow name
         if (ObjectFind(0, arrowName) < 0) {  //--- Check if arrow exists
            ObjectCreate(0, arrowName, OBJ_ARROW, 0, time(1), breakPrice); //--- Create break arrow
            ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode); //--- Set arrow code
            ObjectSetInteger(0, arrowName, OBJPROP_ANCHOR, anchor); //--- Set arrow anchor
            ObjectSetInteger(0, arrowName, OBJPROP_COLOR, invalidatedColor); //--- Set arrow color
         }
         if (enableSwingValidation && showSwingPoints && swingShift > 0) { //--- Check swing point display
            string swingLabelName = currentBlockName + "_invalid_swing"; //--- Generate swing label name
            if (ObjectFind(0, swingLabelName) < 0) { //--- Check if swing label exists
               datetime swingTime = time(swingShift); //--- Get swing time
               ObjectCreate(0, swingLabelName, OBJ_TEXT, 0, swingTime, swingPrice); //--- Create swing label
               string swingText = isForBullishBB ? "LL" : "HH"; //--- Set swing text
               ObjectSetString(0, swingLabelName, OBJPROP_TEXT, swingText); //--- Set swing label text
               ObjectSetInteger(0, swingLabelName, OBJPROP_COLOR, swingLabelColor); //--- Set swing label color
               ObjectSetInteger(0, swingLabelName, OBJPROP_FONTSIZE, swingFontSize); //--- Set swing label font size
               ObjectSetInteger(0, swingLabelName, OBJPROP_ANCHOR, isForBullishBB ? ANCHOR_LEFT_UPPER : ANCHOR_LEFT_LOWER); //--- Set swing label anchor
            }
         }
         ChartRedraw(0);                       //--- Redraw chart
         invalidatedStatus[j] = true;          //--- Set invalidated status
         blockTypes[j] = newBlockType;         //--- Update block type
         movedAwayStatus[j] = false;           //--- Reset moved away status
         retestedStatus[j] = false;            //--- Reset retested status
         blockLabels[j] = newLabelObjectName;  //--- Update label name
         invalidationTimes[j] = time(1);       //--- Set invalidation time
         invalidationSwings[j] = isForBullishBB ? high(1) : low(1); //--- Set invalidation swing
         Print("Order Block invalidated: ", currentBlockName); //--- Log invalidation
      }
   }
   if (!doesBlockExist) {                     //--- Check if block expired
      ArrayRemove(blockNames, j, 1);          //--- Remove block name
      ArrayRemove(blockEndTimes, j, 1);       //--- Remove end time
      ArrayRemove(invalidatedStatus, j, 1);   //--- Remove invalidated status
      ArrayRemove(blockTypes, j, 1);          //--- Remove block type
      ArrayRemove(movedAwayStatus, j, 1);     //--- Remove moved away status
      ArrayRemove(retestedStatus, j, 1);      //--- Remove retested status
      ArrayRemove(blockLabels, j, 1);         //--- Remove label name
      ArrayRemove(creationTimes, j, 1);       //--- Remove creation time
      ArrayRemove(invalidationTimes, j, 1);   //--- Remove invalidation time
      ArrayRemove(invalidationSwings, j, 1);  //--- Remove invalidation swing
      Print("Removed expired block at index ", j); //--- Log block removal
   }
}

Um die Logik für die Verwaltung und Ungültigmachung von Orderblöcken zu implementieren, iterieren wir rückwärts durch „blockNames“, um jeden Block zu verarbeiten, wobei wir seine Höchst- und Tiefstpreise mit ObjectGetDouble und seine Start-/Endzeiten mit ObjectGetInteger abrufen und ihn als vorhanden markieren, wenn die Zeit des aktuellen Balkens („time(1)“) vor seiner Endzeit liegt. Bei gültigen Orderblöcken (Typ beginnend mit „OB-“ und nicht ungültig) prüfen wir die Ungültigkeit: Wenn „OB-bearish“ und der Schlusskurs („close(1)“) das Hoch des Blocks übersteigt, wird mit „ObjectCreate“ (OBJ_ARROW) und einer verdunkelten Farbe aus „DarkenColor“ ein Aufwärts-Blockausbruch mit einem Aufwärtspfeil (Code 233) erstellt (OBJ_ARROW) und einer verdunkelten Farbe aus „DarkenColor“; wenn „OB-bullish“ und der Schlusskurs unter dem Tiefstkurs des Blocks liegt, setzen wir einen Abwärts-Blockausbruch mit einem Pfeil nach unten (Code 234). MQL5 bietet eine umfangreiche Liste von Wingdings-Codes an, die Sie nach Belieben verwenden können.

MQL5 WINGDINGS

Wenn „enableSwingValidation“ wahr ist, wird der Block validiert, indem mit iBarShift und „low“ bzw. „high“ geprüft wird, ob seit der Erstellung ein tieferes Tief (abwärts) oder ein höheres Hoch (aufwärts) erreicht wurde, und die Farbe und Kennzeichnung des Blocks mit ObjectSetInteger und ObjectCreate (OBJ_TEXT) aktualisiert, falls sie gültig sind. Wenn „showSwingPoints“ aktiviert ist, fügen wir ein Umkehrkennzeichnung („LL“ oder „HH“) mit „ObjectCreate“ zum Zeitpunkt und Preis des Umkehrpunktes hinzu. Wenn sie für ungültig erklärt werden, aktualisieren wir den Blockstatus mit „invalidatedStatus“, „blockTypes“ und „invalidationTimes“, protokollieren die Ungültigkeitserklärung und setzen den Status der Wiederholungsprüfung zurück. Wenn ein Block nicht vorhanden ist, werden seine Zustände, wie z. B. „invalidatedStatus“, mit ArrayRemove aus den Speicher-Arrays entfernt und die Entfernung protokolliert, dann wird das Chart mit der Funktion ChartRedraw neu gezeichnet. Nach der Kompilierung sollten Sie folgendes Ergebnis sehen.

UNGÜLTIG GEMACHTE AUFTRAGSBLÖCKE

Nun, da der zweite Schritt der Ungültigkeitserklärung abgeschlossen ist, können wir zum nächsten Schritt übergehen, bei dem wir den Kurs verfolgen und darauf warten, dass der Preis unsere ungültig gemachten Orderblöcke erneut testet und sie als Blockausbrüche markiert.

for (int j = ArraySize(blockNames) - 1; j >= 0; j--) { //--- Iterate invalidated blocks
   if (StringFind(blockTypes[j], "Invalidated-") != 0) continue; //--- Skip non-invalidated
   string currentBlockName = blockNames[j];    //--- Get current block name
   double blockHigh = ObjectGetDouble(0, currentBlockName, OBJPROP_PRICE, 0); //--- Get block high
   double blockLow = ObjectGetDouble(0, currentBlockName, OBJPROP_PRICE, 1); //--- Get block low
   datetime blockStartTime = (datetime)ObjectGetInteger(0, currentBlockName, OBJPROP_TIME, 0); //--- Get block start
   datetime blockEndTime = (datetime)ObjectGetInteger(0, currentBlockName, OBJPROP_TIME, 1); //--- Get block end
   bool isForBullishBB = (blockTypes[j] == "Invalidated-bearish"); //--- Check for bullish breaker block
   datetime currentBarTime = time(1);          //--- Get current bar time
   if (currentBarTime <= invalidationTimes[j]) continue; //--- Skip if same or earlier bar
   if (!movedAwayStatus[j]) {                  //--- Check if not moved away
      if (isForBullishBB && close(1) > blockHigh + moveAwayDistance * _Point) { //--- Check bullish move away
         movedAwayStatus[j] = true;           //--- Set moved away
         Print("Moved away for bullish BB setup: ", currentBlockName); //--- Log move away
      } else if (!isForBullishBB && close(1) < blockLow - moveAwayDistance * _Point) { //--- Check bearish move away
         movedAwayStatus[j] = true;           //--- Set moved away
         Print("Moved away for bearish BB setup: ", currentBlockName); //--- Log move away
      }
   }
   if (movedAwayStatus[j] && !retestedStatus[j]) { //--- Check for retest
      bool retestCondition = false;            //--- Initialize retest condition
      if (isForBullishBB && low(1) <= blockHigh && close(1) > blockHigh) { //--- Check bullish retest
         retestCondition = true;               //--- Set retest condition
      } else if (!isForBullishBB && high(1) >= blockLow && close(1) < blockLow) { //--- Check bearish retest
         retestCondition = true;               //--- Set retest condition
      }
      bool validSwingForRetest = true;         //--- Assume valid swing
      int swingShift = -1;                     //--- Initialize swing shift
      double swingPrice = 0.0;                 //--- Initialize swing price
      if (enableSwingValidation && retestCondition) { //--- Check swing validation
         int invalidShift = iBarShift(_Symbol, _Period, invalidationTimes[j], false); //--- Get invalidation shift
         if (invalidShift > 1) {               //--- Ensure enough bars
            double extreme = invalidationSwings[j]; //--- Get invalidation swing
            if (isForBullishBB) {              //--- Handle bullish breaker block
               double maxHigh = extreme;       //--- Initialize maximum high
               for (int k = invalidShift - 1; k > 1; k--) { //--- Find higher high
                  if (high(k) > maxHigh) {     //--- Check higher high
                     maxHigh = high(k);        //--- Update maximum high
                     swingShift = k;           //--- Update swing shift
                  }
               }
               validSwingForRetest = maxHigh > extreme; //--- Validate swing
               swingPrice = maxHigh;           //--- Set swing price
            } else {                           //--- Handle bearish breaker block
               double minLow = extreme;        //--- Initialize minimum low
               for (int k = invalidShift - 1; k > 1; k--) { //--- Find lower low
                  if (low(k) < minLow) {       //--- Check lower low
                     minLow = low(k);          //--- Update minimum low
                     swingShift = k;           //--- Update swing shift
                  }
               }
               validSwingForRetest = minLow < extreme; //--- Validate swing
               swingPrice = minLow;            //--- Set swing price
            }
         } else {                              //--- Insufficient bars
            validSwingForRetest = false;       //--- Invalidate swing
         }
      }
   }
}

Um die Logik zur Erkennung von ungültig gewordenen Blockausbrüchen zu implementieren, iterieren wir rückwärts durch „blockNames“ nach Blöcken mit „Invalidated-“ in „blockTypes“, rufen Hoch- und Tiefstpreise mit ObjectGetDouble und Start-/Endzeiten mit ObjectGetInteger ab und prüfen, ob der Block ein Aufwärts-Blockausbruch ist („Invalidated-bearish“). Wenn die Zeit des aktuellen Balkens („time(1)“) nicht nach „invalidationTimes“ liegt, wird zum nächsten Block übergegangen. Bei Blöcken, die noch nicht weggezogen wurden („movedAwayStatus“ false), wird geprüft, ob der Schlusskurs („close(1)“) „blockHigh + moveAwayDistance * _Point“ für aufwärts übersteigt oder „blockLow – moveAwayDistance * _Point“ für abwärts unterschreitet, und „movedAwayStatus“ auf true gesetzt.

Für Blöcke, die sich entfernt haben, aber nicht erneut getestet wurden („retestedStatus“ false), setzen wir einen Aufwärts-Retest, wenn „low(1)“ „blockHigh“ erreicht und „close(1)“ darüber liegt, oder einen Abwärts-Retest, wenn „high(1)“ „blockLow“ erreicht und „close(1)“ darunter liegt. Wenn „enableSwingValidation“ und eine Wiederholungsbedingung erfüllt sind, validieren wir die Umkehrpunkte mit Hilfe von iBarShift, um den Ungültigkeitsbalken zu erhalten, und prüfen, ob seit der Ungültigkeitserklärung ein höheres Hoch („high“) für einen Aufwärtstrend oder ein niedrigeres Tief („low“) für einen Abwärtstrend eingetreten ist, und setzen „validSwingForRetest“ und „swingPrice“ entsprechend. Wir können die Wiederholungstests verfolgen, die Blöcke als Unterbrechungsblöcke markieren, ihre Farbe zur Unterscheidung ändern und Positionen eröffnen. Hier ist die Logik, mit der wir das erreichen.

if (retestCondition && validSwingForRetest) { //--- Confirm retest and swing
   if (enableTrading) {                  //--- Check trading enabled
      double entryPrice = 0.0;           //--- Initialize entry price
      double stopLossPrice = 0.0;        //--- Initialize stop loss
      double takeProfitPrice = 0.0;      //--- Initialize take profit
      if (isForBullishBB) {              //--- Handle bullish trade
         entryPrice = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits); //--- Set entry at ask
         stopLossPrice = entryPrice - stopLossDistance * _Point; //--- Set stop loss
         takeProfitPrice = entryPrice + takeProfitDistance * _Point; //--- Set take profit
         obj_Trade.Buy(tradeLotSize, _Symbol, entryPrice, stopLossPrice, takeProfitPrice); //--- Execute buy trade
         Print("Buy trade on bullish BB retest: ", currentBlockName); //--- Log buy trade
      } else {                           //--- Handle bearish trade
         entryPrice = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits); //--- Set entry at bid
         stopLossPrice = entryPrice + stopLossDistance * _Point; //--- Set stop loss
         takeProfitPrice = entryPrice - takeProfitDistance * _Point; //--- Set take profit
         obj_Trade.Sell(tradeLotSize, _Symbol, entryPrice, stopLossPrice, takeProfitPrice); //--- Execute sell trade
         Print("Sell trade on bearish BB retest: ", currentBlockName); //--- Log sell trade
      }
   }
   color bbColor = isForBullishBB ? clrBlueViolet : clrOrange; //--- Set breaker block color
   ObjectSetInteger(0, currentBlockName, OBJPROP_COLOR, bbColor); //--- Update block color
   ObjectDelete(0, blockLabels[j]);     //--- Delete old label
   string newLabel = isForBullishBB ? "Bullish Breaker Block" : "Bearish Breaker Block"; //--- Set new label
   datetime labelTime = blockStartTime + (blockEndTime - blockStartTime) / 2; //--- Calculate label time
   double labelPrice = (blockHigh + blockLow) / 2; //--- Calculate label price
   string newLabelObjectName = currentBlockName + " Label"; //--- Generate new label name
   ObjectCreate(0, newLabelObjectName, OBJ_TEXT, 0, labelTime, labelPrice); //--- Create new label
   ObjectSetString(0, newLabelObjectName, OBJPROP_TEXT, newLabel); //--- Set label text
   ObjectSetInteger(0, newLabelObjectName, OBJPROP_COLOR, labelTextColor); //--- Set label color
   ObjectSetInteger(0, newLabelObjectName, OBJPROP_FONTSIZE, dynamicFontSize); //--- Set label font size
   ObjectSetInteger(0, newLabelObjectName, OBJPROP_ANCHOR, ANCHOR_CENTER); //--- Set label anchor
   if (enableSwingValidation && showSwingPoints && swingShift > 0) { //--- Check swing point display
      string swingLabelName = currentBlockName + "_retest_swing"; //--- Generate swing label name
      if (ObjectFind(0, swingLabelName) < 0) { //--- Check if swing label exists
         datetime swingTime = time(swingShift); //--- Get swing time
         ObjectCreate(0, swingLabelName, OBJ_TEXT, 0, swingTime, swingPrice); //--- Create swing label
         string swingText = isForBullishBB ? "HH" : "LL"; //--- Set swing text
         ObjectSetString(0, swingLabelName, OBJPROP_TEXT, swingText); //--- Set swing label text
         ObjectSetInteger(0, swingLabelName, OBJPROP_COLOR, swingLabelColor); //--- Set swing label color
         ObjectSetInteger(0, swingLabelName, OBJPROP_FONTSIZE, swingFontSize); //--- Set swing label font size
         ObjectSetInteger(0, swingLabelName, OBJPROP_ANCHOR, isForBullishBB ? ANCHOR_LEFT_LOWER : ANCHOR_LEFT_UPPER); //--- Set swing label anchor
      }
   }
   ChartRedraw(0);                       //--- Redraw chart
   blockTypes[j] = isForBullishBB ? "BB-bullish" : "BB-bearish"; //--- Update block type
   retestedStatus[j] = true;             //--- Set retested status
   blockLabels[j] = newLabelObjectName;  //--- Update label name
   Print("Converted to ", newLabel, ": ", currentBlockName); //--- Log conversion
}

Schließlich implementieren wir die Handels- und Visualisierungslogik für neu getestete Unterbrecherblöcke. Wenn ein Retest bestätigt wird („retestCondition“ und „validSwingForRetest“ sind true) und der Handel aktiviert ist („enableTrading“), führen wir Handelsgeschäfte aus: bei einen Aufwärts-Blockausbruch („isForBullishBB“) setzen wir den Einstieg zum Briefkurs mit SymbolInfoDouble, berechnen StopLoss („stopLossDistance * _Point“ unterhalb des Einstiegs) und TakeProfit („takeProfitDistance * _Point“ oberhalb des Einstiegs) und führen einen Kauf mit „obj_Trade.Buy“; bei einen Abwärts-Blockausbruch verwenden wir den Geldkurs, setzen den Stop-Loss über und den Take-Profit unter und führen einen Verkauf mit „obj_Trade.Sell“ aus und protokollieren entsprechend.

Anschließend aktualisieren wir das Erscheinungsbild des Blocks, indem wir seine Farbe mit ObjectSetInteger auf clrBlueViolet für aufwärts oder „clrOrange” für abwärts setzen, die alte Kennzeichnung mit ObjectDelete löschen und mit OBJ_TEXT eine neue Kennzeichnung („Bullish Breaker Block” oder „Bearish Breaker Block”) am Mittelpunkt des Blocks mit „ObjectCreate” mit „labelTextColor” und „dynamicFontSize”. Wenn „enableSwingValidation“ und „showSwingPoints“ wahr sind und ein gültiger „swingShift“ vorliegt, fügen wir eine Kennzeichnung des Umkehrpunkts („HH“ für bullish, „LL“ für bearish) zur Zeit und zum Preis des Umkehrpunks mit ObjectCreate mit „swingLabelColor“ und „swingFontSize“ hinzu. Schließlich aktualisieren wir „blockTypes“ auf „BB-bullish“ oder „BB-bearish“, setzen „retestedStatus“ auf true, aktualisieren „blockLabels“, protokollieren die Umrechnung und zeichnen das Chart neu. Nach dem Kompilieren erhalten wir folgendes Ergebnis.

BESTÄTIGTE AUSBRUCHSBLÖCKE

Anhand des Bildes können wir sehen, dass wir die Ausbruchsblöcke erkennen, visualisieren und entsprechend handeln können. Jetzt müssen wir nur noch eine Trailing-Stop-Logik hinzufügen, um die Gewinne zu maximieren, und das war's. Wir definieren eine Funktion dafür, um den Code zu modularisieren.

//+------------------------------------------------------------------+
//| Apply trailing stop to open positions                            |
//+------------------------------------------------------------------+
void applyTrailingStop(double trailingPoints, CTrade &trade_object, int magicNo = 0) {
   double buyStopLoss = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - trailingPoints * _Point, _Digits); //--- Calculate buy stop loss
   double sellStopLoss = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + trailingPoints * _Point, _Digits); //--- Calculate sell stop loss
   for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through open positions
      ulong ticket = PositionGetTicket(i);         //--- Get position ticket
      if (ticket > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol && (magicNo == 0 || PositionGetInteger(POSITION_MAGIC) == magicNo)) { //--- Verify position
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && buyStopLoss > PositionGetDouble(POSITION_PRICE_OPEN) && (buyStopLoss > PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL) == 0)) { //--- Check buy trailing
            trade_object.PositionModify(ticket, buyStopLoss, PositionGetDouble(POSITION_TP)); //--- Update buy stop loss
         } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && sellStopLoss < PositionGetDouble(POSITION_PRICE_OPEN) && (sellStopLoss < PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL) == 0)) { //--- Check sell trailing
            trade_object.PositionModify(ticket, sellStopLoss, PositionGetDouble(POSITION_TP)); //--- Update sell stop loss
         }
      }
   }
}

//--- Call the function on every tick

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   if (enableTrailingStop) {                      //--- Check if trailing stop enabled
      applyTrailingStop(trailingStopPoints, obj_Trade, uniqueMagicNumber); //--- Apply trailing stop
   }

   //---

}

Hier implementieren wir die Trailing-Stop-Funktionalität und integrieren sie in die ereignisgesteuerte Logik. Zunächst entwickeln wir die Funktion „applyTrailingStop“, die einen Kauf-Stop-Loss als aktuellen Geldkurs (SymbolInfoDouble mit SYMBOL_BID) minus „trailingPoints * _Point“ und einen Verkaufs-Stop-Loss als Briefkurs („SYMBOL_ASK“) plus „trailingPoints * _Point“ berechnet, beide normalisiert mit NormalizeDouble auf die Ziffern des Symbols. Wir iterieren rückwärts durch die offenen Positionen mit PositionsTotal, rufen das Ticket jeder Position mit „PositionGetTicket“ ab und überprüfen, ob es mit dem Symbol und der magicNo“ (falls ungleich Null) übereinstimmt, mit den Funktionen PositionGetString und „PositionGetInteger“.

Bei Kaufpositionen (POSITION_TYPE_BUY) prüfen wir, ob „buyStopLoss“ über dem Eröffnungskurs („PositionGetDouble(POSITION_PRICE_OPEN)“) und höher als der aktuelle Stop-Loss oder nicht gesetzt ist, und aktualisieren ihn mit „trade_object.PositionModify“; bei Verkaufspositionen stellen wir sicher, dass „sellStopLoss“ unter dem Eröffnungskurs liegt und niedriger als der aktuelle Stop-Loss oder nicht gesetzt ist, und aktualisieren ihn auf ähnliche Weise. In der Funktion OnTick prüfen wir dann, ob „enableTrailingStop“ wahr ist und rufen „applyTrailingStop“ mit „trailingStopPoints“, „obj_Trade“ und „uniqueMagicNumber“ auf, um offene Positionen bei jedem Tick zu verwalten. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

TRAILING-STOP AKTIVIERT

Aus dem Bild können wir ersehen, dass der Trailing-Stop vollständig aktiviert ist, wenn sich der Kurs zu unseren Gunsten entwickelt. Hier ist ein einheitlicher Test sowohl für Abwärts- als auch für Aufwärts-Ausbruchsblöcke.

VEREINHEITLICHTE AUSBRUCHSBLÖCKE GIF

Anhand der Visualisierung können wir sehen, dass das Programm alle Einstiegsbedingungen identifiziert und überprüft und bei Bestätigung die entsprechende Position mit den entsprechenden Einstiegsparametern öffnet und somit unser Ziel erreicht. Bleiben nur noch die Backtests des Programms, und das wird im nächsten Abschnitt behandelt.


Backtests

Nach einem gründlichen Backtest erhalten wir folgende Ergebnisse.

Backtest-Grafik:

GRAFIK

Bericht des Backtest:

BERICHT


Schlussfolgerung

Zusammenfassend haben wir das Handelssystem der Ausbruchsblöcke in MQL5 erstellt, um Konsolidierungsbereiche zu identifizieren, Ausbruchsblöcke mit Umkehrpunkten zu validieren und Retest-Trades mit anpassbaren Risikoparametern und Trailing-Stops auszuführen. Das System visualisiert Auftrags- und Ausbruchsblöcke mit dynamischen Kennzeichnungen und Pfeilen, was die Klarheit von Handelsentscheidungen erhöht.

Haftungsausschluss: Dieser Artikel ist nur für Bildungszwecke gedacht. Der Handel ist mit erheblichen finanziellen Risiken verbunden, und die Volatilität der Märkte kann zu Verlusten führen. Gründliche Backtests und sorgfältiges Risikomanagement sind entscheidend, bevor Sie dieses Programm auf den Live-Märkten einsetzen.

Mit dieser Strategie der Ausbruchsblöcke sind Sie bestens gerüstet, um Preiswiederholungsgelegenheiten zu nutzen und Ihre Handelsreise weiter zu verfeinern. Viel Spaß beim Handeln!

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

Beigefügte Dateien |
Letzte Kommentare | Zur Diskussion im Händlerforum (1)
hiteshdaoya
hiteshdaoya | 27 Nov. 2025 in 08:06
Hallo,

Ich habe versucht, die freigegebene Datei hinzuzufügen, aber sie wird nicht gezeichnet, oder ich kann sagen, dass nichts auf dem Diagramm passiert oder keine Trades gemacht werden. Bitte helfen Sie mit der Ausführung Fluss.

Vielen Dank im Voraus
Entwicklung des Price Action Analysis Toolkit (Teil 45): Erstellen eines dynamischen Level-Analyse-Panels in MQL5 Entwicklung des Price Action Analysis Toolkit (Teil 45): Erstellen eines dynamischen Level-Analyse-Panels in MQL5
In diesem Artikel stellen wir Ihnen ein leistungsstarkes MQL5-Tool vor, mit dem Sie jedes gewünschte Preisniveau mit nur einem Klick testen können. Geben Sie einfach das von Ihnen gewählte Niveau ein und drücken Sie auf „Analyze“. Der EA scannt sofort die historischen Daten, hebt jede Berührung und jeden Durchbruch im Chart hervor und zeigt die Statistiken in einem übersichtlichen Dashboard an. Sie werden genau sehen, wie oft der Kurs Ihr Niveau respektiert oder durchbrochen hat und ob es sich eher wie eine Unterstützung oder ein Widerstand verhielt. Lesen Sie weiter, um das genaue Verfahren zu erfahren.
Entwicklung des Price Action Analysis Toolkit (Teil 43): Wahrscheinlichkeit und Ausbrüche von Kerzen Entwicklung des Price Action Analysis Toolkit (Teil 43): Wahrscheinlichkeit und Ausbrüche von Kerzen
Verbessern Sie Ihre Marktanalyse mit dem Candlestick Probability EA in MQL5, einem leichtgewichtigen Tool, das rohe Preisbalken in Echtzeit in instrumentenspezifische Wahrscheinlichkeiten umwandelt. Es klassifiziert Pinbars, Engulfing und Doji-Muster, wenn der Balken schließt, verwendet ATR-fähige Filterung und optionale Ausbruchsbestätigung. Der EA berechnet rohe und volumengewichtete Follow-Through-Prozentsätze, die Ihnen helfen, das typische Ergebnis jedes Musters für bestimmte Symbole und Zeitrahmen zu verstehen. Markierungen auf dem Chart, ein kompaktes Dashboard und interaktive Kippschalter ermöglichen eine einfache Validierung und Fokussierung. Exportieren Sie detaillierte CSV-Protokolle für Offline-Tests. Nutzen Sie es, um Wahrscheinlichkeitsprofile zu entwickeln, Strategien zu optimieren und Mustererkennung in einen messbaren Vorteil zu verwandeln.
Schnellhandel meistern: Überwindung der Umsetzungslähmung Schnellhandel meistern: Überwindung der Umsetzungslähmung
Der Indikator UT BOT ATR Trailing ist ein persönlicher und anpassbarer Indikator, der sehr effektiv für Händler ist, die gerne schnelle Entscheidungen treffen und Geld aus Preisunterschieden machen, die als kurzfristiger Handel bezeichnet werden (Scalper), und sich auch als wichtig und sehr effektiv für langfristige Händler (positionelle Händler) erweist.
Automatisieren von Handelsstrategien in MQL5 (Teil 34): Trendline Breakout System mit R-Squared Goodness of Fit Automatisieren von Handelsstrategien in MQL5 (Teil 34): Trendline Breakout System mit R-Squared Goodness of Fit
In diesem Artikel entwickeln wir ein Trendlinen-Ausbruchssystem in MQL5, das Unterstützungs- und Widerstandstrendlinien mit Hilfe von Umkehrpunkte identifiziert, die durch die R-Quadrat-Anpassungsgüte und Winkelbeschränkungen validiert werden, um den Ausbruch-Handel zu automatisieren. Unser Plan ist es, innerhalb eines bestimmten Rückblickzeitraums hohe und tiefe Umkehrpunkte zu erkennen, Trendlinien mit einer Mindestanzahl von Berührungspunkten zu konstruieren und sie mithilfe von R-Quadrat-Metriken und Winkelbeschränkungen zu validieren, um Zuverlässigkeit zu gewährleisten.