Code: Select all
//+------------------------------------------------------------------+
//| Liquidity_Sweeps_EQH_EQL.mq5 |
//| Mechanical MT5 Indicator |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_plots 0
input int InpSwingBars = 5; // Fractal Swing Length (Bars left & right)
input int InpHistory = 500; // Number of HTF bars to scan
input int InpPointTolerance = 150; // Tolerance in Points (150 = 15 pips)
input color InpColorD1 = clrCrimson; // D1 EQH/EQL Color
input color InpColorH4 = clrDarkOrange; // H4 EQH/EQL Color
// Structure to store swing data in memory
struct SwingPoint {
datetime time;
double price;
bool used;
};
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit() {
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
ObjectsDeleteAll(0, "EQH_EQL_");
}
//+------------------------------------------------------------------+
//| 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[]) {
// Only recalculate on a new bar to save CPU cycles
if(rates_total == prev_calculated) return rates_total;
ObjectsDeleteAll(0, "EQH_EQL_");
MapEqualLiquidity(PERIOD_D1, "D1", InpColorD1, STYLE_SOLID, 2);
MapEqualLiquidity(PERIOD_H4, "H4", InpColorH4, STYLE_DASH, 1);
ChartRedraw(0);
return(rates_total);
}
//+------------------------------------------------------------------+
//| Core logic to find and pair EQH / EQL pools |
//+------------------------------------------------------------------+
void MapEqualLiquidity(ENUM_TIMEFRAMES tf, string prefix, color clr, int style, int width) {
double h[], l[];
datetime t[];
ArraySetAsSeries(h, true);
ArraySetAsSeries(l, true);
ArraySetAsSeries(t, true);
if(CopyHigh(Symbol(), tf, 0, InpHistory, h) < InpHistory) return;
if(CopyLow(Symbol(), tf, 0, InpHistory, l) < InpHistory) return;
if(CopyTime(Symbol(), tf, 0, InpHistory, t) < InpHistory) return;
SwingPoint highs[];
SwingPoint lows[];
// Step 1: Scan for strict structural extremes and store them
for(int i = InpSwingBars; i < InpHistory - InpSwingBars; i++) {
bool isRes = true;
bool isSup = true;
for(int j = 1; j <= InpSwingBars; j++) {
if(h[i] <= h[i-j] || h[i] <= h[i+j]) isRes = false;
if(l[i] >= l[i-j] || l[i] >= l[i+j]) isSup = false;
}
if(isRes) {
int s = ArraySize(highs);
ArrayResize(highs, s + 1);
highs[s].time = t[i];
highs[s].price = h[i];
highs[s].used = false;
}
if(isSup) {
int s = ArraySize(lows);
ArrayResize(lows, s + 1);
lows[s].time = t[i];
lows[s].price = l[i];
lows[s].used = false;
}
}
double point_size = SymbolInfoDouble(Symbol(), SYMBOL_POINT);
double tolerance = InpPointTolerance * point_size;
// Step 2 & 3: Find EQH pairs (Double/Triple Tops)
// Array is filled from recent (index 0) to older history.
for(int i = 0; i < ArraySize(highs); i++) {
if(highs[i].used) continue;
for(int j = i + 1; j < ArraySize(highs); j++) {
if(!highs[j].used && MathAbs(highs[i].price - highs[j].price) <= tolerance) {
// Pair found! Draw line connecting older point (j) to recent point (i)
DrawLine("EQH_EQL_" + prefix + "_Res_" + TimeToString(highs[i].time), highs[j].time, highs[j].price, highs[i].time, highs[i].price, clr, style, width);
highs[i].used = true;
highs[j].used = true;
break; // Stop after finding the first valid historical pair for this recent swing
}
}
}
// Step 4: Find EQL pairs (Double/Triple Bottoms)
for(int i = 0; i < ArraySize(lows); i++) {
if(lows[i].used) continue;
for(int j = i + 1; j < ArraySize(lows); j++) {
if(!lows[j].used && MathAbs(lows[i].price - lows[j].price) <= tolerance) {
DrawLine("EQH_EQL_" + prefix + "_Sup_" + TimeToString(lows[i].time), lows[j].time, lows[j].price, lows[i].time, lows[i].price, clr, style, width);
lows[i].used = true;
lows[j].used = true;
break;
}
}
}
}
//+------------------------------------------------------------------+
//| Renders a trendline connecting the touches, extended right |
//+------------------------------------------------------------------+
void DrawLine(string name, datetime t1, double p1, datetime t2, double p2, color clr, int style, int width) {
if(ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2)) {
ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_STYLE, style);
ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
ObjectSetInteger(0, name, OBJPROP_BACK, true);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}
}
//+------------------------------------------------------------------+