Page 2 of 2
Re: How many fills do you need before ranking two brokers?
Posted: Sat Sep 19, 2026 12:17 pm
by PTScalper
Here is the C# cAlgo implementation. It calculates the structural vacuums programmatically and plots heavy data points right at the sweep extremes.
Code: Select all
using System;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class LiquiditySweepRisk : Indicator
{
[Parameter("ATR & SMA Lookback", DefaultValue = 20)]
public int Lookback { get; set; }
[Parameter("Slippage Risk Multiplier", DefaultValue = 1.5)]
public double SlipMultiplier { get; set; }
[Parameter("Enable Terminal Print Alerts", DefaultValue = true)]
public bool EnableAlerts { get; set; }
[Output("Upper Risk Band", LineColor = "Gray", PlotType = PlotType.Line, Thickness = 1)]
public IndicatorDataSeries UpperBand { get; set; }
[Output("Lower Risk Band", LineColor = "Gray", PlotType = PlotType.Line, Thickness = 1)]
public IndicatorDataSeries LowerBand { get; set; }
[Output("Bull Sweep Risk", LineColor = "Red", PlotType = PlotType.Points, Thickness = 4)]
public IndicatorDataSeries BullSweep { get; set; }
[Output("Bear Sweep Risk", LineColor = "Red", PlotType = PlotType.Points, Thickness = 4)]
public IndicatorDataSeries BearSweep { get; set; }
private AverageTrueRange _atr;
private SimpleMovingAverage _sma;
private DateTime _lastAlertTime;
protected override void Initialize()
{
_atr = Indicators.AverageTrueRange(Lookback, MovingAverageType.Simple);
_sma = Indicators.SimpleMovingAverage(Bars.ClosePrices, Lookback);
}
public override void Calculate(int index)
{
if (index < Lookback)
return;
double atrValue = _atr.Result[index];
double smaValue = _sma.Result[index];
// Map stable execution zones
UpperBand[index] = smaValue + (atrValue * SlipMultiplier);
LowerBand[index] = smaValue - (atrValue * SlipMultiplier);
double lowestLow = double.MaxValue;
double highestHigh = double.MinValue;
// Define the structural boundaries
for (int i = 1; i <= Lookback; i++)
{
if (Bars.LowPrices[index - i] < lowestLow)
lowestLow = Bars.LowPrices[index - i];
if (Bars.HighPrices[index - i] > highestHigh)
highestHigh = Bars.HighPrices[index - i];
}
// Identify structural sweeps (Liquidity vacuums)
bool bullSweep = Bars.LowPrices[index] < lowestLow && Bars.ClosePrices[index] > Bars.OpenPrices[index];
bool bearSweep = Bars.HighPrices[index] > highestHigh && Bars.ClosePrices[index] < Bars.OpenPrices[index];
if (bullSweep)
BullSweep[index] = Bars.LowPrices[index] - (atrValue * 0.5);
if (bearSweep)
BearSweep[index] = Bars.HighPrices[index] + (atrValue * 0.5);
// Log high-risk environments directly to the cTrader journal
if (IsLastBar && EnableAlerts && (bullSweep || bearSweep) && Bars.OpenTimes[index] != _lastAlertTime)
{
Print("Liquidity Sweep Detected on {0} - LP withdrawal imminent. High slippage risk.", SymbolName);
_lastAlertTime = Bars.OpenTimes[index];
}
}
}
}
Re: How many fills do you need before ranking two brokers?
Posted: Sat Sep 19, 2026 12:17 pm
by PTScalper
If you are running automated rejection trackers in a separate cBot, you can initialize this indicator inside your bot using Indicators.GetIndicator<LiquiditySweepRisk>(). When the BullSweep or BearSweep output is not double.NaN, you know you are executing during a high-risk structural sweep.
By logging the exact millisecond latency of ExecuteMarketOrder against these specific red sweep markers, you will immediately expose whether your broker is routing your trades to a genuine dark pool or simply holding the order artificially during volatility to pass negative slippage onto you.
Re: How many fills do you need before ranking two brokers?
Posted: Sat Sep 19, 2026 12:19 pm
by PTScalper
To make this professional, the architecture needs three upgrades:
State Exposure: Exposing the microstructure state via public enum properties so an execution cBot can read the environment in $O(1)$ time without interrogating data arrays.
Memory Optimization: Using cTrader’s native DataSeries extension methods (Maximum, Minimum) instead of manual for loops to offload calculations to the optimized engine and avoid heap allocations during high-tick-rate volatility sweeps.
Structured Logging: Stripping out string allocations in the main Calculate method to prevent GC pauses right when you need maximum execution velocity.
Re: How many fills do you need before ranking two brokers?
Posted: Sat Sep 19, 2026 12:19 pm
by PTScalper
Here is the optimized C# implementation designed to feed directly into a high-frequency cBot latency tracker.
Code: Select all
using System;
using System.Diagnostics;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
namespace cAlgo.Indicators
{
public enum LiquidityRegime
{
Stable,
BullishSweep,
BearishSweep
}
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class MicrostructureSweepRisk : Indicator
{
[Parameter("Structural Lookback", DefaultValue = 20, MinValue = 5)]
public int Lookback { get; set; }
[Parameter("Risk Multiplier (ATR)", DefaultValue = 1.5, MinValue = 0.1)]
public double SlipMultiplier { get; set; }
[Parameter("Enable Console Logging", DefaultValue = false)]
public bool EnableLogging { get; set; }
[Output("Upper Risk Boundary", LineColor = "DimGray", PlotType = PlotType.Line, Thickness = 1)]
public IndicatorDataSeries UpperBoundary { get; set; }
[Output("Lower Risk Boundary", LineColor = "DimGray", PlotType = PlotType.Line, Thickness = 1)]
public IndicatorDataSeries LowerBoundary { get; set; }
[Output("Sell-Side Sweep (Bullish)", LineColor = "Red", PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries BullSweepMarker { get; set; }
[Output("Buy-Side Sweep (Bearish)", LineColor = "Red", PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries BearSweepMarker { get; set; }
// Public state exposed for O(1) access by execution cBots
public LiquidityRegime CurrentRegime { get; private set; }
private AverageTrueRange _atr;
private SimpleMovingAverage _sma;
private DateTime _lastLogTime;
protected override void Initialize()
{
// Initialize unmanaged indicator components
_atr = Indicators.AverageTrueRange(Lookback, MovingAverageType.Simple);
_sma = Indicators.SimpleMovingAverage(Bars.ClosePrices, Lookback);
CurrentRegime = LiquidityRegime.Stable;
}
public override void Calculate(int index)
{
if (index <= Lookback) return;
// Cache current references to avoid repeated array lookups
double currentClose = Bars.ClosePrices[index];
double currentOpen = Bars.OpenPrices[index];
double currentLow = Bars.LowPrices[index];
double currentHigh = Bars.HighPrices[index];
double atrVal = _atr.Result[index];
double smaVal = _sma.Result[index];
// Define dynamic risk boundaries
UpperBoundary[index] = smaVal + (atrVal * SlipMultiplier);
LowerBoundary[index] = smaVal - (atrVal * SlipMultiplier);
// Utilize native cAlgo DataSeries extensions (avoids manual loops & allocations)
double structuralLow = Bars.LowPrices.Minimum(Lookback, index - 1);
double structuralHigh = Bars.HighPrices.Maximum(Lookback, index - 1);
// Boolean structural evaluation
bool isBullSweep = currentLow < structuralLow && currentClose > currentOpen;
bool isBearSweep = currentHigh > structuralHigh && currentClose < currentOpen;
// State management and rendering
if (isBullSweep)
{
CurrentRegime = LiquidityRegime.BullishSweep;
BullSweepMarker[index] = currentLow - (atrVal * 0.4);
LogSweep(index, "Sell-side liquidity vacuum");
}
else if (isBearSweep)
{
CurrentRegime = LiquidityRegime.BearishSweep;
BearSweepMarker[index] = currentHigh + (atrVal * 0.4);
LogSweep(index, "Buy-side liquidity vacuum");
}
else
{
CurrentRegime = LiquidityRegime.Stable;
BullSweepMarker[index] = double.NaN;
BearSweepMarker[index] = double.NaN;
}
}
/// <summary>
/// Structurally isolates string allocations to prevent GC spikes on stable ticks.
/// </summary>
private void LogSweep(int index, string sweepType)
{
if (!EnableLogging || !IsLastBar || Bars.OpenTimes[index] == _lastLogTime) return;
Print($"[Microstructure Warning] {SymbolName} | {sweepType} detected. High probability of asymmetric slippage.");
_lastLogTime = Bars.OpenTimes[index];
}
}
}
Re: How many fills do you need before ranking two brokers?
Posted: Sat Sep 19, 2026 12:19 pm
by PTScalper
Implementing the Execution Diagnostic Bot
Because the indicator now exposes a LiquidityRegime enum, your cBot does not need to waste CPU cycles scanning the indicator's data arrays. You instantiate the indicator inside the bot and query the regime directly before routing the order.
By wrapping your execution command in a Stopwatch, you can track the exact millisecond delay between the terminal requesting a fill on a 15-minute sweep and the broker’s server returning the confirmation.
Re: How many fills do you need before ranking two brokers?
Posted: Sat Sep 19, 2026 12:20 pm
by PTScalper
C# Code:
Code: Select all
// Inside your Execution cBot:
private MicrostructureSweepRisk _sweepRisk;
private Stopwatch _latencyTracker = new Stopwatch();
protected override void OnStart()
{
_sweepRisk = Indicators.GetIndicator<MicrostructureSweepRisk>(20, 1.5, false);
}
private void ExecuteDiagnosticTrade(TradeType tradeType, double volume)
{
if (_sweepRisk.CurrentRegime == LiquidityRegime.Stable) return; // Only test during stress events
_latencyTracker.Restart();
var result = ExecuteMarketOrder(tradeType, SymbolName, volume, "Execution_Diagnostic");
_latencyTracker.Stop();
double latencyMs = _latencyTracker.Elapsed.TotalMilliseconds;
double slippageTicks = Math.Abs(result.Position.EntryPrice - Symbol.Ask) / Symbol.TickSize;
Print($"[Execution Log] {tradeType} | Regime: {_sweepRisk.CurrentRegime} | Latency: {latencyMs}ms | Slippage: {slippageTicks} ticks");
}
Re: How many fills do you need before ranking two brokers?
Posted: Sat Sep 19, 2026 12:20 pm
by PTScalper
This setup completely removes the "feel" aspect from broker evaluation. When a broker claims superior aggregation, this combination of C# scripts will definitively prove whether their B-book plugin is intentionally stalling fills during structural liquidity sweeps to force negative slippage.
Re: How many fills do you need before ranking two brokers?
Posted: Sat Sep 19, 2026 6:20 pm
by LondonScalper
PTScalper wrote:I look for at least 200-300 live market orders executed specifically during high-volume windows.
I can live with 200 to 300 if they are genuinely your hours, not a quiet overnight grind padded to look large. The comparison still has to be the same order type, size band and session on both accounts. A fast EURUSD book and a poor gold book can sit in the same shop, so testing them apart is right. A tidy euro sample does not licence a metals ranking. I still want the ranking from the fill log: median spread, signed slip, rejects, and the time of day.
A chart script that paints risk zones from volatility is a hypothesis about when the book might thin. It is not a broker sample. Checking those markers against actual rejects is the useful half. Posting several versions of the same marker is not. I re-run after a high-impact week because calm tape flatters everyone. If the sample is still thin, I say so. Under-claiming has saved me more than any brochure.
Re: How many fills do you need before ranking two brokers?
Posted: Thu Sep 24, 2026 1:25 am
by PropScalpDesk
PTScalper wrote:MQL5: Liquidity Sweep & Execution Risk Place this in MQL5\Indicators. MQL5 handles arrays differently, so this implementation forces ArraySetAsSeries and uses CopyBuffer to pull indicator handles smoothly without memory leaks. Code: Select all //+------------------------------------------------------------------+ //| Liquidity_Sweep_Risk_MT5.
I want a real sample of fills before I rank brokers — not a morning of screenshots. Reject rate and slip into London open matter more than the marketing spread.
For prop execution, a “cheap” book that rejects at the open is expensive.
How many fills do you require per session hour before you trust a comparison?
I also log refused tickets so flat time counts as work — otherwise the desk invents activity.
If the idea needs a story longer than one line, it waits for another window.
Topic note from my sheet for t=12529: keep risk unchanged until the sample says otherwise.
Re: How many fills do you need before ranking two brokers?
Posted: Thu Sep 24, 2026 9:41 am
by LondonNewsTrader
PTScalper wrote:MQL5: Liquidity Sweep & Execution Risk Place this in MQL5\Indicators. MQL5 handles arrays differently, so this implementation forces ArraySetAsSeries and uses CopyBuffer to pull indicator handles smoothly without memory leaks. Code: Select all //+------------------------------------------------------------------+ //| Liquidity_Sweep_Risk_MT5.
The bands are a reasonable way to flag when price is stretched, but for the question in this thread I'm not sure they help directly. ATR and SMA are computed from each broker's own price feed, and two ECN feeds on EURUSD will produce almost identical bands. The indicator can't show that broker A slipped you half a pip more than broker B at 08:00; only the fill records can.
Where it could earn its place is as a tagging tool. If each fill is stamped with whether price was outside the 1.5 × ATR band at the time, the comparison splits into normal and stretched conditions. That matters because a broker can look fine on calm fills and fall apart when price is extended, which is exactly when a scalper needs it. A few hundred fills might only contain thirty stretched ones, so that bucket takes longer to mean anything, but it's the one I'd care about most.
On the code: the input is called InpSlipMultiplier, yet it scales volatility, not slippage. Renaming it would avoid confusion later when someone assumes it models execution cost.
I'd also add a flag for fills within a few minutes of scheduled releases, since those are driven by the calendar rather than the band.