English 日本語
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 12): Umsetzung der Strategie der Mitigation Order Blocks (MOB)

Automatisieren von Handelsstrategien in MQL5 (Teil 12): Umsetzung der Strategie der Mitigation Order Blocks (MOB)

MetaTrader 5Handel |
143 5
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In unserem letzten Artikel (Teil 11) haben wir ein Multi-Level-Raster-Handelssystem in MetaQuotes Language 5 (MQL5) entwickelt, um von Marktschwankungen zu profitieren. In Teil 12 konzentrieren wir uns nun auf die Umsetzung der Strategie Mitigation Order Blocks (MOB), ein Smart Money Konzept, das wichtige Preiszonen identifiziert, in denen institutionelle Aufträge vor großen Marktbewegungen abgeschwächt (mitigated) werden. Wir werden die folgenden Themen behandeln:

  1. Blaupause der Strategie
  2. Implementation in MQL5
  3. Backtests
  4. Schlussfolgerung

Am Ende dieses Artikels werden Sie ein vollautomatisches Mitigation Orderblock-Handelssystem haben, das für den Handel bereit ist. Fangen wir an!


Blaupause der Strategie

Zur Umsetzung der Strategie Mitigation Order Block werden wir ein automatisiertes System entwickeln, das Handelsgeschäfte auf der Basis von Order Block Mitigation Events erkennt, validiert und ausführt. Die Strategie wird sich darauf konzentrieren, institutionelle Preiszonen zu identifizieren, in denen die Liquidität vor der Trendfortsetzung absorbiert wird. Unser System enthält präzise Bedingungen für den Einstieg, die Platzierung von Stop-Loss und das Handelsmanagement, um Effizienz und Genauigkeit zu gewährleisten. Wir werden die Entwicklung wie folgt strukturieren:

  • Identifizierung von Orderblöcken - Das System scannt die historische Preisentwicklung, um Auf- und Abwärts-Orderblöcke zu erkennen, und filtert schwache Zonen auf der Grundlage von Volatilität, Liquiditätsengpässen und Preisungleichgewichten heraus.
  • Mitigation Validation - Wir programmieren Bedingungen, die ein gültiges Mitigation-Ereignis bestätigen und sicherstellen, dass der Preis den Orderblock erneut besucht und mit Ablehnungssignalen wie Dochten oder Momentum Shifts reagiert.
  • Bestätigung der Marktstruktur - Der EA analysiert Trends in einem größeren Zeitrahmen und Liquiditätssweeps, um sicherzustellen, dass die identifizierten Abhilfemaßnahmen mit dem breiteren Marktfluss übereinstimmen.
  • Regeln für die Handelsausführung - Sobald die Mitigation bestätigt ist, definiert das System präzise Einstiegspunkte, berechnet dynamisch Stop-Loss-Levels auf der Grundlage der Orderblockstruktur und setzt Take-Profit-Ziele auf der Basis von Risiko-Rendite-Parametern.
  • Risiko- und Geldmanagement - Die Strategie wird Positionsgrößen, Schutz vor Drawdowns und Ausstiegsstrategien integrieren, um das Handelsrisiko effektiv zu verwalten.

Kurz gesagt, hier eine allgemeine Darstellung der Strategie.

Mitigation Order Block


Implementation in MQL5

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

//+------------------------------------------------------------------+
//|                        Copyright 2025, Forex Algo-Trader, Allan. |
//|                                 "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 description "This EA trades based on Mitigation Order Blocks Strategy"
#property strict

//--- Include the trade library for managing positions
#include <Trade/Trade.mqh>
CTrade obj_Trade;

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 Nutzer-Schnittstelle (UI) ändern und steuern kann.

//+------------------------------------------------------------------+
//| Input Parameters                                                 |
//+------------------------------------------------------------------+
input double tradeLotSize = 0.01;           // Trade size for each position
input bool enableTrading = true;            // Toggle to allow or disable trading
input bool enableTrailingStop = true;       // Toggle to enable or disable trailing stop
input double trailingStopPoints = 30;       // Distance in points for trailing stop
input double minProfitToTrail = 50;         // Minimum profit in points before trailing starts (not used yet)
input int uniqueMagicNumber = 12345;        // Unique identifier for EA trades
input int consolidationBars = 7;            // Number of bars to check for consolidation
input double maxConsolidationSpread = 50;   // Maximum allowed spread in points for consolidation
input int barsToWaitAfterBreakout = 3;      // Bars to wait after breakout before checking impulse
input double impulseMultiplier = 1.0;       // Multiplier for detecting impulsive moves
input double stopLossDistance = 1500;       // Stop loss distance in points
input double takeProfitDistance = 1500;     // Take profit distance in points
input color bullishOrderBlockColor = clrGreen;    // Color for bullish order blocks
input color bearishOrderBlockColor = clrRed;     // Color for bearish order blocks
input color mitigatedOrderBlockColor = clrGray;  // Color for mitigated order blocks
input color labelTextColor = clrBlack;           // Color for text labels

Hier definieren wir die 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 Handelsgeschäfte, und Konsolidierung wird mit „consolidationBars“ und „maxConsolidationSpread“ erkannt. Ausbrüche werden mit „barsToWaitAfterBreakout“ und „impulseMultiplier“ bestätigt. Mit „stopLossDistance“ und „takeProfitDistance“ wird das Risiko verwaltet, während „bullishOrderBlockColor“, „bearishOrderBlockColor“, „mitigatedOrderBlockColor“ und „labelTextColor“ die Chartdarstellung regeln.

Zum Schluss müssen wir noch einige globale Variablen definieren, die wir für die gesamte Systemsteuerung verwenden werden.

//--- Struct to store price and index for highs and lows
struct PriceAndIndex {
   double price;  // Price value
   int    index;  // Bar index where this price occurs
};

//--- Global variables for tracking market state
PriceAndIndex rangeHighestHigh = {0, 0};    // Highest high in the consolidation range
PriceAndIndex rangeLowestLow = {0, 0};      // Lowest low in the consolidation range
bool isBreakoutDetected = false;            // Flag for when a breakout occurs
double lastImpulseLow = 0.0;                // Low price after breakout for impulse check
double lastImpulseHigh = 0.0;               // High price after breakout for impulse check
int breakoutBarNumber = -1;                 // Bar index where breakout happened
datetime breakoutTimestamp = 0;             // Time of the breakout
string orderBlockNames[];                   // Array of order block object names
datetime orderBlockEndTimes[];              // Array of order block end times
bool orderBlockMitigatedStatus[];           // Array tracking if order blocks are mitigated
bool isBullishImpulse = false;              // Flag for bullish impulsive move
bool isBearishImpulse = false;              // Flag for bearish impulsive move

