
Die Strategie des Handel eines Liquiditätshungers
Der Liquiditätshunger (liquidity grab) ist eine Schlüsselkomponente von Smart Money Concepts (SMC), die darauf abzielt, die Aktionen institutioneller Marktteilnehmer zu identifizieren und auszunutzen. Dabei werden Bereiche mit hoher Liquidität, wie z. B. Unterstützungs- oder Widerstandszonen, ins Visier genommen, in denen große Aufträge Kursbewegungen auslösen können, bevor der Markt seinen Trend wieder aufnimmt. In diesem Artikel wird das Konzept des Liquiditätshungers im Detail erklärt und der Entwicklungsprozess des Expert Advisor der Liquiditätshunger-Handelsstrategie in MQL5 skizziert.
Überblick über die Strategie: Schlüsselkonzepte und -taktiken
Die Strategie des Liquiditätshungers konzentriert sich auf die Ausnutzung von Bereichen mit hoher Liquidität, wie z. B. Unterstützungs- und Widerstandsniveaus, wo institutionelle Händler häufig Kurse manipulieren, um Stop Losses auszulösen und die Volatilität zu erhöhen. Diese Strategie nutzt die gemeinsame Marktdynamik, um diese Bewegungen zu antizipieren und daraus Kapital zu schlagen.
Zentrale Konzepte
- Die Jagd nach den Stop Loss: Sie treiben die Kurse an, um Stop Loss zu aktivieren, was zu kaskadenartigen Käufen oder Verkäufen führt.
- Layering und Spoofing: Verwendung von Scheinaufträgen zur Irreführung von Händlern über die Marktrichtung.
- Eisberg-Aufträge: Großen Geschäften werden durch Aufteilung in kleinere, sichtbare Teile verborgen.
- Zünden eines Momentums: Es wird eine eine künstliche Dynamik erzeugt, um andere Händler anzulocken, bevor sich der Markt dreht.
- Manipulation von Unterstützung und Widerstand: Ausnutzung der wichtigsten Preisniveaus für vorhersehbare Reaktionen.
- Psychologische Preispunkte: Nutzen von runde Zahlen, um das Verhalten zu beeinflussen.
Markt-Manipulation bedeutet die absichtliche Beeinflussung des Kurses oder Volumens eines Wertpapiers, um irreführende Handelsbedingungen zu schaffen. Zwar halten sich die meisten institutionellen Händler an rechtliche und ethische Standards, aber einige lassen sich auf manipulative Praktiken ein, um bestimmte strategische Ziele zu erreichen. Im Folgenden finden Sie einen kurzen Überblick darüber, warum und wie dies geschieht:
Beweggründe:
- Maximierung der Gewinne durch kurzfristige Kursbewegungen und Arbitrage.
- Verheimlichung der Handelsabsichten vor ihren Konkurrenten.
- Ausführung großer Geschäfte mit minimalen Auswirkungen auf den Markt.
Taktik:
- Auslösen von Stop Loss, um Liquidität zu schaffen oder Preise zu bewegen.
- Kontrolle des Auftragsflusses durch Eisberg- oder Schein-Aufträge.
- Anvisieren von Unterstützungs-, Widerstands- oder psychologischen Kursniveaus.
Das Verständnis dieser Dynamik ermöglicht es den Händlern, das Verhalten der Institutionen zu antizipieren und diese Erkenntnisse in automatisierte Instrumente für effektivere Strategien zu integrieren.
Wir wollen von vorübergehenden Kursbewegungen profitieren, die durch viele Stop Loss und Aktionen großer Marktteilnehmer verursacht werden. Durch die Identifizierung von Liquiditätsbereichen zielen Händler darauf ab, zu optimalen Zeitpunkten in den Markt einzusteigen, bevor der Kurs umschlägt und sich im vorherrschenden Trend fortsetzt, wodurch sich günstige Chancen für ein gutes Risiko-Ertrags-Verhältnis ergeben.
Hier ist ein grobes Diagramm, wie es aussehen sollte.
Entwicklung einer Strategie
Ich schlage vor, dass wir die folgende Reihenfolge annehmen, wenn wir einen EA kodieren:
- Überlegen Sie, welche Funktionen für diese Strategie erforderlich sind, und legen Sie fest, wie sie in verschiedenen Komponenten gekapselt werden sollen.
- Programmieren Sie jede Funktion einzeln und deklarieren Sie dabei die zugehörigen globalen Variablen oder initialisieren Sie die entsprechenden Handles, während Sie fortfahren.
- Nachdem Sie jede Funktion implementiert haben, überprüfen Sie sie und überlegen Sie, wie Sie sie verbinden können, z. B. durch die Übergabe von Parametern oder den Aufruf von Funktionen innerhalb anderer Funktionen.
- Navigieren Sie schließlich zu OnTick() und entwickeln Sie die Logik mithilfe der Funktionen aus den vorherigen Schritten.
Erstens versuchen wir, die Regeln zu quantifizieren. SMC wird hauptsächlich von diskretionären Händlern gehandelt, da es viele nicht quantifizierbare Nuancen gibt. Objektiv gesehen ist es schwierig, die genauen Merkmale zu definieren, die eine Manipulation aufweisen muss. Ein Ansatz besteht darin, die Veränderung des Auftragsvolumens zu analysieren, aber die von den Brokern gelieferten Volumendaten sind oft unzuverlässig. Auf Märkten wie dem Devisenmarkt sind die Transaktionen nicht zentralisiert, und für zentralisierte Börsen wie Futures stellen die meisten Makler eher Daten von ihren Liquiditätsanbietern als zentralisierte Daten zur Verfügung. Eine einfachere und optimierbare Methode ist die technische Analyse, die wir in diesem Artikel anwenden werden. Um die Regeln zu quantifizieren, werden wir die Strategie in die folgenden Segmente vereinfachen:
- Eine abprallende Kerze, die sich an einem Schlüsselniveau gebildet wird, wobei das Schlüsselniveau als der höchste oder niedrigste Punkt innerhalb des Rückblickzeitraums definiert ist.
- Nach dieser abprallenden Kerze kehrt der Kurs um und durchbricht das Schlüsselniveau auf der gegenüberliegenden Seite mit einer kürzeren Rückblickzeit.
- Wenn schließlich die Gesamtbewegung mit dem allgemeinen Trend übereinstimmt, wie die Position des Kurses im Verhältnis zum gleitenden Durchschnitt zeigt, gehen wir mit einem festen Stop Loss und Take Profit in den Handel.
Wir könnten weitere Regeln hinzufügen, um die Merkmale der Marktmanipulation besser nachzubilden, aber es ist ratsam, die Strategie so einfach wie möglich zu halten, um ein Überanpassen zu vermeiden.
Als Nächstes codieren wir die entsprechenden Funktionen. Diese Funktionen sind unerlässlich, um Auftragsausführungen nach der Berechnung von Take Profit und Stop Loss vorzunehmen und die Auftragstickets zu verfolgen.
//+------------------------------------------------------------------+ //| Expert trade transaction handling function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { if (trans.type == TRADE_TRANSACTION_ORDER_ADD) { COrderInfo order; if (order.Select(trans.order)) { if (order.Magic() == Magic) { if (order.OrderType() == ORDER_TYPE_BUY) { buypos = order.Ticket(); } else if (order.OrderType() == ORDER_TYPE_SELL) { sellpos = order.Ticket(); } } } } } //+------------------------------------------------------------------+ //| Execute sell trade function | //+------------------------------------------------------------------+ void executeSell() { double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); bid = NormalizeDouble(bid, _Digits); double tp = NormalizeDouble(bid - tpp * _Point, _Digits); double sl = NormalizeDouble(bid + slp * _Point, _Digits); trade.Sell(lott, _Symbol, bid, sl, tp); sellpos = trade.ResultOrder(); } //+------------------------------------------------------------------+ //| Execute buy trade function | //+------------------------------------------------------------------+ void executeBuy() { double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); ask = NormalizeDouble(ask, _Digits); double tp = NormalizeDouble(ask + tpp * _Point, _Digits); double sl = NormalizeDouble(ask - slp * _Point, _Digits); trade.Buy(lott, _Symbol, ask, sl, tp); buypos = trade.ResultOrder(); }
Diese beiden Funktionen identifizieren den höchsten bzw. niedrigsten Punkt innerhalb einer bestimmten Bereichs und geben ihn zurück. Dabei wird sichergestellt, dass dieser Punkt als Schlüsselniveau gilt, indem das Vorhandensein einer Unterstützung oder eines Widerstands überprüft wird, der eine Umkehr von diesem Niveau aus auslöst.
//+------------------------------------------------------------------+ //| find the key level high given a look-back period | //+------------------------------------------------------------------+ double findhigh(int Range = 0) { double highesthigh = 0; for (int i = BarsN; i < Range; i++) { double high = iHigh(_Symbol, PERIOD_CURRENT, i); if (i > BarsN && iHighest(_Symbol, PERIOD_CURRENT, MODE_HIGH, BarsN * 2 + 1, i - BarsN) == i) //used to make sure there's rejection for this high { if (high > highesthigh) { return high; } } highesthigh = MathMax(highesthigh, high); } return 99999; } //+------------------------------------------------------------------+ //| find the key level low given a look-back period | //+------------------------------------------------------------------+ double findlow(int Range = 0) { double lowestlow = DBL_MAX; for (int i = BarsN; i < Range; i++) { double low = iLow(_Symbol, PERIOD_CURRENT, i); if (i > BarsN && iLowest(_Symbol, PERIOD_CURRENT, MODE_LOW, BarsN * 2 + 1, i - BarsN) == i) { if (lowestlow > low) { return low; } } lowestlow = MathMin(lowestlow, low); } return -1; }
Die Funktion findhigh() stellt sicher, dass der Abprall vom ermittelten Hoch geschah, indem sie prüft, ob das höchste Hoch innerhalb eines bestimmten Bereichs beim aktuellen Balken (i) auftritt und ob das höchste Hoch innerhalb eines größeren Bereichs (doppelter Rückblickzeitraum) mit diesem Balken zusammenfällt. Dies deutet auf ein Abprallen hin, da der Kurs nach Erreichen dieses Niveaus nicht nach oben ausbrechen konnte. Bei „true“ wird das Hoch als potenzieller Schlüsselwert zurückgegeben. Die Funktion findlow() ist einfach die umgekehrte Logik.
Diese beiden Funktionen erkennen, ob es sich bei der letzten geschlossenen Kerze um eine Abprall-Kerze auf der Schlüsselebene handelt, was als Liquiditätsaufnahmeverhalten angesehen werden kann.
//+------------------------------------------------------------------+ //| Check if the market rejected in the upward direction | //+------------------------------------------------------------------+ bool IsRejectionUp(int shift=1) { // Get the values of the last candle (shift = 1) double open = iOpen(_Symbol,PERIOD_CURRENT, shift); double close = iClose(_Symbol,PERIOD_CURRENT, shift); double high = iHigh(_Symbol,PERIOD_CURRENT, shift); double low = iLow(_Symbol,PERIOD_CURRENT,shift); // Calculate the body size double bodySize = MathAbs(close - open); // Calculate the lower wick size double lowerWickSize = open < close ? open - low : close - low; // Check if the lower wick is significantly larger than the body if (lowerWickSize >= wickToBodyRatio * bodySize&&low<findlow(DistanceRange)&&high>findlow(DistanceRange)) { return true; } return false; } //+------------------------------------------------------------------+ //| Check if the market rejected in the downward direction | //+------------------------------------------------------------------+ bool IsRejectionDown(int shift = 1) { // Get the values of the last candle (shift = 1) double open = iOpen(_Symbol,PERIOD_CURRENT, shift); double close = iClose(_Symbol,PERIOD_CURRENT, shift); double high = iHigh(_Symbol,PERIOD_CURRENT, shift); double low = iLow(_Symbol,PERIOD_CURRENT,shift); // Calculate the body size double bodySize = MathAbs(close - open); // Calculate the upper wick size double upperWickSize = open > close ? high - open : high - close; // Check if the upper wick is significantly larger than the body if (upperWickSize >= wickToBodyRatio * bodySize&&high>findhigh(DistanceRange)&&low<findhigh(DistanceRange)) { return true; } return false; }
Eine Kerze weist ein Abprall-Muster auf, wenn der Docht deutlich größer ist als der Kerzenkörper und sich die Richtung der Kerze gegenüber der vorherigen Richtung umkehrt.
Unter Verwendung der letzten beiden Funktionen wurden diese erstellt, um in einer Schleife einen bestimmten Rückblickzeitraum zu durchlaufen, um jeglichen Liquiditätshunger zu erkennen, das verwendet wird, um zu überprüfen, ob ein solches Verhalten vor der Beobachtung eines Umkehr- und Ausbruchsignals auftrat.
//+------------------------------------------------------------------+ //| check if there were rejection up for the short look-back period | //+------------------------------------------------------------------+ bool WasRejectionUp(){ for(int i=1; i<CandlesBeforeBreakout;i++){ if(IsRejectionUp(i)) return true; } return false; } //+------------------------------------------------------------------+ //| check if there were rejection down for the short look-back period| //+------------------------------------------------------------------+ bool WasRejectionDown(){ for(int i=1; i<CandlesBeforeBreakout;i++){ if(IsRejectionDown(i)) return true; } return false; }
Um die Daten für den aktuellen gleitenden Durchschnittswert abzurufen, wird zunächst das Handle in der Funktion OnInit() initialisiert.
int handleMa; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { trade.SetExpertMagicNumber(Magic); handleMa = iMA(_Symbol, PERIOD_CURRENT, MaPeriods, 0, MODE_SMA,PRICE_CLOSE); if (handleMa == INVALID_HANDLE) { Print("Failed to get indicator handles. Error: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; }
Auf den Wert des gleitenden Durchschnitts kann dann leicht zugegriffen werden, indem ein Puffer-Array erstellt und der Handle-Wert wie folgt in das Puffer-Array kopiert wird:
double ma[]; if (CopyBuffer(handleMa, 0, 1, 1, ma) <= 0) { Print("Failed to copy MA data. Error: ", GetLastError()); return; }
Schließlich fahren wir mit OnTick() fort, um die Handelslogik mithilfe der definierten Funktionen im Programm zu verwenden. Dadurch wird sichergestellt, dass wir das Signal nur dann berechnen, wenn sich ein neuer Balken gebildet hat, indem wir prüfen, ob sich der aktuelle Balken von dem zuletzt gespeicherten geschlossenen Balken unterscheidet. Das spart Rechenleistung und ermöglicht reibungslosere Transaktionen.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { int bars = iBars(_Symbol, PERIOD_CURRENT); if (barsTotal != bars) { barsTotal = bars;
Dann wenden wir die Signalbedingung einfach wie folgt an:
if(WasRejectionDown()&&bid<ma[0]&&bid<findlow(CandlesBeforeBreakout)) executeSell(); else if(WasRejectionUp()&&ask>ma[0]&&ask>findhigh(CandlesBeforeBreakout)) executeBuy();
Nach diesem Schritt versuchen wir, das Programm zu kompilieren und den Backtest-Visualizer aufzurufen, um zu prüfen, ob der EA funktioniert.
In der Backtest-Visualisierung würde ein typischer Einstieg wie folgt aussehen:
Vorschläge
Obwohl wir die Hauptidee der Strategie abgeschlossen haben, habe ich ein paar Vorschläge für die Umsetzung dieser EA in den Live-Markt:
1. Marktmanipulationen passieren schnell, daher ist es am besten, mit dieser Strategie innerhalb eines Tages zu handeln und Zeitrahmen wie 5 Minuten oder 15 Minuten zu verwenden. Niedrigere Zeitrahmen können anfälliger für falsche Signale sein, während höhere Zeitrahmen zu langsam auf Marktmanipulationen reagieren können.
2. Marktmanipulationen finden in der Regel in Zeiten hoher Volatilität statt, z. B. während der Forex-Sitzungen in New York und London oder zu den Eröffnungs- und Schließzeiten der Börsen. Es ist ratsam, eine Funktion zu implementieren, die den Handel auf diese spezifischen Stunden beschränkt, wie unten dargestellt:
//+------------------------------------------------------------------+ //| Check if the current time is within the specified trading hours | //+------------------------------------------------------------------+ bool IsWithinTradingHours() { datetime currentTime = TimeTradeServer(); MqlDateTime timeStruct; TimeToStruct(currentTime, timeStruct); int currentHour = timeStruct.hour; if (( currentHour >= startHour1 && currentHour < endHour1) || ( currentHour >= startHour2 && currentHour < endHour2)) { return true; } return false; }
3. Wenn sich der Kurs um die Schlüsselniveaus herum konsolidiert, kann dies zu mehreren aufeinanderfolgenden Abschlüssen in beide Richtungen führen. Um sicherzustellen, dass jeweils nur ein Handel ausgeführt wird, fügen wir ein weiteres Kriterium hinzu: Beide Positionstickets müssen auf 0 gesetzt worden sein, was bedeutet, dass keine offenen Positionen für diesen EA bestehen. Wir setzen sie auf 0 zurück, indem wir diese Zeilen in die Funktion OnTick() schreiben.
if(buypos>0&&(!PositionSelectByTicket(buypos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ buypos = 0; } if(sellpos>0&&(!PositionSelectByTicket(sellpos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ sellpos = 0; }
Wir aktualisieren unseren ursprünglichen Code, um die soeben vorgenommenen Änderungen einzubeziehen.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { int bars = iBars(_Symbol, PERIOD_CURRENT); if (barsTotal != bars) { barsTotal = bars; double ma[]; double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); if (CopyBuffer(handleMa, 0, 1, 1, ma) <= 0) { Print("Failed to copy MA data. Error: ", GetLastError()); return; } if(WasRejectionDown()&&IsWithinTradingHours()&&sellpos==buypos&&bid<ma[0]&&bid<findlow(CandlesBeforeBreakout)) executeSell(); else if(WasRejectionUp()&&IsWithinTradingHours()&&sellpos==buypos&&ask>ma[0]&&ask>findhigh(CandlesBeforeBreakout)) executeBuy(); if(buypos>0&&(!PositionSelectByTicket(buypos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ buypos = 0; } if(sellpos>0&&(!PositionSelectByTicket(sellpos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ sellpos = 0; } } }
Backtest
In diesem Artikel werden wir diesen EA für GBPUSD auf dem 5-Minuten-Zeitrahmen verwenden.
Hier sind die Parametereinstellungen, die wir für diesen Expert Advisor gewählt haben:
Wichtige Hinweise:
- Für Take Profit und Stop Loss wählen wir einen angemessenen Punktbetrag auf der Grundlage der Intraday-Volatilität. Da diese Strategie im Wesentlichen dem Trend folgt, wird empfohlen, dass das Verhältnis von Gewinn zu Risiko größer als 1 ist.
- DistanceRange ist der Rückblickzeitraum für die Suche nach Schlüsselniveaus für Liquiditätssignale.
- In ähnlicher Weise ist CandlesBeforeBreakout der Rückblickszeitraum für die Suche nach Ausbruchssignalen auf den jüngsten Schlüsselwerten.
- Das Verhältnis des Dochts zum Kerzenkörper (wick-to-body) kann auf einen Wert eingestellt werden, den der Händler für ausreichend hält, um ein Abprallmuster darzustellen.
- Die Handelszeiten richten sich nach der Serverzeit Ihres Brokers. Bei meinem Broker (GMT+0) ist der Zeitraum der volatilen New Yorker Devisensitzung von 13:00 bis 19:00 Uhr.
Führen wir nun den Backtest vom 2020.11.1 - 2024.11.1 durch:
Die Strategie hat in den letzten 4 Jahren gute Ergebnisse erzielt.
Schlussfolgerung
In diesem Artikel haben wir zunächst das Konzept des Liquiditätshungers und die ihm zugrunde liegenden Beweggründe vorgestellt. Anschließend haben wir eine Schritt-für-Schritt-Anleitung zum Aufbau des Expert Advisor (EA) für diese Strategie von Grund auf erstellt. Anschließend haben wir weitere Empfehlungen zur Optimierung des EA gegeben. Schließlich haben wir seine potenzielle Rentabilität in einem vierjährigen Backtest mit über 200 Geschäften nachgewiesen.
Wir hoffen, dass Sie diese Strategie nützlich finden und dass sie Sie dazu inspiriert, darauf aufzubauen, sei es durch die Entwicklung ähnlicher Strategien oder durch die Optimierung unserer Einstellungen. Die entsprechende Datei für den Expert Advisor ist unten angehängt. Sie können es gerne herunterladen und damit experimentieren.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/16518





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Vielen Dank für den Code, sehr schön geschriebenen Artikel und schön zusammengesetzt, der Code ist sehr hilfreich, danke. Interessant, wenn Sie die SMC-Händler in sozialen Medien sehen die Renditen sehr unterschiedlich. Werden die Transaktionen zu überprüfen und versuchen, ein Trailing-Stop und ein Trailing tp oder einige Fibonacci auf die externen Bereiche
Vielen Dank für Ihren Kommentar! Ja, ich sehe die SMC-Händler in den sozialen Medien. Im Allgemeinen denke ich, dass sie sich nicht wirklich über die Strategie in Bezug auf die Liquiditätsbeschaffung einig sind. Einige suchen nach zwei Fakeouts anstelle von einem, und einige achten auf das Handelsvolumen. Insgesamt sind ihre Handlungen mit einigen Diskrepanzen behaftet, die es schwer machen, die Gültigkeit ihrer Strategien zu beurteilen. Nichtsdestotrotz bin ich gespannt auf Ihre Ergebnisse beim Experimentieren mit Trailing-Sl/Tp und Fibonacci-Ranges.