English
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 45): Inverse Fair Value Gap (IFVG)

Automatisieren von Handelsstrategien in MQL5 (Teil 45): Inverse Fair Value Gap (IFVG)

MetaTrader 5Handel |
26 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In unserem letzten Artikel (Teil 44) haben wir das Erkennungssystem des Change of Character (CHoCH) in MetaQuotes Language 5 (MQL5) entwickelt. Mit diesem System wurden die Balken gescannt, um Hochs und Tiefs für die Trendbestimmung zu identifizieren und zu kennzeichnen. Es löste Handelsgeschäfte aus bei Durchbrüchen, die Umkehrungen signalisierten. Das System unterstützte die Modi „pro Balken“ und „pro Tick“ sowie die Visualisierung mit Symbolen, Kennzeichnungen, Bruchlinien und dynamischen Schriftarten. In Teil 45 entwickeln wir das System der Inversen Fair-Value-Gap (IFVG).

Dieses System identifiziert steigende oder fallende Fair-Value-Gaps (FVGs) auf den letzten Balken und wendet einen Filter für die Mindestgröße der Lücken an. Es verfolgt die Zustände als normal, abgeschwächt oder invertiert auf der Grundlage von Preisinteraktionen. Eine Abschwächung (mitigation) erfolgt bei Durchbrüchen auf der Gegenseite, ein Rücksetzer beim Wiedereinstieg und eine Umkehr des Schlusskurses auf der Gegenseite hinaus von innen. Das System ignoriert Überschneidungen und begrenzt die verfolgten FVGs. Es unterstützt einen, begrenzte oder unbegrenzte Anzahl von Handelsgeschäften pro FVG. Es kauft bei steigenden IFVGs oder verkauft bei fallenden. Die Zonen werden als farbige Rechtecke mit Kernzeichnungen für Staat und Handel sowie mit Symbolen zur Schadensbegrenzung dargestellt. Wir werden die folgenden Themen behandeln:

  1. Das Verständnis des Systems des Inverse Fair-Value-Gaps (IFVG)
  2. Implementation in MQL5
  3. Backtests
  4. Schlussfolgerung

Am Ende werden Sie eine funktionierende MQL5-Strategie zum Erkennen und Handeln von IFVGs mit Zustandsverfolgung, adaptiver Grafik und konfigurierbaren Modi haben – fangen wir an!


Das Verständnis des Systems des Inverse Fair-Value-Gaps (IFVG)

Die Kurslücke Fair-Value-Gap (FVG) ist ein Preisaktionskonzept, das Ungleichgewichte oder Lücken zwischen Kerzen darstellt, bei denen der Kauf- oder Verkaufsdruck eine unausgefüllte Lücke geschaffen hat. Diese Lücken werden oft als „bullish“, also Aufwärts-FVGs“ (Tief der späteren Kerze über dem Hoch der früheren Kerze), oder „bearish“, also Abwärts-FVGs“ (Hoch der späteren Kerze unter dem Tief der früheren Kerze), angesehen und fungieren als potenzielle Unterstützungs-/Widerstandszonen, die der Preis wieder auffüllen kann. Eine inverses Fair-Value-Lücke (IFVG) tritt auf, wenn ein abgeschwächtes FVG (der Kurs hat die Gegenseite durchbrochen) zurückverfolgt und dann invertiert wird, indem der Kurs von innen über die Gegenseite hinaus schließt, was eine Umkehr signalisiert: ein abgeschwächtes Aufwärts-FVG, das sich in ein fallendes umkehrt (der Kurs schließt nach dem Wiedereinstieg unter dem Tief), oder ein abgeschwächtes Abwärts-FVG, das sich in ein steigendes umkehrt (schließt über dem Hoch). Entwicklungskontrolle der Zustände – normal (anfängliche Lücke), abgeschwächt (Durchbruch auf der Gegenseite), zurückgegangen (Wiedereinstieg nach Abschwächung), invertiert (Schließen über die Gegenseite hinaus nach der Rückkehr) –, wobei die Inversion das wichtigste Handelssignal ist.

Bei einem abgeschwächten Abwärts-FVG (ursprünglich eine Abwärtslücke) wird ein Aufwärts-IFVG ausgelöst, wenn der Kurs nach der Abschwächung wieder eintritt und über dem Hoch schließt, wobei der Kauf mit einem Stop-Loss unterhalb des Tiefs und einem Take-Profit bei festen Punkten erfolgt. Umgekehrt wird bei einem abgeschwächten Aufwärts-FVG (ursprünglicher aufsteigend) ein Abwärts-IFVG bei einem Schlusskurs unter dem Tiefpunkt mit einem Stop-Loss über dem Hoch verkauft. Sehen wir uns unten die verschiedenen Möglichkeiten an, die wir haben.

INVERSE FAIR-VALUE-GAP (IFVG) SETUPS

Unser Plan ist es, FVGs auf aktuellen Balken mit einem Mindestlückenfilter zu erkennen, historische FVGs bei der Initialisierung zu laden, Zustände (normal/gemildert/invertiert) basierend auf Preisinteraktionen zu verfolgen/aktualisieren, Überlappungen zu ignorieren, verfolgte FVGs mit Bereinigung von abgelaufenen zu begrenzen, Handelsumkehrungen mit Käufen bei Aufwärts-IFVGs (orig. abwärts, invertiert) oder Verkäufen bei Abwärts-IFVGs (orig. aufwärts, invertiert), feste Handelsniveaus, Handelsmodi/-zahlen pro FVG, Trailing Stops und Visualisierung farbiger Rechtecke (normal/abgeschwächt/invertierte Schatten) mit Zustands-/Handelsbeschriftungen und Abschwächungssymbolen. Kurz gesagt, hier ist eine visuelle Darstellung unserer Ziele.

IFVGS-SYSTEM


Implementation in MQL5

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

//+------------------------------------------------------------------+
//|                                                  FVG Inverse.mq5 |
//|                           Copyright 2025, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Allan Munene Mutiiria."
#property link      "https://t.me/Forex_Algo_Trader"
#property version   "1.00"

#include <Trade/Trade.mqh>

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
CTrade obj_Trade;                                                 //--- Trade object
#define FVG_Prefix "IFVG REC "                                    //--- FVG prefix
// Normal FVGs
#define CLR_UP clrGreen                                           // Green for normal up (Bullish FVG)
#define CLR_DOWN clrRed                                           // Red for normal down (Bearish FVG)
// Mitigated FVGs
#define CLR_MIT_UP clrPurple                                      // Purple for mitigated up (Mitigated Bullish FVG)
#define CLR_MIT_DOWN clrOrange                                    // Orange for mitigated down (Mitigated Bearish FVG)
// Inverted FVGs
#define CLR_INV_UP clrRed                                         // Red for inverted up (Bearish IFVG)
#define CLR_INV_DOWN clrGreen                                     // Green for inverted down (Bullish IFVG)

//+------------------------------------------------------------------+
//| Enums                                                            |
//+------------------------------------------------------------------+
enum TradeMode {                                                  // Define trade mode enum
   TradeOnce,                                                     // Trade Once
   LimitedTrades,                                                 // Limited Trades
   UnlimitedTrades                                                // Unlimited Trades
};

enum FVGState {                                                   // Define FVG state enum
   Normal,                                                        // Normal
   Mitigated,                                                     // Mitigated
   Inverted                                                       // Inverted
};

enum TrailingTypeEnum {                                           // Define enum for trailing stop types
   Trailing_None   = 0,                                           // None
   Trailing_Points = 2                                            // By Points
};

//+------------------------------------------------------------------+
//| Input Parameters                                                 |
//+------------------------------------------------------------------+
input group "EA GENERAL SETTINGS"
input double inpLot             = 0.01;                           // Lotsize
input int    sl_pts             = 300;                            // Stop Loss Points
input int    tp_pts             = 300;                            // Take Profit Points
input int    minPts             = 100;                            // Minimum Gap Size in Points
input int    FVG_Rec_Ext_Bars   = 30;                             // FVG Extension Bars
input bool   prt                = true;                           // Print Statements
input long   magic_number       = 123456789;                      // Magic Number
input bool   ignoreOverlaps     = true;                           // Ignore new FVGs that overlap existing ones
input TradeMode tradeMode       = TradeOnce;                      // Mode for trading FVGs
input int    maxTradesPerFVG    = 2;                              // Maximum trades per FVG for LimitedTrades
input int    maxFVGs            = 50;                             // Maximum FVGs to track in array
input TrailingTypeEnum TrailingType = Trailing_None;              // Trailing Stop Type
input double Trailing_Stop_Pips = 30.0;                           // Trailing Stop in Pips (for Points type)
input double Min_Profit_To_Trail_Pips = 50.0;                     // Min Profit to Start Trailing in Pips

Wir beginnen die Implementierung, indem wir die Handelsbibliothek mit „#include <Trade/Trade.mqh>“ einbinden, die die Klasse CTrade für die Verwaltung von Aufträgen und Positionen bereitstellt. Wir deklarieren „obj_Trade“ als eine globale Instanz von „CTrade“, um alle Handelsoperationen abzuwickeln. Wir definieren eine String-Konstante „FVG_Prefix“ als „IFVG REC“ für die Benennung von FVG-Rechteckobjekten. Wir legen Farbkonstanten für verschiedene FVG-Zustände und Richtungen fest: „CLR_UP“ in grün für normale Aufwärts-FVGs, „CLR_DOWN“ in rot für normale Abwärts-FVGs, „CLR_MIT_UP“ in lila für abgemilderte Aufwärts-FVGs, „CLR_MIT_DOWN“ in orange für abgeschwächte Aufwärts-FVGs, „CLR_INV_UP“ in rot für invertierte Abwärts-FVGs (orig. Aufwärts) und „CLR_INV_DOWN“ in grün für eine invertierte Aufwärts-FVGs. Sie können diese in die von Ihnen gewünschten Farben umwandeln; es handelt sich lediglich um willkürliche Farben, die wir für unsere Grafik mit weißem Hintergrund verwendet haben.

