preview
Optimizing Liquidity Raids: Mastering the Difference Between Liquidity Raids and Market Structure Shifts

Optimizing Liquidity Raids: Mastering the Difference Between Liquidity Raids and Market Structure Shifts

MetaTrader 5Trading |
151 0
Eugene Mmene
Eugene Mmene

Introduction

This article aims to address traders who have a difficulty in determining the difference between market structure shifts and liquidity raids and hence choosing the correct trade action to execute when either of these actions occurs. From my prior experience and traders charter it has occured to me that very many traders make wrong decisions because of lack of experience or skill when it comes to utilizing market structure shifts and liquidity grabs whereby traders are not keen or skilled enough to know correct liquidity grabs or market structure shifts that will work hence picking the wrong one which quickly result into losses and drawdowns hence giving rise to unbased rumours and chatter that the liquidity grabs and market structure shifts concepts do not work.

The second group this article will be very beneficial to is the traders who have partially moderate skills and experience when trading these concepts and hence have a sort of hit-and-miss record where sometimes they are successful in their selection of trading setups, but sometimes they also fail terribly. This article will aim to reinforce their knowledge base and in detail thoroughly explain the key difference between liquidity grabs and market structure shifts, and hence try to mitigate losses that occur from confusing the two. It will clearly demonstrate that both liquidity grabs and market structure shifts, when used optimally, can be very effective and rewarding. The article will clearly describe checkmarks for use when identifying either a valid market structure or liquidity grabs to use; hence, a trader can quickly reference these checkmarks and ascertain his trading decisions, bringing calm, which helps avoid both impulse trading and execution paralysis. 

As a trader myself, I know most new and intermediate traders who trade using ICT concepts are faced with this challenge, in-fact all traders face almost similar, if not identical, challenges at some point in their trading careers, but what separates all of them is that different traders handle, react to, and anticipate this challenges differently; hence the difference in trading results, which has proven to be colossal even when traders who are evenly matched and separated by fine margins in skill, experience, composure, decision-making, and technique are different. Also, the same challenge might just be named differently using other concepts, e.g., breakouts; thus, this article will prove beneficial to such traders too and not only to ICT concepts traders.

This article will explore in detail an EA that is specifically designed for traders who are keen on learning how to optimally identify and use liquidity grabs, which usually offer excellent trading opportunities when trading in an identified trend and narrative, avoiding trading against the trend direction. It will also be designed to identify market structure shifts which occur at specific market points such as end of trends or some major reversals on the higher time frame hence it is specifically optimized to identify either liquidity grabs or market structure shifts and when to execute each when it presents itself differentiating them so that traders dont execute liquidity grabs when what is occuring is a market structure shift or executing market structure shifts when what is occuring is a simple liquidity grab.

This EA is also critical for intraday traders and short-term traders since it also helps them trade according to the most possible price movement in the trend's direction likely to occur on that day, hence increasing the success rate as liquidity grabs occur almost daily in different pairs and at least multiple times a week on the same pair.



Definitions: Imbalances, Market Structure Shifts, and Liquidity Grabs

In this chapter I will make definitions and explanations of the most common phrases and tools we will try to use and utilize in this article, since I am very confident not all who will utilize this article are familiar with this concepts and this may be their first time hearing of them, so we have to be inclusive and accommodate everyone.

1.Imbalances/Inefficiencies

These imbalances/inefficiencies are created when there are rapid price movements in either direction, indicating strong buying or selling pressure, primarily from institutional players. In simple terms, this trading pressure often leads to a scenario where traders do not have equal opportunities to place or execute trades; therefore, in a bullish inefficiency, where price has left a point with force and speed toward higher prices and sellers have not had enough time or opportunity to execute them, while in a bearish inefficiency, where price has left a point with force and speed toward lower prices and buyers have not had enough time or opportunity to execute them, this area is called an unbalanced price range, as there has been no price movement in the opposite direction from where the expansion came from.

This can be explained most clearly through painter's logic: when a painter paints a wall, they must paint it with equal, or even similar, brushstrokes in opposite directions so that the paint is applied evenly and is balanced, or rather, of high quality. This same logic applies to price. When a bullish candle expands and breaks through a price point, for example, from 1.3010 to 1.3030, a bearish candle must also break through that price point to balance the price, and if it doesn't, it becomes an imbalance. Often, if not always, the price will return to this point eventually. This may occur in any timeframe.

image showing price Imbalance

2.Market Structure Shifts

This refers to a particular phenomenon in the price action and trading where price, which was initially moving towards a particular direction, for example, bullish, and making new higher lows and new higher highs, suddenly shifts from the bullish structure being created to a new bearish structure where price breaks the most recent higher low with power and speed and begins pushing lower and lower, now creating lower lows and lower highs. For a market structure to be valid, there must be that break and shift in price action structure with speed and power.

Also, for market structure shifts to be high probability and not false structure shifts/Judas swings/liquidity grabs, price must come from high-interest points, which we often refer to as liquidity. Market structure shifts that occur after liquidity grabs are very high probability in terms of success. Furthermore, this phenomenon occurs in any time frame from M1 to MN. 

Market structure shift

3.Liquidity Grabs

