Page 2 of 2

Re: Session Overlap Volatility Scalping

Posted: Tue Sep 22, 2026 6:01 pm
by PTScalper
High volume during the overlap is guaranteed, but clarity is not. If algorithms are just passing heavy inventory back and forth inside the London range, volume spikes, but the trend dies.

To filter this, I’ve integrated the Average Directional Index (ADX) into the script. ADX doesn't care if the market is going up or down, and it doesn't care about volume; it strictly measures trend strength. If the ADX drops below a certain threshold (usually 20-25), it mathematically confirms the market is in a choppy, non-directional state.

Here is the updated Pine Script. It now dynamically changes the color of the NY Overlap background to warn you when the market is chopping.

The "Clarity Over Volume" Session Filter (Pine Script v5)

Code: Select all

//@version=5
indicator("London Range vs NY Overlap [Chop Filter]", overlay=true)

// --- Session & Time Inputs ---
londonSession   = input.session("0700-1300", title="Early/Mid London Session")
overlapSession  = input.session("1300-1700", title="London/NY Overlap")
tz              = input.string("UTC", title="Timezone")

// --- Chop Filter Inputs ---
adxLen          = input.int(14, title="ADX Length", group="Chop Filter")
adxThreshold    = input.int(25, title="ADX Chop Threshold", group="Chop Filter", tooltip="ADX below 25 generally indicates ranging/choppy conditions.")

// --- Session Logic ---
inLondon  = time(timeframe.period, londonSession, tz)
inOverlap = time(timeframe.period, overlapSession, tz)

// --- Track Early London Range ---
var float londonHigh = na
var float londonLow  = na

if inLondon and not inLondon[1]
    londonHigh := high
    londonLow  := low
else if inLondon
    londonHigh := math.max(londonHigh, high)
    londonLow  := math.min(londonLow, low)

// --- Trend/Chop Filter (ADX) ---
// ta.dmi returns [DI+, DI-, ADX]. We only need the ADX line to gauge trend strength.
[diPlus, diMinus, adx] = ta.dmi(adxLen, adxLen)

// It is considered "choppy" if directional momentum falls below our threshold
isChoppy = adx < adxThreshold

// --- Dynamic Background Highlighting ---
// Overlap background warns you based on market state:
// RED = Choppy (High risk of mean-reversion/chop)
// GREEN = Trending (Clear permission window)
overlapColor = isChoppy ? color.new(color.red, 92) : color.new(color.green, 92)

bgcolor(inLondon ? color.new(color.blue, 92) : na, title="London Background")
bgcolor(inOverlap ? overlapColor : na, title="Overlap Background")

// --- Plotting the Range ---
showLines = inLondon or inOverlap

plot(showLines ? londonHigh : na, color=color.new(color.blue, 30), style=plot.style_linebr, linewidth=2, title="London High")
plot(showLines ? londonLow : na, color=color.new(color.blue, 30), style=plot.style_linebr, linewidth=2, title="London Low")

// Midpoint is highly useful for spotting algorithmic mean-reversion during chop
londonMid = (londonHigh + londonLow) / 2
plot(showLines ? londonMid : na, color=color.new(color.gray, 50), style=plot.style_cross, linewidth=1, title="London Midpoint")

// --- Visual Safety Markers ---
// Plots small indicators at the bottom of the screen during the Overlap
plotshape(inOverlap and isChoppy, style=shape.xcross, location=location.bottom, color=color.new(color.red, 50), size=size.tiny, title="Chop Warning")
plotshape(inOverlap and not isChoppy, style=shape.circle, location=location.bottom, color=color.new(color.green, 50), size=size.tiny, title="Trend Active")

Re: Session Overlap Volatility Scalping

Posted: Tue Sep 22, 2026 6:02 pm
by PTScalper
How this visualizes your thesis:

Dynamic Overlap Background: Instead of a static color, the 13:00–17:00 UTC window will now paint Green if the market has clear directional momentum, and Red if the market is chopping. (Note: Green doesn't mean "buy" and Red doesn't mean "sell"—Red just means "stay away").