#define OB_Prefix "OB REC "     // Prefix for order block object names

Zunächst definieren wir die Struktur „PriceAndIndex“, die einen „Preis“-Wert und den „Index“ des Balkens speichert, in dem dieser Preis auftritt. Diese Struktur hilft, bestimmte Preispunkte innerhalb einer definierten Spanne zu verfolgen. Die globalen Variablen verwalten wichtige Aspekte der Marktstruktur und der Ausbruchserkennung: „rangeHighestHigh“ und „rangeLowestLow“ speichern den höchsten bzw. den niedrigsten Preis im Konsolidierungsbereich und helfen so, die Grenzen potenzieller Orderblöcke zu definieren. „isBreakoutDetected“ dient als Flag, die anzeigt, wann ein Ausbruch stattgefunden hat, während „lastImpulseLow“ und „lastImpulseHigh“ den ersten Tiefst- und Höchststand nach einem Ausbruch speichern und zur Bestätigung impulsiver Bewegungen verwendet werden.

Mit „breakoutBarNumber“ wird der Bar-Index aufgezeichnet, bei dem der Ausbruch stattfand, und mit „breakoutTimestamp“ wird die genaue Zeit des Ausbruchs gespeichert. Die Arrays „orderBlockNames“, „orderBlockEndTimes“ und „orderBlockMitigatedStatus“ dienen der Identifizierung, Lebensdauer und Nachverfolgung von Auftragsblöcken. Die booleschen Flags „isBullishImpulse“ und „isBearishImpulse“ bestimmen, ob die Ausbruchsbewegung als Aufwärts- oder oder Abwärts-Impuls einzustufen ist. Schließlich ist „OB_Prefix“ ein vordefinierter String-Präfix, der durch das Makro #define definiert ist und bei der Benennung von Auftragsblockobjekten verwendet wird, um die Konsistenz der grafischen Darstellung zu gewährleisten. Mit den Variablen sind wir bereit, die Programmlogik zu beginnen.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   //--- Set the magic number for the trade object to identify EA trades
   obj_Trade.SetExpertMagicNumber(uniqueMagicNumber);
   return(INIT_SUCCEEDED);
}

Hier initialisieren wir den Expert Advisor in OnInit. Wir setzen die magische Zahl des Experten mit der Methode „SetExpertMagicNumber“ und stellen damit sicher, dass alle von unserem EA ausgeführten Handelsgeschäfte eindeutig gekennzeichnet sind, um Konflikte mit anderen Trades zu vermeiden. Dieser Schritt ist wichtig, um nur die von unserer Strategie eröffneten Handelsgeschäfte zu verfolgen und zu verwalten. Sobald die Initialisierung abgeschlossen ist, geben wir INIT_SUCCEEDED zurück und bestätigen damit, dass unser Programm einsatzbereit ist. Wir können dann zur Ereignisbehandlung durch OnTick zu unserer Hauptsteuerungslogik übergehen.

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

   //--- Check for a new bar to process logic only once per bar
   static bool isNewBar = false;
   int currentBarCount = iBars(_Symbol, _Period);
   static int previousBarCount = currentBarCount;
   if (previousBarCount == currentBarCount) {
      isNewBar = false;
   } else if (previousBarCount != currentBarCount) {
      isNewBar = true;
      previousBarCount = currentBarCount;
   }

   //--- Exit if not a new bar to avoid redundant processing
   if (!isNewBar)
      return;
   //---
}

Um sicherzustellen, dass die Daten bei jedem Balken und nicht bei jedem Tick verarbeitet werden, verwenden wir in der Funktion OnTick, die bei jedem neu empfangenen Tick ausgeführt wird, die Funktion iBars, um die Gesamtzahl der Balken im Chart zu ermitteln und sie in „currentBarCount“ zu speichern. Wir vergleichen ihn dann mit „previousBarCount“, und wenn sie gleich sind, bleibt „isNewBar“ falsch, um eine redundante Verarbeitung zu vermeiden. Wenn ein neuer Balken erkannt wird, aktualisieren wir „previousBarCount“ und setzen „isNewBar“ auf true, sodass die Strategielogik ausgeführt werden kann. Wenn „isNewBar“ falsch ist, kehren wir vorzeitig zurück, um die Leistung zu optimieren, indem wir unnötige Berechnungen auslassen. Wenn es sich um einen neuen Balken handelt, suchen wir weiter nach Konsolidierungen.

//--- Define the starting bar index for consolidation checks
int startBarIndex = 1;

//--- Check for consolidation or extend the existing range
if (!isBreakoutDetected) {
   if (rangeHighestHigh.price == 0 && rangeLowestLow.price == 0) {
      //--- Check if bars are in a tight consolidation range
      bool isConsolidated = true;
      for (int i = startBarIndex; i < startBarIndex + consolidationBars - 1; i++) {
         if (MathAbs(high(i) - high(i + 1)) > maxConsolidationSpread * Point()) {
            isConsolidated = false;
            break;
         }
         if (MathAbs(low(i) - low(i + 1)) > maxConsolidationSpread * Point()) {
            isConsolidated = false;
            break;
         }
      }
      if (isConsolidated) {
         //--- Find the highest high in the consolidation range
         rangeHighestHigh.price = high(startBarIndex);
         rangeHighestHigh.index = startBarIndex;
         for (int i = startBarIndex + 1; i < startBarIndex + consolidationBars; i++) {
            if (high(i) > rangeHighestHigh.price) {
               rangeHighestHigh.price = high(i);
               rangeHighestHigh.index = i;
            }
         }
         //--- Find the lowest low in the consolidation range
         rangeLowestLow.price = low(startBarIndex);
         rangeLowestLow.index = startBarIndex;
         for (int i = startBarIndex + 1; i < startBarIndex + consolidationBars; i++) {
            if (low(i) < rangeLowestLow.price) {
               rangeLowestLow.price = low(i);
               rangeLowestLow.index = i;
            }
         }
         //--- Log the established consolidation range
         Print("Consolidation range established: Highest High = ", rangeHighestHigh.price,
               " at index ", rangeHighestHigh.index,
               " and Lowest Low = ", rangeLowestLow.price,
               " at index ", rangeLowestLow.index);
      }
   } else {
      //--- Check if the current bar extends the existing range
      double currentHigh = high(1);
      double currentLow = low(1);
      if (currentHigh <= rangeHighestHigh.price && currentLow >= rangeLowestLow.price) {
         Print("Range extended: High = ", currentHigh, ", Low = ", currentLow);
      } else {
         Print("No extension: Bar outside range.");
      }
   }
}

