English 日本語
preview
Beherrschung der Fair Value Gaps: Bildung, Logik und automatisierter Handel von Ausbrüchen und Marktstrukturverschiebungen

Beherrschung der Fair Value Gaps: Bildung, Logik und automatisierter Handel von Ausbrüchen und Marktstrukturverschiebungen

MetaTrader 5Handel |
18 2
Eugene Mmene
Eugene Mmene

Einführung

Fair Value Gaps sind heute ein weit verbreitetes Phänomen, das oft als Repertoire des Smart-Money-Konzepts (SMC) bezeichnet wird oder vielmehr aus Smart-Money-Konzepten hervorgegangen ist. Man findet sie auch häufig in den sozialen Medien, wenn sich Händler auf Handelsplattformen und in Online-Charting-Räumen unterhalten, aber die meisten Leute betrachten sie eher als Referenzpunkt denn als etwas, das ihren Handel verbessert oder ihnen sogar Instrumente bietet, um ihre Neigung zu definieren und auf der Grundlage der Zeit und des Ortes zu handeln, an dem diese Fair Value Gaps auftreten.

Mein Ziel ist es nicht nur, Fair Value Gaps anzusprechen, sondern auch, wie sie zum Vorteil des Händlers genutzt werden können, um Vorurteile und Erzählungen zu formen und sogar Handelsgeschäfte auszuführen. Ich werde auch analysieren, wo diese Fair Value Gaps am wahrscheinlichsten auftreten, wann und wie sie auftreten und wie man sie als Handelseinstieg oder Gewinnmitnahmepunkte und schließlich als Wiedereinstiegspunkte für die Handelsausführung nach einem Rücksetzer, der nach einer starken Expansion auftritt, validieren kann.

Nach dem Verständnis der Fair Value Gaps und der Handelslogik, die zur Validierung, Bestimmung und Identifizierung der zugrundeliegenden Tendenz und des Narrativs verwendet wird, werde ich die Bildung von Ausbruchsfaktoren und die Veränderungen in der Marktstruktur erklären, die typischerweise Handelsgeschäfte hinsichtlich der zugrundeliegenden Tendenz und des Narrativs validieren. Diese Ausbruchsfaktoren und Veränderungen in der Marktstruktur sind überzeugend und bestätigen oft sogar langfristige Handelsgeschäfte. In diesem Artikel wird die Wirksamkeit einer Kombination aus Fair Value Gaps und Breakout-Faktoren klar beschrieben und erläutert, insbesondere in Anbetracht der Tatsache, dass sie in allen Zeitrahmen, sowohl kurz- als auch langfristig, auftreten.


Einführung in die Fair Value Gap

Eine Fair Value Gap, entsteht, wenn es zu raschen Kursbewegungen in beide Richtungen kommt, die auf einen starken Kauf- oder Verkaufsdruck, vor allem von institutionellen Akteuren, hinweisen. Einfach ausgedrückt, führt dieser Handelsdruck oft zu einem Szenario, in dem die Händler nicht die gleichen Möglichkeiten haben, Handelsgeschäfte zu platzieren oder auszuführen; Daher wird dieser Bereich in einer steigenden Fair Value Gap (FVG), in der der Preis einen Punkt mit Kraft und Geschwindigkeit in Richtung höherer Preise verlassen hat und die Verkäufer nicht genug Zeit oder Gelegenheit hatten, sie auszuführen, und in einer fallenden Fair Value Gap (FVG), in der der Preis einen Punkt mit Kraft und Geschwindigkeit in Richtung niedrigerer Preise verlassen hat und die Käufer nicht genug Zeit oder Gelegenheit hatten, sie auszuführen, als unausgeglichene Preisspanne bezeichnet, da es keine Preisbewegung in die entgegengesetzte Richtung gab, aus der die Expansion kam.

Dies lässt sich am besten mit der Logik eines Malers erklären: Wenn ein Maler eine Wand bemalt, muss er sie mit gleichen oder sogar ähnlichen Pinselstrichen in entgegengesetzte Richtungen streichen, damit die Farbe gleichmäßig aufgetragen wird und ausgewogen, d. h. von hoher Qualität ist. Diese Logik gilt auch für den Preis. Wenn sich eine Aufwärtskerze ausdehnt und einen Preispunkt durchbricht, z. B. von 1,3010 auf 1,3030, muss eine Abwärtskerze ebenfalls diesen Preispunkt durchbrechen, um den Preis auszugleichen, und wenn sie das nicht tut, wird sie zu einer Fair Value Gap (FVG). Oft, wenn auch nicht immer, wird der Preis irgendwann wieder auf diesen Punkt zurückkehren. Dies ist der Zeitpunkt, an dem wir diese Fair Value Gaps nutzen sollten, da sie große und lohnende Chancen bieten.

Die folgende Abbildung zeigt Beispiele für Fair Value Gaps und wie sie genutzt wurden.

fallende Fair Value Gap

Wie, wann und warum sie gebildet werden

Nachdem wir nun definiert haben, was Fair Value Gaps sind, ist es viel einfacher zu verstehen, wie sie entstehen. Dies geschieht in sehr genau definierten Situationen. Das erste Szenario tritt ein, wenn eine Pressemitteilung veröffentlicht wird. Dabei kann es sich um eine der typischen Nachrichten (CPI, FOMC oder NFP) oder auch um andere Arten von Pressemitteilungen handeln – alles hängt von den Auswirkungen ab, die sie haben.

Meistens erfolgt diese Pressemitteilung erst, nachdem die großen Marktteilnehmer bereits Aufträge erteilt hatten und daher mit dieser Volatilität, Geschwindigkeit und Stärke gerechnet hatten; daher bewegt sich der Markt schnell zu ihren Gunsten und hinterlässt Fair Value Gaps, von denen wir meiner Meinung nach profitieren könnten. Wie in der Branche üblich, ist es nicht ideal, vor einer großen Pressemitteilung zu handeln, aber es ist oft ratsam, die Pressemitteilung und die damit einhergehende Volatilität abzuwarten, bevor man Handelsausführungen platziert, sodass wir idealerweise diese frühen Handelsausführungen vermeiden sollten, da die meisten von ihnen nicht einmal Handels-Setups bieten.

Ein weiteres Szenario, in dem sie entstehen, ist, wenn große Akteure wie Banken und Hedgefonds Aufträge erteilen; sie beeinflussen somit die Marktbewegungen, da ihre Aufträge Milliarden von Dollar wert sind.

Diese großen Orders lassen sich leicht durch riesige Kerzen und scharfe, plötzliche Bewegungen in eine Richtung erkennen, auch ohne den Einfluss von Nachrichtenmeldungen. In diesem Artikel versuche ich zu veranschaulichen, wie wir nach diesen großen Bewegungen unsere Aufmerksamkeit schulen müssen, um die Fair Value Gaps zu erkennen, die bei diesen großen Bewegungen auftreten.

Die folgende Abbildung zeigt, wie der Kurs schnell und kraftvoll aus einem Bereich ausbrach und die nächste Kerze nicht zurückging, um die Aufwärtskerze zu überdecken, sodass ein ungedeckter Bereich zurückblieb, der als Fair Value Gap bezeichnet wird.

steigende Fair Value Gap


Wie analysiert man Fair Value Gaps?

In diesem Kapitel werden wir uns mit der Analyse von Fair Value Gaps befassen, nachdem wir in den vorangegangenen Kapiteln erklärt haben, was sie sind, wann sie auftreten und wie sie entstehen. Jetzt werden wir uns darauf konzentrieren, wie man dieselben Gaps analysiert, um festzustellen, auf welche Handelsgeschäfte man warten und sie ausführen sollte.

Die erste Grundregel lautet, dass wir in den meisten Fällen den Handel mit Fair Value Gaps vermeiden sollten, aber wir müssen akzeptieren, dass wir von ihnen weg handeln, da sie als Antrieb wirken, wenn sie effektiv in Richtung des Trends, der Tendenz und der Erzählung eingesetzt werden. Warum also gegen einen Antriebsblock handeln, der in wenigen Augenblicken mit voller Wucht gegen uns vorgehen wird? Und das ist sowohl für kürzere als auch für längere Zeiträume sehr zutreffend; sie verhalten sich alle gleich. Der einzige Unterschied ist die Zeit, die es braucht, um sich in einem Handel zu materialisieren, aber sie handeln immer, die meiste Zeit, auf diese Weise 90% der Zeit.