The "Magnet" Mid-line: Notice how often the background turns Red exactly when price fails to break the blue London boundaries and instead just snakes back and forth across the grey dotted mid-line.

The Correlation Cap: If you are holding a trade from early London and the overlap opens with a Red background, you immediately know you are in that "two-way recycle" phase. That is your cue to tighten stops or take profits, rather than hoping NY expands the range.

This directly codifies your rule of "treating the overlap as a permission window, not a mandate." If it's red, permission is denied.

Re: Session Overlap Volatility Scalping

Posted: Tue Sep 22, 2026 6:02 pm
by PTScalper
Here are the direct translations of the volume vs. clarity filter for both MetaTrader 4 and MetaTrader 5.

Since MetaTrader handles object rendering differently than TradingView (dynamically painting the entire Y-axis background per-bar via bgcolor causes severe scaling/lag issues in MT), I have optimized the visual delivery for the MetaTrader environment:

The Range: The London High, Low, and Midpoint are drawn dynamically using indicator buffers (Lines) across both sessions.

The Filter: Instead of coloring the background, the script prints visual markers directly beneath the candles during the Overlap window. A Red X signifies chop (ADX < Threshold), and a Green Circle signifies active trend (ADX ≥ Threshold).

This approach keeps your MT terminal lightweight while delivering the exact same analytical permission window for your A+ setups.

Re: Session Overlap Volatility Scalping

Posted: Tue Sep 22, 2026 6:03 pm
by PTScalper
MQL4 Version (.mq4)

Code: Select all

//+------------------------------------------------------------------+
//|                                     London_Overlap_Filter.mq4    |
//+------------------------------------------------------------------+
#property copyright "Custom Indicator"
#property link      ""
#property version   "1.00"
#property strict
#property indicator_chart_window

#property indicator_buffers 5
#property indicator_color1 clrDodgerBlue
#property indicator_color2 clrDodgerBlue
#property indicator_color3 clrGray
#property indicator_color4 clrRed
#property indicator_color5 clrLimeGreen

//--- inputs
input string InpLondonSession  = "07:00-13:00"; // London Session (HH:MM-HH:MM)
input string InpOverlapSession = "13:00-17:00"; // Overlap Session (HH:MM-HH:MM)
input int    InpAdxPeriod      = 14;            // ADX Period
input int    InpAdxThreshold   = 25;            // ADX Chop Threshold
input int    InpArrowOffsetPips= 5;             // Arrow Offset (Pips)

//--- indicator buffers
double BufLonHigh[];
double BufLonLow[];
double BufLonMid[];
double BufChop[];
double BufTrend[];

//--- session tracking
int lonStartMins, lonEndMins;
int ovrStartMins, ovrEndMins;
double currentLonHigh = 0;
double currentLonLow  = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, BufLonHigh); SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2); SetIndexLabel(0, "Lon High");
   SetIndexBuffer(1, BufLonLow);  SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 2); SetIndexLabel(1, "Lon Low");
   SetIndexBuffer(2, BufLonMid);  SetIndexStyle(2, DRAW_LINE, STYLE_DOT, 1);   SetIndexLabel(2, "Lon Mid");
   
   SetIndexBuffer(3, BufChop);    SetIndexStyle(3, DRAW_ARROW); SetIndexArrow(3, 251); SetIndexLabel(3, "Chop Warning");
   SetIndexBuffer(4, BufTrend);   SetIndexStyle(4, DRAW_ARROW); SetIndexArrow(4, 108); SetIndexLabel(4, "Trend Active");

   ParseSession(InpLondonSession, lonStartMins, lonEndMins);
   ParseSession(InpOverlapSession, ovrStartMins, ovrEndMins);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Helper: Parse Time String (HH:MM-HH:MM) to minutes from midnight |