Hier wird der Konsolidierungsbereich durch die Analyse der jüngsten Kursbewegungen definiert und festgelegt. Wir beginnen, indem wir „startBarIndex“ auf 1 setzen und damit den Startpunkt für unsere Konsolidierungsprüfungen festlegen. Wenn wir noch keinen Ausbruch festgestellt haben, was durch „isBreakoutDetected“ angezeigt wird, bewerten wir, ob sich der Markt in einer engen Konsolidierungsphase befindet. Wir iterieren durch die letzte „consolidationBars“-Anzahl von Balken und verwenden die Funktion MathAbs, um die absoluten Differenzen zwischen aufeinanderfolgenden Hochs und Tiefs zu messen. Bleiben alle Differenzen innerhalb von „maxConsolidationSpread“, bestätigen wir die Konsolidierung.

Sobald eine Konsolidierung festgestellt wird, bestimmen wir das höchste Hoch und das tiefste Tief innerhalb der Spanne. Wir initialisieren „rangeHighestHigh“ und „rangeLowestLow“ mit dem Hoch und dem Tief von „startBarIndex“ und iterieren dann durch den Bereich, um diese Werte immer dann zu aktualisieren, wenn wir einen neuen Höchst- oder Tiefstwert finden. Diese Werte definieren unsere Konsolidierungsgrenzen.

Wenn der Konsolidierungsbereich bereits festgelegt ist, prüfen wir, ob der aktuelle Balken den bestehenden Bereich erweitert. Wir rufen „currentHigh“ und „currentLow“ mit den Funktionen „high“ und „low“ ab und vergleichen sie mit „rangeHighestHigh.price“ und „rangeLowestLow.price“. Bleibt der Kurs innerhalb der Spanne, drucken wir mit der Funktion Print eine Meldung über die Erweiterung der Spanne aus. Andernfalls drucken wir, dass keine Ausdehnung stattgefunden hat, was auf ein mögliches Ausbruchsszenario hindeutet. Die nutzerdefinierten Preisfunktionen sind wie folgt.

//+------------------------------------------------------------------+
//| Price data accessors                                                 |
//+------------------------------------------------------------------+
double high(int index) { return iHigh(_Symbol, _Period, index); }   //--- Get high price of a bar
double low(int index) { return iLow(_Symbol, _Period, index); }     //--- Get low price of a bar
double open(int index) { return iOpen(_Symbol, _Period, index); }   //--- Get open price of a bar
double close(int index) { return iClose(_Symbol, _Period, index); } //--- Get close price of a bar
datetime time(int index) { return iTime(_Symbol, _Period, index); } //--- Get time of a bar

Diese nutzerdefinierten Funktionen helfen uns beim Abrufen von Preisdaten. Die Funktion „high“ verwendet iHigh, um das Hoch eines Balkens bei einem bestimmten „Index“ zurückzugeben, während die Funktion „low“ iLow aufruft, um das entsprechende Tief zu erhalten. Die Funktion „open“ holt den Eröffnungskurs über iOpen und die Funktion „close“ den Schlusskurs über iClose. Außerdem verwendet die Funktion „time“ iTime, um den Zeitstempel des angegebenen Balkens zurückzugeben. Wenn wir das Programm ausführen, erhalten wir folgendes Ergebnis.

Konsolidierungsbestätigung

Aus dem Bild ist ersichtlich, dass wir, sobald die Preisspanne festgelegt ist und der Preis innerhalb der Spanne rotiert, diese erweitern, bis wir eine Spanne durchbrechen. Jetzt müssen wir also einen Ausbruch aus dem bestätigten Kursrückstand erkennen. Dies geschieht nach der folgenden Logik.

//--- Detect a breakout from the consolidation range
if (rangeHighestHigh.price > 0 && rangeLowestLow.price > 0) {
   double currentClosePrice = close(1);
   if (currentClosePrice > rangeHighestHigh.price) {
      Print("Upward breakout at ", currentClosePrice, " > ", rangeHighestHigh.price);
      isBreakoutDetected = true;
   } else if (currentClosePrice < rangeLowestLow.price) {
      Print("Downward breakout at ", currentClosePrice, " < ", rangeLowestLow.price);
      isBreakoutDetected = true;
   }
}

//--- Reset state after a breakout is detected
if (isBreakoutDetected) {
   Print("Breakout detected. Resetting for the next range.");
   breakoutBarNumber = 1;
   breakoutTimestamp = TimeCurrent();
   lastImpulseHigh = rangeHighestHigh.price;
   lastImpulseLow = rangeLowestLow.price;

   isBreakoutDetected = false;
   rangeHighestHigh.price = 0;
   rangeHighestHigh.index = 0;
   rangeLowestLow.price = 0;
   rangeLowestLow.index = 0;
}

Um Ausbrüche aus einer zuvor identifizierten Konsolidierungsspanne zu erkennen und zu behandeln, überprüfen wir zunächst, ob die Werte „rangeHighestHigh.price“ und „rangeLowestLow.price“ gültig sind, um sicherzustellen, dass eine Konsolidierungsspanne festgelegt wurde. Anschließend vergleichen wir den „currentClosePrice“, den wir mit der Funktion „close“ erhalten, mit den Bereichsgrenzen. Wenn der Schlusskurs den „rangeHighestHigh.price“ überschreitet, erkennen wir einen Ausbruch nach oben, protokollieren das Ereignis und setzen „isBreakoutDetected“ auf true. Ähnlich verhält es sich, wenn der Schlusskurs unter den „rangeLowestLow.price“ fällt: Wir erkennen einen Ausbruch nach unten und kennzeichnen ihn entsprechend.