Der erste Schritt besteht idealerweise darin, von oben nach unten, vom höchsten zum niedrigsten Zeitrahmen zu analysieren und dabei alle Fair Value Gaps zu beobachten und hervorzuheben. Der wichtigste Schritt ist dann die Analyse der Trendrichtung, der Verzerrung und des Narrativs, das im Spiel ist. So deuten beispielsweise der Trend, die Tendenz und der Bericht für Gold auf MN, W1 und D1 auf Kaufgeschäfte hin. Dies kann auf drei Arten bestätigt werden: Der Kurs notiert oberhalb der gleitenden Durchschnitte (einfache und exponentielle gleitende Durchschnitte); eine weitere Möglichkeit ist, dass die Marktstruktur eindeutig ein Kaufmomentum zeigt; außerdem sind die grünen Aufwärtskerzen viel zahlreicher als die roten Abwärtskerzen.

Sobald der Trend und die Erzählung im Spiel definiert sind, müssen wir einen weiteren wichtigen Aspekt berücksichtigen. Der Preis ist ständig in Bewegung und sucht nach Liquidität, um sich zu klären, und das Narrativ, die Richtung und die Tendenz werden auf höheren Zeitrahmen bestimmt. Liquidität tritt in erster Linie an früheren Tages-, Wochen- und Monatstiefs und -hochs auf, und hier sind deutliche Reaktionen zu erwarten. Wenn wir also eine Vorstellung von der Liquidität und dem Trend haben, können wir leicht gut handeln.

Ein Beispiel für dieses Szenario ist, dass wir jetzt festgestellt haben, dass sich der Goldpreis dem Höchststand der Vorwoche nähert, was durch die Marktstruktur/beweglichen Durchschnitte bestätigt wird. Während der Woche oder in den gleichzeitigen Sitzungen bewegt sich der Kurs weiter auf das Hoch der Vorwoche zu, und es bilden sich weiterhin Fair Value Gaps. Diese Fair Value Gaps werden als aufwärts gekennzeichnet und besser noch, wenn sie auf längeren Zeitskalen (z. B. H4, D1, H1) auftreten, sollten sie als aufwärts markiert werden. Händler sollten sich immer auf den Kauf einstellen, wenn der Kurs zu diesen Lücken zurückkehrt; auf diese Weise können sie nach Handelsausführungen suchen.

Es ist wichtig zu wissen, dass der Preis in den meisten Fällen, wenn auch nicht immer, die Fair Value Gaps immer ausgleicht. Befindet sich der Kurs jedoch, wie oben erwähnt, in der Nähe seines Ziels (z. B. Wochen-, Monats- oder Tageshöchststand), so ist die Wahrscheinlichkeit, dass seine Fair Value Gap nicht für neue Handelsgeschäfte genutzt wird, deutlich geringer, und es könnte zu einem Fehlkauf kommen, sobald dieses Ziel erreicht und die Liquidität bereinigt ist. Daher sollten wir diese Art von Handelsgeschäften vermeiden.


Wie sie sich auf Transaktionen und ihre Adresse auswirken

Grundsätzlich geht es darum, dass bei einem bestimmten Trend und einer bestimmten Richtung (z. B. nach unten) durch eine Top-down-Analyse Fair Value Gaps gesucht und markiert werden. Es ist zu beachten, dass der Kurs zu diesen Fair Value Gaps (Preisen) zurückkehrt, um Handels- und Einstiegsmöglichkeiten zu bieten, solange die Wochen-, Tages- oder Monatsziele eingehalten werden. In der Regel muss der Preis das Gleichgewicht wiederherstellen und die Fair Value Gap schließen. Daher wirkt es als Triebkraft für die Stärke eines Paares/Anlagewertes, wenn der Preis als Zweitkäufer zurückkehrt, da mehr Händler zu diesem Zeitpunkt neue Positionen eröffnen, was die Geschwindigkeit und Stärke des Marktes erklärt. Dies führt zu starken, positiven Kursausschlägen. Außerdem verstärkt sie die Richtung, die Tendenz und die Darstellung des Trends.


Wie man Fair Value Gaps erfolgreich für den Einstieg in den Handel nutzt

Dies ist die wichtigste und wertvollste Information in diesem Artikel, denn nachdem wir alle unsere Kriterien gründlich validiert und analysiert haben, wissen wir, wo und wie wir Fair Value Gaps entkräften oder validieren können, wie sie entstehen und wo sie am wahrscheinlichsten auftreten. Dieser Teil wird vereinfacht, da die harte Arbeit bereits erledigt ist.

Jetzt sind nur noch Geduld, Disziplin und Konzentration gefragt. Wir werden in erster Linie von D1 aus analysieren, da dies der Referenzchart ist, der uns erlaubt, nach Fair Value Gaps zu suchen. Wenn wir eine Fair Value Gap entdecken und der Kurs dorthin zurückkehrt, wechseln wir schnell zu H4 und suchen nach einer Bullish-Engulfing Kerze (wenn der Trend, eine Tendenz und das Narrativ Käufer sind) oder einer Bearish-Engulfing Kerze (wenn die Tendenz, der Trend und das Narrativ Verkäufer sind). Dies kann zu jedem beliebigen Zeitpunkt innerhalb der Fair Value Gaps geschehen, sollte aber idealerweise zu Beginn der Handelssitzungen, z. B. in London oder New York, erfolgen. Nachdem wir die Engulfing-Kerze entdeckt haben, gehen wir zurück auf H1; hier suchen wir nach einem Bruch in der Marktstruktur.

Ein Ausbruch in der Marktstruktur bezieht sich auf eine Änderung des Lieferstatus des Preises; wenn Sie verkaufen und dann zum Käufer werden, bedeutet dies eine Änderung des Lieferstatus und somit eine Änderung der Marktstruktur. Auf H1 ist dies also das, wonach Sie suchen. Stop-Losses sollten an den entgegengesetzten Enden der Ausbrüche platziert werden, und Einstiege können bei Ausbrüchen in kleinen, vernünftig bewerteten Lücken erfolgen, die beim H1-Ausbruch oder beim 60 %igen Rücksetzer-Niveau auftreten können, falls dieses erreicht wird.

Ausbrüche beziehen sich auf den letzten Höchst- oder Tiefststand, bevor eine Änderung der Marktstruktur eintritt. Wenn der Kurs ein Verkaufskurs ist und der letzte Höchststand schnell und stark durchbrochen wird, handelt es sich um einen Aufwärtsausbruch, und wenn der Kurs ein Kaufkurs ist und eine Marktstruktur eintritt und der letzte Tiefststand schnell und stark durchbrochen wird, handelt es sich um einen Abwärtsausbruch.

Das gleiche Phänomen tritt auch in anderen Zeiträumen auf. Wenn wir auf H4 eine Fair Value Gap feststellen, suchen wir auf H1 nach einer Engulfing-Kerze und dann auf M15 nach einer Veränderung der Marktstruktur. Wir können diese Strategie auf Händler mit kürzeren Zeitrahmen anwenden. Das gleiche Phänomen wiederholt sich: eine Fair Value Gap auf H1 und eine Engulfing-Kerze auf M15; daher kommt es auf dem Zeitrahmen M5 zu einer Veränderung der Marktstruktur. Außerdem traten beim Scalping Fair Value Gaps auf M15 auf. Wir können auf Engulfing-Kerzen auf M5 und Veränderungen der Marktstruktur auf M1 achten.


Wie und wann man den Handel mit Fair Value Gaps vermeidet

Die meisten Fair Value Gaps bieten oft Handelsmöglichkeiten, aber wie oben erläutert, sind einige Fair Value Gaps nicht ideal für den Handel.

1. Fair Value Gaps, die entgegen dem Trend, der Tendenz und der narrativen Richtung auftreten, werden überhaupt nicht beachtet. Wenn beispielsweise der Trend oder die Tendenz auf Kaufen steht und der Kurs nach unten dreht und eine Fair Value Gap hinterlässt, möglicherweise bei Rücksetzern, erwarten Sie nicht, dass der Kurs die Fair Value Gap darüber beachtet, wenn er seinen Aufwärtstrend wieder aufnimmt; sie wird schnell ignoriert und übergangen.

2. Das zweite Szenario, in dem diese Fair Value Gaps nicht erfolgreich sind, ist, wenn die wöchentlichen, täglichen oder monatlichen Gesamtziele erreicht werden. Fair Value Gaps können scheitern, weil der Kurs keinen Grund hat, den Trend in diese Richtung fortzusetzen, da die Liquidität bereits abgeflossen ist; es steht also keine Kraft oder Geschwindigkeit mehr zur Verfügung, da die Gewinne der großen Unternehmen bereits verbucht sind. Daher können Fair Value Gaps scheitern, weil der Kurs möglicherweise zurückgehen oder sich sogar umkehren möchte.