//+------------------------------------------------------------------+
void ParseSession(string sessionStr, int &startMins, int &endMins)
  {
   int dashPos = StringFind(sessionStr, "-");
   if(dashPos > 0)
     {
      startMins = ParseTimeStr(StringSubstr(sessionStr, 0, dashPos));
      endMins   = ParseTimeStr(StringSubstr(sessionStr, dashPos + 1));
     }
  }

int ParseTimeStr(string timeStr)
  {
   int sep = StringFind(timeStr, ":");
   if(sep > 0)
     {
      int h = (int)StringToInteger(StringSubstr(timeStr, 0, sep));
      int m = (int)StringToInteger(StringSubstr(timeStr, sep + 1));
      return h * 60 + m;
     }
   return 0;
  }

bool IsInSession(datetime t, int startMins, int endMins)
  {
   int barMins = TimeHour(t) * 60 + TimeMinute(t);
   if(startMins <= endMins) return (barMins >= startMins && barMins < endMins);
   else return (barMins >= startMins || barMins < endMins); // Handles overnight
  }

//+------------------------------------------------------------------+
//| 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[])
  {
   int limit = rates_total - prev_calculated;
   if(limit == 0) limit = 1; // Update current bar
   if(prev_calculated == 0) limit = rates_total - 1; // Initial run
   
   double offset = InpArrowOffsetPips * Point * (Digits == 3 || Digits == 5 ? 10 : 1);

   // Iterate oldest to newest
   for(int i = limit; i >= 0; i--)
     {
      BufLonHigh[i] = EMPTY_VALUE;
      BufLonLow[i]  = EMPTY_VALUE;
      BufLonMid[i]  = EMPTY_VALUE;
      BufChop[i]    = EMPTY_VALUE;
      BufTrend[i]   = EMPTY_VALUE;

      bool inLondon = IsInSession(time[i], lonStartMins, lonEndMins);
      bool inOverlap = IsInSession(time[i], ovrStartMins, ovrEndMins);
      
      // i+1 is the previous chronological bar (arrays are series)
      bool wasInLondon = (i < rates_total - 1) ? IsInSession(time[i+1], lonStartMins, lonEndMins) : false;

      // Track London Range
      if(inLondon && !wasInLondon)
        {
         currentLonHigh = high[i];
         currentLonLow  = low[i];
        }
      else if(inLondon)
        {
         currentLonHigh = MathMax(currentLonHigh, high[i]);
         currentLonLow  = MathMin(currentLonLow, low[i]);
        }

      // Draw Lines if in either session
      if((inLondon || inOverlap) && currentLonHigh > 0 && currentLonLow > 0)
        {
         BufLonHigh[i] = currentLonHigh;
         BufLonLow[i]  = currentLonLow;
         BufLonMid[i]  = (currentLonHigh + currentLonLow) / 2.0;
        }

      // Overlap Filter (ADX)
      if(inOverlap)
        {
         double adx = iADX(Symbol(), Period(), InpAdxPeriod, PRICE_CLOSE, MODE_MAIN, i);
         if(adx < InpAdxThreshold)
            BufChop[i] = low[i] - offset;
         else
            BufTrend[i] = low[i] - offset;
        }
     }
   return(rates_total);
  }

Re: Session Overlap Volatility Scalping

Posted: Tue Sep 22, 2026 6:03 pm
by PTScalper
MQL5 Version (.mq5)

Code: Select all

//+------------------------------------------------------------------+
//|                                     London_Overlap_Filter.mq5    |
//+------------------------------------------------------------------+
#property copyright "Custom Indicator"
#property link      ""
#property version   "1.00"
#property indicator_chart_window

#property indicator_buffers 5
#property indicator_plots   5

//--- plot LonHigh
#property indicator_label1  "Lon High"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//--- plot LonLow
#property indicator_label2  "Lon Low"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//--- plot LonMid
#property indicator_label3  "Lon Mid"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrGray
#property indicator_style3  STYLE_DOT
#property indicator_width3  1

//--- plot Chop Warning
#property indicator_label4  "Chop Warning"
#property indicator_type4   DRAW_ARROW
#property indicator_color4  clrRed