Dann erstellen wir drei Enumerationen für die Konfiguration. Die Enumeration „TradeMode“ bietet „TradeOnce“ zur Begrenzung auf einen Handel pro FVG, „LimitedTrades“ für eine nutzerdefinierte Höchstgrenze pro FVG und „UnlimitedTrades“ ohne Limit pro FVG. Dies ist wichtig, wenn Sie mit mehreren Positionen handeln wollen. Die Enumeration „FVGState“ definiert „Normal“ für anfängliche Lücken, „Mitigated“ nach Durchbrüchen auf der Gegenseite und „Inverted“ bei Inversionssignalen. Die Enumeration „TrailingTypeEnum“ bietet „Trailing_None“ zur Deaktivierung des Trailing und „Trailing_Points“ für punktbasierte Trailing-Stops.

Wir gruppieren die Eingabeparameter unter „EA GENERAL SETTINGS“ für den Eigenschaftsdialog. Dazu gehören „inpLot“ für die Losgröße, „sl_pts“ und „tp_pts“ für Stop-Loss- und Take-Profit-Abstände in Punkten, „minPts“ als Mindestlückengröße, um als FVG zu gelten, „FVG_Rec_Ext_Bars“ für die Anzahl der Balken, um die FVG-Rechtecke nach rechts zu verlängern, „prt“, um die Druckprotokollierung einzuschalten, „magic_number“ für die Handelsidentifikation, „ignoreOverlaps“ um neue FVGs, die bestehende überlappen, zu überspringen (wir dachten, dies wäre extrem wichtig für die visuelle Klarheit, aber Sie können es ignorieren), „tradeMode“ für die Verwendung des Enums für Handelslimits, „maxTradesPerFVG“ für das Limit im limitierten Modus, „maxFVGs“ für die Begrenzung der verfolgten FVGs im Array, „TrailingType“ mit seinem Enum, „Trailing_Stop_Pips“ für den Trailing-Abstand und „Min_Profit_To_Trail_Pips“ für die Gewinnschwelle, bevor das Trailing beginnt. Mit den vorhandenen Eingaben werden wir einige Strukturen und Hilfsfunktionen definieren, die uns bei der Verwaltung unserer Setups helfen.

//+------------------------------------------------------------------+
//| Structure for FVG zone information                               |
//+------------------------------------------------------------------+
struct FVGZone {                                                  // Define FVG zone structure
   string   name;                                                 //--- Zone name
   datetime startTime;                                            //--- Start time
   datetime origEndTime;                                          //--- Original end time
   datetime mitTime;                                              //--- Mitigation time
   bool     signal;                                               //--- Signal flag
   bool     inverted;                                             //--- Inverted flag
   bool     mit;                                                  //--- Mitigated flag
   bool     ret;                                                  //--- Retraced flag
   bool     origUp;                                               //--- Original up flag
   int      tradeCount;                                           //--- Trade count
   FVGState state;                                                //--- State
   bool     newSignal;                                            //--- New signal flag
};
FVGZone fvgs[];                                                   //--- FVG zones array

//+------------------------------------------------------------------+
//| Get color based on state and direction                           |
//+------------------------------------------------------------------+
color GetFVGColor(bool isUp, FVGState currentState) {
   if (currentState == Normal) return isUp ? CLR_UP : CLR_DOWN;   //--- Return normal color
   if (currentState == Mitigated) return isUp ? CLR_MIT_UP : CLR_MIT_DOWN; //--- Return mitigated color
   if (currentState == Inverted) return isUp ? CLR_INV_UP : CLR_INV_DOWN; //--- Return inverted color
   return clrNONE;                                                //--- Return none
}

//+------------------------------------------------------------------+
//| Print FVGs for debugging                                         |
//+------------------------------------------------------------------+
void PrintFVGs() {
   if (!prt) return;                                              //--- Return if no print
   Print("Current FVGs count: ", ArraySize(fvgs));                //--- Print count
   for (int i = 0; i < ArraySize(fvgs); i++) {                    //--- Iterate FVGs
      Print("FVG ", i, ": ", fvgs[i].name, " state=", EnumToString(fvgs[i].state), " mit=", fvgs[i].mit, " ret=", fvgs[i].ret, " inverted=", fvgs[i].inverted, " tradeCount=", fvgs[i].tradeCount, " newSignal=", fvgs[i].newSignal, " endTime=", TimeToString(fvgs[i].origEndTime)); //--- Print details
   }
}

Zunächst definieren wir die Struktur „FVGZone“, um alle relevanten Informationen für jede erkannte Fair-Value-Lücke zu speichern, einschließlich „name“ als String für den Objektidentifikator, „startTime“ und „origEndTime“ als Datumsangaben für die anfängliche Spanne der Lücke, „mitTime“ für den Zeitpunkt der Abschwächung, boolesche Flags wie „signal“ für Inversionsauslöser, „inverted“ für den Inversionsstatus, „mit“ für Mitigation, „ret“ für Retracement, „origUp“ um anzuzeigen, ob es sich ursprünglich um eine Aufwärtslücke handelte, „tradeCount“ als Ganzzahl, um den Handel mit diesem FVG zu verfolgen, „state“ unter Verwendung des State-Enums und „newSignal“ als Boolescher Wert für neue Inversionssignale. Wir deklarieren dann ein globales Array „fvgs[]“ vom Typ „FVGZone“, um alle aktiven FVGs zu speichern, sodass wir mehrere Lücken mit ihren Zuständen effizient verfolgen können.

Wir implementieren die Funktion „GetFVGColor“, um die geeignete Farbe für ein FVG-Rechteck auf der Grundlage seiner Richtung „isUp“ und „currentState“ zu bestimmen: Für „Normal“ geben wir „CLR_UP“ (grün) zurück, wenn es aufwärts geht, oder „CLR_DOWN“ (rot), wenn es abwärts geht; für „Mitigated“, „CLR_MIT_UP“ (violett) oder „CLR_MIT_DOWN“ (orange); für „Inverted“, „CLR_INV_UP“ (rot) oder „CLR_INV_DOWN“ (grün); ansonsten keine. Wir erstellen auch die Funktion „PrintFVGs“ für die Fehlersuche, die frühzeitig zurückkehrt, wenn „prt“ falsch ist, andernfalls den aktuellen Zählerstand aus ArraySize ausgibt und dann in Schleifen jeden Eintrag durchläuft, um Details wie Index, Name, Status über EnumToString, Flags für mit/ret/inverted, Trade Count, neues Signal und Endzeit mit der Funktion TimeToString auszugeben. Nun können wir die visuellen Funktionen für die Rechtecke und Beschriftungen definieren.

//+------------------------------------------------------------------+
//| Create Rectangle                                                 |
//+------------------------------------------------------------------+
void CreateRec(string objName, datetime time1, double price1, datetime time2, double price2, color clr) {
   ObjectCreate(0, objName, OBJ_RECTANGLE, 0, time1, price1, time2, price2); //--- Create rectangle
   ObjectSetInteger(0, objName, OBJPROP_FILL, true);              //--- Set fill
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);              //--- Set color
   ObjectSetInteger(0, objName, OBJPROP_BACK, false);             //--- Set foreground
   datetime midTime = time1 + (time2 - time1) / 2;                //--- Calc mid time
   double midPrice = (price1 + price2) / 2;                       //--- Calc mid price
   CreateLabel(objName, midTime, midPrice);                       //--- Create label
   ChartRedraw(0);                                                //--- Redraw chart
}

//+------------------------------------------------------------------+
//| Update Rectangle                                                 |
//+------------------------------------------------------------------+
void UpdateRec(string objName, datetime time1, double price1, datetime time2, double price2, color clr) {
   if (ObjectFind(0, objName) >= 0) {                             //--- Check exists
      ObjectSetInteger(0, objName, OBJPROP_TIME, 0, time1);       //--- Set time1
      ObjectSetDouble(0, objName, OBJPROP_PRICE, 0, price1);      //--- Set price1
      ObjectSetInteger(0, objName, OBJPROP_TIME, 1, time2);       //--- Set time2
      ObjectSetDouble(0, objName, OBJPROP_PRICE, 1, price2);      //--- Set price2
      ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);           //--- Set color
      datetime midTime = time1 + (time2 - time1) / 2;             //--- Calc mid time
      double midPrice = (price1 + price2) / 2;                    //--- Calc mid price
      UpdateLabel(objName, midTime, midPrice);                    //--- Update label
      ChartRedraw(0);                                             //--- Redraw chart
   }
}

//+------------------------------------------------------------------+
//| Create label                                                     |
//+------------------------------------------------------------------+
void CreateLabel(string zoneName, datetime time, double price) {
   string lblName = zoneName + "_Label";                          //--- Label name
   ObjectCreate(0, lblName, OBJ_TEXT, 0, time, price);            //--- Create text
   ObjectSetInteger(0, lblName, OBJPROP_ANCHOR, ANCHOR_CENTER);   //--- Set anchor
   ObjectSetInteger(0, lblName, OBJPROP_COLOR, clrBlack);         //--- Set color
   UpdateLabelText(lblName, zoneName);                            //--- Update text
}

//+------------------------------------------------------------------+
//| Update label position                                            |
//+------------------------------------------------------------------+
void UpdateLabel(string zoneName, datetime time, double price) {
   string lblName = zoneName + "_Label";                          //--- Label name
   if (ObjectFind(0, lblName) >= 0) {                             //--- Check exists
      ObjectSetInteger(0, lblName, OBJPROP_TIME, 0, time);        //--- Set time
      ObjectSetDouble(0, lblName, OBJPROP_PRICE, 0, price);       //--- Set price
      UpdateLabelText(lblName, zoneName);                         //--- Update text
   }
}