3. Das dritte und letzte Szenario, in dem dieses Phänomen wieder versagt, ist, wenn z. B. Veränderungen in der Engulfing-Kerze und der Marktstruktur nicht auf niedrigeren Zeitrahmen auftreten, wie oben erläutert. Außerdem müssen sie während sehr aktiver Handelssitzungen und in dieser Reihenfolge stattfinden.

Zuerst bilden sich Fair Value Gaps, dann Engulfing-Kerzen, und dann ändert sich die Marktstruktur. Daher können wir nur dann, wenn sie in dieser Reihenfolge auftreten, Handelsgeschäfte mit erstklassigen, hochwertigen Setups ausführen. Dies zeigt deutlich, dass ein Händler diese Methode oder eine Fair Value Gap nicht verwenden sollte, wenn er nicht alle vorgenannten Faktoren erfüllt, da die Wahrscheinlichkeit eines Fehlschlags hoch ist.


Fair Value Gaps über längere Zeiträume

Gaps auf höheren Zeitrahmen beziehen sich auf H4-, D1- und W1, und diese können leicht für große oder langfristige Kursschwankungen, sogar 200 bis 500 Pips, verwendet werden, da sie stabiler und langsamer sind, ohne kurzfristige Volatilität oder einseitige Bewegungen, wodurch sie sich besser für langfristige Händler eignen, die ihre Handelsgeschäfte geduldig und ohne Nervosität oder Angst halten können.


Fair Value Gaps in niedrigeren Zeitrahmen

Gaps in niedrigeren Zeitrahmen beziehen sich auf die Fair Value Gaps im H1-, M15- und M5-Zeitrahmen. Diese Gaps werden in erster Linie und idealerweise von kurzfristigen und nicht von langfristigen Händlern genutzt, von Scalpers bis hin zu kurzfristigen Händlern, die Handelsgeschäfte für ein paar Stunden halten und sie dann aufgeben. Sie können in trendigen Marktumgebungen auftreten, insbesondere auf der M15, und in Sitzungen, wenn sie gehebelt werden, sind sie sehr zuverlässig. Sie sind für eine schnelle Ausführung konzipiert und bieten leicht 100-150 Pips. Je nach Stärke und Geschwindigkeit der Kursbewegung sind sie durch kurzfristige Kursschwankungen und hohe Volatilität gekennzeichnet. Sie erfordern Händler, die schnelle Handelsentscheidungen treffen und sich in diesem Umfeld auszeichnen.


Automatisierter Handel mit Fair Value Gaps in MQL5

Um diese Strategie zu automatisieren, habe ich Folgendes erstellt

  • Ein FVG-Indikator zur Erkennung und Darstellung der Gaps bzw. Lücken.
  • Ein FVG + MSS EA für den Handel auf der Grundlage von Erkennungen, unter Einbeziehung von MSS für Einträge.

Quellcode des FVG-Indikators

Dieser Indikator erkennt FVGs durch die Suche nach großen, sich nicht überschneidenden Kerzen (Ungleichgewicht > Tiefststände). Es zeichnet Rechtecke, um visuelle Lücken zu erkennen.

#property copyright "Eugene Mmene"
#property link      "EMcapital"
#property version   "1.0"

input int minPts = 100; // Minimum points for FVG gap to be valid
input int FVG_Rec_Ext_Bars = 10; // Length of FVG rectangle in bars
input bool DrawFVG = true; // Draw FVG rectangles on chart

#define FVG_Prefix "FVG_REC_"
#define CLR_UP clrLime
#define CLR_DOWN clrRed

struct TimeframeData {
   ENUM_TIMEFRAMES tf;
   datetime lastBar;
};

TimeframeData tfs[];
string eaSymbol;

void CreateRec(string objName, datetime time1, double price1, datetime time2, double price2, color clr) {
   Print("CreateRec called: objName=", objName, ", time1=", TimeToString(time1), ", price1=", DoubleToString(price1, _Digits));
   if(DrawFVG && ObjectFind(0, objName) < 0) {
      if(ObjectCreate(0, objName, OBJ_RECTANGLE, 0, time1, price1, time2, price2)) {
         ObjectSetInteger(0, objName, OBJPROP_TIME, 0, time1);
         ObjectSetDouble(0, objName, OBJPROP_PRICE, 0, price1);
         ObjectSetInteger(0, objName, OBJPROP_TIME, 1, time2);
         ObjectSetDouble(0, objName, OBJPROP_PRICE, 1, price2);
         ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, objName, OBJPROP_FILL, true);
         ObjectSetInteger(0, objName, OBJPROP_BACK, false);
         Print("CreateRec: Rectangle created successfully: ", objName);
      } else {
         Print("CreateRec: Failed to create rectangle: ", objName, ", Error=", GetLastError());
      }
   } else {
      Print("CreateRec: Skipped - DrawFVG=", DrawFVG, ", Object exists=", ObjectFind(0, objName) >= 0);
   }
}

void DetectFVGs() {
   Print("DetectFVGs started");
   int maxObjects = 100; // Limit total objects to prevent overload
   int currentObjects = ObjectsTotal(0, 0, OBJ_RECTANGLE);
   if(currentObjects >= maxObjects) {
      Print("DetectFVGs: Object limit reached (", currentObjects, "/", maxObjects, "), skipping FVG creation");
      return;
   }

   for(int i = 0; i < ArraySize(tfs); i++) {
      if(!NewBar(tfs[i].tf)) continue;
      double low0 = iLow(eaSymbol, tfs[i].tf, 0);
      double high2 = iHigh(eaSymbol, tfs[i].tf, 2);
      double gap_L0_H2 = NormalizeDouble((low0 - high2) / _Point, 0);
      
      double high0 = iHigh(eaSymbol, tfs[i].tf, 0);
      double low2 = iLow(eaSymbol, tfs[i].tf, 2);
      double gap_H0_L2 = NormalizeDouble((low2 - high0) / _Point, 0);
      
      if(gap_L0_H2 > minPts) {
         string fvgName = FVG_Prefix + EnumToString(tfs[i].tf) + "_" + TimeToString(iTime(eaSymbol, tfs[i].tf, 0), TIME_DATE|TIME_MINUTES);
         Print(StringFormat("%s Bullish FVG detected: Low=%s, High[2]=%s, Gap=%s points", 
               EnumToString(tfs[i].tf), DoubleToString(low0, _Digits), DoubleToString(high2, _Digits), DoubleToString(gap_L0_H2, 0)));
         CreateRec(fvgName, iTime(eaSymbol, tfs[i].tf, 0), high2, iTime(eaSymbol, tfs[i].tf, 0) + PeriodSeconds(tfs[i].tf) * FVG_Rec_Ext_Bars, low0, CLR_UP);
      }
      if(gap_H0_L2 > minPts) {
         string fvgName = FVG_Prefix + EnumToString(tfs[i].tf) + "_" + TimeToString(iTime(eaSymbol, tfs[i].tf, 0), TIME_DATE|TIME_MINUTES);
         Print(StringFormat("%s Bearish FVG detected: High=%s, Low[2]=%s, Gap=%s points", 
               EnumToString(tfs[i].tf), DoubleToString(high0, _Digits), DoubleToString(low2, _Digits), DoubleToString(gap_H0_L2, 0)));
         CreateRec(fvgName, iTime(eaSymbol, tfs[i].tf, 0), low2, iTime(eaSymbol, tfs[i].tf, 0) + PeriodSeconds(tfs[i].tf) * FVG_Rec_Ext_Bars, high0, CLR_DOWN);
      }
   }
   if(DrawFVG) ChartRedraw(0); // Single redraw per tick
   Print("DetectFVGs completed");
}

bool NewBar(ENUM_TIMEFRAMES tf) {
   int idx = TimeframeIndex(tf);
   if(idx < 0) return false;
   datetime cur = iTime(eaSymbol, tf, 0);
   if(cur != tfs[idx].lastBar) {
      tfs[idx].lastBar = cur;
      return true;
   }
   return false;
}

int TimeframeIndex(ENUM_TIMEFRAMES tf) {
   for(int i = 0; i < ArraySize(tfs); i++) {
      if(tfs[i].tf == tf) return i;
   }
   return -1;
}

int OnInit() {
   eaSymbol = _Symbol;
   if(!SymbolSelect(eaSymbol, true)) {
      Print("Error: Failed to select ", eaSymbol, " in Market Watch");
      return(INIT_FAILED);
   }

   ArrayResize(tfs, 3);
   tfs[0].tf = PERIOD_M5;
   tfs[1].tf = PERIOD_M15;
   tfs[2].tf = PERIOD_H1;
   for(int i = 0; i < 3; i++) {
      tfs[i].lastBar = 0;
   }

   Print("FVG Detector initialized for ", eaSymbol, ". Timeframes: M5, M15, H1");
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) {
   if(DrawFVG) ObjectsDeleteAll(0, FVG_Prefix);
   Print("FVG Detector stopped: ", reason);
}

