Advertisement IC Markets

Recognizing wide-to-tight spread transition on EURUSD M1

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
LondonScalper
Posts: 701
Joined: Sat Sep 05, 2026 7:54 am

Recognizing wide-to-tight spread transition on EURUSD M1

Post by LondonScalper »

Wide-to-tight spread transition on EURUSD M1 is a filter I trust more than a new pattern.

Right after a burst, the quote can be untradeable. When it snaps back toward my normal London band and stays there for more than a blink, the tape is telling me costs are usable again. I do not need a story about "who won the news" to use that information.

How I use the transition
1. No entries while wide — journal as wait.
2. First tight prints still get half size if I engage at all.
3. If wide returns immediately, I treat the snap-back as a fake and step away.

This is not an entry signal by itself. It is permission for other rules to speak.

Do you wait for a measured tight period after a wide burst, or is the first normal tick enough for you?

I timestamp the first sustained tight cluster in the journal when I trade after a burst. Later I can see whether tight-enough was premature. That feedback loop matters more than any single anecdote from a fast morning.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Recognizing wide-to-tight spread transition on EURUSD M1

Post by PTScalper »

LondonScalper wrote: Tue Sep 22, 2026 1:30 pm Wide-to-tight spread transition on EURUSD M1 is a filter I trust more than a new pattern.

Right after a burst, the quote can be untradeable. When it snaps back toward my normal London band and stays there for more than a blink, the tape is telling me costs are usable again. I do not need a story about "who won the news" to use that information.

How I use the transition
1. No entries while wide — journal as wait.
2. First tight prints still get half size if I engage at all.
3. If wide returns immediately, I treat the snap-back as a fake and step away.

This is not an entry signal by itself. It is permission for other rules to speak.

Do you wait for a measured tight period after a wide burst, or is the first normal tick enough for you?

I timestamp the first sustained tight cluster in the journal when I trade after a burst. Later I can see whether tight-enough was premature. That feedback loop matters more than any single anecdote from a fast morning.
Hi LondonScalper,

A measured tight period is absolutely necessary. A single normal tick—or even a single 1-minute close—is often just a momentary liquidity vacuum before the next wave of post-news repricing hits. Trusting the first "normal" print with full size is a great way to get caught in a secondary spread-widening whip.

Your rule of using half-size on that first tight print is the perfect compromise between capitalizing on a fast technical setup and protecting capital from a fake snap-back. Treating the transition as a filter rather than a trigger removes the psychological trap of trying to outsmart the news. You are letting the tape dictate the terms of engagement.

Since TradingView's Pine Script does not store historical bid/ask spread data for backtesting, the most robust way to build this in TV is to use the 1-minute candle's True Range as a highly correlated proxy. When the spread goes untradeable, M1 volatility almost always spikes in tandem.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Recognizing wide-to-tight spread transition on EURUSD M1

Post by PTScalper »

Here is a Pine Script (v5) that maps directly to your three rules and visualizes your feedback loop:

Code: Select all

//@version=5
indicator("Spread Transition Filter [EURUSD M1]", overlay=true)

// --- Inputs ---
// 0.0002 equals 2 pips on EURUSD. Adjust this to match your "normal London band".
normalBand = input.float(0.00020, title="Normal Band Max Range (Price)", format=format.price)
sustainedBars = input.int(3, title="Measured Tight Period (Bars)", minval=1, tooltip="How many consecutive tight bars to wait before full size is permitted.")

// --- Logic ---
// Using True Range handles gaps better than simple High-Low during news bursts
candleRange = ta.tr
isWide = candleRange > normalBand
isTight = candleRange <= normalBand

// Track consecutive tight prints
var int tightCount = 0
if isWide
    tightCount := 0 // Wide returns immediately (Rule 3) -> Reset to 0
else if isTight
    tightCount += 1

// --- State Definitions ---
stateWide = isWide
stateWaiting = tightCount > 0 and tightCount < sustainedBars
stateClear = tightCount >= sustainedBars

// --- Visualizing the Rules ---

// 1. No entries while wide (Red Background)
bgcolor(stateWide ? color.new(color.red, 85) : na, title="Wide / Untradeable")

// 2. First tight prints (Orange Background) -> Rule 2: Half size permission
bgcolor(stateWaiting ? color.new(color.orange, 85) : na, title="Waiting / Half Size")

// 3. Clear to trade (Green Background) -> Filter is fully open
bgcolor(stateClear ? color.new(color.green, 90) : na, title="Clear to Trade")

// --- Chart Markers for Journaling ---
// Timestamping the first tight print (Half Size Entry Point)
plotshape(tightCount == 1, title="First Tight Print", style=shape.triangledown, location=location.abovebar, color=color.orange, size=size.small, text="1/2")

// Timestamping the sustained tight cluster (Full Size Permission)
plotshape(tightCount == sustainedBars, title="Sustained Cluster", style=shape.triangleup, location=location.belowbar, color=color.green, size=size.small, text="Clear")
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Recognizing wide-to-tight spread transition on EURUSD M1