//+------------------------------------------------------------------+
//| Update label text                                                |
//+------------------------------------------------------------------+
void UpdateLabelText(string lblName, string zoneName) {
   string text = "";                                              //--- Init text
   int tradeCnt = 0;                                              //--- Init count
   FVGState state = Normal;                                       //--- Init state
   bool origUp = false;                                           //--- Init orig up
   for (int idx = 0; idx < ArraySize(fvgs); idx++) {              //--- Iterate FVGs
      if (fvgs[idx].name == zoneName) {                           //--- Check match
         tradeCnt = fvgs[idx].tradeCount;                         //--- Get count
         state = fvgs[idx].state;                                 //--- Get state
         origUp = fvgs[idx].origUp;                               //--- Get orig up
         break;                                                   //--- Break loop
      }
   }
   if (state == Normal) {                                         //--- Check normal
      text = origUp ? "Bullish FVG" : "Bearish FVG";              //--- Set text
   } else if (state == Mitigated) {                               //--- Check mitigated
      text = origUp ? "Mitigated Bullish FVG" : "Mitigated Bearish FVG"; //--- Set text
   } else if (state == Inverted) {                                //--- Check inverted
      text = origUp ? "Bearish Inversed FVG" : "Bullish Inversed FVG"; //--- Set text
   }
   if (tradeCnt > 0) {                                            //--- Check traded
      text += " (Traded " + IntegerToString(tradeCnt) + " times)"; //--- Add traded
   }
   ObjectSetString(0, lblName, OBJPROP_TEXT, text);               //--- Set text
}

//+------------------------------------------------------------------+
//| Draw mitigation icon                                              |
//+------------------------------------------------------------------+
void DrawMitIcon(string fvgNAME, datetime mitTime, double fvgHigh, double fvgLow, bool isUp) {
   string iconName = fvgNAME + "_MitIcon";                        //--- Icon name
   double iconPrice = isUp ? fvgLow : fvgHigh;                    //--- Icon price
   ObjectCreate(0, iconName, OBJ_ARROW, 0, mitTime, iconPrice);   //--- Create arrow
   ObjectSetInteger(0, iconName, OBJPROP_ARROWCODE, 251);         //--- Set code
   ObjectSetInteger(0, iconName, OBJPROP_COLOR, clrBlue);         //--- Set color
   ObjectSetInteger(0, iconName, OBJPROP_ANCHOR, isUp ? ANCHOR_TOP : ANCHOR_BOTTOM); //--- Set anchor
   ChartRedraw(0);                                                //--- Redraw chart
}

Für die Visualisierung definieren wir zunächst die Funktion „CreateRec“, um ein neues Rechteck zu zeichnen, das eine FVG-Zone auf dem Chart darstellt. Wir erstellen das Rechteckobjekt mit ObjectCreate unter Verwendung von OBJ_RECTANGLE, das sich vom Zeitpunkt 1 zum Preis 1 bis zum Zeitpunkt 2 zum Preis 2 erstreckt. Wir aktivieren die Füllung, indem wir OBJPROP_FILL auf true setzen, wenden die angegebene Farbe über OBJPROP_COLOR an, positionieren sie im Vordergrund, indem wir OBJPROP_BACK auf false setzen, berechnen die Midpoint-Zeit als time1 plus die Hälfte der Dauer und den Midpoint-Preis als Durchschnitt von price1 und price2, rufen dann CreateLabel auf, um einen beschreibenden Text am Midpoint hinzuzufügen, und zeichnen schließlich das Chart mit der Funktion ChartRedraw neu. Wir implementieren die Funktion „UpdateRec“, um ein bestehendes FVG-Rechteck zu ändern, wenn sich sein Zustand oder seine Farbe ändert. Wenn das Objekt per ObjectFind vorhanden ist, aktualisieren wir seine Koordinaten, indem wir „OBJPROP_TIME“ und „OBJPROP_PRICE“ für beide Anker setzen, die neue Farbe anwenden, den Mittelpunkt der Zeit und des Preises wie zuvor neu berechnen, „UpdateLabel“ aufrufen, um den Text neu zu positionieren und zu aktualisieren, und das Chart neu zeichnen.

Als Nächstes erstellen wir die Funktion „CreateLabel“, um eine Textbeschriftung innerhalb des FVG-Rechtecks hinzuzufügen. Wir bilden den Labelnamen, indem wir „_Label“ an den Zonennamen anhängen, erstellen ein Objekt vom Typ OBJ_TEXT zur angegebenen Zeit und zum angegebenen Preis mit „ObjectCreate“, setzen seinen Anker mit OBJPROP_ANCHOR auf die Mitte, die Farbe auf Schwarz und rufen dann „UpdateLabelText“ auf, um den anfänglichen beschreibenden Text zu setzen. Wir definieren die Funktion „UpdateLabel“, um eine vorhandene Beschriftung zu verschieben, wenn sich das Rechteck anpasst. Wenn die Kennzeichnung vorhanden ist, aktualisieren wir seine Position und rufen dann „UpdateLabelText“ auf, um seinen Inhalt auf der Grundlage des aktuellen Zustands zu aktualisieren. Anschließend implementieren wir die Funktion „UpdateLabelText“, um die Zeichenfolge des Kennzeichens dynamisch zu erstellen und festzulegen. Wir initialisieren einen leeren Text und gehen dann in einer Schleife durch das Array „fvgs“, um die passende Zone anhand ihres Namens zu finden und ihre Handelsanzahl, ihren Status und ihre ursprüngliche Aufwärtsrichtung zu ermitteln.

Je nach Status wird der Text auf „Bullish FVG“ oder „Bearish FVG“ für normal, „Mitigated Bullish FVG“ oder „Mitigated Bearish FVG“ für abgemildert, „Bearish Inversed FVG“ oder „Bullish Inversed FVG“ für invertiert; wenn die Anzahl der Transaktionen 0 übersteigt, wird „(Traded X times)“ angehängt. Wir wenden diesen Text mit ObjectSetString und OBJPROP_TEXT auf die Kennzeichnung an. Schließlich erstellen wir die Funktion „DrawMitIcon“, um eine visuelle Markierung bei Mitigationsereignissen zu setzen. Wir bilden den Namen des Symbols, indem wir „_MitIcon“ an den FVG-Namen anhängen, erstellen einen OBJ_ARROW zur Mitigationszeit und zum entsprechenden Preis (niedrig für Aufwärtslücken, hoch für Abwärtslücken), setzen seinen Pfeilcode auf 251, die Farbe auf blau, den Anker nach oben für Aufwärts- oder unten für Abwärtslücken, basierend auf „isUp“, und zeichnen das Chart neu. Sie können den Pfeilcode nach Belieben ändern, basierend auf dem Schrifttyp MQL5 Wingdings wie unten.

MQL5 WINGDINGS

Mit diesen Funktionen können wir das anfänglichen Bereiche für die Einrichtung auf dem Chart erstellen, indem wir die verfügbaren Balken auf dem Chart verwenden, um das Styling des Indikators zu erstellen, sodass wir in der Lage sind, die Konstellationen bei der Initialisierung des Programms zu sehen. Wir werden die Logik ebenfalls in einer Funktion unterbringen.

//+------------------------------------------------------------------+
//| Process historical mitigation, retracement, signal for an FVG    |
//+------------------------------------------------------------------+
void ProcessHistoricalState(int idx) {
   string fvgNAME = fvgs[idx].name;                               //--- Get name
   datetime timeSTART = fvgs[idx].startTime;                      //--- Get start time
   datetime endTime = fvgs[idx].origEndTime;                      //--- Get end time
   double fvgLow = MathMin(ObjectGetDouble(0, fvgNAME, OBJPROP_PRICE, 0), ObjectGetDouble(0, fvgNAME, OBJPROP_PRICE, 1)); //--- Calc low
   double fvgHigh = MathMax(ObjectGetDouble(0, fvgNAME, OBJPROP_PRICE, 0), ObjectGetDouble(0, fvgNAME, OBJPROP_PRICE, 1)); //--- Calc high
   int fvgBar = iBarShift(_Symbol, _Period, timeSTART);           //--- Get bar
   if (fvgBar < 0) return;                                        //--- Return invalid
   bool isMit = false, isRet = false, isSig = false;              //--- Init flags
   datetime mitTime = 0;                                          //--- Init mit time
   int mitK = -1, sigK = -1;                                      //--- Init indices
   for (int k = fvgBar - 1; k >= 0; k--) {                        //--- Iterate bars
      double barLow = iLow(_Symbol, _Period, k);                  //--- Get bar low
      double barHigh = iHigh(_Symbol, _Period, k);                //--- Get bar high
      double barClose = iClose(_Symbol, _Period, k);              //--- Get bar close
      if (!isMit) {                                               //--- Check not mit
         bool breakFar = (fvgs[idx].origUp && barLow < fvgLow) || (!fvgs[idx].origUp && barHigh > fvgHigh); //--- Check break far
         if (breakFar) {                                          //--- Break far
            isMit = true;                                         //--- Set mit
            mitK = k;                                             //--- Set mit k
            mitTime = iTime(_Symbol, _Period, k);                 //--- Set mit time
            if (prt) Print("Historical Mitigated: ", fvgNAME, " at bar ", k, " time=", TimeToString(mitTime)); //--- Log mitigated
         }
      }
      if (isMit && !isRet) {                                      //--- Check mit and not ret
         bool inside = (barHigh > fvgLow && barLow < fvgHigh);    //--- Check inside
         if (inside) {                                            //--- Inside
            isRet = true;                                         //--- Set ret
            if (prt) Print("Historical Retraced: ", fvgNAME, " at bar ", k); //--- Log retraced
         }
      }
      if (isMit && isRet && !isSig) {                             //--- Check mit ret not sig
         bool signal = (fvgs[idx].origUp && barClose < fvgLow) || (!fvgs[idx].origUp && barClose > fvgHigh); //--- Check signal
         if (signal) {                                            //--- Signal
            if (k + 1 < iBars(_Symbol, _Period)) {                //--- Check prev bar
               double prevClose = iClose(_Symbol, _Period, k + 1); //--- Get prev close
               bool prevInside = (prevClose > fvgLow && prevClose < fvgHigh); //--- Check prev inside
               if (prevInside) {                                  //--- Prev inside
                  isSig = true;                                   //--- Set sig
                  sigK = k;                                       //--- Set sig k
                  if (prt) Print("Historical Signal/Inverted: ", fvgNAME, " at bar ", k, " time=", TimeToString(iTime(_Symbol, _Period, k))); //--- Log signal
               }
            }
         }
      }
   }
   fvgs[idx].mit = isMit;                                         //--- Set mit
   fvgs[idx].ret = isRet;                                         //--- Set ret
   fvgs[idx].inverted = isSig;                                    //--- Set inverted
   fvgs[idx].signal = isSig;                                      //--- Set signal
   fvgs[idx].mitTime = mitTime;                                   //--- Set mit time
   fvgs[idx].state = isSig ? Inverted : (isMit ? Mitigated : Normal); //--- Set state
   fvgs[idx].newSignal = false;                                   //--- Set no new signal
   color currentClr = GetFVGColor(fvgs[idx].origUp, fvgs[idx].state); //--- Get color
   UpdateRec(fvgs[idx].name, fvgs[idx].startTime, fvgLow, fvgs[idx].origEndTime, fvgHigh, currentClr); //--- Update rec
   if (mitTime > 0) DrawMitIcon(fvgs[idx].name, mitTime, fvgHigh, fvgLow, fvgs[idx].origUp); //--- Draw mit icon
}

