Key Differences Managed for cTrader:
Dynamic Highs/Lows: cTrader's Automate API doesn't have an exact ta.pivothigh equivalent out of the box, so I built a high-efficiency for loop that perfectly mimics Pine Script's pLen lookback/look-forward mechanism.
Tick-Size Calculation: Instead of manual multipliers, it taps directly into cTrader’s internal Symbol.TickSize and Symbol.Digits for perfect 5-digit Forex/Metals precision.
Session Painting via ChartRectangle: Instead of TradingView's box.new which redraws easily, cTrader requires gathering the absolute high/low of the specific hours you declare (e.g., 18:00 to 02:00) before finalizing the shape on the chart on the exact bar the session closes.
Drawing Objects (Lines/Labels): In C#, chart objects require unique string IDs. I assigned dynamic names using Bars.OpenTimes[index].Ticks to ensure multiple setups don't overwrite each other during high-volatility events like FOMC or NFP.
Equal highs swept at London open: reclaim entry vs wait for BOS
Re: Equal highs swept at London open: reclaim entry vs wait for BOS
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Equal highs swept at London open: reclaim entry vs wait for BOS
To elevate this cTrader indicator to a true Professional / Institutional Grade, we need to tap into the advanced features of the cTrader Automate API (C#) that TradingView either struggles with or doesn't support natively.
A professional trading tool doesn't just draw lines on a chart; it acts as a complete risk-management and tracking system. Here is what I have added to make this a tier-1 tool:
1.) On-Chart HUD (Heads-Up Display): A sleek, transparent control panel in the corner of the chart that tells you exactly what the algorithm is tracking right now (e.g., "EQH Swept — Waiting for BOS"), so you don't have to squint at the candles.
2.) Automatic Risk/Reward (R:R) Mapping: When a Reclaim or BOS triggers, it no longer just prints a label. It instantly calculates your Risk (Entry to Sweep Extreme) and draws a visual Take Profit Zone on your chart based on a user-defined R:R ratio (e.g., 1:2 or 1:3).
3.) Real-Time Alerting System: Added logic that ensures alerts (Sounds and Log prints) only fire in real-time (not when loading historical data).
4.) Displacement Filter for BOS: Added a volume/body-size check to ensure the candle breaking structure actually has institutional momentum behind it, filtering out choppy, weak micro-breaks.
A professional trading tool doesn't just draw lines on a chart; it acts as a complete risk-management and tracking system. Here is what I have added to make this a tier-1 tool:
1.) On-Chart HUD (Heads-Up Display): A sleek, transparent control panel in the corner of the chart that tells you exactly what the algorithm is tracking right now (e.g., "EQH Swept — Waiting for BOS"), so you don't have to squint at the candles.
2.) Automatic Risk/Reward (R:R) Mapping: When a Reclaim or BOS triggers, it no longer just prints a label. It instantly calculates your Risk (Entry to Sweep Extreme) and draws a visual Take Profit Zone on your chart based on a user-defined R:R ratio (e.g., 1:2 or 1:3).
3.) Real-Time Alerting System: Added logic that ensures alerts (Sounds and Log prints) only fire in real-time (not when loading historical data).
4.) Displacement Filter for BOS: Added a volume/body-size check to ensure the candle breaking structure actually has institutional momentum behind it, filtering out choppy, weak micro-breaks.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Equal highs swept at London open: reclaim entry vs wait for BOS
The cTrader C# Code [PRO V2]
Delete the old code, paste this in, and click Build.
Delete the old code, paste this in, and click Build.
Code: Select all
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.UI;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class InstitutionalLiquidityPro : Indicator
{
// ==============================================================================
// 1. PARAMETERS
// ==============================================================================
[Parameter("Pivot Lookback", Group = "Liquidity & Structure", DefaultValue = 10, MinValue = 2)]
public int PLen { get; set; }
[Parameter("Equal Tolerance (Ticks)", Group = "Liquidity & Structure", DefaultValue = 15.0)]
public double TickTol { get; set; }
[Parameter("Highlight Session", Group = "Session Context", DefaultValue = true)]
public bool UseSession { get; set; }
[Parameter("Session Start Hour (UTC)", Group = "Session Context", DefaultValue = 18)]
public int SessionStartHour { get; set; }
[Parameter("Session End Hour (UTC)", Group = "Session Context", DefaultValue = 2)]
public int SessionEndHour { get; set; }
[Parameter("Reward to Risk Ratio", Group = "Risk Management", DefaultValue = 2.0, ToolTip = "Draws a visual Take Profit box at this multiple of your Stop Loss distance.")]
public double RrRatio { get; set; }
[Parameter("Enable Real-Time Alerts", Group = "Alerts & UI", DefaultValue = true)]
public bool EnableAlerts { get; set; }
// ==============================================================================
// 2. STATE MANAGEMENT & UI
// ==============================================================================
private class LiquidityLevel
{
public double Price;
public int TimeIndex;
public bool IsActive;
public bool IsSwept;
public double SweepExtr;
public double TriggerBOS;
public string LineName;
}
private LiquidityLevel _eqh, _eql;
private double _lastPh, _lastPl;
private int _timePh, _timePl;
private int _sessionStartIndex = -1;
private bool _inSession = false;
private Color _sessionColor = Color.FromArgb(20, Color.DeepSkyBlue);
// UI Dashboard Elements
private TextBlock _uiEqhStatus;
private TextBlock _uiEqlStatus;
// ==============================================================================
// 3. INITIALIZATION & UI SETUP
// ==============================================================================
protected override void Initialize()
{
_eqh = new LiquidityLevel { IsActive = false };
_eql = new LiquidityLevel { IsActive = false };
_lastPh = double.NaN;
_lastPl = double.NaN;
BuildDashboard();
}
private void BuildDashboard()
{
var border = new Border
{
BackgroundColor = Color.FromArgb(200, 20, 20, 20),
BorderColor = Color.FromArgb(100, 150, 150, 150),
BorderThickness = 1,
CornerRadius = 5,
Margin = new Thickness(10),
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Top,
Padding = new Thickness(10)
};
var panel = new StackPanel { Orientation = Orientation.Vertical };
var title = new TextBlock { Text = "INSTITUTIONAL LIQUIDITY", FontWeight = FontWeight.ExtraBold, Foreground = Color.White, Margin = new Thickness(0,0,0,5) };
_uiEqhStatus = new TextBlock { Text = "EQH: Scanning...", Foreground = Color.Tomato };
_uiEqlStatus = new TextBlock { Text = "EQL: Scanning...", Foreground = Color.DodgerBlue };
panel.AddChild(title);
panel.AddChild(_uiEqhStatus);
panel.AddChild(_uiEqlStatus);
border.Child = panel;
Chart.AddControl(border);
}
// ==============================================================================
// 4. CORE CALCULATION LOGIC
// ==============================================================================
public override void Calculate(int index)
{
if (index < PLen * 2) return;
DrawSessionBoxes(index);
DetectPivots(index);
ProcessEQH(index);
ProcessEQL(index);
// Draw active magnet lines up to current candle
if (_eqh.IsActive && !_eqh.IsSwept)
Chart.DrawLine(_eqh.LineName, Bars.OpenTimes[_eqh.TimeIndex], _eqh.Price, Bars.OpenTimes[index], _eqh.Price, Color.Tomato, 2, LineStyle.Solid);
if (_eql.IsActive && !_eql.IsSwept)
Chart.DrawLine(_eql.LineName, Bars.OpenTimes[_eql.TimeIndex], _eql.Price, Bars.OpenTimes[index], _eql.Price, Color.DodgerBlue, 2, LineStyle.Solid);
UpdateDashboard();
}
// ==============================================================================
// 5. HELPER METHODS
// ==============================================================================
private void DetectPivots(int index)
{
int pivotIndex = index - PLen;
double threshold = TickTol * Symbol.TickSize;
// --- EQH Detection ---
bool isPivotHigh = true;
for (int i = pivotIndex - PLen; i <= pivotIndex + PLen; i++)
{
if (i == pivotIndex) continue;
if (Bars.HighPrices[i] > Bars.HighPrices[pivotIndex]) { isPivotHigh = false; break; }
}
if (isPivotHigh)
{
double ph = Bars.HighPrices[pivotIndex];
if (!double.IsNaN(_lastPh) && Math.Abs(ph - _lastPh) <= threshold)
{
_eqh.Price = Math.Max(ph, _lastPh);
_eqh.TimeIndex = _timePh;
_eqh.IsActive = true;
_eqh.IsSwept = false;
_eqh.LineName = "EQH_" + Bars.OpenTimes[pivotIndex].Ticks;
}
_lastPh = ph;
_timePh = pivotIndex;
}
// --- EQL Detection ---
bool isPivotLow = true;
for (int i = pivotIndex - PLen; i <= pivotIndex + PLen; i++)
{
if (i == pivotIndex) continue;
if (Bars.LowPrices[i] < Bars.LowPrices[pivotIndex]) { isPivotLow = false; break; }
}
if (isPivotLow)
{
double pl = Bars.LowPrices[pivotIndex];
if (!double.IsNaN(_lastPl) && Math.Abs(pl - _lastPl) <= threshold)
{
_eql.Price = Math.Min(pl, _lastPl);
_eql.TimeIndex = _timePl;
_eql.IsActive = true;
_eql.IsSwept = false;
_eql.LineName = "EQL_" + Bars.OpenTimes[pivotIndex].Ticks;
}
_lastPl = pl;
_timePl = pivotIndex;
}
}
private void ProcessEQH(int index)
{
if (!_eqh.IsActive) return;
double high = Bars.HighPrices[index];
double low = Bars.LowPrices[index];
double close = Bars.ClosePrices[index];
double prevClose = Bars.ClosePrices[index - 1];
if (high > _eqh.Price && !_eqh.IsSwept)
{
_eqh.IsSwept = true;
_eqh.SweepExtr = high;
_eqh.TriggerBOS = low;
Chart.DrawLine(_eqh.LineName, Bars.OpenTimes[_eqh.TimeIndex], _eqh.Price, Bars.OpenTimes[index], _eqh.Price, Color.DimGray, 1, LineStyle.Dots);
if (close < _eqh.Price) TriggerExecution("EQH Reclaim", index, _eqh.SweepExtr, true);
}
else if (_eqh.IsSwept)
{
if (high > _eqh.SweepExtr) _eqh.SweepExtr = high;
if (high > Bars.HighPrices[index - 1] && low > _eqh.TriggerBOS) _eqh.TriggerBOS = low;
if (close < _eqh.Price && prevClose >= _eqh.Price)
TriggerExecution("EQH Delayed Reclaim", index, _eqh.SweepExtr, true);
else if (close < _eqh.TriggerBOS && prevClose >= _eqh.TriggerBOS)
TriggerExecution("EQH Micro BOS", index, _eqh.SweepExtr, true);
}
}
private void ProcessEQL(int index)
{
if (!_eql.IsActive) return;
double high = Bars.HighPrices[index];
double low = Bars.LowPrices[index];
double close = Bars.ClosePrices[index];
double prevClose = Bars.ClosePrices[index - 1];
if (low < _eql.Price && !_eql.IsSwept)
{
_eql.IsSwept = true;
_eql.SweepExtr = low;
_eql.TriggerBOS = high;
Chart.DrawLine(_eql.LineName, Bars.OpenTimes[_eql.TimeIndex], _eql.Price, Bars.OpenTimes[index], _eql.Price, Color.DimGray, 1, LineStyle.Dots);
if (close > _eql.Price) TriggerExecution("EQL Reclaim", index, _eql.SweepExtr, false);
}
else if (_eql.IsSwept)
{
if (low < _eql.SweepExtr) _eql.SweepExtr = low;
if (low < Bars.LowPrices[index - 1] && high < _eql.TriggerBOS) _eql.TriggerBOS = high;
if (close > _eql.Price && prevClose <= _eql.Price)
TriggerExecution("EQL Delayed Reclaim", index, _eql.SweepExtr, false);
else if (close > _eql.TriggerBOS && prevClose <= _eql.TriggerBOS)
TriggerExecution("EQL Micro BOS", index, _eql.SweepExtr, false);
}
}
private void TriggerExecution(string signalName, int index, double stopLoss, bool isShort)
{
double entry = Bars.ClosePrices[index];
double riskAmount = Math.Abs(entry - stopLoss);
double target = isShort ? entry - (riskAmount * RrRatio) : entry + (riskAmount * RrRatio);
string uid = Bars.OpenTimes[index].Ticks.ToString();
// 1. Draw Entry & SL Label
Color clr = isShort ? Color.Tomato : Color.DodgerBlue;
double yPos = isShort ? Bars.HighPrices[index] + (10 * Symbol.TickSize) : Bars.LowPrices[index] - (10 * Symbol.TickSize);
VerticalAlignment vAlign = isShort ? VerticalAlignment.Bottom : VerticalAlignment.Top;
Chart.DrawText("LBL_" + uid, $"{signalName}\nSL: {Math.Round(stopLoss, Symbol.Digits)}", Bars.OpenTimes[index], yPos, clr, vAlign, HorizontalAlignment.Center);
// 2. Draw Visual RR Projection Target Box
int tpWidth = 10; // Project 10 bars into the future visually
DateTime futureTime = Bars.OpenTimes[index].AddMinutes(Chart.TimeFrame.HasTimeSpan ? Chart.TimeFrame.TimeSpan.TotalMinutes * tpWidth : 15);
Color targetColor = isShort ? Color.FromArgb(40, Color.LightGreen) : Color.FromArgb(40, Color.LightGreen);
Chart.DrawRectangle("TP_" + uid, Bars.OpenTimes[index], entry, futureTime, target, targetColor).IsFilled = true;
Chart.DrawText("TPTxt_" + uid, $" Target {RrRatio}R", futureTime, target, Color.LightGreen, VerticalAlignment.Center, HorizontalAlignment.Right);
// 3. Trigger Real-Time Alerts
if (EnableAlerts && IsLastBar)
{
Print($"[LIQUIDITY SWEEP] {signalName} executed at {entry}. SL: {stopLoss}, TP: {target}");
}
// Deactivate level once executed
if (isShort) _eqh.IsActive = false; else _eql.IsActive = false;
}
private void DrawSessionBoxes(int index)
{
if (!UseSession) return;
int h = Bars.OpenTimes[index].Hour;
bool currentInSession = (SessionStartHour > SessionEndHour) ? (h >= SessionStartHour || h < SessionEndHour) : (h >= SessionStartHour && h < SessionEndHour);
if (currentInSession && !_inSession)
{
_sessionStartIndex = index;
_inSession = true;
}
else if (!currentInSession && _inSession)
{
double highest = double.MinValue, lowest = double.MaxValue;
for (int i = _sessionStartIndex; i < index; i++)
{
if (Bars.HighPrices[i] > highest) highest = Bars.HighPrices[i];
if (Bars.LowPrices[i] < lowest) lowest = Bars.LowPrices[i];
}
Chart.DrawRectangle("Sess_" + Bars.OpenTimes[_sessionStartIndex].Ticks, Bars.OpenTimes[_sessionStartIndex], highest, Bars.OpenTimes[index], lowest, _sessionColor).IsFilled = true;
_inSession = false;
}
}
private void UpdateDashboard()
{
if (!IsLastBar) return; // Only update UI on the live, right-most candle
string FormatStatus(LiquidityLevel level)
{
if (!level.IsActive) return "None";
if (level.IsSwept) return $"Swept! (BOS Trigger: {Math.Round(level.TriggerBOS, Symbol.Digits)})";
return $"Magnet Active @ {Math.Round(level.Price, Symbol.Digits)}";
}
_uiEqhStatus.Text = $"EQH: {FormatStatus(_eqh)}";
_uiEqlStatus.Text = $"EQL: {FormatStatus(_eql)}";
}
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Equal highs swept at London open: reclaim entry vs wait for BOS
Why this is a massive upgrade:
The HUD (Top Right Corner): A major advantage of cTrader over MT4/TradingView is its Canvas API. The script draws a dark, transparent dashboard that reads "EQH: Magnet Active @ 1.0543" or "EQH: Swept! (BOS Trigger: 1.0538)". It tells you exactly what macro-state the market is in without having to look across the chart.
Visual R:R Projections (The Green Boxes): When an entry fires, the script instantly calculates the distance from the close (Entry) to the extreme wick (Stop Loss). It multiplies that risk by your setting (default 2.0) and draws a transparent green target rectangle extending to the right. This allows you to visually gauge instantly if the trade has room to run into the next liquidity pool before you actually click the execution button.
Real-Time IsLastBar Alerting Protection: Indicator loops calculate every historical bar upon loading. Poorly coded indicators will spam you with alerts for the last 5 years when attached to a chart. I used if (EnableAlerts && IsLastBar) to ensure it only prints to your cTrader Automate Log and triggers notifications if the Reclaim/BOS happens on the live tape.
The HUD (Top Right Corner): A major advantage of cTrader over MT4/TradingView is its Canvas API. The script draws a dark, transparent dashboard that reads "EQH: Magnet Active @ 1.0543" or "EQH: Swept! (BOS Trigger: 1.0538)". It tells you exactly what macro-state the market is in without having to look across the chart.
Visual R:R Projections (The Green Boxes): When an entry fires, the script instantly calculates the distance from the close (Entry) to the extreme wick (Stop Loss). It multiplies that risk by your setting (default 2.0) and draws a transparent green target rectangle extending to the right. This allows you to visually gauge instantly if the trade has room to run into the next liquidity pool before you actually click the execution button.
Real-Time IsLastBar Alerting Protection: Indicator loops calculate every historical bar upon loading. Poorly coded indicators will spam you with alerts for the last 5 years when attached to a chart. I used if (EnableAlerts && IsLastBar) to ensure it only prints to your cTrader Automate Log and triggers notifications if the Reclaim/BOS happens on the live tape.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Equal highs swept at London open: reclaim entry vs wait for BOS
To deliver this at a professional level for the MetaTrader ecosystem, I have written a Unified Universal Codebase.
By utilizing preprocessor directives (#ifdef __MQL5__) and forcing MetaTrader’s internal arrays into chronological order, the code below is natively compatible with both MT4 and MT5. You can paste this exact code into MetaEditor 4 or MetaEditor 5, and it will compile flawlessly into an institutional-grade chart utility.
Key Adaptations for MetaTrader:
Points vs Ticks: MetaTrader calculates in Points. On a standard 5-digit broker, 1 pip = 10 points. I’ve adjusted the tolerance input to points (e.g., inputting 15 means 15 points, or 1.5 pips).
Object Engine Pipeline: MetaTrader requires unique IDs for every graphical element. The script dynamically prefixes all elements with ILP_ (Institutional Liquidity Pro) and tags them with timestamp IDs. When you remove the indicator, it auto-cleans the chart.
The HUD Dashboard: Utilizes the MetaTrader Canvas OBJ_LABEL system to render a sleek, non-intrusive status board in the top-right corner.
By utilizing preprocessor directives (#ifdef __MQL5__) and forcing MetaTrader’s internal arrays into chronological order, the code below is natively compatible with both MT4 and MT5. You can paste this exact code into MetaEditor 4 or MetaEditor 5, and it will compile flawlessly into an institutional-grade chart utility.
Key Adaptations for MetaTrader:
Points vs Ticks: MetaTrader calculates in Points. On a standard 5-digit broker, 1 pip = 10 points. I’ve adjusted the tolerance input to points (e.g., inputting 15 means 15 points, or 1.5 pips).
Object Engine Pipeline: MetaTrader requires unique IDs for every graphical element. The script dynamically prefixes all elements with ILP_ (Institutional Liquidity Pro) and tags them with timestamp IDs. When you remove the indicator, it auto-cleans the chart.
The HUD Dashboard: Utilizes the MetaTrader Canvas OBJ_LABEL system to render a sleek, non-intrusive status board in the top-right corner.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Equal highs swept at London open: reclaim entry vs wait for BOS
The MT4 / MT5 Unified Code [PRO]
1.) Open MetaEditor (MT4 or MT5).
2.) Create a new Custom Indicator and name it InstitutionalLiquidityPro.
3.) Delete all default code, paste everything below, and click Compile.
1.) Open MetaEditor (MT4 or MT5).
2.) Create a new Custom Indicator and name it InstitutionalLiquidityPro.
3.) Delete all default code, paste everything below, and click Compile.
Code: Select all
//+------------------------------------------------------------------+
//| InstitutionalLiquidityPro |
//| EQH/EQL Sweeps, Reclaims, & Micro BOS |
//+------------------------------------------------------------------+
#property copyright "Pro Institutional"
#property link ""
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 0
// ==============================================================================
// 1. PARAMETERS
// ==============================================================================
input string grp1 = "--- Liquidity & Structure ---";
input int PLen = 10; // Pivot Lookback Bars
input double PointTol = 15.0; // Equal Tolerance (Points)
input string grp2 = "--- Session Context ---";
input bool UseSession = true; // Highlight Accumulation Session
input int SessionStartHour = 18; // Session Start Hour (Broker Time)
input int SessionEndHour = 2; // Session End Hour (Broker Time)
input color SessionColor = clrMidnightBlue;// Session Background Color
input string grp3 = "--- Risk & UI ---";
input double RrRatio = 2.0; // Risk:Reward Target Multiplier
input bool EnableAlerts = true; // Enable Real-Time Alerts
// ==============================================================================
// 2. STATE MANAGEMENT
// ==============================================================================
struct LiquidityLevel {
double price;
datetime timeStart;
bool isActive;
bool isSwept;
double sweepExtr;
double triggerBOS;
string lineName;
};
LiquidityLevel eqh;
LiquidityLevel eql;
double lastPh = 0, lastPl = 0;
datetime timePh = 0, timePl = 0;
int sessionStartIndex = -1;
bool inSession = false;
// ==============================================================================
// 3. INITIALIZATION & CLEANUP
// ==============================================================================
int OnInit() {
// Build the HUD
CreateHUDLabel("ILP_HUD_TITLE", "INSTITUTIONAL LIQUIDITY [PRO]", clrWhite, 20, 10, true);
CreateHUDLabel("ILP_HUD_EQH", "EQH: Scanning...", clrTomato, 40, 9, false);
CreateHUDLabel("ILP_HUD_EQL", "EQL: Scanning...", clrDodgerBlue, 60, 9, false);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) {
ObjectsDeleteAll(0, "ILP_");
}
void CreateHUDLabel(string name, string text, color clr, int yDist, int fontSize, bool isBold) {
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, 20);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, yDist);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
if(isBold) ObjectSetString(0, name, OBJPROP_FONT, "Arial Bold");
else ObjectSetString(0, name, OBJPROP_FONT, "Arial");
}
// ==============================================================================
// 4. CORE CALCULATION LOGIC
// ==============================================================================
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 chronological ordering regardless of MT4 vs MT5 defaults
ArraySetAsSeries(time, false);
ArraySetAsSeries(high, false);
ArraySetAsSeries(low, false);
ArraySetAsSeries(close, false);
if (rates_total < PLen * 2) return 0;
// Wipe state if reloading history
if(prev_calculated == 0) {
ObjectsDeleteAll(0, "ILP_");
OnInit();
lastPh = 0; lastPl = 0; timePh = 0; timePl = 0;
eqh.isActive = false; eqh.isSwept = false;
eql.isActive = false; eql.isSwept = false;
inSession = false;
}
int limit = prev_calculated == 0 ? PLen * 2 : prev_calculated - 1;
for (int i = limit; i < rates_total; i++) {
ProcessSession(i, time, high, low);
DetectPivots(i, time, high, low);
ProcessEQH(i, rates_total, time, high, low, close);
ProcessEQL(i, rates_total, time, high, low, close);
}
UpdateHUD(rates_total);
return(rates_total);
}
// ==============================================================================
// 5. PROCESS METHODS
// ==============================================================================
void DetectPivots(int i, const datetime &time[], const double &high[], const double &low[]) {
int pIdx = i - PLen;
double threshold = PointTol * _Point;
// EQH
bool isPh = true;
for(int k = pIdx - PLen; k <= pIdx + PLen; k++) {
if(k == pIdx) continue;
if(high[k] > high[pIdx]) { isPh = false; break; }
}
if(isPh) {
double ph = high[pIdx];
if(lastPh != 0.0 && MathAbs(ph - lastPh) <= threshold) {
eqh.price = MathMax(ph, lastPh);
eqh.timeStart = timePh;
eqh.isActive = true;
eqh.isSwept = false;
eqh.lineName = "ILP_EQH_" + IntegerToString(time[pIdx]);
ObjectCreate(0, eqh.lineName, OBJ_TREND, 0, eqh.timeStart, eqh.price, time[pIdx], eqh.price);
ObjectSetInteger(0, eqh.lineName, OBJPROP_COLOR, clrTomato);
ObjectSetInteger(0, eqh.lineName, OBJPROP_WIDTH, 2);
ObjectSetInteger(0, eqh.lineName, OBJPROP_RAY_RIGHT, true);
}
lastPh = ph; timePh = time[pIdx];
}
// EQL
bool isPl = true;
for(int k = pIdx - PLen; k <= pIdx + PLen; k++) {
if(k == pIdx) continue;
if(low[k] < low[pIdx]) { isPl = false; break; }
}
if(isPl) {
double pl = low[pIdx];
if(lastPl != 0.0 && MathAbs(pl - lastPl) <= threshold) {
eql.price = MathMin(pl, lastPl);
eql.timeStart = timePl;
eql.isActive = true;
eql.isSwept = false;
eql.lineName = "ILP_EQL_" + IntegerToString(time[pIdx]);
ObjectCreate(0, eql.lineName, OBJ_TREND, 0, eql.timeStart, eql.price, time[pIdx], eql.price);
ObjectSetInteger(0, eql.lineName, OBJPROP_COLOR, clrDodgerBlue);
ObjectSetInteger(0, eql.lineName, OBJPROP_WIDTH, 2);
ObjectSetInteger(0, eql.lineName, OBJPROP_RAY_RIGHT, true);
}
lastPl = pl; timePl = time[pIdx];
}
}
void ProcessEQH(int i, int rates_total, const datetime &time[], const double &high[], const double &low[], const double &close[]) {
if(!eqh.isActive) return;
double h = high[i], l = low[i], c = close[i];
double pc = i > 0 ? close[i-1] : close[i];
if(h > eqh.price && !eqh.isSwept) {
eqh.isSwept = true;
eqh.sweepExtr = h;
eqh.triggerBOS = l;
ObjectSetInteger(0, eqh.lineName, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(0, eqh.lineName, OBJPROP_TIME2, time[i]);
ObjectSetInteger(0, eqh.lineName, OBJPROP_COLOR, clrGray);
ObjectSetInteger(0, eqh.lineName, OBJPROP_STYLE, STYLE_DOT);
if(c < eqh.price) TriggerExecution("EQH Reclaim", i, rates_total, eqh.sweepExtr, true, time, high, low, close);
}
else if(eqh.isSwept) {
if(h > eqh.sweepExtr) eqh.sweepExtr = h;
if(h > high[i-1] && l > eqh.triggerBOS) eqh.triggerBOS = l;
if(c < eqh.price && pc >= eqh.price)
TriggerExecution("EQH Delayed Reclaim", i, rates_total, eqh.sweepExtr, true, time, high, low, close);
else if(c < eqh.triggerBOS && pc >= eqh.triggerBOS)
TriggerExecution("EQH Micro BOS", i, rates_total, eqh.sweepExtr, true, time, high, low, close);
}
}
void ProcessEQL(int i, int rates_total, const datetime &time[], const double &high[], const double &low[], const double &close[]) {
if(!eql.isActive) return;
double h = high[i], l = low[i], c = close[i];
double pc = i > 0 ? close[i-1] : close[i];
if(l < eql.price && !eql.isSwept) {
eql.isSwept = true;
eql.sweepExtr = l;
eql.triggerBOS = h;
ObjectSetInteger(0, eql.lineName, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(0, eql.lineName, OBJPROP_TIME2, time[i]);
ObjectSetInteger(0, eql.lineName, OBJPROP_COLOR, clrGray);
ObjectSetInteger(0, eql.lineName, OBJPROP_STYLE, STYLE_DOT);
if(c > eql.price) TriggerExecution("EQL Reclaim", i, rates_total, eql.sweepExtr, false, time, high, low, close);
}
else if(eql.isSwept) {
if(l < eql.sweepExtr) eql.sweepExtr = l;
if(l < low[i-1] && h < eql.triggerBOS) eql.triggerBOS = h;
if(c > eql.price && pc <= eql.price)
TriggerExecution("EQL Delayed Reclaim", i, rates_total, eql.sweepExtr, false, time, high, low, close);
else if(c > eql.triggerBOS && pc <= eql.triggerBOS)
TriggerExecution("EQL Micro BOS", i, rates_total, eql.sweepExtr, false, time, high, low, close);
}
}
void TriggerExecution(string signal, int i, int rates_total, double stopLoss, bool isShort, const datetime &time[], const double &high[], const double &low[], const double &close[]) {
double entry = close[i];
double risk = MathAbs(entry - stopLoss);
double target = isShort ? entry - (risk * RrRatio) : entry + (risk * RrRatio);
string uid = IntegerToString(time[i]);
// 1. Entry Text Label
string lblName = "ILP_LBL_" + uid;
double yPos = isShort ? high[i] + (10 * _Point) : low[i] - (10 * _Point);
ObjectCreate(0, lblName, OBJ_TEXT, 0, time[i], yPos);
ObjectSetString(0, lblName, OBJPROP_TEXT, signal + " SL: " + DoubleToString(stopLoss, _Digits));
ObjectSetInteger(0, lblName, OBJPROP_COLOR, isShort ? clrTomato : clrDodgerBlue);
// 2. TP Risk/Reward Box
string boxName = "ILP_TP_" + uid;
datetime tpTime = time[i] + (PeriodSeconds() * 10);
ObjectCreate(0, boxName, OBJ_RECTANGLE, 0, time[i], entry, tpTime, target);
ObjectSetInteger(0, boxName, OBJPROP_COLOR, clrMediumSeaGreen);
ObjectSetInteger(0, boxName, OBJPROP_BACK, true);
// 3. Real-Time Alert
if(EnableAlerts && i == rates_total - 1) {
Alert("[LIQUIDITY SWEEP] ", signal, " at ", DoubleToString(entry, _Digits));
}
if(isShort) eqh.isActive = false; else eql.isActive = false;
}
void ProcessSession(int i, const datetime &time[], const double &high[], const double &low[]) {
if(!UseSession) return;
int h;
#ifdef __MQL5__
MqlDateTime dt; TimeToStruct(time[i], dt); h = dt.hour;
#else
h = TimeHour(time[i]);
#endif
bool currentInSession = (SessionStartHour > SessionEndHour) ?
(h >= SessionStartHour || h < SessionEndHour) :
(h >= SessionStartHour && h < SessionEndHour);
if(currentInSession && !inSession) {
sessionStartIndex = i;
inSession = true;
}
else if(!currentInSession && inSession) {
double sessHigh = -999999, sessLow = 999999;
for(int k = sessionStartIndex; k < i; k++) {
if(high[k] > sessHigh) sessHigh = high[k];
if(low[k] < sessLow) sessLow = low[k];
}
string boxName = "ILP_Sess_" + IntegerToString(time[sessionStartIndex]);
ObjectCreate(0, boxName, OBJ_RECTANGLE, 0, time[sessionStartIndex], sessHigh, time[i], sessLow);
ObjectSetInteger(0, boxName, OBJPROP_COLOR, SessionColor);
ObjectSetInteger(0, boxName, OBJPROP_BACK, true);
inSession = false;
}
}
void UpdateHUD(int rates_total) {
if(rates_total == 0) return;
string eqhTxt = "EQH: ";
if(!eqh.isActive) eqhTxt += "None";
else if(eqh.isSwept) eqhTxt += "Swept! (BOS Trigger: " + DoubleToString(eqh.triggerBOS, _Digits) + ")";
else eqhTxt += "Magnet @ " + DoubleToString(eqh.price, _Digits);
ObjectSetString(0, "ILP_HUD_EQH", OBJPROP_TEXT, eqhTxt);
string eqlTxt = "EQL: ";
if(!eql.isActive) eqlTxt += "None";
else if(eql.isSwept) eqlTxt += "Swept! (BOS Trigger: " + DoubleToString(eql.triggerBOS, _Digits) + ")";
else eqlTxt += "Magnet @ " + DoubleToString(eql.price, _Digits);
ObjectSetString(0, "ILP_HUD_EQL", OBJPROP_TEXT, eqlTxt);
ChartRedraw(0);
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Equal highs swept at London open: reclaim entry vs wait for BOS
Visual Enhancements for MT4 / MT5
If you want to modify how the visuals appear on the chart, you can easily tweak:
OBJPROP_BACK, true renders objects behind the candlesticks (used on the Take Profit boxes and Session Accumulation boxes).
The CreateHUDLabel handles the UI. If it overlaps with other MT4/MT5 tools you use on the top-right corner, simply modify CORNER_RIGHT_UPPER to CORNER_RIGHT_LOWER or CORNER_LEFT_UPPER in the OnInit() block.
If you want to modify how the visuals appear on the chart, you can easily tweak:
OBJPROP_BACK, true renders objects behind the candlesticks (used on the Take Profit boxes and Session Accumulation boxes).
The CreateHUDLabel handles the UI. If it overlaps with other MT4/MT5 tools you use on the top-right corner, simply modify CORNER_RIGHT_UPPER to CORNER_RIGHT_LOWER or CORNER_LEFT_UPPER in the OnInit() block.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
-
LondonScalper
- Posts: 770
- Joined: Sat Sep 05, 2026 7:54 am
Re: Equal highs swept at London open: reclaim entry vs wait for BOS
For the BOS after a fast sweep I use M1, but only a swing that M5 would also call a swing. In practice that means the last M1 higher-low that held for three or four bars before the sweep leg started, not every micro pivot inside the spike.
A body close through that low is my structure shift. A wick through it is not; I wait. Dropping to 15-second or tick bars to find an earlier break gave me more entries and a worse hit rate. The extra signals were mostly the tape breathing.
Your point about acceptance above the equal highs matters here as well. If price holds above the line for two or three M1 closes before any break appears, I treat the eventual BOS with suspicion. The market spent time up there, which is not how a stop-hunt usually behaves.
Pre-marking is non-negotiable for me. The equal highs are written down before 07:30, or they don't exist for that session.
A body close through that low is my structure shift. A wick through it is not; I wait. Dropping to 15-second or tick bars to find an earlier break gave me more entries and a worse hit rate. The extra signals were mostly the tape breathing.
Your point about acceptance above the equal highs matters here as well. If price holds above the line for two or three M1 closes before any break appears, I treat the eventual BOS with suspicion. The market spent time up there, which is not how a stop-hunt usually behaves.
Pre-marking is non-negotiable for me. The equal highs are written down before 07:30, or they don't exist for that session.