void OnTick() {
   DetectFVGs();
}

Einbau: In MetaEditor kompilieren und an das Chart anhängen. Er zeichnet grüne Rechtecke für Aufwärts-FVGs und rote Rechtecke für Abwärts-FVGs.

Beispiel für die Verwendung: Mit GOLD M15, Gaps bzw. Lücken für die visuelle Analyse erkennen.


Quellcode des FVG + MSS Expert Advisors

Dieser EA erkennt FVGs, wartet auf einen Rücksetzer in Richtung der Lücke, prüft auf einen Ausbruch des letzten Hochs/Tiefs (LH) und geht in Handelsgeschäfte ein. Es beinhaltet ein Risikomanagement (2% Risiko pro Handel).  

#property copyright "Eugene Mmene"
#property link      "EMcapital"
#property version   "2.27.2"

#include <Trade\Trade.mqh>

input double RiskPct = 2.0; // Base risk per trade %
input double MaxLossUSD = 110.0; // Maximum loss per trade in USD
input double RecTgt = 7000.0; // Equity recovery target
input int ATR_Prd = 14; // ATR period
input int Brk_Prd = 10; // Breakout period
input int EMA_Prd = 20; // EMA period
input string GS_Url = ""; // Google Sheets webhook URL
input bool NewsFilt = true; // News filter
input int NewsPause = 15; // Pause minutes
input double MinBrkStr = 0.1; // Min breakout strength (x ATR)
input int Vol_Prd = 1; // Volume period
input bool Bypass = true; // Bypass volume, breakout, HTF trend filters for testing
input bool useHTF = false; // Use D1 or H4 EMA trend filter
input string NewsAPI_Url = "https://www.alphavantage.co/query?function=NEWS_SENTIMENT&apikey="; // Alpha Vantage API URL
input string NewsAPI_Key = "pub_3f54bba977384ac19b6839a744444aba"; // Alpha Vantage API key
input double DailyDDLimit = 2.5; // Daily drawdown limit (%)
input double OverallDDLimit = 5.5; // Overall drawdown limit (%)
input double TargetBalanceOrEquity = 6600.0; // Target balance or equity to pass challenge ($)
input bool ResetProfitTarget = false; // Reset target to resume trading
input int minPts = 100; // Minimum points for FVG gap to be valid
input int FVG_Rec_Ext_Bars = 10; // Length of FVG rectangle in bars
input bool DrawFVG = true; // Draw FVG rectangles on chart

double CurRisk = RiskPct;
double OrigRisk = RiskPct;
double LastEqHigh = 0;
double StartingBalance = 0;
double DailyBalance = 0;
datetime LastDay = 0;
bool ProfitTargetReached = false;
bool DailyDDPaused = false;
CTrade trade;
int h_ema_d1 = INVALID_HANDLE;
int h_ema_h4 = INVALID_HANDLE;
int winStreak = 0;
int lossStreak = 0;
string eaSymbol = _Symbol;

struct TimeframeData {
   ENUM_TIMEFRAMES tf;
   int h_atr;
   int h_vol;
   int h_vol_ma;
   datetime lastSig;
   datetime lastBar;
};

TimeframeData tfs[];
struct NewsEvt { 
   datetime time; 
   string evt; 
   int impact; 
};
NewsEvt newsCal[];
int newsCnt = 0;

struct TradeLog {
   ulong ticket;
   bool isWin;
   double profit;
   double brkStr;
   double vol;
   double risk;
   ENUM_TIMEFRAMES tf;
};
TradeLog tradeHistory[];
int tradeCnt = 0;
double dynBrkStr = MinBrkStr;

#define FVG_Prefix "FVG_REC_"
#define CLR_UP clrLime
#define CLR_DOWN clrRed

void CreateRec(string objName, datetime time1, double price1, datetime time2, double price2, color clr) {
   Print("CreateRec called: objName=", objName, ", time1=", TimeToString(time1), ", price1=", DoubleToString(price1, _Digits));
   if(DrawFVG && ObjectFind(0, objName) < 0) {
      if(ObjectCreate(0, objName, OBJ_RECTANGLE, 0, time1, price1, time2, price2)) {
         ObjectSetInteger(0, objName, OBJPROP_TIME, 0, time1);
         ObjectSetDouble(0, objName, OBJPROP_PRICE, 0, price1);
         ObjectSetInteger(0, objName, OBJPROP_TIME, 1, time2);
         ObjectSetDouble(0, objName, OBJPROP_PRICE, 1, price2);
         ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, objName, OBJPROP_FILL, true);
         ObjectSetInteger(0, objName, OBJPROP_BACK, false);
         Print("CreateRec: Rectangle created successfully: ", objName);
      } else {
         Print("CreateRec: Failed to create rectangle: ", objName, ", Error=", GetLastError());
      }
   } else {
      Print("CreateRec: Skipped - DrawFVG=", DrawFVG, ", Object exists=", ObjectFind(0, objName) >= 0);
   }
}

void DetectFVGs() {
   Print("DetectFVGs started");
   int maxObjects = 100; // Limit total objects to prevent overload
   int currentObjects = ObjectsTotal(0, 0, OBJ_RECTANGLE);
   if(currentObjects >= maxObjects) {
      Print("DetectFVGs: Object limit reached (", currentObjects, "/", maxObjects, "), skipping FVG creation");
      return;
   }

   for(int i = 0; i < ArraySize(tfs); i++) {
      if(!NewBar(tfs[i].tf)) continue;
      double low0 = iLow(eaSymbol, tfs[i].tf, 0);
      double high2 = iHigh(eaSymbol, tfs[i].tf, 2);
      double gap_L0_H2 = NormalizeDouble((low0 - high2) / _Point, 0);
      
      double high0 = iHigh(eaSymbol, tfs[i].tf, 0);
      double low2 = iLow(eaSymbol, tfs[i].tf, 2);
      double gap_H0_L2 = NormalizeDouble((low2 - high0) / _Point, 0);
      
      if(gap_L0_H2 > minPts) {
         string fvgName = FVG_Prefix + EnumToString(tfs[i].tf) + "_" + TimeToString(iTime(eaSymbol, tfs[i].tf, 0), TIME_DATE|TIME_MINUTES);
         Print(StringFormat("%s Bullish FVG detected: Low=%s, High[2]=%s, Gap=%s points", 
               EnumToString(tfs[i].tf), DoubleToString(low0, _Digits), DoubleToString(high2, _Digits), DoubleToString(gap_L0_H2, 0)));
         CreateRec(fvgName, iTime(eaSymbol, tfs[i].tf, 0), high2, iTime(eaSymbol, tfs[i].tf, 0) + PeriodSeconds(tfs[i].tf) * FVG_Rec_Ext_Bars, low0, CLR_UP);
      }
      if(gap_H0_L2 > minPts) {
         string fvgName = FVG_Prefix + EnumToString(tfs[i].tf) + "_" + TimeToString(iTime(eaSymbol, tfs[i].tf, 0), TIME_DATE|TIME_MINUTES);
         Print(StringFormat("%s Bearish FVG detected: High=%s, Low[2]=%s, Gap=%s points", 
               EnumToString(tfs[i].tf), DoubleToString(high0, _Digits), DoubleToString(low2, _Digits), DoubleToString(gap_H0_L2, 0)));
         CreateRec(fvgName, iTime(eaSymbol, tfs[i].tf, 0), low2, iTime(eaSymbol, tfs[i].tf, 0) + PeriodSeconds(tfs[i].tf) * FVG_Rec_Ext_Bars, high0, CLR_DOWN);
      }
   }
   if(DrawFVG) ChartRedraw(0); // Single redraw per tick
   Print("DetectFVGs completed");
}