Liquidity refers to high-interest points that institutional investors often mark and identify as potential areas to cover longs or shorts from previously executed trades or also provide opportunities for new trade executions. These areas are of interest because most are areas where retail traders place stop losses or execute breakout trades, thus offering a lot of liquidity to cover trades of the institutional traders; thus, they are referred to as liquidity. As buy-stops, sell-stops, and stop losses are being triggered, the institutional traders are also triggering their trades here with the newfound liquidity; hence, it's why it's called a liquidity grab occurrence.

These liquidity areas are characterized by either double bottoms, double tops, relatively equal highs and lows, or previous days, weeks', and months' lows and highs. There is always liquidity forming here, and this areas are highly volatile and reactionary areas and offer vast trade entry opportunities. 

Image showing liquidity purge



Introspection of How Liquidity Grabs and Market Structure Shifts Occur

One of the most important first steps to effectively use this concept is determining trend direction and narrative, which this article aims to address. This is a very major step for the success of this concept, as it gives us a basis and beginning point for how to navigate and determine the narrative in play. Inshort this whole concept cannot work without determining the trend or narrative in play.

With this article and EA, it will help align the trades executed after liquidity grabs relative to the trend, hence avoiding short-term trades against the trend. This will help developing traders to be more confident and discerning when executing trades, as they will eventually learn and grasp how to only execute trades after liquidity grabs relative to the trends and narrative in play. And once this challenge of determining trend and narrative direction is solved, all that is left now is finding the correct setup after liquidity grabs or even market structure shifts.

The need for a trader to make profitable and excellent decisions in trading, especially intraday and long-term trades, is largely dependent on how one positions oneself relative to the trend direction and strength. Statistically, it is often proven time and again that trading against the trend often offers short-term price movements, which are mostly retracements just before a big move in the direction of the trend, and if one is not careful, he might find himself with very heavy losses, and if one does not cut, he/she losses may end up blowing the account since trending markets may push price for days if not weeks.

Yet executions relative to trend strength and direction often prove more forgiving even if one has poor trade entry positions and at times can result in extra hundreds of dollars, if not thousands, made in profits if the trader is patient enough to wait for the overall trend to continue from reversals or liquidity grabs.

Also, trades analyzed, executed, traded, and tracked regarding the trend direction have proven not only to be more accurate faster but also to have very little drawdown as price moves quickly towards its target points with more speed and less drawdown. Besides, price swings are more powerful, meaning even if one is in drawdown, price will soon trade according to the trend's direction and strength narrative in play and will come out of drawdown.

And the best way the narrative and trend are determined is by using the higher timeframes, mainly  W1 and D1, and inspecting them clearly. The first step the EA takes, which can also be done manually, is by using simple and exponential moving averages to at least help give a clear picture if market is trending or consolidated. Trending markets offer far safer and better quality trading opportunities. Trending markets are identified when the 9-period simple moving average is either below or above the 21-period moving average and is in a clear rising or falling pattern and stacking. The second important thing is to investigate the price action on this two higher timeframes and determine where it is heading. With these steps, this is how the trend is easily determined as either bullish or bearish.     

Now after a trend has been identified and it has also been determined that the price is not in consolidation, the next step is to identify the narrative in play or simply what the price is trying to reach for or achieve. Price is always reaching for three points: either liquidity, PD arrays, or imbalances, and it also trades mostly one liquidity point to another. The main importance and purpose of this EA arise here, and one should note this down: when liquidity is taken at this major areas like the previous day's low, the previous week's low, the previous month's low, and relative equal lows, we now begin to anticipate a market structure shift to occur here, as there is no more reason for the selling narrative. This market structure shift may occur in a slightly lower time frame, like H1/H4/M15, which will initiate a new buying narrative, meaning buys to nearest liquidity.

That is how this EA traces, anticipates, and trades its market structure shifts, and now for the liquidity grabs trades, they are executed in the following simple way: once we have our narrative on lock, we switch our focus to the lower time frames, such as  M15, M5, and M1. These lower time frames are excellent for intraday trading and offer possible trading setups almost daily.

Price simply works this way: it is first determined by time, the prime trading hours of major sessions. We may witness what we call a Judas swing or liquidity grab on recent lows if narrative is bullish. After the liquidity grab and fake breakout, price will leave that area with power and speed, which is a strong determiner that it was indeed a liquidity grab. Sometimes as price does this it may create an imbalance, which may be used to enter the new trading positions. The appearance of an imbalance is a great indicator of a liquidity grab.

So, in short the EA will identify each scenario as it occurs, evaluate and determine what is really taking place there before deciding if it is a liquidity grab or market structure shift occurring. Once it determines what scenario is at play, it will execute according to the rules of engagement pertaining to that particular scenario.

Image showing liquidity purge



Automating trading decisions with the Liquidity Grab expert advisor in MQL5

To automate, illustrate, and implement this strategy, I have created:

  • A liquidity purge expert advisor to analyze trends, narratives and execute possible trade entry points only in the direction of the trend and narrative after liquidity purges, incorporating trailing stop loss and moving averages to aid in trend detection.

Decision-making process

This expert advisor’s decisions (trend detection, liquidity detection, price action detection, and trade execution) are driven by the following logic:

Source code of the Liquidity Grab expert advisor:

This expert advisor detects potential trade signals that align with the overall trend direction and narrative and waits for trade setups only after liquidity purges occur, avoiding positions opposite to the trend or without a preceding liquidity purge. It also utilizes exponential and simple moving averages to get the general trend direction to make trade entries and applies a trailing stop-loss logic to determine stop-loss levels and protect capital when price moves in its favor. It includes risk management (1% risk per trade). 

Input Parameters: Configurable Trading Settings

The input parameters allow users to tailor the EA’s behavior for GBPJPY, GBPUSD and EURUSD trading without modifying the core code. `LotSize` is set to 0.1, a conservative size suitable for GBP pairs to limit risk in their moderate volatility (100–300 pips daily). `LookbackBars` (3) defines the window for identifying recent highs/lows on H1/H4/D1, balancing responsiveness with reliability. `StopLossPips` (150) and `TakeProfitPips` (600) are adjusted for GBPJPY, GBPUSD/EURUSD’s typical ranges, targeting a 1:4 risk-to-reward ratio to capitalize on strong trends post-purge. `TrailingStopPips` (100) ensures profits are locked in during GBPJPY’s sharper moves. `EngulfingMinBodyRatio` (0.3) ensures the engulfing candle’s body is significant relative to the previous candle, filtering weak signals.

`TradeTimeframe` (H1) and `ConfirmationTimeframe` (M15) allow flexible purge detection and faster entry confirmation, respectively. `MaxCandlesPostPurge` (3) limits the window for engulfing signals post-purge, avoiding late entries. `VolumeThreshold` (1.0) requires a volume spike to confirm liquidity, critical for GBPUSD’s news-driven moves. UseTrendFilter` (true) and `SMAPeriod` (50) enable a 50-period SMA to ensure trades align with the broader trend, reducing false breakouts in choppy GBP markets.

//+-------------------------------------------------------------------------------+
//|                 Liquidity GrabEA.mq5                                          |
//| Expert Advisor for MetaTrader 5                                               |
//| Description:Trades based on liquidity grabs and market structure shifts, waits|
//| for trade set ups after liquidity grab, with flexible execution.              |
//+-------------------------------------------------------------------------------+
#property copyright "Eugene Mmene"
#property link "https://www.EMcapital.com"
#property version "1.10"
//--- Input parameters
input double LotSize = 0.05;                             // Lot size for trades
input int LookbackBars = 1;                              // Number of bars to check for highs/lows
input double StopLossPips = 20.0;                        // Stop loss in pips (adjusted for GOLD#)
input double TakeProfitPips = 60.0;                      // Take profit in pips (adjusted for GOLD#)
input double TrailingStopPips = 30.0;                    // Trailing stop in pips
input double BreakevenPips = 20.0;                       // Move to BE after 30 pips profit
input double EngulfingMinBodyRatio = 0.5;                // Min body ratio for engulfing candle
input ENUM_TIMEFRAMES TradeTimeframe = PERIOD_M15;       // Primary timeframe for trading
input ENUM_TIMEFRAMES ConfirmationTimeframe = PERIOD_M5; // Lower timeframe for engulfing
input ENUM_TIMEFRAMES TrendTimeframe = PERIOD_H1;        // Trend from H1 SMA
input int MaxCandlesPostPurge = 5;                       // Max H1 candles to wait for engulfing
input double VolumeThreshold = 1.2;                      // Volume multiplier for liquidity confirmation
input bool UseTrendFilter = true;                        // Use SMA trend filter
input int SMAPeriod = 50;                                // SMA period for trend filter

OnInit Function: Setup and Validation for Robust Trading

The `OnInit` function is executed once when the EA is loaded onto the chart, ensuring proper setup for trading GBPUSD, GBPJPY, and EURUSD. It validates the `TradeTimeframe` (H1, H4, or D1) and `ConfirmationTimeframe` (M15 or M30) to ensure compatibility with the strategy’s multi-timeframe approach, rejecting invalid inputs to prevent errors. The function retrieves the symbol’s contract size using `SymbolInfoDouble`, critical for accurate position sizing in GBP pairs, where pip values differ (e.g., GBPJPY’s pip value is smaller due to JPY’s scale).

Global variables tracking highs/lows (`lastHighH1`, `lastLowH1`, etc.), candle times, and trade status are initialized to zero or false, resetting the EA’s state. The contract size check ensures the EA can calculate correct lot sizes, especially important for GBPJPY’s higher pip value sensitivity. A successful initialization logs the timeframe and contract size, confirming readiness for trading. This setup ensures the EA is robust and tailored to the specific market dynamics of EURUSD/GBPUSD/GBPJPY, unlike gold’s broader price swings.   

int OnInit()
{
   if(TradeTimeframe != PERIOD_M15 && TradeTimeframe != PERIOD_M30)
   {
      Print("Invalid trade timeframe. Use M15 or M30.");
      return(INIT_PARAMETERS_INCORRECT);
   }
   if(ConfirmationTimeframe != PERIOD_M5 && ConfirmationTimeframe != PERIOD_M15)
   {
      Print("Invalid confirmation timeframe. Use M5 or M15.");
      return(INIT_PARAMETERS_INCORRECT);
   }
  
   contractSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
   if(contractSize == 0)
   {
      Print("Failed to get contract size for ", _Symbol);
      return(INIT_PARAMETERS_INCORRECT);
   }
  
   lastHigh = 0; lastLow = 0;
   lastCandleTime = 0;
   purgeDetected = false;
   lastPurgeTime = 0;
  
   Print("EA Initialized on ", EnumToString(TradeTimeframe), ", Confirmation TF: ", EnumToString(ConfirmationTimeframe), ", Contract Size: ", contractSize);
   return(INIT_SUCCEEDED);
}

IsNewCandle Function: Trigger Logic on New Bar  

The `IsNewCandle` function checks if a new candle has formed on the primary trade timeframe (default H1) by comparing the current candle’s timestamp (`iTime`) with the last recorded candle time (`lastCandleTime`). If a new candle is detected, `lastCandleTime` is updated, and the function returns `true`, triggering the main trading logic in `OnTick`. This ensures the EA evaluates trade conditions only once per H1 candle, reducing processing overhead and preventing redundant checks during volatile intrabar movements in GBPUSD/GBPJPY. By focusing on new bars, the EA aligns with the strategy’s reliance on complete candle data for purge detection and engulfing patterns, critical for capturing significant price moves in GBP pairs’ trend-driven markets. This function is lightweight yet essential for maintaining the EA’s efficiency and accuracy.

bool IsNewCandle()
{
   datetime currentCandleTime = iTime(_Symbol, TradeTimeframe, 0);
   if(currentCandleTime != lastCandleTime)
   {
      lastCandleTime = currentCandleTime;
      return true;
   }
   return false;
}

UpdateHighsLows Function: Monitor Liquidity Purge Levels

The `UpdateHighsLows` function tracks the highest highs and lowest lows over the past `LookbackBars` (default 3) on H1, H4, and D1 timeframes, storing them in global variables (`lastHighH1`, `lastLowH1`, etc.). It uses `CopyRates` to fetch historical price data, with `ArraySetAsSeries` ensuring the most recent candle is at index 0. For each timeframe, it initializes the high/low with the first candle’s values and updates them by iterating through the lookback period, capturing the extreme prices.

This function is critical for detecting liquidity purges—when the current H1 candle breaks above a previous high or below a low on any timeframe, signaling a potential trend reversal or continuation in EURUSD GBPUSD, or GBPJPY. The multi-timeframe approach ensures the EA captures significant liquidity sweeps, such as those triggered by stop hunts in GBPJPY’s volatile moves. Error handling logs failures to load rates, ensuring robustness. This function lays the foundation for the strategy’s purge-based entry logic, tailored to GBP pairs’ sensitivity to key support/resistance levels.

void UpdateHighsLows()
{
   // H1
   MqlRates ratesH1[];
   ArraySetAsSeries(ratesH1, true);
   if(CopyRates(_Symbol, PERIOD_H1, 1, LookbackBars, ratesH1) >= LookbackBars)
   {
      lastHighH1 = ratesH1[0].high;
      lastLowH1 = ratesH1[0].low;
      for(int i = 1; i < LookbackBars; i++)
      {
         if(ratesH1[i].high > lastHighH1) lastHighH1 = ratesH1[i].high;
         if(ratesH1[i].low < lastLowH1) lastLowH1 = ratesH1[i].low;
      }
   }
   else Print("Failed to load H1 rates");
   
   // H4
   MqlRates ratesH4[];
   ArraySetAsSeries(ratesH4, true);
   if(CopyRates(_Symbol, PERIOD_H4, 1, LookbackBars, ratesH4) >= LookbackBars)
   {
      lastHighH4 = ratesH4[0].high;
      lastLowH4 = ratesH4[0].low;
      for(int i = 1; i < LookbackBars; i++)
      {
         if(ratesH4[i].high > lastHighH4) lastHighH4 = ratesH4[i].high;
         if(ratesH4[i].low < lastLowH4) lastLowH4 = ratesH4[i].low;
      }
   }
   else Print("Failed to load H4 rates");
   
   // D1
   MqlRates ratesD1[];
   ArraySetAsSeries(ratesD1, true);
   if(CopyRates(_Symbol, PERIOD_D1, 1, LookbackBars, ratesD1) >= LookbackBars)
   {
      lastHighD1 = ratesD1[0].high;
      lastLowD1 = ratesD1[0].low;
      for(int i = 1; i < LookbackBars; i++)
      {
         if(ratesD1[i].high > lastHighD1) lastHighD1 = ratesD1[i].high;
         if(ratesD1[i].low < lastLowD1) lastLowD1 = ratesD1[i].low;
      }
   }
   else Print("Failed to load D1 rates");
}

IsVolumeSpike Function: Validate Trade Liquidity

The `IsVolumeSpike` function confirms trade setups by checking if the current candle’s tick volume exceeds the average volume over `LookbackBars` (3) by a factor of `VolumeThreshold` (1.0). It uses `CopyRates` to fetch recent candles on the trade timeframe (H1), calculating the average volume of valid bars (those with volume > 1 to avoid data issues). If no valid bars exist, it bypasses the check to prevent stalling, logging a warning for debugging.

For EURUSD, GBPUSD, and GBPJPY, high volume often accompanies significant price moves, such as post-news breakouts or liquidity sweeps, making this filter crucial for validating purges. The function logs current and average volumes for transparency. By ensuring volume confirmation, the EA avoids low-liquidity setups that lead to false signals in GBP pairs, enhancing trade quality and profitability in trend-driven markets like GBPJPY.

bool IsVolumeSpike(long currentVolume)
{
   double avgVolume = 0;
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   if(CopyRates(_Symbol, TradeTimeframe, 1, LookbackBars, rates) < LookbackBars)
   {
      Print("Failed to load rates for volume check");
      return true;
   }
   
   int validBars = 0;
   for(int i = 0; i < LookbackBars; i++)
   {
      if(rates[i].tick_volume > 1)
      {
         avgVolume += rates[i].tick_volume;
         validBars++;
      }
   }
   if(validBars == 0)
   {
      Print("Warning: No valid volume data. Average volume is 0. Bypassing volume check.");
      return true;
   }
   avgVolume /= validBars;
   
   Print("Current volume: ", currentVolume, ", Average volume: ", avgVolume, ", Valid bars: ", validBars);
   return currentVolume >= VolumeThreshold * avgVolume;
}

IsBullishTrend Function: Ensure Trend Alignment

The `IsBullishTrend` function uses a 50-period Simple Moving Average (SMA) on the trade timeframe (H1) to determine the market’s trend direction. It creates an SMA handle with `iMA`, retrieves the latest SMA value via `CopyBuffer`, and compares it to the current closing price (`iClose`). If the price is above the SMA, the function returns `true`, indicating a bullish trend; otherwise, it implies a bearish or neutral trend.

When `UseTrendFilter` is enabled (default true), this function ensures buy trades are only taken in bullish conditions and sell trades in bearish conditions, aligning with EURUSD/GBPUSD/GBPJPY’s tendency to follow sustained trends during London/New York sessions. Error handling logs SMA data failures, ensuring reliability. This filter reduces false breakouts in choppy markets, a common issue in GBP pairs, by confirming the broader market context before entering trades post-purge.

bool IsBullishTrend()
{
   double sma[];
   ArraySetAsSeries(sma, true);
   int smaHandle = iMA(_Symbol, TradeTimeframe, SMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
   if(CopyBuffer(smaHandle, 0, 0, 1, sma) < 1)
   {
      Print("Failed to load SMA data");
      return false;
   }
   double currentPrice = iClose(_Symbol, TradeTimeframe, 0);
   Print("Current price: ", currentPrice, ", SMA: ", sma[0]);
   return currentPrice > sma[0];
}

IsBullishEngulfing and IsBearishEngulfing: Confirm Reversal Signals

These functions detect bullish and bearish engulfing candles, key reversal patterns used to confirm entries after a purge. `IsBullishEngulfing` checks if the current candle is bullish (close > open) the previous candle is bearish (close < open) the current candle engulfs the previous one (open ≤ previous close, close ≥ previous open), and the current candle’s body is at least `EngulfingMinBodyRatio` (0.3) times the previous candle’s body. `IsBearishEngulfing` applies the inverse logic for bearish setups. For EURUSD, GBPUSD and GBPJPY, engulfing candles on M15 or H1 post-purge often signal strong reversals or continuations, especially after liquidity sweeps at key levels.

The body ratio check ensures the engulfing candle is significant, filtering out weak patterns in GBP pairs’ volatile sessions. These functions are applied to the last three candles on both H1 and M15, providing flexibility to catch signals on either timeframe, enhancing the EA’s responsiveness to GBP market dynamics.

bool IsBullishEngulfing(MqlRates &current, MqlRates &previous)
{
   double currentBody = MathAbs(current.close - current.open);
   double prevBody = MathAbs(previous.close - previous.open);
   
   if(current.close > current.open && 
      previous.close < previous.open && 
      current.open <= previous.close && 
      current.close >= previous.open && 
      currentBody >= EngulfingMinBodyRatio * prevBody)
   {
      return true;
   }
   return false;
}

bool IsBearishEngulfing(MqlRates &current, MqlRates &previous)
{
   double currentBody = MathAbs(current.close - current.open);
   double prevBody = MathAbs(previous.close - previous.open);
   
   if(current.close < current.open && 
      previous.close > previous.open && 
      current.open >= previous.close && 
      current.close <= previous.open && 
      currentBody >= EngulfingMinBodyRatio * prevBody)
   {
      return true;
   }
   return false;
}

PlaceTrade Function: Execute Market Orders with SL/TP

The `PlaceTrade` function executes buy or sell orders when a valid signal is confirmed (purge + engulfing + volume + trend). It constructs an `MqlTradeRequest` with the symbol, fixed `LotSize` (0.1), order type (buy/sell), and entry price. Stop-loss is set at 150 pips and take-profit at 600 pips, adjusted for EURUSD GBPUSD, and GBPJPY’s point value (`_Point * 100`) to account for their pip scaling. The `ORDER_FILLING_IOC` (Immediate or Cancel) ensures fast execution, critical in GBPJPY’s fast-moving market.

On success, it logs the trade details, sets `tradePlaced` to true to prevent multiple trades, and resets `purgeDetected` to avoid re-entering on the same signal. On failure, it logs the error code for debugging. This function ensures precise trade execution with predefined risk parameters, optimizing profitability by targeting high-probability setups in GBP pairs’ trend-following behavior.

void PlaceTrade(ENUM_ORDER_TYPE orderType, double price)
{
   MqlTradeRequest request = {};
   MqlTradeResult result = {};
   
   request.action = TRADE_ACTION_DEAL;
   request.symbol = _Symbol;
   request.volume = LotSize;
   request.type = orderType;
   request.price = price;
   request.sl = (orderType == ORDER_TYPE_BUY) ? price - StopLossPips * _Point * 100 : price + StopLossPips * _Point * 100;
   request.tp = (orderType == ORDER_TYPE_BUY) ? price + TakeProfitPips * _Point * 100 : price - TakeProfitPips * _Point * 100;
   request.type_filling = ORDER_FILLING_IOC;
   
   if(OrderSend(request, result))
   {
      Print("Trade placed successfully: ", orderType == ORDER_TYPE_BUY ? "BUY" : "SELL", " at ", price, " SL: ", request.sl, " TP: ", request.tp);
      tradePlaced = true;
      purgeDetected = false; // Reset purge after trade
   }
   else
   {
      Print("Trade failed: ", result.retcode);
   }
}

ManageTrailingStop Function: Protect Profits with Trailing Stops

The `ManageTrailingStop` function dynamically adjusts the stop-loss of open positions to lock in profits as the price moves favorably. It checks if a position exists for the symbol using `PositionSelect`. For buy positions, it calculates a new stop-loss at the current bid price minus `TrailingStopPips` (100 pips), adjusted for GBPUSD EURUSD, and GBPJPY’s point value. The new stop-loss is applied only if it’s higher than the current stop-loss and above the open price, ensuring breakeven or profit. For sell positions, it sets the stop-loss above the current ask price, applying it if lower than the current stop-loss (or if none exists) and below the open price.

The function uses `MqlTradeRequest` with `TRADE_ACTION_SLTP` to update the stop-loss, preserving the take-profit. This trailing mechanism is crucial for GBPJPY’s volatile swings, allowing the EA to capture large moves while protecting gains. Error handling logs failed updates, ensuring transparency. This function enhances profitability by reducing the risk of giving back profits in GBP pairs’ trend-driven markets.

void ManageTrailingStop()
{
   if(!PositionSelect(_Symbol)) return;
   
   double currentPrice = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? 
                         SymbolInfoDouble(_Symbol, SYMBOL_BID) : 
                         SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double currentSL = PositionGetDouble(POSITION_SL);
   
   double newSL = 0;
   if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
   {
      newSL = currentPrice - TrailingStopPips * _Point * 100;
      if(newSL > currentSL && newSL > openPrice)
      {
         MqlTradeRequest request = {};
         MqlTradeResult result = {};
         request.action = TRADE_ACTION_SLTP;
         request.position = PositionGetInteger(POSITION_TICKET);
         request.symbol = _Symbol;
         request.sl = newSL;
         request.tp = PositionGetDouble(POSITION_TP);
         if(OrderSend(request, result))
            Print("Trailing stop updated for BUY: ", newSL);
         else
            Print("Failed to update trailing stop: ", result.retcode);
      }
   }
   else // POSITION_TYPE_SELL
   {
      newSL = currentPrice + TrailingStopPips * _Point * 100;
      if((newSL < currentSL || currentSL == 0) && newSL < openPrice)
      {
         MqlTradeRequest request = {};
         MqlTradeResult result = {};
         request.action = TRADE_ACTION_SLTP;
         request.position = PositionGetInteger(POSITION_TICKET);
         request.symbol = _Symbol;
         request.sl = newSL;
         request.tp = PositionGetDouble(POSITION_TP);
         if(OrderSend(request, result))
            Print("Trailing stop updated for SELL: ", newSL);
         else
            Print("Failed to update trailing stop: ", result.retcode);
      }
   }
}

OnTick Function: Core Trading Logic for Purge and Engulfing

The `OnTick` function is the EA’s main decision-making hub, executed on every price tick but processing only on new H1 candles via `IsNewCandle`. It resets `tradePlaced` if no positions are open, ensuring one trade per purge cycle. Trailing stops are managed for open positions using `ManageTrailingStop`. The function loads candle data for H1 and M15 using `CopyRates`, checking the current H1 candle against previous highs/lows (updated via `UpdateHighsLows`) to detect purges—price breaking above H1/H4/D1 highs or below lows, signaling liquidity sweeps common in EURUSD, GBPUSD/GBPJPY during key sessions. If a purge is detected, `purgeDetected` is set, and `lastPurgeTime` tracks the window (up to 3 H1 candles).

Volume is validated with `IsVolumeSpike`, and the trend is checked with `IsBullishTrend` (if enabled). Engulfing patterns are identified on both H1 and M15, allowing flexible confirmation. A buy trade is triggered if a high purge is followed by a bullish engulfing candle (H1 or M15), high volume, and a bullish trend; a sell trade requires a low purge, bearish engulfing, high volume, and a bearish trend. Extensive logging provides transparency for debugging. This logic ensures high-probability trades in GBP pairs by combining multi-timeframe purges with robust confirmation signals, optimizing for their trend-following nature.

void OnTick()
{
   // Check if a new candle has formed on the trade timeframe
   if(!IsNewCandle()) return;
   
   // Reset tradePlaced if no open positions
   if(!PositionSelect(_Symbol)) tradePlaced = false;
   
   // Manage trailing stop for open positions
   ManageTrailingStop();
   
   // Get current and previous candle data for trade timeframe
   MqlRates ratesH1[];
   ArraySetAsSeries(ratesH1, true);
   if(CopyRates(_Symbol, TradeTimeframe, 0, 4, ratesH1) < 4)
   {
      Print("Failed to load H1 rates data");
      return;
   }
   
   // Get candle data for confirmation timeframe (M15)
   MqlRates ratesM15[];
   ArraySetAsSeries(ratesM15, true);
   if(CopyRates(_Symbol, ConfirmationTimeframe, 0, 4, ratesM15) < 4)
   {
      Print("Failed to load M15 rates data");
      return;
   }
   
   // Current H1 candle (index 0)
   double currentOpenH1 = ratesH1[0].open;
   double currentCloseH1 = ratesH1[0].close;
   double currentHighH1 = ratesH1[0].high;
   double currentLowH1 = ratesH1[0].low;
   long currentVolumeH1 = ratesH1[0].tick_volume;
   datetime currentTimeH1 = ratesH1[0].time;
   
   // Update highs and lows for all timeframes
   UpdateHighsLows();
   
   // Check for purge (liquidity sweep) on any timeframe
   bool highPurgedH1 = (currentHighH1 > lastHighH1 && lastHighH1 > 0);
   bool highPurgedH4 = (currentHighH1 > lastHighH4 && lastHighH4 > 0);
   bool highPurgedD1 = (currentHighH1 > lastHighD1 && lastHighD1 > 0);
   bool lowPurgedH1 = (currentLowH1 < lastLowH1 && lastLowH1 > 0);
   bool lowPurgedH4 = (currentLowH1 < lastLowH4 && lastLowH4 > 0);
   bool lowPurgedD1 = (currentLowH1 < lastLowD1 && lastLowD1 > 0);
   bool highPurged = highPurgedH1 || highPurgedH4 || highPurgedD1;
   bool lowPurged = lowPurgedH1 || lowPurgedH4 || lowPurgedD1;
   
   // Update purge status
   if(highPurged || lowPurged)
   {
      purgeDetected = true;
      lastPurgeTime = currentTimeH1;
      highPurge = highPurged;
   }
   
   // Check if within the post-purge window
   bool withinPurgeWindow = false;
   if(purgeDetected)
   {
      int candlesSincePurge = iBarShift(_Symbol, TradeTimeframe, lastPurgeTime, true);
      withinPurgeWindow = candlesSincePurge <= MaxCandlesPostPurge;
      if(!withinPurgeWindow)
      {
         purgeDetected = false; // Reset if window expires
         Print("Purge window expired: ", candlesSincePurge, " candles since last purge");
      }
   }
   
   // Check volume for liquidity confirmation
   bool volumeConfirmed = IsVolumeSpike(currentVolumeH1);
   if(currentVolumeH1 <= 1) 
   {
      Print("Warning: Tick volume is ", currentVolumeH1, ". Possible data issue. Bypassing volume check.");
      volumeConfirmed = true;
   }
   
   // Check trend with SMA
   bool isBullishTrend = UseTrendFilter ? IsBullishTrend() : true;
   
   // Check for engulfing candles on H1 (current + previous 2 candles)
   bool bullishEngulfingH1 = false, bearishEngulfingH1 = false;
   for(int i = 0; i < 3; i++)
   {
      if(IsBullishEngulfing(ratesH1[i], ratesH1[i+1]))
         bullishEngulfingH1 = true;
      if(IsBearishEngulfing(ratesH1[i], ratesH1[i+1]))
         bearishEngulfingH1 = true;
   }
   
   // Check for engulfing candles on M15 (current + previous 2 candles)
   bool bullishEngulfingM15 = false, bearishEngulfingM15 = false;
   for(int i = 0; i < 3; i++)
   {
      if(IsBullishEngulfing(ratesM15[i], ratesM15[i+1]))
         bullishEngulfingM15 = true;
      if(IsBearishEngulfing(ratesM15[i], ratesM15[i+1]))
         bearishEngulfingM15 = true;
   }
   
   // Trade logic
   if(purgeDetected && withinPurgeWindow && !tradePlaced)
   {
      if(highPurge && (bullishEngulfingH1 || bullishEngulfingM15) && volumeConfirmed && isBullishTrend)
      {
         Print("Buy signal: High purged, bullish engulfing on ", bullishEngulfingH1 ? "H1" : "M15", ", volume confirmed, bullish trend");
         PlaceTrade(ORDER_TYPE_BUY, currentCloseH1);
      }
      else if(!highPurge && (bearishEngulfingH1 || bearishEngulfingM15) && volumeConfirmed && !isBullishTrend)
      {
         Print("Sell signal: Low purged, bearish engulfing on ", bearishEngulfingH1 ? "H1" : "M15", ", volume confirmed, bearish trend");
         PlaceTrade(ORDER_TYPE_SELL, currentCloseH1);
      }
   }
}

Installation and backtesting: Compile on MetaEditor and attach to chart. Backtesting on GBPJPY, H1 (2025 NOV-2026 FEB) with 1% risk. 



Strategy testing

Strategy testing on the Liquidity Grab EA

The strategy works best on most, if not all, pairs due to its core logic or quick adaptability to trend trading and narrative following. It uses a liquidity purge concept for trade setups and high volatility, which are beneficial and essential for most trading strategies. We will test this strategy by trading GBPJPY from January 1, 2025, to February 8, 2026, on the 60-minute (H1) timeframe. Here are the parameters I have chosen for this strategy. 

GBPJPY

Input data

Input data


Strategy tester results

Upon testing on the strategy tester, here are the results of how it works, analyzes, and Performs.

Strategy tester results on Liquidity Grab EA

Balance/Equity graph: 

GBPJPY

results graph

Backtest results:

GBPJPY

Backtest results


Summary

I wrote this article to try to explain a MetaTrader 5 Expert Advisor that is specifically tailored for trend following, differentiating between liquidity grabs and market structure shifts before utilizing and optimizing whichever it finds, and also incorporates trade and risk management techniques to systematically reduce risk, exposure, and human errors while identifying and executing high-probability trading setups on GBPJPY and also possible exit points using the same trade and risk management protocol.

This Expert Advisor is one of the most simple and yet powerful trading Expert Advisors, utilizing trend-based concepts and liquidity purges used to capture possible trade price entries and trend shifts. The robust and well-adaptive risk and trade management logic helps the Expert Advisor perform at an optimum level and minimize drawdown and trading losses. 

I tested the Expert Advisor on GBPJPY, and it revealed its ability to detect possible trade entries efficiently and aptly on any time frame, but the trade entry point detection is only part of the equation because it has an optimum entry validation strategy built into the core logic that allows execution only if certain criteria are met. As soon as the trades are validated and executed, then the trade and risk management logic is quickly implemented to ensure proper execution until the trade is closed.

To implement this Expert Advisor strategy, configure the input parameters on the Expert Advisor as shown below to get desirable results. The Expert Advisor is designed to scan for liquidity, liquidity purges, and possible trade entries on the set timeframe a trader selects to view, from M15 to D1, ensuring the possible trade entry points align with the trend and moving averages and the average true range for trailing stop-loss. Interested traders should back-test this Expert Advisor on their demo accounts with GBPJPY; it works optimally well and is designed for GBPJPY but may also be applied to GBPUSD, EURUSD, and GOLD.

The main agenda and goal for this Expert Advisor were to optimize it for strictly liquidity-purged trades and narrative-following trading with advanced trade logic and for high-probability setups that occur in any time frame, for depending on a trader's choice, and also incorporate risk management with the implemented trailing stops.

I would also advise traders to regularly review performance logs to refine settings and input parameters depending on one's goals, asset class or risk appetite. Disclaimer: Anybody using this Expert Advisor should first test and start trading on his demo account to master this liquidity purge concept and trading idea approach for consistent profits before risking live funds.


Conclusion

The article highlights the main challenges traders face in determining narrative, liquidity areas, liquidity purges, and trade entry; risk management; trade management; and avoiding drawdowns—and explains how to design an Expert Advisor that simplifies this process and increases the chances of being profitable and executing only high-probability trades. The article further elaborates how trading using setups that occur after liquidity purges and following the narrative in play and ignoring countertrend trade ideas and setups greatly aids in this.  

Many traders lack a clear understanding of how liquidity and liquidity purges as concepts in trading can really have a major positive impact on their performance, especially when combined with proper risk and trade management; it becomes a very major game changer that separates the average from the very best. The proposed Expert Advisor helps enforce discipline and allows traders to validate their trade ideas, position sizing, and setups even if they don’t use its entries directly.

The automated MQL5 Expert Advisor provides:

  • high probability trades from the set ups arising from liquidity purges of the asset being traded;
  • protection from news volatility by blocking trades around news releases;
  • trade entries only on confirmed signals and validated entry logic with dynamic SL/TP;
  • adaptive risk management (reducing lot size during losing streaks, increasing during winning streaks);
  • logging of results for ongoing strategy optimization;
  • strict adherence to first identifying narrative in play and also determine type of set up e.g market structure shift or liquidity purge ;
  • removal of emotional decision-making;
  • automated trade management (SL, TP, partial closes).

Together, these features deliver consistent execution of high-probability trades and optimal risk management, increasing the chance of profitable trading and improving performance for traders.

All code referenced in the article is attached below. The following table describes all the source code files that accompany the article.

File NameDescription:
Liquidity Grab EA.mq5File containing the full source code for the Liquidity Grab EA
Attached files |
Liquidity_Grab.mq5 (16.29 KB)
Features of Custom Indicators Creation Features of Custom Indicators Creation
Creation of Custom Indicators in the MetaTrader trading system has a number of features.
MQL5 Trading Tools (Part 17): Exploring Vector-Based Rounded Rectangles and Triangles MQL5 Trading Tools (Part 17): Exploring Vector-Based Rounded Rectangles and Triangles
In this article, we explore vector-based methods for drawing rounded rectangles and triangles in MQL5 using canvas, with supersampling for anti-aliased rendering. We implement scanline filling, geometric precomputations for arcs and tangents, and border drawing to create smooth, customizable shapes. This approach lays the groundwork for modern UI elements in future trading tools, supporting inputs for sizes, radii, borders, and opacities.
Features of Experts Advisors Features of Experts Advisors
Creation of expert advisors in the MetaTrader trading system has a number of features.
From Basic to Intermediate: Struct (VI) From Basic to Intermediate: Struct (VI)
In this article, we will explore how to approach the implementation of a common structural code base. The goal is to reduce the programming workload and leverage the full potential of the programming language itself—in this case, MQL5.