Hier definieren wir die Funktion „ProcessHistoricalState“, um den Zustand eines bestimmten FVG im Array während der Initialisierung zu analysieren und zu aktualisieren, indem wir auf der Grundlage historischer Balken nach dem Start der Lücke auf Mitigation, Retracement und Inversion prüfen. Wir beginnen damit, den Namen, die Startzeit und die ursprüngliche Endzeit des FVG aus der Struktur am angegebenen Index abzurufen, und berechnen dann die Tiefst- und Höchstpreise mit MathMin und MathMax auf den Preisen des Rechteckobjekts über ObjectGetDouble mit OBJPROP_PRICE. Mit iBarShift wird der Balkenindex der Startzeit ermittelt und bei Ungültigkeit vorzeitig zurückgekehrt. Wir initialisieren die Flags für Mitigation, Retracement und Signal auf false, zusammen mit dem Abschwächungszeitpunkt und dem Balkenindex auf -1. Anschließend wird eine Rückwärtsschleife vom Balken vor dem FVG bis zum Balken 0 durchgeführt: Für jeden Balken werden die Tiefst-, Höchst- und Schlusskurse mit den Funktionen iLow, iHigh und iClose ermittelt. Falls noch nicht abgeschwächt, prüfen wir, ob ein Durchbruch auf der Gegenseite vorliegt – für ursprüngliche Aufwärtslücken (Aufwärts-FVG), wenn der Tiefststand des Balkens unter den Tiefststand des FVG fällt; für Abwärtslücken, wenn der Höchststand des Balkens den Höchststand des FVG übersteigt – und setzen das Flag für die Abschwächung, den Balkenindex, die Zeit über iTime und protokollieren, wenn „prt“ wahr ist.

Ist der Kurs abgeschwächt, aber nicht zurückgekehrt, wird geprüft, ob der Balken die FVG durchbrochen hat (Hoch über Tief und Tief unter Hoch), das Retracement-Flag gesetzt und protokolliert. Ist die Situation sowohl abgeschwächt als auch zurückgekehrt, aber ohne Signal, erkennen wir eine Inversion: für Aufwärtslücken, wenn der Schlusskurs des Balkens unter dem FVG-Tief liegt; für Abwärtslücken, über dem FVG-Hoch – dann überprüfen wir, ob der Schlusskurs des vorherigen Balkens innerhalb des FVG lag (über dem Tief und unter dem Hoch), um den Ausgang von innen zu bestätigen, und setzen das Signal-Flag, den Balken-Index und die Protokollierung.

Wir aktualisieren die Strukturfelder „mitigation“, „retracement“, „inverted“ und „signal“ auf die Flag-Werte, die Abschwächungszeit, den Status „Inverted“, wenn Signal vorliegt, „Mitigated“, wenn abgeschwächt, oder „Normal“, und setzen das neue Signal auf false zurück. Wir ermitteln die aktuelle Farbe mit „GetFVGColor“ auf der Grundlage der ursprünglichen Richtung und des ursprünglichen Zustands, aktualisieren das Rechteck mit „UpdateRec“ mit den möglicherweise angepassten Koordinaten und der neuen Farbe und zeichnen, falls eine Milderung erfolgt ist, das Milderungssymbol mit „DrawMitIcon“ zum Zeitpunkt der Milderung und zum entsprechenden Kantenpreis (niedrig für Lücken nach oben, hoch für Lücken nach unten). Wir können diese Funktion nun in der Initialisierungsfunktion verwenden, um unsere ersten Aufstellungen zu zeichnen.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   obj_Trade.SetExpertMagicNumber(magic_number);                  //--- Set magic number
   ObjectsDeleteAll(0, FVG_Prefix);                               //--- Delete FVG objects
   ArrayResize(fvgs, 0);                                          //--- Reset array
   if (prt) Print("Initializing: Deleted all existing FVG objects and reset array."); //--- Log init
   int visibleBars = (int)ChartGetInteger(0, CHART_VISIBLE_BARS); //--- Get visible bars
   if (prt) Print("Total Visible Bars On Chart = ", visibleBars); //--- Log visible bars
   // Detect historical FVGs from older to newer
   for (int i = visibleBars - 3; i >= 0; i--) {                   //--- Iterate bars
      double low0 = iLow(_Symbol, _Period, i);                    //--- Get low0
      double high2 = iHigh(_Symbol, _Period, i + 2);              //--- Get high2
      double gap_L0_H2 = NormalizeDouble((low0 - high2) / _Point, _Digits); //--- Calc gap L0 H2
      double high0 = iHigh(_Symbol, _Period, i);                  //--- Get high0
      double low2 = iLow(_Symbol, _Period, i + 2);                //--- Get low2
      double gap_H0_L2 = NormalizeDouble((low2 - high0) / _Point, _Digits); //--- Calc gap H0 L2
      bool FVG_UP = low0 > high2 && gap_L0_H2 > minPts;           //--- Check up FVG
      bool FVG_DOWN = low2 > high0 && gap_H0_L2 > minPts;         //--- Check down FVG
      if (FVG_UP || FVG_DOWN) {                                   //--- Check FVG
         datetime time1 = iTime(_Symbol, _Period, i + 1);         //--- Get time1
         double price1 = FVG_UP ? high2 : high0;                  //--- Set price1
         double price2 = FVG_UP ? low0 : low2;                    //--- Set price2
         double newLow = MathMin(price1, price2);                 //--- Calc new low
         double newHigh = MathMax(price1, price2);                //--- Calc new high
         bool overlaps = false;                                   //--- Init overlaps
         if (ignoreOverlaps) {                                    //--- Check ignore overlaps
            for (int ex = 0; ex < ArraySize(fvgs); ex++) {        //--- Iterate existing
               double exLow = ObjectGetDouble(0, fvgs[ex].name, OBJPROP_PRICE, 0); //--- Get ex low
               double exHigh = ObjectGetDouble(0, fvgs[ex].name, OBJPROP_PRICE, 1); //--- Get ex high
               exLow = MathMin(exLow, exHigh);                    //--- Min ex low
               exHigh = MathMax(exLow, exHigh);                   //--- Max ex high
               if (MathMax(newLow, exLow) < MathMin(newHigh, exHigh)) { //--- Check overlap
                  overlaps = true;                                //--- Set overlaps
                  if (prt) Print("Historical: Skipping overlapping FVG at ", TimeToString(time1)); //--- Log skip
                  break;                                          //--- Break loop
               }
            }
         }
         if (overlaps) continue;                                  //--- Continue if overlaps
         string fvgNAME = FVG_Prefix + "(" + TimeToString(time1) + ")"; //--- FVG name
         color fvgClr = FVG_UP ? CLR_UP : CLR_DOWN;               //--- Set color
         CreateRec(fvgNAME, time1, price1, time1 + PeriodSeconds(_Period) * FVG_Rec_Ext_Bars, price2, fvgClr); //--- Create rec
         int size = ArraySize(fvgs);                              //--- Get size
         if (size >= maxFVGs) {                                   //--- Check max
            if (prt) Print("Historical: Max FVGs reached, removing oldest."); //--- Log max
            ArrayRemove(fvgs, 0, 1);                              //--- Remove oldest
            PrintFVGs();                                          //--- Print FVGs
         }
         ArrayResize(fvgs, size + 1);                             //--- Resize array
         fvgs[size].name = fvgNAME;                               //--- Set name
         fvgs[size].startTime = time1;                            //--- Set start time
         fvgs[size].origEndTime = time1 + PeriodSeconds(_Period) * FVG_Rec_Ext_Bars; //--- Set end time
         fvgs[size].mitTime = 0;                                  //--- Set mit time
         fvgs[size].signal = false;                               //--- Set signal
         fvgs[size].inverted = false;                             //--- Set inverted
         fvgs[size].mit = false;                                  //--- Set mit
         fvgs[size].ret = false;                                  //--- Set ret
         fvgs[size].origUp = FVG_UP;                              //--- Set orig up
         fvgs[size].tradeCount = 0;                               //--- Set trade count
         fvgs[size].state = Normal;                               //--- Set state
         fvgs[size].newSignal = false;                            //--- Set new signal
         if (prt) Print("Historical FVG created: ", fvgNAME, " origUp=", FVG_UP, " endTime=", TimeToString(fvgs[size].origEndTime)); //--- Log created
         PrintFVGs();                                             //--- Print FVGs
      }
   }
   // Process historical states
   for (int j = 0; j < ArraySize(fvgs); j++) {                    //--- Iterate FVGs
      ProcessHistoricalState(j);                                  //--- Process state
   }
   PrintFVGs();                                                   //--- Print FVGs
   return(INIT_SUCCEEDED);                                        //--- Return success
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   for (int i = 0; i < ArraySize(fvgs); i++) {                    //--- Iterate FVGs
      ObjectDelete(0, fvgs[i].name);                              //--- Delete name
      ObjectDelete(0, fvgs[i].name + "_Label");                   //--- Delete label
      ObjectDelete(0, fvgs[i].name + "_MitIcon");                 //--- Delete mit icon
   }
   ArrayResize(fvgs, 0);                                          //--- Reset array
   ChartRedraw(0);                                                //--- Redraw chart
   if (prt) Print("Deinit: Deleted all FVG objects and reset array."); //--- Log deinit
}

