Code: Select all
//+------------------------------------------------------------------+
//| LiquiditySweep.mq5 |
//| MQL5 Translation of Liquidity Sweep Detector |
//+------------------------------------------------------------------+
#property copyright "Custom Indicator"
#property version "1.00"
#property indicator_chart_window
//--- 4 Buffers and 4 Visual Plots
#property indicator_buffers 4
#property indicator_plots 4
//--- Plot 1: Swing High Arrows
#property indicator_label1 "Swing High"
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrDimGray
#property indicator_style1 STYLE_SOLID
//--- Plot 2: Swing Low Arrows
#property indicator_label2 "Swing Low"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrDimGray
#property indicator_style2 STYLE_SOLID
//--- Plot 3: Bearish Sweep (Liquidity Grab Top)
#property indicator_label3 "Bearish Sweep"
#property indicator_type3 DRAW_ARROW
#property indicator_color3 clrRed
#property indicator_style3 STYLE_SOLID
#property indicator_width3 2
//--- Plot 4: Bullish Sweep (Liquidity Grab Bottom)
#property indicator_label4 "Bullish Sweep"
#property indicator_type4 DRAW_ARROW
#property indicator_color4 clrLimeGreen
#property indicator_style4 STYLE_SOLID
#property indicator_width4 2
//--- Inputs
input int InpLeftBars = 5; // Pivot Left Bars
input int InpRightBars = 5; // Pivot Right Bars
//--- Buffers
double SwingHighBuffer[];
double SwingLowBuffer[];
double BearishSweepBuffer[];
double BullishSweepBuffer[];
//--- State variables for real-time projection lines
double lastHigh = 0.0;
double lastLow = 0.0;
datetime lastHighTime = 0;
datetime lastLowTime = 0;
string highLineName = "LiqSweep_HighLine";
string lowLineName = "LiqSweep_LowLine";
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Swing High Configuration
SetIndexBuffer(0, SwingHighBuffer, INDICATOR_DATA);
PlotIndexSetInteger(0, PLOT_ARROW, 116); // Small triangle down
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
// Swing Low Configuration
SetIndexBuffer(1, SwingLowBuffer, INDICATOR_DATA);
PlotIndexSetInteger(1, PLOT_ARROW, 115); // Small triangle up
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
// Bearish Sweep Configuration
SetIndexBuffer(2, BearishSweepBuffer, INDICATOR_DATA);
PlotIndexSetInteger(2, PLOT_ARROW, 226); // Large arrow down
PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, 0.0);
// Bullish Sweep Configuration
SetIndexBuffer(3, BullishSweepBuffer, INDICATOR_DATA);
PlotIndexSetInteger(3, PLOT_ARROW, 225); // Large arrow up
PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, 0.0);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Clean up chart lines on removal
ObjectDelete(0, highLineName);
ObjectDelete(0, lowLineName);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
// Ensure enough bars exist to calculate pivots
if(rates_total <= InpLeftBars + InpRightBars) return(0);
// Configure arrays as time series (Index 0 = current live bar)
ArraySetAsSeries(time, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(close, true);
ArraySetAsSeries(SwingHighBuffer, true);
ArraySetAsSeries(SwingLowBuffer, true);
ArraySetAsSeries(BearishSweepBuffer, true);
ArraySetAsSeries(BullishSweepBuffer, true);
int limit = rates_total - prev_calculated;
// Full chart load or recompile
if(prev_calculated == 0)
{
limit = rates_total - InpLeftBars - InpRightBars - 1;
lastHigh = 0.0;
lastLow = 0.0;
ArrayInitialize(SwingHighBuffer, 0.0);
ArrayInitialize(SwingLowBuffer, 0.0);
ArrayInitialize(BearishSweepBuffer, 0.0);
ArrayInitialize(BullishSweepBuffer, 0.0);
}
else if(limit <= 0)
{
limit = 1; // Standard update for current live bar
}
// Process from older bars to the newest forming bar
for(int i = limit; i >= 0 && !IsStopped(); i--)
{
// Reset current bar buffers
SwingHighBuffer[i] = 0.0;
SwingLowBuffer[i] = 0.0;
BearishSweepBuffer[i] = 0.0;
BullishSweepBuffer[i] = 0.0;
// 1. Identify structural pivots historically
int shift = i + InpRightBars;
if(shift < rates_total - InpLeftBars)
{
bool isHigh = true;
bool isLow = true;
double currentHigh = high[shift];
double currentLow = low[shift];
// Left side check
for(int j = 1; j <= InpLeftBars; j++)
{
if(high[shift + j] > currentHigh) isHigh = false;
if(low[shift + j] < currentLow) isLow = false;
}
// Right side check
for(int j = 1; j <= InpRightBars; j++)
{
if(high[shift - j] >= currentHigh) isHigh = false;
if(low[shift - j] <= currentLow) isLow = false;
}
// Register Swing High and project line
if(isHigh)
{
SwingHighBuffer[shift] = currentHigh;
lastHigh = currentHigh;
lastHighTime = time[shift];
if(ObjectFind(0, highLineName) < 0)
ObjectCreate(0, highLineName, OBJ_TREND, 0, lastHighTime, lastHigh, time[i], lastHigh);
else
{
ObjectSetInteger(0, highLineName, OBJPROP_TIME, 0, lastHighTime);
ObjectSetDouble(0, highLineName, OBJPROP_PRICE, 0, lastHigh);
ObjectSetDouble(0, highLineName, OBJPROP_PRICE, 1, lastHigh);
}
ObjectSetInteger(0, highLineName, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(0, highLineName, OBJPROP_STYLE, STYLE_DASH);
ObjectSetInteger(0, highLineName, OBJPROP_COLOR, clrRed);
}
// Register Swing Low and project line
if(isLow)
{
SwingLowBuffer[shift] = currentLow;
lastLow = currentLow;
lastLowTime = time[shift];
if(ObjectFind(0, lowLine mName) < 0) // Typo fix to lowLineName below
ObjectCreate(0, lowLineName, OBJ_TREND, 0, lastLowTime, lastLow, time[i], lastLow);
else
{
ObjectSetInteger(0, lowLineName, OBJPROP_TIME, 0, lastLowTime);
ObjectSetDouble(0, lowLineName, OBJPROP_PRICE, 0, lastLow);
ObjectSetDouble(0, lowLineName, OBJPROP_PRICE, 1, lastLow);
}
ObjectSetInteger(0, lowLineName, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(0, lowLineName, OBJPROP_STYLE, STYLE_DASH);
ObjectSetInteger(0, lowLineName, OBJPROP_COLOR, clrLimeGreen);
}
}
// 2. Evaluate current candle against established liquidity pools
double atrOffset = (high[i] - low[i]) * 0.2; // Visual gap for arrows
// Bearish Sweep: Grab liquidity above the structural high, close below
if(lastHigh > 0 && high[i] > lastHigh && close[i] < lastHigh)
{
BearishSweepBuffer[i] = high[i] + atrOffset;
}
// Bullish Sweep: Grab liquidity below the structural low, close above
if(lastLow > 0 && low[i] < lastLow && close[i] > lastLow)
{
BullishSweepBuffer[i] = low[i] - atrOffset;
}
// 3. Anchor the dashed lines to the current forming bar
if(lastHigh > 0 && ObjectFind(0, highLineName) >= 0)
ObjectSetInteger(0, highLineName, OBJPROP_TIME, 1, time[i]);
if(lastLow > 0 && ObjectFind(0, lowLineName) >= 0)
ObjectSetInteger(0, lowLineName, OBJPROP_TIME, 1, time[i]);
}
return(rates_total);
}
//+------------------------------------------------------------------+