Sobald ein Ausbruch bestätigt ist, setzen wir die notwendigen Zustandsvariablen zurück, um die Verfolgung einer neuen Konsolidierungsphase vorzubereiten. Wir protokollieren das Auftreten des Ausbruchs und speichern die „breakoutBarNumber“ als 1, was den ersten Balken der Ausbruchssequenz markiert. Der „breakoutTimestamp“ wird mit TimeCurrent aufgezeichnet, um den genauen Zeitpunkt des Ausbruchs festzuhalten. Zusätzlich speichern wir „lastImpulseHigh“ und „lastImpulseLow“, um das Kursverhalten nach einem Ausbruch zu verfolgen. Abschließend setzen wir „isBreakoutDetected“ auf false zurück und löschen die vorherige Konsolidierungsspanne, indem wir rangeHighestHigh.price“ und rangeLowestLow.price“ auf 0 setzen, um sicherzustellen, dass das System bereit ist, die nächste Handelsmöglichkeit zu erkennen.

Wenn es bestätigte Ausbrüche gibt, warten wir ab und verifizieren sie durch impulsive Bewegungen, um sie dann auf dem Chart darzustellen.

//--- Check for impulsive movement after breakout and create order blocks
if (breakoutBarNumber >= 0 && TimeCurrent() > breakoutTimestamp + barsToWaitAfterBreakout * PeriodSeconds()) {
   double impulseRange = lastImpulseHigh - lastImpulseLow;
   double impulseThresholdPrice = impulseRange * impulseMultiplier;
   isBullishImpulse = false;
   isBearishImpulse = false;
   for (int i = 1; i <= barsToWaitAfterBreakout; i++) {
      double closePrice = close(i);
      if (closePrice >= lastImpulseHigh + impulseThresholdPrice) {
         isBullishImpulse = true;
         Print("Impulsive upward move: ", closePrice, " >= ", lastImpulseHigh + impulseThresholdPrice);
         break;
      } else if (closePrice <= lastImpulseLow - impulseThresholdPrice) {
         isBearishImpulse = true;
         Print("Impulsive downward move: ", closePrice, " <= ", lastImpulseLow - impulseThresholdPrice);
         break;
      }
   }
   if (!isBullishImpulse && !isBearishImpulse) {
      Print("No impulsive movement detected.");
   }
   //---
}

Hier analysieren wir die Kursentwicklung nach einem Ausbruch, um festzustellen, ob eine impulsive Bewegung stattgefunden hat, was für die Identifizierung gültiger Orderblöcke entscheidend ist. Zunächst wird geprüft, ob „breakoutBarNumber“ gültig ist und ob die aktuelle Zeit, die über TimeCurrent abgerufen wird, den „breakoutTimestamp“ plus „barsToWaitAfterBreakout“ multipliziert mit PeriodSeconds überschritten hat, um sicherzustellen, dass eine ausreichende Wartezeit verstrichen ist. Anschließend berechnen wir „impulseRange“ als die Differenz zwischen „lastImpulseHigh“ und „lastImpulseLow“, die die Kursschwankung nach dem Ausbruch darstellt. Auf dieser Grundlage berechnen wir „impulseThresholdPrice“, indem wir „impulseRange“ mit „impulseMultiplier“ multiplizieren, um die für eine impulsive Bewegung erforderliche Mindestpreisausdehnung zu definieren.

Als Nächstes initialisieren wir „isBullishImpulse“ und „isBearishImpulse“ als „false“ und bereiten so die Auswertung der Kursbewegung über die letzten „barsToWaitAfterBreakout“-Balken vor. Diese Balken werden in einer for-Schleife durchlaufen, wobei der Schlusskurs mit der Funktion „close“ ermittelt wird. Wenn „closePrice“ größer oder gleich „lastImpulseHigh + impulseThresholdPrice“ ist, erkennen wir eine impulsive Aufwärts-Bewegung, setzen „isBullishImpulse“ auf true und protokollieren das Ereignis. Wenn „closePrice“ kleiner oder gleich „lastImpulseLow - impulseThresholdPrice“ ist, identifizieren wir eine impulsive Abwärtsbewegung, setzen „isBearishImpulse“ auf true und protokollieren das. Wenn keine der beiden Bedingungen erfüllt ist, wird die Meldung ausgegeben, dass keine impulsive Bewegung festgestellt wurde. Diese Logik stellt sicher, dass nur starke Ausbruchsfortsetzungen als gültige Auftragsblöcke für die weitere Verarbeitung in Frage kommen. Um sie zu visualisieren, verwenden wir die folgende Logik.

bool isOrderBlockValid = isBearishImpulse || isBullishImpulse;