Post by PTScalper »

How this script supports your workflow:

Visualizes the Wait: The background turns red during the burst. When the quote snaps back, it turns orange. If it bursts again, it immediately snaps back to red (enforcing your Rule 3).

Flags the Half-Size Window: The orange 1/2 triangle marks exactly when your first tight print occurs, visually logging where your half-size entries are valid.

Timestamps the Cluster: The green Clear triangle marks the exact moment the "measured tight period" is achieved, helping you journal and review whether your half-size entries were premature or perfectly timed against the fully stabilized tape.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Recognizing wide-to-tight spread transition on EURUSD M1

Post by PTScalper »

Because MetaTrader handles historical data and background colors differently than TradingView, we need to make a few specific adjustments for MQL4 and MQL5.

Most importantly: MT5 natively stores historical spread data, meaning we do not need the True Range proxy for MT5—we can measure the actual historical spread directly. For MT4, we will stick to the True Range proxy to ensure historical backtesting works, as MT4 only records High/Low/Open/Close.

Both indicators plot a downward orange arrow for your "Half-Size" permission (first tight print) and an upward green arrow for your "Clear" permission (sustained cluster).
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Recognizing wide-to-tight spread transition on EURUSD M1

Post by PTScalper »

1. MT4 Version (MQL4) - True Range Proxy

MT4 doesn't store historical spread, so this maps the exact True Range logic from your Pine Script.

Code: Select all

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 clrDarkOrange
#property indicator_color2 clrLimeGreen

input double NormalBandPips = 2.0;    // Normal Band Max Range (Pips)
input int SustainedBars = 3;          // Measured Tight Period (Bars)

double FirstTightBuffer[];
double SustainedTightBuffer[];

int OnInit() {
    SetIndexBuffer(0, FirstTightBuffer);
    SetIndexStyle(0, DRAW_ARROW);
    SetIndexArrow(0, 234); // Downward arrow
    SetIndexLabel(0, "First Tight (Half Size)");

    SetIndexBuffer(1, SustainedTightBuffer);
    SetIndexStyle(1, DRAW_ARROW);
    SetIndexArrow(1, 233); // Upward arrow
    SetIndexLabel(1, "Clear (Full Size)");

    return(INIT_SUCCEEDED);
}

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 < SustainedBars + 1) return 0;

    double pipSize = Point;
    if(Digits == 3 || Digits == 5) pipSize *= 10;
    double normalBand = NormalBandPips * pipSize;

    // Determine how many bars to calculate to avoid recalculating the whole chart every tick
    int limit = rates_total - prev_calculated;
    if(limit == 0) limit = 1; 

    // Calculate from oldest to newest
    for(int i = limit; i >= 0; i--) {
        FirstTightBuffer[i] = EMPTY_VALUE;
        SustainedTightBuffer[i] = EMPTY_VALUE;
        
        int currentTightCount = 0;
        
        // Look backward in time (higher index in MT4) to count consecutive tight bars
        for(int j = i; j < rates_total - 1; j++) {
            // True Range calculation
            double tr = MathMax(high[j] - low[j], 
                        MathMax(MathAbs(high[j] - close[j+1]), MathAbs(low[j] - close[j+1])));
            
            if(tr <= normalBand) {
                currentTightCount++;
            } else {
                break; // Wide bar breaks the chain
            }
        }

        // Plot signals based on count
        if(currentTightCount == 1) {
            FirstTightBuffer[i] = high[i] + (2 * pipSize); // Plot slightly above High
        } 
        else if(currentTightCount == SustainedBars) {
            SustainedTightBuffer[i] = low[i] - (2 * pipSize);  // Plot slightly below Low
        }
    }
    
    return(rates_total);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Recognizing wide-to-tight spread transition on EURUSD M1

Post by PTScalper »

2. MT5 Version (MQL5) - Actual Spread Measurement

Because MT5 stores the spread[] array on every historical candle, we can use the literal spread in Points rather than the True Range proxy. This is much more accurate for your specific strategy.

Code: Select all

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2

#property indicator_label1  "First Tight (Half Size)"
#property indicator_type1   DRAW_ARROW
#property indicator_color1  clrDarkOrange

#property indicator_label2  "Clear (Full Size)"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrLimeGreen

// Note: MT5 uses Points. 20 Points = 2 Pips on standard 5-digit broker
input int NormalBandPoints = 20;     // Normal Band Max Spread (in POINTS)
input int SustainedBars = 3;         // Measured Tight Period (Bars)

double FirstTightBuffer[];
double SustainedTightBuffer[];