int OnInit() {
   if(AccountInfoDouble(ACCOUNT_BALANCE) < 10.0) {
      Print("Low balance: ", DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2));
      return(INIT_FAILED);
   }
   string sym = Symbol();
   bool selected;
   if(!SymbolExist(sym, selected)) {
      Print("Error: Symbol ", sym, " not found in Market Watch. Available: ", _Symbol);
      eaSymbol = _Symbol;
   } else {
      eaSymbol = sym;
      Print("Symbol validated: ", eaSymbol, ", Selected in Market Watch: ", selected);
   }
   if(!SymbolSelect(eaSymbol, true)) {
      Print("Error: Failed to select ", eaSymbol, " in Market Watch");
      return(INIT_FAILED);
   }

   Print("Please ensure ", NewsAPI_Url, " is added to allowed WebRequest URLs in MT5 settings");

   StartingBalance = AccountInfoDouble(ACCOUNT_BALANCE);
   LastEqHigh = AccountInfoDouble(ACCOUNT_EQUITY);
   DailyBalance = StartingBalance;
   LastDay = TimeCurrent() / 86400 * 86400;
   ProfitTargetReached = ResetProfitTarget ? false : ProfitTargetReached;
   DailyDDPaused = false;
   ArrayResize(newsCal, 100);
   ArrayResize(tradeHistory, 100);
   ArrayResize(tfs, 3);
   tfs[0].tf = PERIOD_M5;
   tfs[1].tf = PERIOD_M15;
   tfs[2].tf = PERIOD_H1;
   for(int i = 0; i < 3; i++) {
      tfs[i].h_atr = iATR(eaSymbol, tfs[i].tf, ATR_Prd);
      tfs[i].h_vol = iVolumes(eaSymbol, tfs[i].tf, VOLUME_TICK);
      tfs[i].h_vol_ma = iMA(eaSymbol, tfs[i].tf, Vol_Prd, 0, MODE_SMA, PRICE_CLOSE);
      tfs[i].lastSig = 0;
      tfs[i].lastBar = 0;
      if(tfs[i].h_atr == INVALID_HANDLE || tfs[i].h_vol == INVALID_HANDLE || tfs[i].h_vol_ma == INVALID_HANDLE) {
         Print("Indicator init failed for ", EnumToString(tfs[i].tf));
         return(INIT_FAILED);
      }
   }
   h_ema_d1 = iMA(eaSymbol, PERIOD_D1, EMA_Prd, 0, MODE_EMA, PRICE_CLOSE);
   h_ema_h4 = iMA(eaSymbol, PERIOD_H4, EMA_Prd, 0, MODE_EMA, PRICE_CLOSE);
   if(h_ema_d1 == INVALID_HANDLE || h_ema_h4 == INVALID_HANDLE) {
      Print("EMA init failed");
      return(INIT_FAILED);
   }
   if(NewsFilt) FetchNewsCalendar();
   Print("EA initialized. Timeframes: M5, M15, H1, News events: ", newsCnt, ", Bypass: ", Bypass, ", UseHTF: ", useHTF, 
         ", Time时间: EAT (UTC+3), Server: ", TerminalInfoString(TERMINAL_NAME), 
         ", Starting Balance: ", DoubleToString(StartingBalance, 2), ", Target Balance/Equity: ", DoubleToString(TargetBalanceOrEquity, 2));
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) {
   if(h_ema_d1 != INVALID_HANDLE) IndicatorRelease(h_ema_d1);
   if(h_ema_h4 != INVALID_HANDLE) IndicatorRelease(h_ema_h4);
   for(int i = 0; i < ArraySize(tfs); i++) {
      if(tfs[i].h_atr != INVALID_HANDLE) IndicatorRelease(tfs[i].h_atr);
      if(tfs[i].h_vol != INVALID_HANDLE) IndicatorRelease(tfs[i].h_vol);
      if(tfs[i].h_vol_ma != INVALID_HANDLE) IndicatorRelease(tfs[i].h_vol_ma);
   }
   if(DrawFVG) ObjectsDeleteAll(0, FVG_Prefix);
   Print("EA stopped: ", reason);
}

void CloseAllPositions() {
   for(int i = PositionsTotal() - 1; i >= 0; i--) {
      ulong ticket = PositionGetTicket(i);
      if(!PositionSelectByTicket(ticket) || PositionGetString(POSITION_SYMBOL) != eaSymbol) continue;
      long magic = PositionGetInteger(POSITION_MAGIC);
      if(magic == MagicNumber(PERIOD_M5) || magic == MagicNumber(PERIOD_M15) || magic == MagicNumber(PERIOD_H1)) {
         trade.PositionClose(ticket);
         Print("Closed position: Ticket=", ticket, ", Symbol=", eaSymbol, ", Magic=", magic);
      }
   }
}