//--- plot Trend Active
#property indicator_label5  "Trend Active"
#property indicator_type5   DRAW_ARROW
#property indicator_color5  clrLimeGreen

//--- inputs
input string InpLondonSession  = "07:00-13:00"; // London Session (HH:MM-HH:MM)
input string InpOverlapSession = "13:00-17:00"; // Overlap Session (HH:MM-HH:MM)
input int    InpAdxPeriod      = 14;            // ADX Period
input int    InpAdxThreshold   = 25;            // ADX Chop Threshold
input int    InpArrowOffsetPips= 5;             // Arrow Offset (Pips)

//--- indicator buffers
double BufLonHigh[];
double BufLonLow[];
double BufLonMid[];
double BufChop[];
double BufTrend[];

//--- variables
int lonStartMins, lonEndMins;
int ovrStartMins, ovrEndMins;
double currentLonHigh = 0;
double currentLonLow  = 0;
int handleADX;
double adxBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, BufLonHigh, INDICATOR_DATA);
   SetIndexBuffer(1, BufLonLow, INDICATOR_DATA);
   SetIndexBuffer(2, BufLonMid, INDICATOR_DATA);
   
   SetIndexBuffer(3, BufChop, INDICATOR_DATA);
   PlotIndexSetInteger(3, PLOT_ARROW, 251); // X
   PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, 0);

   SetIndexBuffer(4, BufTrend, INDICATOR_DATA);
   PlotIndexSetInteger(4, PLOT_ARROW, 108); // Circle
   PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, 0);

   ParseSession(InpLondonSession, lonStartMins, lonEndMins);
   ParseSession(InpOverlapSession, ovrStartMins, ovrEndMins);

   handleADX = iADX(_Symbol, _Period, InpAdxPeriod);
   if(handleADX == INVALID_HANDLE)
     {
      Print("Failed to create ADX handle");
      return(INIT_FAILED);
     }
   ArraySetAsSeries(adxBuffer, true);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Helpers                                                          |
//+------------------------------------------------------------------+
void ParseSession(string sessionStr, int &startMins, int &endMins)
  {
   int dashPos = StringFind(sessionStr, "-");
   if(dashPos > 0)
     {
      startMins = ParseTimeStr(StringSubstr(sessionStr, 0, dashPos));
      endMins   = ParseTimeStr(StringSubstr(sessionStr, dashPos + 1));
     }
  }

int ParseTimeStr(string timeStr)
  {
   int sep = StringFind(timeStr, ":");
   if(sep > 0)
     {
      int h = (int)StringToInteger(StringSubstr(timeStr, 0, sep));
      int m = (int)StringToInteger(StringSubstr(timeStr, sep + 1));
      return h * 60 + m;
     }
   return 0;
  }

bool IsInSession(datetime t, int startMins, int endMins)
  {
   MqlDateTime dt;
   TimeToStruct(t, dt);
   int barMins = dt.hour * 60 + dt.min;
   if(startMins <= endMins) return (barMins >= startMins && barMins < endMins);
   else return (barMins >= startMins || barMins < endMins); 
  }