int OnInit() {
    SetIndexBuffer(0, FirstTightBuffer, INDICATOR_DATA);
    PlotIndexSetInteger(0, PLOT_ARROW, 234); 

    SetIndexBuffer(1, SustainedTightBuffer, INDICATOR_DATA);
    PlotIndexSetInteger(1, PLOT_ARROW, 233); 
    
    return(INIT_SUCCEEDED);
}

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 < SustainedBars) return 0;

    // Optimize iteration
    int start = (prev_calculated > 0) ? prev_calculated - 1 : 0;

    // MT5 iterates 0 (oldest) to rates_total (newest) by default
    for(int i = start; i < rates_total; i++) {
        FirstTightBuffer[i] = EMPTY_VALUE;
        SustainedTightBuffer[i] = EMPTY_VALUE;

        int currentTightCount = 0;
        
        // Look backward in time to check historical spread
        for(int j = i; j >= 0; j--) {
            if(spread[j] <= NormalBandPoints) {
                currentTightCount++;
            } else {
                break; // Wide spread breaks the chain
            }
        }

        // Plot signals
        if(currentTightCount == 1) {
            FirstTightBuffer[i] = high[i] + (10 * _Point); // Plot above High
        } 
        else if(currentTightCount == SustainedBars) {
            SustainedTightBuffer[i] = low[i] - (10 * _Point);  // Plot below Low
        }
    }
    
    return(rates_total);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Recognizing wide-to-tight spread transition on EURUSD M1

Post by PTScalper »

Installation Instructions

1.) Open your MT4/MT5 terminal and press F4 to open the MetaEditor.

2.) In the Navigator on the left, right-click Indicators -> New File -> Custom Indicator.

3.) Name it SpreadTransitionFilter, click next until finish.

4.) Replace all the auto-generated code with the code above.

5.) Hit Compile (F7).

6.) Drag and drop it onto your EURUSD M1 chart.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Recognizing wide-to-tight spread transition on EURUSD M1

Post by PTScalper »

Because cTrader’s standard historical Bars data does not natively store historical bid/ask spreads (similar to MT4), we will use the True Range proxy to ensure your visual journaling works perfectly when looking back over past charts.

In cTrader (C# / cAlgo), we use IndicatorDataSeries plotted as heavy points above and below the candles to replicate the arrows from the other platforms.

Here is the C# code for your cTrader indicator:

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 SpreadTransitionFilter : Indicator
    {
        [Parameter("Normal Band Max Range (Pips)", DefaultValue = 2.0, Group = "Filter Settings")]
        public double NormalBandPips { get; set; }

        [Parameter("Measured Tight Period (Bars)", DefaultValue = 3, MinValue = 1, Group = "Filter Settings")]
        public int SustainedBars { get; set; }

        [Output("First Tight (Half Size)", LineColor = "DarkOrange", PlotType = PlotType.Points, Thickness = 5)]
        public IndicatorDataSeries FirstTightBuffer { get; set; }

        [Output("Clear (Full Size)", LineColor = "LimeGreen", PlotType = PlotType.Points, Thickness = 5)]
        public IndicatorDataSeries SustainedTightBuffer { get; set; }

        protected override void Initialize()
        {
            // Initialization handled dynamically in Calculate
        }

        public override void Calculate(int index)
        {
            // Need at least one previous bar to calculate True Range
            if (index < 1) return;

            // Reset buffers for the current index to clear previous tick calculations
            FirstTightBuffer[index] = double.NaN;
            SustainedTightBuffer[index] = double.NaN;

            double normalBand = NormalBandPips * Symbol.PipSize;
            int currentTightCount = 0;

            // Look backward from the current bar to measure the consecutive tight sequence
            for (int i = index; i >= 1; i--)
            {
                double prevClose = Bars.ClosePrices[i - 1];
                double high = Bars.HighPrices[i];
                double low = Bars.LowPrices[i];

                // True Range calculation (handles gaps during extreme bursts)
                double tr1 = high - low;
                double tr2 = Math.Abs(high - prevClose);
                double tr3 = Math.Abs(low - prevClose);
                
                double tr = Math.Max(tr1, Math.Max(tr2, tr3));

                // If within the normal band, increment tight count. Otherwise, break the chain.
                if (tr <= normalBand)
                {
                    currentTightCount++;
                }
                else
                {
                    break; 
                }
            }

            // Plot signals based on the sequence count
            if (currentTightCount == 1)
            {
                // Plot an Orange point slightly above the High (Half Size)
                FirstTightBuffer[index] = Bars.HighPrices[index] + (2 * Symbol.PipSize);
            }
            else if (currentTightCount == SustainedBars)
            {
                // Plot a Green point slightly below the Low (Clear)
                SustainedTightBuffer[index] = Bars.LowPrices[index] - (2 * Symbol.PipSize);
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Recognizing wide-to-tight spread transition on EURUSD M1

Post by PTScalper »

Installation Instructions

1.) Open cTrader and go to the Automate tab on the left menu.

2.) Under the Indicators section, click the New icon (+ button).

3.) Name it SpreadTransitionFilter and press Enter.

4.) The code editor will open. Replace all the default code with the C# code above.

5.) Click the Build icon (or press Ctrl+B) to compile it. A green "Build Succeeded" message should appear at the bottom.

6.) Switch back to your Trade tab, click the Indicators icon at the top of your chart, go to Custom, and select SpreadTransitionFilter.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply