My EURUSD London spread filter is a number on a sticky area of the screen, not a feeling.
I set it from a month of open-session samples plus a small buffer for ordinary noise. Below the line, setups are allowed to compete. Above the line, I do not negotiate with myself — the click does not happen. That sounds blunt because it has to be; negotiation is how revenge trades dress up as "liquidity will return."
Practical bits
1. Filter is session-aware: London cash open is stricter than a quiet mid-morning.
2. News blackout overrides the filter entirely — flat is flat.
3. I review the number quarterly; if my broker's open behaviour changed, the filter moves with data, not hope.
The filter does not pick entries. It only vetoes bad economics.
What number do you use on EURUSD at London, and do you keep it fixed or retune from your own log?
I occasionally stress-test the filter by noting how many A+ ideas it blocked in a week. If it blocks almost everything, the number may be unrealistically tight for that broker. If it never blocks, it is not a filter — it is wallpaper. Recalibration stays data-led either way.
The spread filter number I use on EURUSD at London
-
LondonScalper
- Posts: 701
- Joined: Sat Sep 05, 2026 7:54 am
Re: The spread filter number I use on EURUSD at London
Hi LondonScalper,LondonScalper wrote: Tue Sep 22, 2026 11:09 am My EURUSD London spread filter is a number on a sticky area of the screen, not a feeling.
I set it from a month of open-session samples plus a small buffer for ordinary noise. Below the line, setups are allowed to compete. Above the line, I do not negotiate with myself — the click does not happen. That sounds blunt because it has to be; negotiation is how revenge trades dress up as "liquidity will return."
Practical bits
1. Filter is session-aware: London cash open is stricter than a quiet mid-morning.
2. News blackout overrides the filter entirely — flat is flat.
3. I review the number quarterly; if my broker's open behaviour changed, the filter moves with data, not hope.
The filter does not pick entries. It only vetoes bad economics.
What number do you use on EURUSD at London, and do you keep it fixed or retune from your own log?
I occasionally stress-test the filter by noting how many A+ ideas it blocked in a week. If it blocks almost everything, the number may be unrealistically tight for that broker. If it never blocks, it is not a filter — it is wallpaper. Recalibration stays data-led either way.
On a true raw/ECN feed, the baseline EURUSD spread at the London open sits between 0.0 and 0.2 pips. Adding a buffer for normal order-book respiration, 0.8 pips is the absolute ceiling for clean execution. If it prints 0.9 or 1.0 during active London hours outside of a news window, liquidity is thinning out, and the microstructure is hostile to tight stops. For a standard spread-markup account, that ceiling shifts to around 1.5 pips.
Recalibration must remain entirely data-led. A quarterly review of execution logs is standard, but the most effective metric to track is the "veto rate." If the filter blocks more than 10-15% of the session's ticks over a rolling 14-day window, the broker's liquidity provider aggregator has likely changed its pricing model. The number must adjust to match the new reality; otherwise, the filter becomes a permanent roadblock rather than an economic shield.
To keep this running continuously as a sticky area on the screen without taking up the single Expert Advisor slot on your chart, the logic is best structured as an MQL4 Custom Indicator. It updates on every tick, turning crimson to serve as a hard visual stop for manual clicks.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The spread filter number I use on EURUSD at London
MQL4 script version 1.0
Code: Select all
//+------------------------------------------------------------------+
//| London_Spread_Veto.mq4 |
//+------------------------------------------------------------------+
#property copyright "Strict Economics Veto"
#property version "1.00"
#property strict
#property indicator_chart_window
//--- Input parameters
input double MaxSpreadPips = 0.8; // Maximum Allowed Spread (Pips)
input int LabelCorner = 0; // Corner (0: Top-Left, 1: Top-Right)
input int LabelX = 20; // X Offset
input int LabelY = 20; // Y Offset
input int FontSize = 14; // Font Size
input color ColorAllowed = clrMediumSeaGreen; // Color when Spread is OK
input color ColorBlocked = clrCrimson; // Color when Vetoed
string labelName = "SpreadVetoLabel";
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(ObjectFind(0, labelName) < 0)
{
ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, labelName, OBJPROP_CORNER, LabelCorner);
ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, LabelX);
ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, LabelY);
ObjectSetString(0, labelName, OBJPROP_FONT, "Arial Bold");
ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, FontSize);
ObjectSetInteger(0, labelName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, labelName, OBJPROP_HIDDEN, true);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectDelete(0, labelName);
}
//+------------------------------------------------------------------+
//| 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[])
{
UpdateSpreadLabel();
return(rates_total);
}
//+------------------------------------------------------------------+
//| Update the visual label on chart |
//+------------------------------------------------------------------+
void UpdateSpreadLabel()
{
double spreadPips = GetCurrentSpreadPips();
string displayText = "Spread: " + DoubleToString(spreadPips, 1) + " / " + DoubleToString(MaxSpreadPips, 1) + " pips";
color currentColor = ColorAllowed;
if(spreadPips > MaxSpreadPips)
{
currentColor = ColorBlocked;
displayText = "VETO - " + displayText;
}
ObjectSetString(0, labelName, OBJPROP_TEXT, displayText);
ObjectSetInteger(0, labelName, OBJPROP_COLOR, currentColor);
}
//+------------------------------------------------------------------+
//| Calculate spread in true pips (handles 4/5 digit brokers) |
//+------------------------------------------------------------------+
double GetCurrentSpreadPips()
{
double spreadPoints = MarketInfo(Symbol(), MODE_SPREAD);
int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
// Convert points to pips
double multiplier = 1.0;
if(digits == 3 || digits == 5)
multiplier = 10.0;
return (spreadPoints / multiplier);
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The spread filter number I use on EURUSD at London
To move this to MetaTrader 5, the core logic remains similar, but the backend calls must adapt to MQL5's stricter object management and symbol property structures. MarketInfo() is replaced by SymbolInfoInteger(), and a ChartRedraw() call is added to ensure the label updates instantly on every single tick without waiting for the next chart rendering cycle.
Save this as an indicator (e.g., London_Spread_Veto.mq5).
Save this as an indicator (e.g., London_Spread_Veto.mq5).
Code: Select all
//+------------------------------------------------------------------+
//| London_Spread_Veto.mq5 |
//+------------------------------------------------------------------+
#property copyright "Strict Economics Veto"
#property version "1.01"
#property indicator_chart_window
#property indicator_plots 0
//--- Input parameters
input double MaxSpreadPips = 0.8; // Maximum Allowed Spread (Pips)
input ENUM_BASE_CORNER LabelCorner = CORNER_LEFT_UP; // Label Corner
input int LabelX = 20; // X Offset
input int LabelY = 20; // Y Offset
input int FontSize = 14; // Font Size
input color ColorAllowed = clrMediumSeaGreen; // Color when Spread is OK
input color ColorBlocked = clrCrimson; // Color when Vetoed
string labelName = "SpreadVetoLabel";
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Check if object exists, if not, create it
if(ObjectFind(0, labelName) < 0)
{
ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, labelName, OBJPROP_CORNER, LabelCorner);
ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, LabelX);
ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, LabelY);
ObjectSetString(0, labelName, OBJPROP_FONT, "Arial Bold");
ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, FontSize);
ObjectSetInteger(0, labelName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, labelName, OBJPROP_HIDDEN, true);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectDelete(0, labelName);
ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| 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[])
{
UpdateSpreadLabel();
return(rates_total);
}
//+------------------------------------------------------------------+
//| Update the visual label on chart |
//+------------------------------------------------------------------+
void UpdateSpreadLabel()
{
double spreadPips = GetCurrentSpreadPips();
string displayText = "Spread: " + DoubleToString(spreadPips, 1) + " / " + DoubleToString(MaxSpreadPips, 1) + " pips";
color currentColor = ColorAllowed;
if(spreadPips > MaxSpreadPips)
{
currentColor = ColorBlocked;
displayText = "VETO - " + displayText;
}
ObjectSetString(0, labelName, OBJPROP_TEXT, displayText);
ObjectSetInteger(0, labelName, OBJPROP_COLOR, currentColor);
// Force the chart to redraw immediately to reflect tick changes
ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| Calculate spread in true pips (handles 4/5 digit brokers) |
//+------------------------------------------------------------------+
double GetCurrentSpreadPips()
{
// Retrieve spread in points
long spreadPoints = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD);
long digits = SymbolInfoInteger(Symbol(), SYMBOL_DIGITS);
// Convert points to pips
double multiplier = 1.0;
if(digits == 3 || digits == 5)
multiplier = 10.0;
return ((double)spreadPoints / multiplier);
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The spread filter number I use on EURUSD at London
Moving this to cTrader is where C# makes the logic much cleaner. Unlike MQL4 and MQL5, which require you to manually check symbol digits and calculate point multipliers, cAlgo's API handles pip normalization natively through Symbol.PipSize.
Because you want this as a visual overlay that leaves your cBot slots open for actual execution scripts, it is structured as an Indicator. It will evaluate exclusively on the live tick (IsLastBar), keeping CPU overhead near zero.
Because you want this as a visual overlay that leaves your cBot slots open for actual execution scripts, it is structured as an Indicator. It will evaluate exclusively on the live tick (IsLastBar), keeping CPU overhead near zero.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The spread filter number I use on EURUSD at London
Save this as a new Indicator in cTrader Automate.
Code: Select all
using System;
using cAlgo.API;
using cAlgo.API.Internals;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class LondonSpreadVeto : Indicator
{
[Parameter("Max Spread (Pips)", DefaultValue = 0.8)]
public double MaxSpreadPips { get; set; }
[Parameter("Label Position", DefaultValue = StaticPosition.TopLeft)]
public StaticPosition LabelPosition { get; set; }
protected override void Initialize()
{
// Initial render on load
UpdateSpreadLabel();
}
public override void Calculate(int index)
{
// Only recalculate on live ticks, ignoring historical back-rendering
if (IsLastBar)
{
UpdateSpreadLabel();
}
}
private void UpdateSpreadLabel()
{
// cAlgo natively handles the pip conversion for 4/5 digit pricing
double spreadPips = Symbol.Spread / Symbol.PipSize;
string displayText = $"Spread: {Math.Round(spreadPips, 1)} / {MaxSpreadPips} pips";
Color currentColor = Color.MediumSeaGreen;
if (spreadPips > MaxSpreadPips)
{
currentColor = Color.Crimson;
displayText = "VETO - " + displayText;
}
// DrawStaticText updates existing labels instantly rather than recreating them
Chart.DrawStaticText("SpreadVetoLabel", displayText, LabelPosition, currentColor);
}
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The spread filter number I use on EURUSD at London
TradingView handles screen-anchored text using table objects and requires request.security to pull 1-tick ask and bid data into your standard 15-minute or daily charts. Because historical bars do not store spread data, the script uses barstate.islast to evaluate and render the HUD exclusively on the live edge.
Code: Select all
//@version=6
indicator("London Spread Veto", overlay=true)
maxSpreadPips = input.float(0.8, title="Max Spread (Pips)")
labelCorner = input.string("Top Left", title="Label Position", options=["Top Right", "Top Left", "Bottom Right", "Bottom Left"])
// Pine Script v6 exposes live 'ask' and 'bid' variables on the 1T timeframe.
// We pull them into your current chart using request.security.
askPrice = request.security(syminfo.tickerid, "1T", ask)
bidPrice = request.security(syminfo.tickerid, "1T", bid)
// Convert raw price difference to true pips.
// For forex, a standard pip is 10x the minimum tick (points).
pipSize = syminfo.type == "forex" ? syminfo.mintick * 10 : syminfo.mintick
spreadPips = (askPrice - bidPrice) / pipSize
// Using a table ensures the label stays pinned to the screen corner like MT4/cTrader.
var pos = labelCorner == "Top Right" ? position.top_right :
labelCorner == "Top Left" ? position.top_left :
labelCorner == "Bottom Right" ? position.bottom_right : position.bottom_left
var table vetoTable = table.new(pos, 1, 1, frame_color=na, border_width=0)
if barstate.islast
bool isBlocked = spreadPips > maxSpreadPips
color currentColor = isBlocked ? color.rgb(220, 20, 60) : color.rgb(60, 179, 113)
string prefix = isBlocked ? "VETO - " : ""
string displayText = na(spreadPips) ? "Waiting for live tick..." : prefix + "Spread: " + str.tostring(spreadPips, "#.#") + " / " + str.tostring(maxSpreadPips, "#.#") + " pips"
// Renders only on the final live bar to keep historical CPU overhead at zero
table.cell(vetoTable, 0, 0, displayText, text_color=currentColor, text_halign=text.align_left, text_size=size.large)Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.