void OnTick() {
   datetime currentDay = TimeCurrent() / 86400 * 86400;
   if(currentDay > LastDay) {
      DailyBalance = AccountInfoDouble(ACCOUNT_BALANCE);
      LastDay = currentDay;
      DailyDDPaused = false;
      Print("Daily balance reset: ", DoubleToString(DailyBalance, 2), " at ", TimeToString(currentDay, TIME_DATE));
   }

   double equity = AccountInfoDouble(ACCOUNT_EQUITY);
   double balance = AccountInfoDouble(ACCOUNT_BALANCE);
   if(balance >= TargetBalanceOrEquity || equity >= TargetBalanceOrEquity) {
      CloseAllPositions();
      ProfitTargetReached = true;
      Print("Trading paused: Target balance or equity reached. Balance=", DoubleToString(balance, 2), ", Equity=", DoubleToString(equity, 2), 
            ", Target=", DoubleToString(TargetBalanceOrEquity, 2), ". All positions closed. Set ResetProfitTarget=true or restart EA to resume trading.");
      return;
   }

   double dailyDD = (DailyBalance - equity) / DailyBalance * 100;
   double overallDD = (StartingBalance - equity) / StartingBalance * 100;

   if(dailyDD >= DailyDDLimit) {
      CloseAllPositions();
      DailyDDPaused = true;
      Print("Trading paused until next trading day: Daily DD=", StringFormat("%.2f", dailyDD), "% reached (Limit: ", DoubleToString(DailyDDLimit, 2), 
            "%), Equity=", DoubleToString(equity, 2), ", Daily Balance=", DoubleToString(DailyBalance, 2), ". All positions closed.");
      return;
   }

   if(overallDD >= OverallDDLimit) {
      CloseAllPositions();
      ProfitTargetReached = true;
      Print("Trading paused: Overall DD=", StringFormat("%.2f", overallDD), "% reached (Limit: ", DoubleToString(OverallDDLimit, 2), 
            "%), Equity=", DoubleToString(equity, 2), ", Starting Balance=", DoubleToString(StartingBalance, 2), ". All positions closed. Set ResetProfitTarget=true or restart EA to resume trading.");
      return;
   }

   if(ProfitTargetReached) {
      Print("Trading paused: Target or overall drawdown previously reached. Balance=", DoubleToString(balance, 2), ", Equity=", DoubleToString(equity, 2), ", Target=", DoubleToString(TargetBalanceOrEquity, 2));
      return;
   }

   if(DailyDDPaused) {
      Print("Trading paused: Daily drawdown limit previously reached. Waiting for next trading day. Equity=", DoubleToString(equity, 2), ", Daily Balance=", DoubleToString(DailyBalance, 2));
      return;
   }

   static datetime lastNewsFetch = 0;
   if(NewsFilt && TimeCurrent() >= lastNewsFetch + 4 * 3600) {
      FetchNewsCalendar();
      lastNewsFetch = TimeCurrent();
   }

   for(int i = 0; i < ArraySize(tfs); i++) {
      if(!NewBar(tfs[i].tf)) continue;

      bool hasPosition = false;
      for(int j = PositionsTotal() - 1; j >= 0; j--) {
         ulong ticket = PositionGetTicket(j);
         if(PositionSelectByTicket(ticket) && PositionGetString(POSITION_SYMBOL) == eaSymbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber(tfs[i].tf)) {
            hasPosition = true;
            break;
         }
      }
      if(hasPosition) {
         ManageTrades(tfs[i].tf);
         continue;
      }

      if(TimeCurrent() <= tfs[i].lastSig + PeriodSeconds(PERIOD_H1)) {
         Print(EnumToString(tfs[i].tf), " Trade skipped: Within 1-hour cooldown");
         continue;
      }

      if(NewsFilt && IsNews()) {
         Print(EnumToString(tfs[i].tf), " Trade skipped: News pause active");
         continue;
      }

      double eq = AccountInfoDouble(ACCOUNT_EQUITY);
      if(eq > LastEqHigh) LastEqHigh = eq;
      if(eq < LastEqHigh && lossStreak >= 2) CurRisk = MathMax(OrigRisk * 0.25, 0.1);
      else if(winStreak >= 3) CurRisk = MathMin(OrigRisk * 1.25, 5.0);
      else CurRisk = OrigRisk;
      if(eq >= RecTgt) {
         CurRisk = OrigRisk;
         winStreak = 0;
         lossStreak = 0;
      }

      bool bullHTF = !useHTF || (BullTrend(PERIOD_D1) || BullTrend(PERIOD_H4));
      bool bearHTF = !useHTF || (BearTrend(PERIOD_D1) || BearTrend(PERIOD_H4));
      bool buyBrk = BuyBrk(tfs[i].tf);
      bool sellBrk = SellBrk(tfs[i].tf);
      Print(EnumToString(tfs[i].tf), " Signal check: BullHTF=", bullHTF, ", BearHTF=", bearHTF, ", BuyBrk=", buyBrk, ", SellBrk=", sellBrk, 
            ", Bid=", DoubleToString(SymbolInfoDouble(eaSymbol, SYMBOL_BID), _Digits), ", Ask=", DoubleToString(SymbolInfoDouble(eaSymbol, SYMBOL_ASK), _Digits));

      double atr[1], vol[2], vol_ma[1];
      if(CopyBuffer(tfs[i].h_atr, 0, 0, 1, atr) < 1 || 
         CopyBuffer(tfs[i].h_vol, 0, 0, 2, vol) < 2 || 
         CopyBuffer(tfs[i].h_vol_ma, 0, 1, 1, vol_ma) < 1) {
         Print(EnumToString(tfs[i].tf), " Trade skipped: Indicator copy failed");
         continue;
      }

      double slPips = atr[0] * 2 / _Point;
      double lots = CalcLots(eq, CurRisk, slPips);
      double margReq = SymbolInfoDouble(eaSymbol, SYMBOL_MARGIN_INITIAL) * lots;
      double freeMarg = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
      Print(EnumToString(tfs[i].tf), " Lot size: ", DoubleToString(lots, 2), ", Margin required: ", DoubleToString(margReq, 2), 
            ", Free margin: ", DoubleToString(freeMarg, 2), ", SL Pips: ", DoubleToString(slPips, 2), 
            ", Contract size: ", DoubleToString(SymbolInfoDouble(eaSymbol, SYMBOL_TRADE_CONTRACT_SIZE), 0));

      if(freeMarg < margReq * 1.2) {
         Print(EnumToString(tfs[i].tf), " Trade skipped: Margin low (", DoubleToString(freeMarg, 2), " < ", DoubleToString(margReq * 1.2, 2), ")");
         continue;
      }

      double brkStr = MathAbs(buyBrk ? SymbolInfoDouble(eaSymbol, SYMBOL_ASK) - iHigh(eaSymbol, tfs[i].tf, iHighest(eaSymbol, tfs[i].tf, MODE_HIGH, Brk_Prd, 1)) :
                                     iLow(eaSymbol, tfs[i].tf, iLowest(eaSymbol, tfs[i].tf, MODE_LOW, Brk_Prd, 1)) - SymbolInfoDouble(eaSymbol, SYMBOL_BID)) / atr[0];
      if(!Bypass && brkStr < dynBrkStr) {
         Print(EnumToString(tfs[i].tf), " Trade skipped: Breakout strength too low (", DoubleToString(brkStr, 2), " < ", DoubleToString(dynBrkStr, 2), ")");
         continue;
      }

      if(bullHTF && buyBrk) {
         double price = SymbolInfoDouble(eaSymbol, SYMBOL_ASK);
         double sl = price - slPips * _Point;
         double tp = price + slPips * 2 * _Point;
         Print(EnumToString(tfs[i].tf), " Attempting Buy: Price=", DoubleToString(price, _Digits), ", SL=", DoubleToString(sl, _Digits), 
               ", TP=", DoubleToString(tp, _Digits), ", Lots=", DoubleToString(lots, 2));
         trade.SetExpertMagicNumber(MagicNumber(tfs[i].tf));
         if(trade.Buy(lots, eaSymbol, price, sl, tp)) {
            tfs[i].lastSig = TimeCurrent();
            LogTrd(trade.ResultOrder(), eaSymbol, price, sl, tp, "Open", brkStr, vol[1], CurRisk, tfs[i].tf);
            Print(EnumToString(tfs[i].tf), " Buy opened: Ticket=", trade.ResultOrder());
         } else {
            Print(EnumToString(tfs[i].tf), " Buy failed: Retcode=", trade.ResultRetcode(), ", Error=", GetLastError(), ", Comment=", trade.ResultComment());
         }
      } else if(bearHTF && sellBrk) {
         double price = SymbolInfoDouble(eaSymbol, SYMBOL_BID);
         double sl = price + slPips * _Point;
         double tp = price - slPips * 2 * _Point;
         Print(EnumToString(tfs[i].tf), " Attempting Sell: Price=", DoubleToString(price, _Digits), ", SL=", DoubleToString(sl, _Digits), 
               ", TP=", DoubleToString(tp, _Digits), ", Lots=", DoubleToString(lots, 2));
         trade.SetExpertMagicNumber(MagicNumber(tfs[i].tf));
         if(trade.Sell(lots, eaSymbol, price, sl, tp)) {
            tfs[i].lastSig = TimeCurrent();
            LogTrd(trade.ResultOrder(), eaSymbol, price, sl, tp, "Open", brkStr, vol[1], CurRisk, tfs[i].tf);
            Print(EnumToString(tfs[i].tf), " Sell opened: Ticket=", trade.ResultOrder());
         } else {
            Print(EnumToString(tfs[i].tf), " Sell failed: Retcode=", trade.ResultRetcode(), ", Error=", GetLastError(), ", Comment=", trade.ResultComment());
         }
      } else {
         Print(EnumToString(tfs[i].tf), " Trade skipped: No valid signal");
      }
   }

   // Run FVG detection after trade logic to ensure non-interference
   DetectFVGs();
}

void ManageTrades(ENUM_TIMEFRAMES tf) {
   for(int i = PositionsTotal() - 1; i >= 0; i--) {
      ulong ticket = PositionGetTicket(i);
      if(!PositionSelectByTicket(ticket) || PositionGetString(POSITION_SYMBOL) != eaSymbol || PositionGetInteger(POSITION_MAGIC) != MagicNumber(tf)) continue;
      double openPrice, sl, tp, currPrice, lots, profit;
      if(!PositionGetDouble(POSITION_PRICE_OPEN, openPrice) ||
         !PositionGetDouble(POSITION_SL, sl) ||
         !PositionGetDouble(POSITION_TP, tp) ||
         !PositionGetDouble(POSITION_VOLUME, lots) ||
         !PositionGetDouble(POSITION_PROFIT, profit)) continue;
      currPrice = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY ? SymbolInfoDouble(eaSymbol, SYMBOL_BID) : SymbolInfoDouble(eaSymbol, SYMBOL_ASK);
      int idx = TimeframeIndex(tf);
      if(idx < 0) continue;
      double atr[1];
      if(CopyBuffer(tfs[idx].h_atr, 0, 0, 1, atr) < 1) continue;

      if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && currPrice >= openPrice + (openPrice - sl) * 3) ||
         (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && currPrice <= openPrice - (sl - openPrice) * 3)) {
         if(lots > SymbolInfoDouble(eaSymbol, SYMBOL_VOLUME_MIN) * 2) {
            trade.PositionClosePartial(ticket, lots / 2);
            trade.PositionModify(ticket, openPrice, tp);
            Print(EnumToString(tf), " Partial close at 1:3 RR: Ticket=", ticket, ", Lots=", DoubleToString(lots / 2, 2));
         }
      }

      double trail = atr[0] * 1.5 / _Point;
      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && currPrice > openPrice + trail * _Point && sl < currPrice - trail * _Point) {
         trade.PositionModify(ticket, currPrice - trail * _Point, tp);
         Print(EnumToString(tf), " Trailing stop updated: Ticket=", ticket, ", New SL=", DoubleToString(currPrice - trail * _Point, _Digits));
      } else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && currPrice < openPrice - trail * _Point && sl > currPrice + trail * _Point) {
         trade.PositionModify(ticket, currPrice + trail * _Point, tp);
         Print(EnumToString(tf), " Trailing stop updated: Ticket=", ticket, ", New SL=", DoubleToString(currPrice + trail * _Point, _Digits));
      }

      if(profit != 0 && !PositionSelectByTicket(ticket)) {
         LogTrd(ticket, eaSymbol, openPrice, sl, tp, "Close", 0, 0, CurRisk, tf);
         Print(EnumToString(tf), " Position closed: Ticket=", ticket, ", Profit=", DoubleToString(profit, 2));
      }
   }
}