//+------------------------------------------------------------------+
//| 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[])
  {
   ArraySetAsSeries(time, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);

   int limit = rates_total - prev_calculated;
   if(limit == 0) limit = 1;
   if(prev_calculated == 0) limit = rates_total - 1;

   // Copy ADX data
   if(CopyBuffer(handleADX, 0, 0, limit + 1, adxBuffer) <= 0) return 0;
   
   double offset = InpArrowOffsetPips * _Point * (_Digits == 3 || _Digits == 5 ? 10 : 1);

   for(int i = limit; i >= 0; i--)
     {
      BufLonHigh[i] = 0;
      BufLonLow[i]  = 0;
      BufLonMid[i]  = 0;
      BufChop[i]    = 0;
      BufTrend[i]   = 0;

      bool inLondon = IsInSession(time[i], lonStartMins, lonEndMins);
      bool inOverlap = IsInSession(time[i], ovrStartMins, ovrEndMins);
      bool wasInLondon = (i < rates_total - 1) ? IsInSession(time[i+1], lonStartMins, lonEndMins) : false;

      if(inLondon && !wasInLondon)
        {
         currentLonHigh = high[i];
         currentLonLow  = low[i];
        }
      else if(inLondon)
        {
         currentLonHigh = MathMax(currentLonHigh, high[i]);
         currentLonLow  = MathMin(currentLonLow, low[i]);
        }

      if((inLondon || inOverlap) && currentLonHigh > 0 && currentLonLow > 0)
        {
         BufLonHigh[i] = currentLonHigh;
         BufLonLow[i]  = currentLonLow;
         BufLonMid[i]  = (currentLonHigh + currentLonLow) / 2.0;
        }

      if(inOverlap)
        {
         double currentAdx = adxBuffer[i]; // Buffer is matched to current 'i'
         if(currentAdx < InpAdxThreshold)
            BufChop[i] = low[i] - offset;
         else
            BufTrend[i] = low[i] - offset;
        }
     }
   return(rates_total);
  }

Re: Session Overlap Volatility Scalping

Posted: Tue Sep 22, 2026 6:04 pm
by PTScalper
cTrader’s API handles this beautifully because we can return double.NaN outside of the session hours, which natively breaks the lines so they don’t drag across the overnight gap between yesterday's overlap and today's London open.

For the visual ADX filter, instead of painting the background (which gets clunky in cTrader), I used PlotType.Points. This renders clean Red or Green dots directly below the candles during the NY Overlap window, based on whether the ADX is crossing your chop threshold.

cTrader C# Version (.algo)

Code: Select all