In der Ereignisbehandlung von OnInit, das beim Programmstart aufgerufen wird, weisen wir zunächst „obj_Trade“ mit „SetExpertMagicNumber“ die Eingabe „magic_number“ zur Handelsidentifikation zu. Anschließend werden alle vorhandenen FVG-Rechtecke durch den Aufruf von ObjectsDeleteAll mit dem aktuellen Chart und „FVG_Prefix“ gelöscht, die Größe des Arrays „fvgs“ mit ArrayResize auf 0 zurückgesetzt und die Initialisierung protokolliert, wenn „prt“ wahr ist. Wir rufen die Anzahl der sichtbaren Balken auf dem Chart mit ChartGetInteger unter Verwendung von CHART_VISIBLE_BARS in „visibleBars“ ab und protokollieren sie, wenn „prt“.

Um historische FVGs von den ältesten bis zu den neuesten zu erkennen, führen wir eine Schleife von „visibleBars – 3“ bis hinunter zu 0: Für jeden Balken i berechnen wir potentielle Lücken, indem wir den Tiefstwert von i mit dem Höchstwert von i+2 vergleichen (normalisierte Lücke in Punkten in „gap_L0_H2“) und den Höchstwert von i mit dem Tiefstwert von i+2 („gap_H0_L2“), wobei „FVG_UP“ gesetzt wird, wenn low0 > high2 und gap > „minPts“ (Aufwärtslücke) oder „FVG_DOWN“, wenn low2 > high0 und gap > „minPts“ (Aufwärtslücke). Wir benötigen mindestens 3 vollständige Balken, um eine Lücke zu erkennen. Es ist besonders wichtig, dass Sie dies ein für alle Mal verstehen. Zur Verdeutlichung haben wir es für Sie wie folgt visualisiert.

ANFORDERUNGEN FÜR EINE FVG-EINRICHTUNG

Wenn einer der beiden Typen gefunden wird, wird die Zeit von Takt i+1 in „time1“ übernommen, die Preise werden entsprechend gesetzt und die Tiefst- und Höchstwerte der Lücke mit den Funktionen MathMin und MathMax berechnet. Wenn „ignoreOverlaps“ wahr ist, überprüfen wir alle in „fvgs“ vorhandenen Preise, indem wir sie mit ObjectGetDouble abrufen und die Bereiche vergleichen – wenn sie sich überschneiden (Maximum der Tiefs < Minimum der Hochs), setzen wir „overlaps“ wahr und protokollieren „prt“ und fahren mit dem nächsten fort. Wenn es keine Überschneidung gibt, bilden wir den Namen als „FVG_Prefix + (TimeToString(time1))“, wählen die Farbe nach oben oder unten und rufen „CreateRec“ auf, um das um die „FVG_Rec_Ext_Bars“-Perioden erweiterte Rechteck zu zeichnen. Wir prüfen, ob die Array-Größe „maxFVGs“ erreicht, entfernen die ältesten mit ArrayRemove und protokollieren, wenn „prt“, dann verkleinern wir „fvgs“ um eins, füllen den neuen Eintrag mit Name, Zeiten, Flags alle false außer „origUp“ als „FVG_UP“, trade count 0, state „Normal“, new signal false, log creation if „prt“, und call „PrintFVGs“. Sie sehen, dass die Überschneidungen die visuelle Attraktivität beeinträchtigen könnten, haben aber kein Problem mit der Handelsaktivität.

BEISPIEL FÜR ÜBERLAPPENDE LÜCKEN

Sie sehen, dass die sich überlappenden Instanzen optisch nicht sehr ansprechend sind. Nachdem wir alle historischen FVGs erkannt haben, durchlaufen wir eine Schleife durch „fvgs“ und rufen „ProcessHistoricalState“ für jeden Index auf, um die Anfangszustände auf der Grundlage vergangener Preisaktionen festzulegen, rufen dann erneut „PrintFVGs“ auf und geben INIT_SUCCEEDED zurück. In der Funktion OnDeinit durchlaufen wir schließlich eine Schleife durch „fvgs“, löschen jedes Rechteck, jede Beschriftung (mit dem Suffix „_Label“) und jedes Mitigations-Symbol („_MitIcon“) mit ObjectDelete, ändern die Größe von „fvgs“ auf 0, zeichnen das Chart neu und protokollieren das Deinit if „prt“. Wenn wir kompilieren, erhalten wir folgendes Ergebnis.

IFVG BELASTUNG BEIM LADEN DER HISTORIE

Aus der Visualisierung geht hervor, dass wir alle FVGs beim Laden scannen, abbilden und aktualisieren. Jetzt müssen wir die Erkennung fortsetzen und die Aktualisierungen für neue Balken einrichten. Beginnen wir mit der Erkennungslogik.

//+------------------------------------------------------------------+
//| Detect new FVGs on recent bars                                   |
//+------------------------------------------------------------------+
void DetectFVGs() {
   for (int i = 3; i >= 1; i--) {                                 //--- Iterate recent bars
      double low0 = iLow(_Symbol, _Period, i);                    //--- Get low0
      double high2 = iHigh(_Symbol, _Period, i + 2);              //--- Get high2
      double gap_L0_H2 = NormalizeDouble((low0 - high2) / _Point, _Digits); //--- Calc gap L0 H2
      double high0 = iHigh(_Symbol, _Period, i);                  //--- Get high0
      double low2 = iLow(_Symbol, _Period, i + 2);                //--- Get low2
      double gap_H0_L2 = NormalizeDouble((low2 - high0) / _Point, _Digits); //--- Calc gap H0 L2
      bool FVG_UP = low0 > high2 && gap_L0_H2 > minPts;           //--- Check up FVG
      bool FVG_DOWN = low2 > high0 && gap_H0_L2 > minPts;         //--- Check down FVG
      if (FVG_UP || FVG_DOWN) {                                   //--- Check FVG
         datetime time1 = iTime(_Symbol, _Period, i + 1);         //--- Get time1
         double price1 = FVG_UP ? high2 : high0;                  //--- Set price1
         double price2 = FVG_UP ? low0 : low2;                    //--- Set price2
         double newLow = MathMin(price1, price2);                 //--- Calc new low
         double newHigh = MathMax(price1, price2);                //--- Calc new high
         bool overlaps = false;                                   //--- Init overlaps
         if (ignoreOverlaps) {                                    //--- Check ignore overlaps
            for (int ex = 0; ex < ArraySize(fvgs); ex++) {        //--- Iterate existing
               double exLow = MathMin(ObjectGetDouble(0, fvgs[ex].name, OBJPROP_PRICE, 0), ObjectGetDouble(0, fvgs[ex].name, OBJPROP_PRICE, 1)); //--- Calc ex low
               double exHigh = MathMax(ObjectGetDouble(0, fvgs[ex].name, OBJPROP_PRICE, 0), ObjectGetDouble(0, fvgs[ex].name, OBJPROP_PRICE, 1)); //--- Calc ex high
               if (MathMax(newLow, exLow) < MathMin(newHigh, exHigh)) { //--- Check overlap
                  overlaps = true;                                //--- Set overlaps
                  if (prt) Print("Detect: Skipping overlapping FVG at ", TimeToString(time1)); //--- Log skip
                  break;                                          //--- Break loop
               }
            }
         }
         if (overlaps) continue;                                  //--- Continue if overlaps
         string fvgNAME = FVG_Prefix + "(" + TimeToString(time1) + ")"; //--- FVG name
         if (ObjectFind(0, fvgNAME) >= 0) continue;               //--- Skip duplicate
         color fvgClr = FVG_UP ? CLR_UP : CLR_DOWN;               //--- Set color
         datetime endTime = time1 + PeriodSeconds(_Period) * FVG_Rec_Ext_Bars; //--- Calc end time
         CreateRec(fvgNAME, time1, price1, endTime, price2, fvgClr); //--- Create rec
         int size = ArraySize(fvgs);                              //--- Get size
         if (size >= maxFVGs) {                                   //--- Check max
            if (prt) Print("Detect: Max FVGs reached, removing oldest."); //--- Log max
            ArrayRemove(fvgs, 0, 1);                              //--- Remove oldest
            PrintFVGs();                                          //--- Print FVGs
         }
         ArrayResize(fvgs, size + 1);                             //--- Resize array
         fvgs[size].name = fvgNAME;                               //--- Set name
         fvgs[size].startTime = time1;                            //--- Set start time
         fvgs[size].origEndTime = endTime;                        //--- Set end time
         fvgs[size].mitTime = 0;                                  //--- Set mit time
         fvgs[size].signal = false;                               //--- Set signal
         fvgs[size].inverted = false;                             //--- Set inverted
         fvgs[size].mit = false;                                  //--- Set mit
         fvgs[size].ret = false;                                  //--- Set ret
         fvgs[size].origUp = FVG_UP;                              //--- Set orig up
         fvgs[size].tradeCount = 0;                               //--- Set trade count
         fvgs[size].state = Normal;                               //--- Set state
         fvgs[size].newSignal = false;                            //--- Set new signal
         if (prt) Print("New FVG added to storage: ", fvgNAME, " origUp=", FVG_UP, " endTime=", TimeToString(endTime)); //--- Log added
         PrintFVGs();                                             //--- Print FVGs
      }
   }
}