void FetchNewsCalendar() {
   string url = NewsAPI_Url + NewsAPI_Key;
   string headers = "";
   char post[], result[];
   string result_headers;
   int timeout = 5000;
   int res = WebRequest("GET", url, headers, timeout, post, result, result_headers);
   if(res != 200) {
      Print("News API request failed: HTTP ", res, ", Error=", GetLastError());
      newsCal[0].time = StringToTime("2025.07.15 14:30");
      newsCal[0].evt = "CPI";
      newsCal[0].impact = 90;
      newsCal[1].time = StringToTime("2025.07.23 20:00");
      newsCal[1].evt = "FOMC";
      newsCal[1].impact = 90;
      newsCnt = 2;
      Print("Using fallback news calendar with ", newsCnt, " events");
      return;
   }

   string response = CharArrayToString(result);
   newsCnt = 0;
   ArrayResize(newsCal, 100);

   int pos = 0;
   while(pos >= 0 && newsCnt < 100) {
      pos = StringFind(response, "\"items\":", pos);
      if(pos < 0) break;
      pos = StringFind(response, "{", pos);
      if(pos < 0) break;

      int end = StringFind(response, "}", pos);
      if(end < 0) break;
      string item = StringSubstr(response, pos, end - pos + 1);

      string evtName = ExtractJsonField(item, "\"title\":");
      string evtTime = ExtractJsonField(item, "\"time_published\":");
      string relevance = ExtractJsonField(item, "\"relevance_score\":");

      if(evtName != "" && evtTime != "") {
         string dt = StringSubstr(evtTime, 0, 4) + "." + StringSubstr(evtTime, 4, 2) + "." + StringSubstr(evtTime, 6, 2) + " " + 
                     StringSubstr(evtTime, 9, 2) + ":" + StringSubstr(evtTime, 11, 2);
         newsCal[newsCnt].time = StringToTime(dt);
         newsCal[newsCnt].evt = evtName;
         newsCal[newsCnt].impact = (relevance != "") ? (int)(StringToDouble(relevance) * 100) : 80;
         if(newsCal[newsCnt].impact > 80 && newsCal[newsCnt].time > TimeCurrent() - 7 * 86400) {
            newsCnt++;
            Print("News event loaded: ", evtName, " at ", TimeToString(newsCal[newsCnt-1].time), ", Impact=", newsCal[newsCnt-1].impact);
         }
      }
      pos = end + 1;
   }
   Print("Loaded ", newsCnt, " high-impact news events from API");
}

string ExtractJsonField(string json, string field) {
   int pos = StringFind(json, field);
   if(pos < 0) return "";
   pos += StringLen(field);
   if(StringFind(json, "\"", pos) == pos) pos++;
   int end = StringFind(json, "\"", pos);
   if(end < 0) return "";
   return StringSubstr(json, pos, end - pos);
}

bool NewBar(ENUM_TIMEFRAMES tf) {
   int idx = TimeframeIndex(tf);
   if(idx < 0) return false;
   datetime cur = iTime(eaSymbol, tf, 0);
   if(cur != tfs[idx].lastBar) {
      tfs[idx].lastBar = cur;
      return true;
   }
   return false;
}

bool BullTrend(ENUM_TIMEFRAMES tf) {
   int handle = (tf == PERIOD_D1) ? h_ema_d1 : h_ema_h4;
   double ema[2];
   if(CopyBuffer(handle, 0, 1, 2, ema) < 2) {
      Print("EMA copy failed for ", EnumToString(tf));
      return false;
   }
   Print("EMA ", EnumToString(tf), ": ", DoubleToString(ema[1], _Digits), " vs ", DoubleToString(ema[0], _Digits));
   return ema[1] > ema[0];
}

bool BearTrend(ENUM_TIMEFRAMES tf) {
   int handle = (tf == PERIOD_D1) ? h_ema_d1 : h_ema_h4;
   double ema[2];
   if(CopyBuffer(handle, 0, 1, 2, ema) < 2) {
      Print("EMA copy failed for ", EnumToString(tf));
      return false;
   }
   Print("EMA ", EnumToString(tf), ": ", DoubleToString(ema[1], _Digits), " vs ", DoubleToString(ema[0], _Digits));
   return ema[1] < ema[0];
}

bool BuyBrk(ENUM_TIMEFRAMES tf) {
   double high = iHigh(eaSymbol, tf, iHighest(eaSymbol, tf, MODE_HIGH, Brk_Prd, 1));
   double price = SymbolInfoDouble(eaSymbol, SYMBOL_ASK);
   Print(EnumToString(tf), " BuyBrk check: Price=", DoubleToString(price, _Digits), ", High=", DoubleToString(high, _Digits));
   return price > high;
}

bool SellBrk(ENUM_TIMEFRAMES tf) {
   double low = iLow(eaSymbol, tf, iLowest(eaSymbol, tf, MODE_LOW, Brk_Prd, 1));
   double price = SymbolInfoDouble(eaSymbol, SYMBOL_BID);
   Print(EnumToString(tf), " SellBrk check: Price=", DoubleToString(price, _Digits), ", Low=", DoubleToString(low, _Digits));
   return price < low;
}

double CalcLots(double eq, double riskPct, double slPips) {
   double pipVal = SymbolInfoDouble(eaSymbol, SYMBOL_TRADE_TICK_VALUE);
   double riskAmt = MathMin(eq * (riskPct / 100), MaxLossUSD);
   double lots = riskAmt / (slPips * pipVal);
   double minLot = SymbolInfoDouble(eaSymbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(eaSymbol, SYMBOL_VOLUME_MAX);
   lots = NormalizeDouble(MathMax(minLot, MathMin(maxLot, lots)), 2);
   Print("CalcLots: Equity=", DoubleToString(eq, 2), ", Risk%=", DoubleToString(riskPct, 2), ", SL Pips=", DoubleToString(slPips, 2), 
         ", PipVal=", DoubleToString(pipVal, 2), ", Lots=", DoubleToString(lots, 2), ", MinLot=", DoubleToString(minLot, 2), ", MaxLot=", DoubleToString(maxLot, 2));
   return lots;
}

bool IsNews() {
   if(newsCnt == 0 && NewsFilt) {
      Print("No news events loaded, bypassing news filter");
      return false;
   }
   datetime now = TimeCurrent();
   Print("News check: Current time=", TimeToString(now, TIME_DATE|TIME_MINUTES));
   for(int i = 0; i < newsCnt; i++) {
      if(now >= newsCal[i].time - NewsPause * 60 && now <= newsCal[i].time + NewsPause * 60 && newsCal[i].impact > 80) {
         Print("News event active: ", newsCal[i].evt, " at ", TimeToString(newsCal[i].time, TIME_DATE|TIME_MINUTES));
         return true;
      }
   }
   Print("No active news events");
   return false;
}

void LogTrd(ulong ticket, string sym, double price, double sl, double tp, string stat, double brkStr, double vol, double risk, ENUM_TIMEFRAMES tf) {
   string data = StringFormat("T=%I64u,S=%s,Tm=%s,P=%f,SL=%f,TP=%f,St=%s,BrkStr=%f,Vol=%f,Risk=%f,TF=%s",
      ticket, sym, TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES), price, sl, tp, stat, brkStr, vol, risk, EnumToString(tf));
   if(StringLen(GS_Url) > 0) Print("Webhook pending: ", data);
   Print("Trd: ", data);

   if(stat == "Close" && tradeCnt < ArraySize(tradeHistory)) {
      double profit;
      if(PositionSelectByTicket(ticket)) {
         PositionGetDouble(POSITION_PROFIT, profit);
         tradeHistory[tradeCnt].ticket = ticket;
         tradeHistory[tradeCnt].isWin = profit > 0;
         tradeHistory[tradeCnt].profit = profit;
         tradeHistory[tradeCnt].brkStr = brkStr;
         tradeHistory[tradeCnt].vol = vol;
         tradeHistory[tradeCnt].risk = risk;
         tradeHistory[tradeCnt].tf = tf;
         tradeCnt++;
         UpdateWinLossStreak();
         AdjustBreakoutStrength();
      }
   }
}

void UpdateWinLossStreak() {
   if(tradeCnt > 0) {
      if(tradeHistory[tradeCnt-1].isWin) {
         winStreak++;
         lossStreak = 0;
      } else {
         lossStreak++;
         winStreak = 0;
      }
   }
}

void AdjustBreakoutStrength() {
   if(tradeCnt < 10) return;
   int lossCnt = 0;
   double avgBrkStr = 0;
   for(int i = tradeCnt - 10; i < tradeCnt; i++) {
      if(!tradeHistory[i].isWin) lossCnt++;
      avgBrkStr += tradeHistory[i].brkStr;
   }
   avgBrkStr /= 10;
   if(lossCnt >= 5 && avgBrkStr < MinBrkStr * 1.5) dynBrkStr = MinBrkStr * 1.5;
   else if(lossCnt <= 2) dynBrkStr = MinBrkStr;
   Print("Breakout strength adjusted: dynBrkStr=", DoubleToString(dynBrkStr, 2));
}