if (isOrderBlockValid) {
   datetime blockStartTime = iTime(_Symbol, _Period, consolidationBars + barsToWaitAfterBreakout + 1);
   double blockTopPrice = lastImpulseHigh;
   int visibleBarsOnChart = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
   datetime blockEndTime = blockStartTime + (visibleBarsOnChart / 1) * PeriodSeconds();
   double blockBottomPrice = lastImpulseLow;
   string orderBlockName = OB_Prefix + "(" + TimeToString(blockStartTime) + ")";
   color orderBlockColor = isBullishImpulse ? bullishOrderBlockColor : bearishOrderBlockColor;
   string orderBlockLabel = isBullishImpulse ? "Bullish OB" : "Bearish OB";

   if (ObjectFind(0, orderBlockName) < 0) {
      //--- Create a rectangle for the order block
      ObjectCreate(0, orderBlockName, OBJ_RECTANGLE, 0, blockStartTime, blockTopPrice, blockEndTime, blockBottomPrice);
      ObjectSetInteger(0, orderBlockName, OBJPROP_TIME, 0, blockStartTime);
      ObjectSetDouble(0, orderBlockName, OBJPROP_PRICE, 0, blockTopPrice);
      ObjectSetInteger(0, orderBlockName, OBJPROP_TIME, 1, blockEndTime);
      ObjectSetDouble(0, orderBlockName, OBJPROP_PRICE, 1, blockBottomPrice);
      ObjectSetInteger(0, orderBlockName, OBJPROP_FILL, true);
      ObjectSetInteger(0, orderBlockName, OBJPROP_COLOR, orderBlockColor);
      ObjectSetInteger(0, orderBlockName, OBJPROP_BACK, false);

      //--- Add a text label in the middle of the order block with dynamic font size
      datetime labelTime = blockStartTime + (blockEndTime - blockStartTime) / 2;
      double labelPrice = (blockTopPrice + blockBottomPrice) / 2;
      string labelObjectName = orderBlockName + orderBlockLabel;
      if (ObjectFind(0, labelObjectName) < 0) {
         ObjectCreate(0, labelObjectName, OBJ_TEXT, 0, labelTime, labelPrice);
         ObjectSetString(0, labelObjectName, OBJPROP_TEXT, orderBlockLabel);
         ObjectSetInteger(0, labelObjectName, OBJPROP_COLOR, labelTextColor);
         ObjectSetInteger(0, labelObjectName, OBJPROP_FONTSIZE, dynamicFontSize);
         ObjectSetInteger(0, labelObjectName, OBJPROP_ANCHOR, ANCHOR_CENTER);
      }
      ChartRedraw(0);

      //--- Store the order block details in arrays
      ArrayResize(orderBlockNames, ArraySize(orderBlockNames) + 1);
      orderBlockNames[ArraySize(orderBlockNames) - 1] = orderBlockName;
      ArrayResize(orderBlockEndTimes, ArraySize(orderBlockEndTimes) + 1);
      orderBlockEndTimes[ArraySize(orderBlockEndTimes) - 1] = blockEndTime;
      ArrayResize(orderBlockMitigatedStatus, ArraySize(orderBlockMitigatedStatus) + 1);
      orderBlockMitigatedStatus[ArraySize(orderBlockMitigatedStatus) - 1] = false;

      Print("Order Block created: ", orderBlockName);
   }
}

Hier bestimmen wir, ob eine Auftragssperre aufgrund der Erkennung einer impulsiven Bewegung erstellt werden sollte. Zunächst wird „isOrderBlockValid“ ausgewertet, indem geprüft wird, ob entweder „isBearishImpulse“ oder „isBullishImpulse“ wahr ist. Falls gültig, definieren wir die wichtigsten Parameter für den Orderblock: „blockStartTime“ wird mit Hilfe der iTime-Funktion ermittelt, um den Bar bei „consolidationBars + barsToWaitAfterBreakout + 1“ zu referenzieren und sicherzustellen, dass er mit der identifizierten Struktur übereinstimmt. „blockTopPrice“ wird auf „lastImpulseHigh“ und „blockBottomPrice“ auf „lastImpulseLow“ gesetzt, wodurch die Preisspanne des Auftragsblocks festgelegt wird. Wir verwenden die Funktion ChartGetInteger, um „visibleBarsOnChart“ zu bestimmen und „blockEndTime“ dynamisch auf der Grundlage von PeriodSeconds zu berechnen, um sicherzustellen, dass das Rechteck innerhalb des aktuellen Bereichs des Charts sichtbar bleibt.

Der Name des Auftragsblocks wird mit „OB_Prefix“ und der Funktion TimeToString konstruiert, um den Zeitstempel für die Eindeutigkeit einzuschließen. Die Farbe und die Bezeichnung werden danach bestimmt, ob der Impuls auf- oder abwärts ist, indem „bullishOrderBlockColor“ oder „bearishOrderBlockColor“ ausgewählt und die entsprechende Bezeichnung zugewiesen wird.

Anschließend wird mit ObjectFind geprüft, ob der Auftragsblock vorhanden ist. Wenn er nicht existiert, zeichnen wir mit der Funktion ObjectCreate ein Rechteck (OBJ_RECTANGLE), das den Auftragsblock darstellt, und setzen seine Zeit- und Preisgrenzen mit ObjectSetInteger und ObjectSetDouble. Das Rechteck wird gefüllt (OBJPROP_FILL), mit Farbe versehen (OBJPROP_COLOR) und im Vordergrund gezeichnet (OBJPROP_BACK = false).

Als Nächstes erstellen wir zur besseren Visualisierung ein Etikett innerhalb des Auftragsblocks. Die Zeit des Etiketts („labelTime“) wird auf den Mittelpunkt von „blockStartTime“ und „blockEndTime“ gesetzt, während der „labelPrice“ als Mittelpunkt von „blockTopPrice“ und „blockBottomPrice“ berechnet wird. Wir generieren einen eindeutigen Etikettennamen, indem wir „orderBlockLabel“ an „orderBlockName“ anhängen. Wenn die Beschriftung nicht vorhanden ist, wird mit „ObjectCreate“ ein Textobjekt (OBJ_TEXT) erstellt, wobei der Textinhalt (OBJPROP_TEXT), die Farbe (OBJPROP_COLOR), die Schriftgröße (OBJPROP_FONTSIZE) und die Zentrierung mit (OBJPROP_ANCHOR = ANCHOR_CENTER) festgelegt werden. Die Funktion ChartRedraw sorgt dafür, dass die neu erstellten Elemente sofort erscheinen. Da die Schriftgröße je nach Maßstab des Charts eine wichtige Rolle spielt, wird sie dynamisch wie folgt berechnet.

//--- Calculate dynamic font size based on chart scale (0 = zoomed out, 5 = zoomed in)
int chartScale = (int)ChartGetInteger(0, CHART_SCALE); // Scale ranges from 0 to 5
int dynamicFontSize = 8 + (chartScale * 2);           // Font size: 8 (min) to 18 (max)

Schließlich speichern wir die Details der Auftragsblocks in Arrays: „orderBlockNames“ (speichert Objektnamen), „orderBlockEndTimes“ (speichert Ablaufzeiten) und „orderBlockMitigatedStatus“ (verfolgt, ob der Auftragsblock abgeschwächt wurde). Wir passen die Größe jedes Arrays mit der Funktion ArrayResize dynamisch an, um neue Einträge unterzubringen und sicherzustellen, dass unsere Auftragsblockverwaltung flexibel bleibt. Eine Bestätigungsmeldung wird gedruckt, um die erfolgreiche Erstellung eines Auftragsblocks anzuzeigen. Zum Schluss müssen wir nur noch die Variablen für das Breakout-Tracking zurücksetzen.