Wir definieren die Funktion „DetectFVGs“ so, dass sie bei jedem neuen Balken die letzten Balken auf neue Fair-Value-Lücken untersucht und sie unserem Tracking-System hinzufügt, wenn sie die Kriterien erfüllen. Wir machen eine Schleife von Index 3 bis 1, um die letzten abgeschlossenen Balken zu prüfen (das ist jetzt nicht neu): Für jedes i holen wir das Tief von i mit iLow in „low0“, das Hoch von i+2 in „high2“, und berechnen den normalisierten Abstand in Punkten in „gap_L0_H2“; ähnlich für das Hoch von i in „high0“, das Tief von i+2 in „low2“, und „gap_H0_L2“. Wir setzen „FVG_UP“ true, wenn „low0 > high2“ und Gap über „minPts“ (Aufwärtslücke), oder „FVG_DOWN“, wenn „low2 > high0“ und Gap > „minPts“ (Abwärtslücke).

Wenn einer von beiden erkannt wird, wird die Zeit von i+1 in „time1“ eingetragen, „price1“ auf „high2“ für Aufwärtsbewegungen oder „high0“ für Abwärtsbewegungen gesetzt, „price2“ auf „low0“ oder „low2“, und die Tiefst- und Höchstwerte der Lücke werden berechnet. Wenn „ignoreOverlaps“ wahr ist, überprüfen wir alle in „fvgs“ vorhandenen Preise, indem wir sie mit ObjectGetDouble abrufen und MathMin/MathMax verwenden, um Bereiche zu erhalten – wenn sich ein neues Gap mit einem überschneidet (max lows < min highs), setzen wir „overlaps“ auf wahr, protokollieren die Lücke, wenn „prt“, und fahren fort. Wenn es keine Überschneidungen und kein doppeltes Objekt per ObjectFind gibt, bilden wir den Namen als „FVG_Prefix + (TimeToString(time1))“, wählen die Farbe nach oben (grün) oder unten (rot), berechnen „endTime“ als „time1 + PeriodSeconds(_Period) * FVG_Rec_Ext_Bars“, und rufen „CreateRec“ auf, um das Rechteck zu zeichnen. Wir verwalten dann das Array „fvgs“: Wenn die Größe „maxFVGs“ erreicht ist, entfernen wir das älteste mit ArrayRemove und protokollieren, wenn „prt“, dann verkleinern wir es um eins mit ArrayResize, füllen den neuen Eintrag mit Name, Zeiten, Abschwächungszeit 0, alle Flags false außer „origUp“ als „FVG_UP“, Handelszähler auf 0, state = „Normal“, newsignal auf false, protokollieren, wenn „prt“ und rufen „PrintFVGs“ auf. Dadurch werden nur gültige, sich nicht überschneidende neue FVGs, die sich nach rechts erstrecken, erkannt und gespeichert. Wir können diese Funktion in der Tick-Ereignishandhabung aufrufen, um die schwere Arbeit wie unten beschrieben zu erledigen.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   static datetime lastBarTime = 0;                               //--- Last bar time
   datetime curBarTime = iTime(_Symbol, _Period, 0);              //--- Current bar time
   bool newBar = (curBarTime != lastBarTime);                     //--- Check new bar
   if (!newBar) return;                                           //--- Return if not new
   lastBarTime = curBarTime;                                      //--- Update last time
   DetectFVGs();                                                  //--- Detect FVGs
}

In der Ereignisbehandlung von OnTick, das bei jedem Preis-Tick ausgeführt wird, um Echtzeit-Aktualisierungen zu verarbeiten, verwenden wir die statische Variable „lastBarTime“, um die Eröffnungszeit des vorherigen Balkens zu verfolgen, holen die Zeit des aktuellen Balkens mit iTime bei Shift 0 in „curBarTime“ und setzen „newBar“ auf true, wenn sie sich unterscheiden, was anzeigt, dass ein neuer Balken entstanden ist. Wenn es sich nicht um einen neuen Balken handelt, kehren wir vorzeitig zurück, um eine überflüssige Bearbeitung zu vermeiden. Andernfalls aktualisieren wir „lastBarTime“ auf „curBarTime“ und rufen „DetectFVGs“ auf, um nach neuen Lücken in den letzten Balken zu suchen. Wir kommen zu folgendem Ergebnis.

INITIALE IFVGS-ERKENNUNG IN ONTICK

Nachdem die erste Erkennung erfolgt ist, können wir mit der Aktualisierung der Einstellungen fortfahren. Wir verwenden die folgende Logik.

//+------------------------------------------------------------------+
//| Update states for all FVGs                                       |
//+------------------------------------------------------------------+
void UpdateFVGs() {
   double prevClose = iClose(_Symbol, _Period, 1);                //--- Get prev close
   double prevLow = iLow(_Symbol, _Period, 1);                    //--- Get prev low
   double prevHigh = iHigh(_Symbol, _Period, 1);                  //--- Get prev high
   double bar2Close = iClose(_Symbol, _Period, 2);                //--- Get bar2 close
   datetime curBarTime = iTime(_Symbol, _Period, 1);              //--- Get prev bar time
   bool removed = false;                                          //--- Init removed
   for (int j = ArraySize(fvgs) - 1; j >= 0; j--) {               //--- Iterate reverse
      if (ObjectFind(0, fvgs[j].name) < 0) {                      //--- Check no object
         if (prt) Print("Update: Removed non-existent FVG from storage: ", fvgs[j].name); //--- Log removed
         ArrayRemove(fvgs, j, 1);                                 //--- Remove from array
         removed = true;                                          //--- Set removed
         continue;                                                //--- Continue
      }
      double fvgLow = MathMin(ObjectGetDouble(0, fvgs[j].name, OBJPROP_PRICE, 0), ObjectGetDouble(0, fvgs[j].name, OBJPROP_PRICE, 1)); //--- Calc low
      double fvgHigh = MathMax(ObjectGetDouble(0, fvgs[j].name, OBJPROP_PRICE, 0), ObjectGetDouble(0, fvgs[j].name, OBJPROP_PRICE, 1)); //--- Calc high
      if (!fvgs[j].mit) {                                         //--- Check not mit
         bool breakFar = (fvgs[j].origUp && prevLow < fvgLow) || (!fvgs[j].origUp && prevHigh > fvgHigh); //--- Check break far
         if (breakFar) {                                          //--- Break far
            fvgs[j].mit = true;                                   //--- Set mit
            fvgs[j].mitTime = curBarTime;                         //--- Set mit time
            fvgs[j].state = Mitigated;                            //--- Set state
            if (prt) Print("Mitigated FVG: ", fvgs[j].name, " at time=", TimeToString(curBarTime)); //--- Log mitigated
            color mitClr = GetFVGColor(fvgs[j].origUp, fvgs[j].state); //--- Get color
            UpdateRec(fvgs[j].name, fvgs[j].startTime, fvgLow, fvgs[j].origEndTime, fvgHigh, mitClr); //--- Update rec
            DrawMitIcon(fvgs[j].name, curBarTime, fvgHigh, fvgLow, fvgs[j].origUp); //--- Draw icon
         }
      }
      if (fvgs[j].mit && !fvgs[j].ret) {                          //--- Check mit not ret
         bool inside = (prevHigh > fvgLow && prevLow < fvgHigh);  //--- Check inside
         if (inside) {                                            //--- Inside
            fvgs[j].ret = true;                                   //--- Set ret
            if (prt) Print("Retraced into FVG: ", fvgs[j].name);  //--- Log retraced
         }
      }
      if (fvgs[j].mit && fvgs[j].ret) {                           //--- Check mit ret
         bool signal = (fvgs[j].origUp && prevClose < fvgLow) || (!fvgs[j].origUp && prevClose > fvgHigh); //--- Check signal
         bool prevInside = (bar2Close > fvgLow && bar2Close < fvgHigh); //--- Check prev inside
         if (signal && curBarTime != fvgs[j].mitTime && prevInside) { //--- Check signal conditions
            fvgs[j].newSignal = true;                             //--- Set new signal
            if (!fvgs[j].inverted) {                              //--- Check not inverted
               fvgs[j].inverted = true;                           //--- Set inverted
               fvgs[j].state = Inverted;                          //--- Set state
               if (prt) Print("Signal/Inverted FVG: ", fvgs[j].name, " at time=", TimeToString(curBarTime)); //--- Log signal
               color sigClr = GetFVGColor(fvgs[j].origUp, fvgs[j].state); //--- Get color
               UpdateRec(fvgs[j].name, fvgs[j].startTime, fvgLow, fvgs[j].origEndTime, fvgHigh, sigClr); //--- Update rec
            }
         }
      }
   }
   if (removed) PrintFVGs();                                      //--- Print if removed
}

