MQL5 requires strict array management. The logic translates cleanly by setting the arrays AsSeries(true) so the indexing matches MQL4 ([0] is current, [1] is previous). This makes porting micro-structure logic between platforms seamless.
Code: Select all
//+------------------------------------------------------------------+
//| M1_Liquidity_Reclaim.mq5 |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2
#property indicator_label1 "Bullish Reclaim"
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrDodgerBlue
#property indicator_label2 "Bearish Reclaim"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrRed
//--- Inputs
input int LeftBars = 15; // Swing Left (Bars)
input int RightBars = 5; // Swing Right (Bars)
input double ReclaimPct = 0.35; // Min Reclaim % of Candle Range
input bool RequireConf = true; // Require Next Candle Confirmation
input bool UseSession = true; // Filter by Session
input string SessionStart = "08:00"; // Session Start
input string SessionEnd = "16:00"; // Session End
//--- Buffers
double BullBuffer[];
double BearBuffer[];
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BullBuffer, INDICATOR_DATA);
PlotIndexSetInteger(0, PLOT_ARROW, 233);
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
SetIndexBuffer(1, BearBuffer, INDICATOR_DATA);
PlotIndexSetInteger(1, PLOT_ARROW, 234);
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
bool InSession(datetime time)
{
if(!UseSession) return true;
MqlDateTime dt;
TimeToStruct(time, dt);
int startMins = (int)StringToInteger(StringSubstr(SessionStart, 0, 2)) * 60 + (int)StringToInteger(StringSubstr(SessionStart, 3, 2));
int endMins = (int)StringToInteger(StringSubstr(SessionEnd, 0, 2)) * 60 + (int)StringToInteger(StringSubstr(SessionEnd, 3, 2));
int currMins = dt.hour * 60 + dt.min;
if(startMins < endMins) return (currMins >= startMins && currMins <= endMins);
return (currMins >= startMins || currMins <= endMins);
}
//+------------------------------------------------------------------+
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[])
{
if(rates_total < LeftBars + RightBars + 5) return 0;
// Set arrays as series (Index 0 = Current Bar)
ArraySetAsSeries(time, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(close, true);
ArraySetAsSeries(BullBuffer, true);
ArraySetAsSeries(BearBuffer, true);
int limit = rates_total - prev_calculated;
if(prev_calculated == 0)
limit = rates_total - LeftBars - RightBars - 3;
for(int i = limit; i >= 1; i--)
{
BullBuffer[i] = 0.0;
BearBuffer[i] = 0.0;
if(!InSession(time[i])) continue;
double c1_high = high[i+1];
double c1_low = low[i+1];
double c1_close = close[i+1];
double c1_range = c1_high - c1_low;
if(c1_range == 0) c1_range = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
// Find Pivot Low
double lastPL = 0;
for(int j = i + 2 + RightBars; j < rates_total - LeftBars; j++)
{
int lowestIdx = ArrayMinimum(low, j - RightBars, LeftBars + RightBars + 1);
if(lowestIdx == j) { lastPL = low[j]; break; }
}
// Find Pivot High
double lastPH = 0;
for(int j = i + 2 + RightBars; j < rates_total - LeftBars; j++)
{
int highestIdx = ArrayMaximum(high, j - RightBars, LeftBars + RightBars + 1);
if(highestIdx == j) { lastPH = high[j]; break; }
}
// Bullish Reclaim Logic
if(lastPL > 0 && c1_low < lastPL && c1_close > lastPL)
{
if((c1_close - lastPL) >= (c1_range * ReclaimPct))
{
if(!RequireConf || (close[i] > lastPL && close[i] >= c1_close))
{
BullBuffer[i] = low[i] - (_Point * 10);
string objName = "L_Purge_" + IntegerToString(time[i]);
if(ObjectFind(0, objName) < 0) {
ObjectCreate(0, objName, OBJ_TREND, 0, time[i+1], lastPL, time[i], lastPL);
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrDodgerBlue);
ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_DASH);
ObjectSetInteger(0, objName, OBJPROP_RAY_RIGHT, false);
}
}
}
}
// Bearish Reclaim Logic
if(lastPH > 0 && c1_high > lastPH && c1_close < lastPH)
{
if((lastPH - c1_close) >= (c1_range * ReclaimPct))
{
if(!RequireConf || (close[i] < lastPH && close[i] <= c1_close))
{
BearBuffer[i] = high[i] + (_Point * 10);
string objName = "H_Purge_" + IntegerToString(time[i]);
if(ObjectFind(0, objName) < 0) {
ObjectCreate(0, objName, OBJ_TREND, 0, time[i+1], lastPH, time[i], lastPH);
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrRed);
ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_DASH);
ObjectSetInteger(0, objName, OBJPROP_RAY_RIGHT, false);
}
}
}
}
}
return(rates_total);
}