//--- Reset breakout tracking variables
breakoutBarNumber = -1;
breakoutTimestamp = 0;
lastImpulseHigh = 0;
lastImpulseLow = 0;
isBullishImpulse = false;
isBearishImpulse = false;

Nach dem Kompilieren und Ausführen des Programms erhalten wir das folgende Ergebnis.

Bestätigte OBs

Aus dem Bild ist ersichtlich, dass wir Orderblöcke, die aus den impulsiven Ausbruchsbewegungen resultieren, bestätigt und beschriftet haben. Jetzt müssen wir nur noch fortfahren, die abgeschwächten Orderblöcke durch fortgesetzte Verwaltung der Setups innerhalb der Chartgrenzen zu validieren.

//--- Process existing order blocks for mitigation and trading
for (int j = ArraySize(orderBlockNames) - 1; j >= 0; j--) {
   string currentOrderBlockName = orderBlockNames[j];
   bool doesOrderBlockExist = false;

   //--- Retrieve order block properties
   double orderBlockHigh = ObjectGetDouble(0, currentOrderBlockName, OBJPROP_PRICE, 0);
   double orderBlockLow = ObjectGetDouble(0, currentOrderBlockName, OBJPROP_PRICE, 1);
   datetime orderBlockStartTime = (datetime)ObjectGetInteger(0, currentOrderBlockName, OBJPROP_TIME, 0);
   datetime orderBlockEndTime = (datetime)ObjectGetInteger(0, currentOrderBlockName, OBJPROP_TIME, 1);
   color orderBlockCurrentColor = (color)ObjectGetInteger(0, currentOrderBlockName, OBJPROP_COLOR);

   //--- Check if the order block is still valid (not expired)
   if (time(1) < orderBlockEndTime) {
      doesOrderBlockExist = true;
   }
   //---
}

Die „orderBlockNames“ werden in umgekehrter Reihenfolge durchlaufen, wobei jeder Auftragsblock auf Entschärfung und Handel geprüft wird. „currentOrderBlockName“ speichert den Namen des geprüften Blocks. Wir verwenden ObjectGetDouble und ObjectGetInteger, um „orderBlockHigh“, „orderBlockLow“, „orderBlockStartTime“, „orderBlockEndTime“ und „orderBlockCurrentColor“ abzurufen, um eine präzise Handhabung der Eigenschaften jedes Auftragsblocks sicherzustellen.

Um zu überprüfen, ob der Auftragsblock noch gültig ist, vergleichen wir „time(1)“ (abgerufen mit der Funktion „time“) mit „orderBlockEndTime“. Wenn der aktuelle Zeitpunkt innerhalb der Lebensdauer des Auftragsblocks liegt, wird „doesOrderBlockExist“ auf true gesetzt, um zu bestätigen, dass der Auftragsblock für die weitere Verarbeitung aktiv bleibt. Wenn dies der Fall ist, verarbeiten wir sie und handeln sie.

//--- Get current market prices
double currentAskPrice = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
double currentBidPrice = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);

//--- Check for mitigation and execute trades if trading is enabled
if (enableTrading && orderBlockCurrentColor == bullishOrderBlockColor && close(1) < orderBlockLow && !orderBlockMitigatedStatus[j]) {
   //--- Sell trade when price breaks below a bullish order block
   double entryPrice = currentBidPrice;
   double stopLossPrice = entryPrice + stopLossDistance * _Point;
   double takeProfitPrice = entryPrice - takeProfitDistance * _Point;
   obj_Trade.Sell(tradeLotSize, _Symbol, entryPrice, stopLossPrice, takeProfitPrice);
   orderBlockMitigatedStatus[j] = true;
   ObjectSetInteger(0, currentOrderBlockName, OBJPROP_COLOR, mitigatedOrderBlockColor);
   string blockDescription = "Bullish Order Block";
   string textObjectName = currentOrderBlockName + blockDescription;
   if (ObjectFind(0, textObjectName) >= 0) {
      ObjectSetString(0, textObjectName, OBJPROP_TEXT, "Mitigated " + blockDescription);
   }
   Print("Sell trade entered upon mitigation of bullish OB: ", currentOrderBlockName);
} else if (enableTrading && orderBlockCurrentColor == bearishOrderBlockColor && close(1) > orderBlockHigh && !orderBlockMitigatedStatus[j]) {
   //--- Buy trade when price breaks above a bearish order block
   double entryPrice = currentAskPrice;
   double stopLossPrice = entryPrice - stopLossDistance * _Point;
   double takeProfitPrice = entryPrice + takeProfitDistance * _Point;
   obj_Trade.Buy(tradeLotSize, _Symbol, entryPrice, stopLossPrice, takeProfitPrice);
   orderBlockMitigatedStatus[j] = true;
   ObjectSetInteger(0, currentOrderBlockName, OBJPROP_COLOR, mitigatedOrderBlockColor);
   string blockDescription = "Bearish Order Block";
   string textObjectName = currentOrderBlockName + blockDescription;
   if (ObjectFind(0, textObjectName) >= 0) {
      ObjectSetString(0, textObjectName, OBJPROP_TEXT, "Mitigated " + blockDescription);
   }
   Print("Buy trade entered upon mitigation of bearish OB: ", currentOrderBlockName);
}

Wir beginnen mit der Abfrage der aktuellen Marktpreise mit der Funktion SymbolInfoDouble und stellen sicher, dass sowohl „currentAskPrice“ als auch „currentBidPrice“ mit _Digits auf die entsprechende Anzahl von Dezimalstellen normalisiert sind. Dies garantiert Präzision bei der Platzierung von Handelsgeschäften. Als Nächstes wird geprüft, ob „enableTrading“ aktiv ist und ob eine Bedingung für die Abschwächung einer Auftragssperre erfüllt wurde. Eine Abschwächung tritt ein, wenn ein Preis einen Auftragsblock durchbricht, was auf eine Störung in seiner Haltestruktur hinweist.