Hier definieren wir die Funktion „UpdateFVGs“, um die Zustände aller verfolgten Fair-Value-Lücken bei jedem neuen Balken zu aktualisieren, wobei die Daten des vorherigen Balkens verwendet werden, um Mitigation, Retracement und Inversion in Echtzeit zu erkennen. Wir beginnen damit, dass wir den Schluss des vorherigen Balkens mit iClose bei Shift 1 in „prevClose“ abrufen, sein Tief mit iLow in „prevLow“, sein Hoch mit iHigh in „prevHigh“, den Schluss des vorangegangenen Balkens in „bar2Close“ bei Shift 2 und die Zeit des vorangegangenen Balkens mit „iTime“ bei Shift 1 in „curBarTime“. Wir initialisieren das Flag „removed“ mit „false“ und gehen dann in einer Schleife rückwärts durch das Array „fvgs“, um Einträge bei Bedarf sicher zu entfernen. Für jedes FVG bei Index j, wenn das Rechteck-Objekt per „ObjectFind“ fehlt und negativ zurückkommt, protokollieren wir das Entfernen; wenn „prt“ = true, löschen den Eintrag mit ArrayRemove, setzen „removed“ auf true und fahren mit dem nächsten fort. Andernfalls berechnen wir den aktuellen Höchst- und Tiefstwert aus den Preisen des Objekts mit „MathMin“ und „MathMax“ auf ObjectGetDouble mit OBJPROP_PRICE für die Anker 0 und 1.

Falls noch nicht abgeschwächt, wird auf einen Durchbruch auf der Gegenseite geprüft: für ursprüngliche Aufwärtslücken, wenn „prevLow“ unter das FVG-Tief fällt; für Abwärtslücken, wenn „prevHigh“ das FVG-Hoch überschreitet – Setzen das Flag der Abschwächung auf „true“, „mitTime“ auf „curBarTime“, Status auf „Mitigated“, protokollieren, wenn „prt“, rufen die neue Farbe mit „GetFVGColor“ ab, aktualisieren das Rechteck über „UpdateRec“ mit den angepassten Koordinaten und der Farbe und Zeichnen des Abschwächungssymbols mit „DrawMitIcon“ an „curBarTime“ und dem äußersten Rand (oben für Abwärtslücken, unten für Aufwärtslücken). Wenn eine Abschwächung erfolgt, aber kein Retracement, wird überprüft, ob der vorherige Balken das FVG überschneidet (Hoch über Tief und Tief unter Hoch), wobei das Retracement-Flag auf true gesetzt und protokolliert wird.

Wenn die Situation sowohl abgeschwächt als auch zurückgekehrt ist, erkennen wir eine Umkehrung: bei Aufwärtslücken, wenn „prevClose“ unter dem FVG-Tief liegt; bei Abwärtslücken, wenn es über dem FVG-Hoch liegt – außerdem stellen wir sicher, dass es sich nicht um den Mitigation Balken selbst handelt und dass „bar2Close“ innerhalb des FVG lag (über dem Tief und unter dem Hoch), um einen Ausstieg von innen zu bestätigen. Wir setzen „newSignal“ true, und wenn nicht bereits invertiert, setzen wir „inverted“ auf true, „state“ auf „Inverted“, protokollieren, wählen die Farbe der Inversion und aktualisieren das Rechteck. Wenn etwas entfernt worden ist, rufen wir „PrintFVGs“ zur Fehlersuche auf. Dadurch bleiben die Zustände aller FVGs auf dem neuesten Stand, was genaue Umkehrsignale für den Handel ermöglicht, während verwaiste Objekte elegant behandelt werden. Wenn wir die Funktion aufrufen, erhalten wir das folgende Ergebnis.

AKTUALISIERTE IFVG-EINSTELLUNGEN GIF

Wir können sehen, dass wir die Situation aktualisieren, wenn die Preise mit ihnen interagieren. Was jetzt noch bleibt, ist der Handel bei einer Inversion, und das wird alles sein. Hier ist die Logik, die wir implementiert haben, um dies in einer Funktion zu erreichen und die Modularisierung beizubehalten.

//+------------------------------------------------------------------+
//| Trade on FVGs with signals                                       |
//+------------------------------------------------------------------+
void TradeOnFVGs() {
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits); //--- Get ask
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits); //--- Get bid
   for (int j = 0; j < ArraySize(fvgs); j++) {                    //--- Iterate FVGs
      if (!fvgs[j].newSignal || fvgs[j].mitTime == 0) continue;   //--- Skip no signal or no mit
      if (tradeMode == TradeOnce && fvgs[j].tradeCount >= 1) {    //--- Check once and traded
         fvgs[j].newSignal = false;                               //--- Reset signal
         continue;                                                //--- Continue
      }
      if (tradeMode == LimitedTrades && fvgs[j].tradeCount >= maxTradesPerFVG) { //--- Check limited and max
         fvgs[j].newSignal = false;                               //--- Reset signal
         continue;                                                //--- Continue
      }
      double fvgLow = MathMin(ObjectGetDouble(0, fvgs[j].name, OBJPROP_PRICE, 0), ObjectGetDouble(0, fvgs[j].name, OBJPROP_PRICE, 1)); //--- Calc low
      double fvgHigh = MathMax(ObjectGetDouble(0, fvgs[j].name, OBJPROP_PRICE, 0), ObjectGetDouble(0, fvgs[j].name, OBJPROP_PRICE, 1)); //--- Calc high
      if (!fvgs[j].origUp) {                                      //--- Check orig down: Bullish IFVG, Buy
         if (prt) Print("BULLISH IFVG TRADE SIGNAL For ", fvgs[j].name, " at ", Bid); //--- Log buy signal
         double SL_Buy = NormalizeDouble(fvgLow - sl_pts * _Point, _Digits); //--- Calc buy SL
         double TP_Buy = NormalizeDouble(Ask + tp_pts * _Point, _Digits); //--- Calc buy TP
         obj_Trade.Buy(inpLot, _Symbol, Ask, SL_Buy, TP_Buy, "IFVG Buy"); //--- Open buy
      } else {                                                    //--- Orig up: Bearish IFVG, Sell
         if (prt) Print("BEARISH IFVG TRADE SIGNAL For ", fvgs[j].name, " at ", Ask); //--- Log sell signal
         double SL_Sell = NormalizeDouble(fvgHigh + sl_pts * _Point, _Digits); //--- Calc sell SL
         double TP_Sell = NormalizeDouble(Bid - tp_pts * _Point, _Digits); //--- Calc sell TP
         obj_Trade.Sell(inpLot, _Symbol, Bid, SL_Sell, TP_Sell, "IFVG Sell"); //--- Open sell
      }
      fvgs[j].tradeCount++;                                       //--- Increment count
      fvgs[j].newSignal = false;                                  //--- Reset signal
      fvgs[j].ret = false;                                        //--- Reset ret
      if (prt) Print("Trade executed on ", fvgs[j].name, ", tradeCount now=", fvgs[j].tradeCount); //--- Log executed
      double midPrice = (fvgLow + fvgHigh) / 2;                   //--- Calc mid price
      datetime midTime = fvgs[j].startTime + (fvgs[j].origEndTime - fvgs[j].startTime) / 2; //--- Calc mid time
      UpdateLabel(fvgs[j].name, midTime, midPrice);               //--- Update label
   }
}

//+------------------------------------------------------------------+
//| Cleanup expired FVGs from array (keep on chart)                  |
//+------------------------------------------------------------------+
void CleanupExpiredFVGs(datetime curBarTime) {
   bool removed = false;                                          //--- Init removed
   for (int j = ArraySize(fvgs) - 1; j >= 0; j--) {               //--- Iterate reverse
      if (curBarTime > fvgs[j].origEndTime) {                     //--- Check expired
         if (prt) Print("Expired FVG removed from storage (kept on chart): ", fvgs[j].name, " endTime=", TimeToString(fvgs[j].origEndTime)); //--- Log expired
         ArrayRemove(fvgs, j, 1);                                 //--- Remove from array
         removed = true;                                          //--- Set removed
      }
   }
   if (removed) PrintFVGs();                                      //--- Print if removed
}

Zunächst definieren wir die Funktion „TradeOnFVGs“, um Handelsgeschäfte auf frische Inversionssignale von den FVGs auszuführen, wobei die konfigurierten Handelsmodi und Limits beachtet werden. Zunächst wird der aktuelle Briefkurs mit SymbolInfoDouble unter Verwendung von SYMBOL_ASK abgerufen und in „Ask“ auf die Dezimalstellen normalisiert; dasselbe gilt für den Geldkurs mit SYMBOL_BID in „Bid“. Anschließend wird das Array „fvgs“ in einer Schleife durchlaufen: Jeder Eintrag wird übersprungen, wenn kein neues Signal vorliegt oder die Abschwächungszeit Null ist. Wir prüfen die Handelsmodi – bei „TradeOnce“, wenn die Anzahl der Trades 1 oder mehr beträgt, oder bei „LimitedTrades“, wenn sie bei oder über „maxTradesPerFVG“ liegt, setzen wir das neue Signal-Flag zurück und fahren fort.

Für gültige Signale berechnen wir den Tiefst- und Höchststand des FVG aus den Preisen des Rechtecks mit MathMin und MathMax der Funktion ObjectGetDouble. Wenn kein ursprünglicher Aufwärtstrend vorliegt (Abwärts-FVG, invertierte Aufwärtsbewegung), protokollieren wir das Kaufsignal, wenn „prt“ true ist, setzen Stop-Loss unter Low minus „sl_pts * _Point“ normalisiert, Take-Profit über Ask plus „tp_pts * _Point“ normalisiert, und eröffnen einen Buy mit „obj_Trade.Buy“ unter Verwendung von „inpLot“, Symbol, „Ask“, berechneten Niveaus und Kommentar „IFVG Buy“. Dieselbe Logik gilt für eine Abwärtsbewegung. Wir erhöhen den Handelszähler, setzen neue Signal- und Retracement-Flags zurück, protokollieren die Ausführung mit dem aktuellen Zählerstand, wenn „prt“, berechnen den mittleren Preis und die Zeit und rufen „UpdateLabel“ auf, um die Position der Kennzeichnung zu aktualisieren.