using System;
using cAlgo.API;
using cAlgo.API.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class LondonOverlapFilter : Indicator
    {
        // --- Session Inputs ---
        [Parameter("London Start (HH:mm)", DefaultValue = "07:00", Group = "Sessions")]
        public string LondonStartStr { get; set; }

        [Parameter("London End / Overlap Start (HH:mm)", DefaultValue = "13:00", Group = "Sessions")]
        public string LondonEndStr { get; set; }

        [Parameter("Overlap End (HH:mm)", DefaultValue = "17:00", Group = "Sessions")]
        public string OverlapEndStr { get; set; }

        // --- Filter Inputs ---
        [Parameter("ADX Period", DefaultValue = 14, Group = "Chop Filter")]
        public int AdxPeriod { get; set; }

        [Parameter("ADX Threshold", DefaultValue = 25.0, Group = "Chop Filter")]
        public double AdxThreshold { get; set; }

        [Parameter("Marker Offset (Pips)", DefaultValue = 5.0, Group = "Visuals")]
        public double MarkerOffset { get; set; }

        // --- Outputs ---
        [Output("London High", LineColor = "DodgerBlue", Thickness = 2, PlotType = PlotType.Line)]
        public IndicatorDataSeries LonHigh { get; set; }

        [Output("London Low", LineColor = "DodgerBlue", Thickness = 2, PlotType = PlotType.Line)]
        public IndicatorDataSeries LonLow { get; set; }

        [Output("London Mid", LineColor = "Gray", Thickness = 1, LineStyle = LineStyle.Lines)]
        public IndicatorDataSeries LonMid { get; set; }

        [Output("Chop Warning", LineColor = "Red", Thickness = 4, PlotType = PlotType.Points)]
        public IndicatorDataSeries ChopWarning { get; set; }

        [Output("Trend Active", LineColor = "LimeGreen", Thickness = 4, PlotType = PlotType.Points)]
        public IndicatorDataSeries TrendActive { get; set; }

        // --- Internal Variables ---
        private DirectionalMovementIndex _dmi;
        private TimeSpan _londonStart, _londonEnd, _overlapEnd;
        private double _currentHigh = double.NaN;
        private double _currentLow = double.NaN;

        protected override void Initialize()
        {
            // Parse session strings
            TimeSpan.TryParse(LondonStartStr, out _londonStart);
            TimeSpan.TryParse(LondonEndStr, out _londonEnd);
            TimeSpan.TryParse(OverlapEndStr, out _overlapEnd);

            // Initialize ADX (DMI contains ADX, DI+, DI-)
            _dmi = Indicators.DirectionalMovementIndex(AdxPeriod);
        }

        public override void Calculate(int index)
        {
            // Use chart's local bar time
            TimeSpan barTime = Bars.OpenTimes[index].TimeOfDay;

            bool inLondon = IsInSession(barTime, _londonStart, _londonEnd);
            bool inOverlap = IsInSession(barTime, _londonEnd, _overlapEnd);
            
            // Check previous bar to detect session start
            bool wasInLondon = index > 0 && IsInSession(Bars.OpenTimes[index - 1].TimeOfDay, _londonStart, _londonEnd);

            // 1. Track London Range
            if (inLondon && !wasInLondon)
            {
                // Reset range at the start of London
                _currentHigh = Bars.HighPrices[index];
                _currentLow = Bars.LowPrices[index];
            }
            else if (inLondon)
            {
                // Expand range if broken
                _currentHigh = Math.Max(_currentHigh, Bars.HighPrices[index]);
                _currentLow = Math.Min(_currentLow, Bars.LowPrices[index]);
            }

            // 2. Plot London Range (Projects through Overlap)
            if ((inLondon || inOverlap) && !double.IsNaN(_currentHigh))
            {
                LonHigh[index] = _currentHigh;
                LonLow[index] = _currentLow;
                LonMid[index] = (_currentHigh + _currentLow) / 2.0;
            }
            else
            {
                // Return NaN outside of sessions so lines do not connect across the daily rollover
                LonHigh[index] = double.NaN;
                LonLow[index] = double.NaN;
                LonMid[index] = double.NaN;
            }

            // 3. Evaluate Overlap Filter (ADX)
            if (inOverlap)
            {
                double currentAdx = _dmi.ADX[index];
                double offset = MarkerOffset * Symbol.PipSize;
                double markerPrice = Bars.LowPrices[index] - offset;

                if (currentAdx < AdxThreshold)
                {
                    // Choppy: Plot Red Dot, clear Green Dot
                    ChopWarning[index] = markerPrice;
                    TrendActive[index] = double.NaN;
                }
                else
                {
                    // Trending: Plot Green Dot, clear Red Dot
                    TrendActive[index] = markerPrice;
                    ChopWarning[index] = double.NaN;
                }
            }
            else
            {
                ChopWarning[index] = double.NaN;
                TrendActive[index] = double.NaN;
            }
        }

        // Helper: Handles cross-midnight sessions if you ever adjust the hours
        private bool IsInSession(TimeSpan time, TimeSpan start, TimeSpan end)
        {
            if (start <= end)
                return time >= start && time < end;
            else
                return time >= start || time < end; 
        }
    }
}

Re: Session Overlap Volatility Scalping

Posted: Tue Sep 22, 2026 9:15 pm
by PropScalpDesk
PTScalper wrote:Early London has definitively been taking more of my R lately. London usually establishes the structural blueprint for the day. When the overlap hits, I treat it as a highly conditional permission window — scale out if already positioned, or only take clean continuation / macro-aligned setups if flat.
Glad the volume-versus-clarity distinction landed. Your early-London R share matches what I see from Frankfurt: by the time NY joins, the useful impulse is often already booked, and the 13:00–16:00 UTC box can turn into two-way recycle that chops mental capital even when spreads look friendly. Treating overlap as exit liquidity when you are already onside is the part I want written into more prop playbooks — "I missed the morning" FOMO is how correlated USD tickets stack into one ugly print.

Desk rule: overlap is permission only with a one-line thesis plus a correlation cap; if flat after London, default is observe unless NY continues London structure or a scheduled USD print aligns. Charts that isolate the early London range into the overlap window are useful furniture; they do not replace the written gate.

On days when London already paid 2R before 12:00 UTC, do you hard-lock new risk for the overlap, or still allow one A+ continuation if NY opens without recycle?