Bei Aufwärts-Orderblöcken wird überprüft, ob der Schlusskurs des vorangegangenen Balkens (der mit der Funktion „close“ ermittelt wurde) unter orderBlockLow“ gefallen ist, und sichergestellt, dass dieser Orderblock nicht bereits abgeschwächt wurde (orderBlockMitigatedStatus[j] == false“). Wenn diese Bedingungen erfüllt sind, platzieren wir ein Verkaufsgeschäft mit Hilfe der Funktion „Sell“ des Objekts „obj_Trade“. Der Handel wird zum „currentBidPrice“ ausgeführt, wobei ein Stop-Loss („stopLossPrice“) um „stopLossDistance * _Point“ über dem Einstiegskurs und ein Take-Profit („takeProfitPrice“) um „takeProfitDistance * _Point“ unter dem Einstiegskurs gesetzt wird.

Sobald der Handel ausgeführt ist, wird der Auftragsblock als mitigiert markiert, indem „orderBlockMitigatedStatus[j]“ auf true aktualisiert wird, und seine Farbe wird mithilfe von ObjectSetInteger geändert, um seinen abgeschwächten Status anzuzeigen. Wenn für diesen Auftragsblock ein Text vorhanden ist (geprüft mit ObjectFind), aktualisieren wir sie mit ObjectSetString, um „Mitigated Bullish Order Block“ anzuzeigen. Ein Print protokolliert die Handelsausführung zur Nachverfolgung und Fehlersuche.

Bei Abwärts-Orderblöcken ist der Prozess ähnlich. Wir prüfen, ob der „close“ über „orderBlockHigh“ gestiegen ist, was auf einen Bruch des Abwärts-Orderblocks hindeutet. Wenn die Bedingungen erfüllt sind, wird ein Kaufgeschäft über die Funktion „Kaufen“ getätigt, wobei der „currentAskPrice“ als Einstiegskurs verwendet wird. Der „stopLossPrice“ liegt unter dem Einstiegskurs und der „takeProfitPrice“ darüber, um ein angemessenes Risikomanagement zu gewährleisten. Nach der Platzierung des Kaufgeschäfts aktualisieren wir „orderBlockMitigatedStatus[j]“, ändern die Farbe des Orderblocks mithilfe von ObjectSetInteger und ändern den Text (falls gefunden), um „Mitigated Bearish Order Block“ anzuzeigen. Schließlich protokolliert „Print“ die Ausführung des Kaufs zu Überwachungszwecken. Das haben wir erreicht.

Abgeschwächte & gehandelte OB

Sobald die Blöcke außerhalb der Grenzen liegen, werden sie aus den Speicherbereichen entfernt.

//--- Remove expired order blocks from arrays
if (!doesOrderBlockExist) {
   bool removedName = ArrayRemove(orderBlockNames, j, 1);
   bool removedTime = ArrayRemove(orderBlockEndTimes, j, 1);
   bool removedStatus = ArrayRemove(orderBlockMitigatedStatus, j, 1);
   if (removedName && removedTime && removedStatus) {
      Print("Success removing OB DATA from arrays at index ", j);
   }
}

Wenn der Auftragsblock nicht mehr existiert, entfernen wir seinen Namen, seine Endzeit und seinen abgeschwächten Status aus den entsprechenden Arrays mit Hilfe der Funktion ArrayRemove. Wenn alle Entfernungen erfolgreich waren, wird die Aktion mit Print protokolliert, um die Bereinigung zu bestätigen. Hier ist ein Beispiel für eine Bereinigungsbestätigung.

Bereinigen der Blöcke

Aus dem Bild geht hervor, dass wir die Blöcke erfolgreich bereinigt haben. Jetzt müssen wir nur noch die Logik für den Trailing-Stop hinzufügen, und dafür verwenden wir eine Funktion, um alles zu kapseln.

//+------------------------------------------------------------------+
//| Trailing stop function                                           |
//+------------------------------------------------------------------+
void applyTrailingStop(double trailingPoints, CTrade &trade_object, int magicNo = 0) {
   //--- Calculate trailing stop levels based on current market prices
   double buyStopLoss = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - trailingPoints * _Point, _Digits);
   double sellStopLoss = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + trailingPoints * _Point, _Digits);
   
   //--- Loop through all open positions
   for (int i = PositionsTotal() - 1; i >= 0; i--) {
      ulong ticket = PositionGetTicket(i);
      if (ticket > 0) {
         if (PositionGetString(POSITION_SYMBOL) == _Symbol && 
             (magicNo == 0 || PositionGetInteger(POSITION_MAGIC) == magicNo)) {
            //--- Adjust stop loss for buy positions
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && 
                buyStopLoss > PositionGetDouble(POSITION_PRICE_OPEN) && 
                (buyStopLoss > PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL) == 0)) {
               trade_object.PositionModify(ticket, buyStopLoss, PositionGetDouble(POSITION_TP));
            } 
            //--- Adjust stop loss for sell positions
            else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && 
                       sellStopLoss < PositionGetDouble(POSITION_PRICE_OPEN) && 
                       (sellStopLoss < PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL) == 0)) {
               trade_object.PositionModify(ticket, sellStopLoss, PositionGetDouble(POSITION_TP));
            }
         }
      }
   }
}

Hier definieren wir die Funktion „applyTrailingStop“, um die Stop-Loss für aktive Positionen dynamisch anzupassen. Wir beginnen mit der Berechnung von „buyStopLoss“ und „sellStopLoss“ unter Verwendung der aktuellen Geld-/Briefkurse und der angegebenen „trailingPoints“. Als Nächstes durchlaufen wir alle offenen Positionen und filtern sie nach Symbol und magischer Zahl (falls vorhanden). Wenn eine Kaufposition ein gültiges Stop-Loss-Niveau über ihrem Einstiegskurs hat und dieses entweder den aktuellen Stop-Loss übersteigt oder nicht gesetzt ist, wird sie aktualisiert. In ähnlicher Weise stellen wir bei Verkaufspositionen sicher, dass der neue Stop-Loss unter dem Einstiegskurs liegt, bevor er geändert wird.

Anschließend rufen wir die Funktion OnTick auf, um sie bei jedem Tick und nicht bei jedem Balken zu verarbeiten, um den Preis in Echtzeit zu überprüfen.

//--- Apply trailing stop to open positions if enabled
if (enableTrailingStop) {
   applyTrailingStop(trailingStopPoints, obj_Trade, uniqueMagicNumber);
}

Nach dem Kompilieren und Ausführen des Programms erhalten wir folgendes Ergebnis.

MOB GIF