Schließlich implementieren wir die Funktion „CleanupExpiredFVGs“, um veraltete FVGs aus dem Array zu entfernen, während ihre Darstellung im Chart beibehalten wird, aufgerufen mit der Zeit des vorherigen Balkens. Wir initialisieren das Flag „removed“ auf „false“ und durchlaufen dann eine Schleife rückwärts durch „fvgs“: Wenn die angegebene Zeit die ursprüngliche Endzeit überschreitet, protokollieren wir den Ablauf, entfernen den Eintrag mit ArrayRemove und setzen „removed“ auf true. Wenn etwas entfernt wurde, rufen wir „PrintFVGs“ zur Fehlersuche auf. Dies ist sehr wichtig, um sicherzustellen, dass wir nur die für uns wichtigen Konfigurationen verfolgen. Wenn wir diese aufrufen und ausführen, erhalten wir das folgende Ergebnis.

HANDELSAUSFÜHRUNGEN

Da wir auf die generierten Signale reagieren und Positionen eröffnen können, müssen wir die Handelsgeschäfte nur noch mit einem Trailing-Stop verwalten. Hier ist die Logik, mit der wir das erreichen.

//+------------------------------------------------------------------+
//| Apply Points Trailing Stop                                       |
//+------------------------------------------------------------------+
void ApplyPointsTrailing() {
   double point = _Point;                                            //--- Get point value
   for (int i = PositionsTotal() - 1; i >= 0; i--) {                 //--- Iterate positions reverse
      if (PositionGetTicket(i) > 0) {                                //--- Check valid ticket
         if (PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) { //--- Check symbol and magic
            double sl = PositionGetDouble(POSITION_SL);              //--- Get SL
            double tp = PositionGetDouble(POSITION_TP);              //--- Get TP
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Get open price
            ulong ticket = PositionGetInteger(POSITION_TICKET);      //--- Get ticket
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy
               double newSL = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - Trailing_Stop_Pips * point, _Digits); //--- Calc new SL
               if (newSL > sl && SymbolInfoDouble(_Symbol, SYMBOL_BID) - openPrice > Min_Profit_To_Trail_Pips * point) { //--- Check conditions
                  obj_Trade.PositionModify(ticket, newSL, tp);       //--- Modify position
               }
            } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell
               double newSL = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + Trailing_Stop_Pips * point, _Digits); //--- Calc new SL
               if (newSL < sl && openPrice - SymbolInfoDouble(_Symbol, SYMBOL_ASK) > Min_Profit_To_Trail_Pips * point) { //--- Check conditions
                  obj_Trade.PositionModify(ticket, newSL, tp);       //--- Modify position
               }
            }
         }
      }
   }
}

//--- Call the function in the tick event handler per tick
if (TrailingType == Trailing_Points && PositionsTotal() > 0) { //--- Check trailing
   ApplyPointsTrailing();                                      //--- Apply trailing
}

Hier definieren wir die Funktion „ApplyPointsTrailing“, um punktbasierte Trailing-Stops zu implementieren, wenn sie ausgewählt werden, und passen die Stop-Loss-Niveaus in Echtzeit an, wenn sich der Preis profitabel bewegt. Wir beginnen damit, dass wir den Punktwert des Symbols „point“ mit _Point zuweisen. Anschließend durchlaufen wir mit PositionsTotal eine Rückwärtsschleife durch alle offenen Positionen, um Indexprobleme bei Änderungen zu vermeiden, und überprüfen die Gültigkeit jedes Tickets mit der Funktion PositionGetTicket. Für Positionen, die mit unserem Symbol und der „magic_number“ übereinstimmen, rufen wir den Stop-Loss mit PositionGetDouble und POSITION_SL, den Take-Profit mit „POSITION_TP“, den offenen Preis mit „POSITION_PRICE_OPEN“ und das Ticket mit „POSITION_TICKET“ ab. Für Kaufpositionen (POSITION_TYPE_BUY) berechnen wir einen neuen Stop-Loss als aktuelles Bid minus „Trailing_Stop_Pips * point“, normalisiert auf die Dezimalstellen – wenn dieser enger ist als der bestehende Stop-Loss und der unrealisierte Gewinn „Min_Profit_To_Trail_Pips * point“ übersteigt, aktualisieren wir die Position mit „obj_Trade.PositionModify“, wobei der Take-Profit unverändert bleibt. Wir wenden eine ähnliche Logik für Verkaufspositionen an: neuer Stop-Loss als Briefkurs plus Trailing-Distanz, die nur geändert wird, wenn sie sich verengt und der Gewinn die Schwelle erreicht.

Innerhalb von „OnTick“, wenn „TrailingType“ „Trailing_Points“ ist und Positionen per „PositionsTotal“ existieren, rufen wir „ApplyPointsTrailing“ bei jedem Tick auf, um rechtzeitige Anpassungen sicherzustellen. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

IFVG ABSCHLIESSENDE PRÜFUNG GIF

Anhand der Visualisierung erkennen wir die Konstellationen der inversen Fair-Value-Lücken, um sie zu kontrollieren und zu handeln und so unsere Ziele zu verwirklichen. Bleibt nur noch der Backtest des Programms, und der wird im nächsten Abschnitt behandelt.


Backtests

Nach einem gründlichen Backtest erhalten wir folgende Ergebnisse.

Backtest-Grafik:

GRAPH

Bericht des Backtests:

BERICHT


Schlussfolgerung

Zusammenfassend haben wir das System Inverse Fair-Value-Gap (IFVG) in MQL5 entwickelt, das steigende/fallende Fair-Value-Gaps (FVGs) auf aktuellen Balken mit einem Filter für die Mindestgröße der Lücken erkennt, Zustände als normal/gemildert/invertiert basierend auf Preisinteraktionen verfolgt, Überschneidungen ignoriert, während es verfolgte FVGs begrenzt, und historische FVGs bei der Initialisierung mit Echtzeit-Updates und abgelaufener Bereinigung lädt. Das System unterstützt einmalige/begrenzte/unbegrenzte Handelsgeschäfte pro Konstellation, eröffnet Käufe bei Aufwärts-IFVGs oder Verkäufe bei Abwärts-IFVGs mit festen Handelsniveaus, Positionslimits, Trailing Stops und visualisiert farbige Rechtecke mit Status-/Handels-Kennzeichnungen und Abschwächungssymbolen.

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 Inverse Fair-Value-Gap-Strategie, die State-Tracking- und Inversionssignale bietet, sind Sie für den Handel mit Gap-Ungleichgewichten gerüstet und bereit für weitere Optimierungen auf Ihrer Handelsreise. Viel Spaß beim Handeln! 

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

Beigefügte Dateien |
FVG_Inverse.mq5 (81.62 KB)
Die Grenzen des maschinellen Lernens überwinden (Teil 9): Korrelationsbasierte Lernen von Merkmalen im selbstüberwachten Finanzwesen Die Grenzen des maschinellen Lernens überwinden (Teil 9): Korrelationsbasierte Lernen von Merkmalen im selbstüberwachten Finanzwesen
Selbstüberwachtes Lernen ist ein leistungsstarkes Paradigma des statistischen Lernens, das nach Überwachungssignalen sucht, die aus den Beobachtungen selbst generiert werden. Mit diesem Ansatz werden schwierige Probleme des unüberwachten Lernens in vertrautere überwachte Probleme umgewandelt. Diese Technologie hat Anwendungen für unser Ziel als Gemeinschaft von algorithmischen Händlern übersehen. Unsere Diskussion zielt daher darauf ab, dem Leser eine leicht verständliche Brücke in das offene Forschungsgebiet des selbstüberwachten Lernens zu schlagen und bietet praktische Anwendungen, die robuste und zuverlässige statistische Modelle der Finanzmärkte ohne Überanpassung an kleine Datensätze liefern.
Klassische Strategien neu interpretieren (Teil 19): Tiefes Eintauchen in das Kreuzen von gleitenden Durchschnitten Klassische Strategien neu interpretieren (Teil 19): Tiefes Eintauchen in das Kreuzen von gleitenden Durchschnitten
In diesem Artikel wird die klassische Strategie des Kreuzens von gleitenden Durchschnitten wieder aufgegriffen und untersucht, warum sie in bewegten, schnelllebigen Märkten oft scheitert. Es werden fünf alternative Filtermethoden vorgestellt, die die Signalqualität verbessern und schwache oder unrentable Handelsgeschäfte entfernen sollen. Die Diskussion zeigt, wie statistische Modelle lernen und Fehler korrigieren können, die der menschlichen Intuition und traditionellen Regeln entgehen. Die Leser erhalten ein besseres Verständnis dafür, wie man eine veraltete Strategie modernisieren kann und welche Fallstricke es gibt, wenn man sich bei der Finanzmodellierung ausschließlich auf Kennzahlen wie den RMSE verlässt.
Reine Implementierung der RSA-Verschlüsselung in MQL5 Reine Implementierung der RSA-Verschlüsselung in MQL5
MQL5 verfügt über keine eingebaute asymmetrische Kryptografie, was den sicheren Datenaustausch über unsichere Kanäle wie HTTP erschwert. Dieser Artikel stellt eine reine MQL5-Implementierung von RSA mit PKCS#1 v1.5 Padding vor, die eine sichere Übertragung von AES-Sitzungsschlüsseln und kleinen Datenblöcken ohne externe Bibliotheken ermöglicht. Dieser Ansatz bietet eine HTTPS-ähnliche Sicherheit über Standard-HTTP und füllt darüber hinaus eine wichtige Lücke in der sicheren Kommunikation für MQL5-Anwendungen.
Der MQL5 Standard Library Explorer (Teil 5): Experte für mehrere Signale Der MQL5 Standard Library Explorer (Teil 5): Experte für mehrere Signale
In dieser Sitzung werden wir einen ausgeklügelten Multi-Signal-Expert Advisor unter Verwendung der MQL5-Standardbibliothek erstellen. Dieser Ansatz ermöglicht es uns, integrierte Signale nahtlos mit unserer eigenen Logik zu kombinieren und so einen leistungsstarken und flexiblen Handelsalgorithmus zu entwickeln. Klicken Sie hier, um mehr zu erfahren.