long MagicNumber(ENUM_TIMEFRAMES tf) {
   if(tf == PERIOD_M5) return 1005;
   if(tf == PERIOD_M15) return 1015;
   if(tf == PERIOD_H1) return 1060;
   return 0;
}

int TimeframeIndex(ENUM_TIMEFRAMES tf) {
   for(int i = 0; i < ArraySize(tfs); i++) {
      if(tfs[i].tf == tf) return i;
   }
   return -1;
}

Installation und Backtests: Zusammenstellen und der Tabelle beifügen. Backtesting auf GOLD M15 (2025) mit 2% Risiko. 


Strategietester

Die Strategie funktioniert am besten bei GOLD, da es sich relativ schnell bewegt und eine hohe Volatilität aufweist, was für den Intraday-Handel von Vorteil ist. Wir werden diese Strategie testen, indem wir GOLD vom 1. Januar 2025 bis zum 29. Juli 2025 auf dem 15-Minuten-Zeitrahmen (M15) handeln. Hier sind die Parameter, die ich für diese Strategie gewählt habe. 

Inputs 2



Ergebnisse im Strategietester

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

  • Salden-/Kapitalkurve:

Diagramm der Ergebnisse

  • Backtest-Ergebnisse:

Testergebnisse


Zusammenfassung

Ich habe diesen Artikel geschrieben, um zu versuchen, einen MetaTrader 5 Expert Advisor (EA) zu erklären, der die Erkennung von Fair Value Gaps (FVG) und Market Structure Shift (MSS) kombiniert, um hochwahrscheinliche Handels-Setups für GOLD zu identifizieren. Fair Value Gaps sind eines der wertvollsten und gängigsten Smart-Money-Konzepte, um Preisineffizienzen und Trendwechsel zu erfassen.

Ich habe den EA auf GOLD getestet, und es hat sich gezeigt, dass der EA in der Lage ist, FVGs effizient und treffend auf M15- und H1-Zeitrahmen zu erkennen, aber die Erkennung von Fair Value Gaps ist nur ein Teil der Gleichung, denn wenn die Marktstrukturverschiebung nicht stattfindet, dann sollen die Handelsgeschäfte nicht ausgeführt werden, selbst wenn Fair Value Gaps gültig sind. Diese Marktstrukturverschiebungen sind Bestätigungen, die dazu beitragen, die Genauigkeit und Qualität des Handels während volatiler Sitzungen zu verbessern.

Um diese Strategie umzusetzen, konfigurieren Sie die Eingabeparameter des EA wie unten gezeigt, um die gewünschten Ergebnisse zu erzielen. Der EA ist so konzipiert, dass er auf M15- oder H1-Charts nach FVGs sucht, um sicherzustellen, dass Marktstrukturverschiebungen mit Trends auf höheren Zeitrahmen (z. B. H4/D1) übereinstimmen. Interessierte Nutzer sollten diesen EA auf ihren Demo-Konten mit GOLD „backtesten“. Mein Hauptanliegen und Ziel für diesen EA war es, ihn für ausgewählte und hochwahrscheinliche Setups zu optimieren, die ein Risikomanagement wie 0,5-2% Risiko pro Handel und Trailing Stops beinhalten.

Ich würde den Nutzern auch empfehlen, die Leistungsprotokolle regelmäßig zu überprüfen, um die Einstellungen und Eingabeparameter je nach den eigenen Zielen oder der Risikobereitschaft zu verfeinern. Haftungsausschluss: Jeder, der diesen EA verwendet, sollte zunächst testen und mit dem Handel auf seinem Demokonto beginnen, um diesen institutionellen Ansatz für beständige Gewinne zu beherrschen, bevor er Live-Gelder riskiert.


Schlussfolgerung

Der Schwerpunkt dieses Artikels liegt darin, klar zu erklären, was Fair Value Gaps sind, wo sie auftreten, wie sie auftreten, warum sie auftreten, und schließlich, wie sie idealerweise genutzt werden können, um sie zu analysieren, zu verstehen und sogar Handelsausführungen unter ihrer Verwendung durchzuführen.

Die meisten Anfänger und sogar einige fortgeschrittene Händler haben keine Ahnung, wie man sich in dieser düsteren Welt der Fair Value Gaps zurechtfindet, und sind sogar frustriert, da sie nicht in der Lage sind, wirklich zu begreifen, was dort vor sich geht, oder sogar nicht das scharfsinnige Auge haben, um zu verstehen, wie der Preis immer zurückkommt und wie er diese Gaps nutzt, um klassische Handelsgeschäfte und Ausführungen zu machen, die für sie tatsächlich profitabel sein können. Oder selbst wenn sie keine Handelsgeschäfte auf Fair Value Gaps ausführen, werden Händler in der Lage sein, ihre eigenen Trades, ihre eigenen Setups und Trades und sogar den Trend und die Richtung in Bezug auf das, was ich in diesem Artikel mitgeteilt habe, zu validieren, und sie werden es sehr interessant finden, wie diese Fair Value Gaps kolossale und wichtige Rollen spielen.

Durch die Automatisierung mit MQL5 reduzieren Händler emotionale Verzerrungen und ermöglichen eine konsistente Ausführung von FVG + Breaker/MSS-Strategien.

Der gesamte Code, auf den in dem Artikel verwiesen wird, ist unten angefügt. In der folgenden Tabelle sind alle Quellcodedateien beschrieben, die dem Artikel beigefügt sind.
Dateiname Beschreibung
Fvg_detector.mq5 Datei mit Code für die Erkennung von Marktwertlücken
fvg-mss.mq5
Datei, die den Code für den gesamten kombinierten EA, die Ermittlung der Marktwertlücke und die Verschiebung der Marktstruktur enthält 

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

Beigefügte Dateien |
fvg-mss.mq5 (54.64 KB)
Fvg_detector.mq5 (9.26 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (2)
Korrect Trades
Korrect Trades | 15 Sept. 2025 in 12:09
Schön 👍.
Ich mag das
Eugene Mmene
Eugene Mmene | 15 Sept. 2025 in 12:12
Korrect Trades #:
Schön 👍.
Ich mag das
Willkommen
Den Marktstimmungsindikator automatisieren Den Marktstimmungsindikator automatisieren
In diesem Artikel entwickeln wir einen nutzerdefinierten Indikator für die Marktstimmung, um die Bedingungen in aufwärts, abwärts, mehr und weniger Risiko oder neutral zu klassifizieren. Der Expert Advisor liefert Echtzeit-Einblicke in die vorherrschende Stimmung und vereinfacht den Analyseprozess für aktuelle Markttrends oder -richtungen.
Aufbau von KI-gesteuerten Handelssystemen in MQL5 (Teil 1): Implementierung der JSON-Verarbeitung für KI-APIs Aufbau von KI-gesteuerten Handelssystemen in MQL5 (Teil 1): Implementierung der JSON-Verarbeitung für KI-APIs
In diesem Artikel entwickeln wir ein System des JSON-Parsing in MQL5, um den Datenaustausch für die KI-API-Integration zu handhaben, wobei wir uns auf eine JSON-Klasse zur Verarbeitung von JSON-Strukturen konzentrieren. Wir implementieren Methoden zur Serialisierung und Deserialisierung von JSON-Daten, die verschiedene Datentypen wie Strings, Zahlen und Objekte unterstützen. Dies ist für die Kommunikation mit KI-Diensten wie ChatGPT unerlässlich und ermöglicht zukünftige KI-gesteuerte Handelssysteme, indem es eine genaue Datenverarbeitung und -manipulation gewährleistet.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Entwicklung des Price Action Analysis Toolkit (Teil 40): Markt-DNA-Pass Entwicklung des Price Action Analysis Toolkit (Teil 40): Markt-DNA-Pass
In diesem Artikel wird die einzigartige Identität der einzelnen Währungspaare anhand ihrer historischen Kursentwicklung untersucht. Inspiriert vom Konzept der genetischen DNA, die den individuellen Bauplan eines jeden Lebewesens kodiert, wenden wir einen ähnlichen Rahmen auf die Märkte an, indem wir die Kursentwicklung als „DNA“ eines jeden Paares betrachten. Durch die Aufschlüsselung struktureller Verhaltensweisen wie Volatilität, Schwankungen, Rückschritte, Ausschläge und Sitzungsmerkmale zeigt das Tool das zugrunde liegende Profil, das ein Paar von einem anderen unterscheidet. Dieser Ansatz bietet einen tieferen Einblick in das Marktverhalten und gibt Händlern eine strukturierte Methode an die Hand, um ihre Strategien auf die natürlichen Tendenzen der einzelnen Instrumente abzustimmen.