Aus der Visualisierung ist ersichtlich, dass das Programm alle Einstiegsbedingungen identifiziert und überprüft und bei Bestätigung die jeweilige Position mit den entsprechenden Einstiegsparametern erö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 haben wir folgende Ergebnisse.

Backtest-Grafik:

Grafik

Bericht des Backtest:

BERICHT


Schlussfolgerung

Zusammenfassend lässt sich sagen, dass wir die Mitigation Order Blocks (MOB) Strategie erfolgreich in MQL5 implementiert haben, was eine präzise Erkennung, Visualisierung und einen automatisierten Handel auf der Grundlage von Smart Money Konzepten ermöglicht. Durch die Integration von Breakout-Validierung, Erkennung impulsiver Bewegungen und abschwächungsbasierter Handelsausführung identifiziert und verarbeitet unser System effektiv Orderblöcke und passt sich dabei der Marktdynamik an. Darüber hinaus haben wir Trailing-Stops und Risikomanagement-Mechanismen eingebaut, um die Handelsperformance zu optimieren und die Robustheit zu erhöhen.

Haftungsausschluss: Dieser Artikel ist nur für Bildungszwecke gedacht. Der Handel ist mit erheblichen finanziellen Risiken verbunden, und die Marktbedingungen können unvorhersehbar sein. Ordnungsgemäße Backtests und ein Risikomanagement sind vor dem Live-Einsatz unerlässlich.

Durch den Einsatz dieser Techniken können Sie Ihre algorithmischen Handelsstrategien verfeinern und die Effizienz des auftragsblockbasierten Handels verbessern. Testen, optimieren und passen Sie Ihren Ansatz weiter an, um langfristig erfolgreich zu sein. Viel Glück!

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

Beigefügte Dateien |
Letzte Kommentare | Zur Diskussion im Händlerforum (5)
linfo2
linfo2 | 28 März 2025 in 04:15
Danke Allan, schön zusammengestellt, ich mag die Visuals und den Farbwechsel bei Mitigated und deine Handhabung der Arrays. Danke fürs Teilen
Allan Munene Mutiiria
Allan Munene Mutiiria | 28 März 2025 in 10:56
linfo2 #:
Danke Allan, schön zusammengestellt, ich mag die Visuals und den Farbwechsel bei Mitigated und deine Handhabung der Arrays. Danke fürs Teilen

Vielen Dank für das freundliche Feedback. Gern geschehen.

davesarge1
davesarge1 | 12 Apr. 2025 in 13:08
Im Strategietester wird nicht gehandelt. Standardeinstellungen für alle Paare. Keine Fehlermeldungen im Journal. Journalmeldungen vorhanden; "Keine Verlängerung: Bar außerhalb des Bereichs" und "Keine impulsive Bewegung erkannt".


Allan Munene Mutiiria
Allan Munene Mutiiria | 14 Apr. 2025 in 13:47
davesarge1 Strategietester werden keine Trades durchgeführt. Standardeinstellungen für alle Paare. Keine Fehlermeldungen im Journal. Journalmeldungen vorhanden; "Keine Verlängerung: Bar außerhalb des Bereichs" und "Keine impulsive Bewegung erkannt".


Haben Sie den Artikel überhaupt gelesen? Denn wir sind uns sicher, dass der Artikel so ziemlich alle Ihre Fragen beantwortet.

Thuan Thai
Thuan Thai | 1 Aug. 2025 in 23:35
Ich danke Ihnen vielmals. Ich weiß das zu schätzen! Ich werde es probieren
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 19): ZigZag Analyzer Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 19): ZigZag Analyzer
Jeder, der Preisaktionen handelt, verwendet Trendlinien manuell, um Trends zu bestätigen und potenzielle Wende- oder Fortsetzungsniveaus zu erkennen. In dieser Serie über die Entwicklung eines Preisaktionsanalyse-Toolkits stellen wir ein Tool vor, das sich auf das Zeichnen von schrägen Trendlinien zur einfachen Marktanalyse konzentriert. Dieses Tool vereinfacht den Prozess für Händler, indem es die wichtigsten Trends und Niveaus, die für eine wirksame Bewertung der Preisaktionen unerlässlich sind, klar umreißt.
Erstellen eines Handelsadministrator-Panels in MQL5 (Teil IX): Code Organisation (IV): Handelsmanagement-Panel-Klasse Erstellen eines Handelsadministrator-Panels in MQL5 (Teil IX): Code Organisation (IV): Handelsmanagement-Panel-Klasse
Diese Diskussion behandelt das aktualisierte TradeManagementPanel in unserem New_Admin_Panel EA. Das Update verbessert das Panel durch die Verwendung integrierter Klassen, um eine nutzerfreundliche Schnittstelle für das Handelsmanagement zu bieten. Es enthält Schaltflächen zum Eröffnen von Positionen und Steuerelemente zur Verwaltung bestehender Handelsgeschäfte und ausstehender Aufträge. Ein wichtiges Merkmal ist das integrierte Risikomanagement, das die Einstellung der Werte von Stop-Loss und Take-Profit direkt in der Nutzeroberfläche ermöglicht. Diese Aktualisierung verbessert die Code-Organisation für große Programme und vereinfacht den Zugang zu den Auftragsverwaltungswerkzeugen, die im Terminal oft komplex sind.
Automatisieren von Handelsstrategien in MQL5 (Teil 13): Aufbau eines Kopf-Schulter-Handelsalgorithmus Automatisieren von Handelsstrategien in MQL5 (Teil 13): Aufbau eines Kopf-Schulter-Handelsalgorithmus
In diesem Artikel automatisieren wir das Muster aus Kopf und Schultern in MQL5. Wir analysieren seine Architektur, implementieren einen EA, um ihn zu erkennen und zu handeln, und führen einen Backtest der Ergebnisse durch. Der Prozess offenbart einen praktischen Handelsalgorithmus, der noch verfeinert werden kann.
Resampling-Techniken für die Bewertung von Vorhersagen und Klassifizierungen in MQL5 Resampling-Techniken für die Bewertung von Vorhersagen und Klassifizierungen in MQL5
In diesem Artikel werden wir Methoden zur Bewertung der Modellqualität erforschen und implementieren, die einen einzigen Datensatz sowohl als Trainings- als auch als Validierungssatz verwenden.