Advertisement IC Markets

Best forex pairs to scalp during london session

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Best forex pairs to scalp during london session

Post by PTScalper »

The Pine Script (v5)

Code: Select all

//@version=5
indicator("EURGBP Quiet Range Sweeps", overlay=true)

// --- Inputs ---
grp1 = "Range Settings"
length = input.int(20, title="Band Length", group=grp1)
mult = input.float(2.0, title="Band Multiplier", group=grp1)

grp2 = "Volatility Filter (Avoid Breakouts)"
atrLen = input.int(14, title="ATR Length", group=grp2)
volThreshold = input.float(1.0, title="Max Volatility Threshold", tooltip="1.0 = Average Volatility. Lower means quieter market.", step=0.1, group=grp2)

// --- Range Calculation (Bollinger Bands) ---
basis = ta.sma(close, length)
dev = mult * ta.stdev(close, length)
upperBand = basis + dev
lowerBand = basis - dev

// --- Volatility Filter (Normalized ATR) ---
// We compare current ATR to a long-term ATR moving average to detect "quiet" vs "catalyst" regimes
currentAtr = ta.atr(atrLen)
avgAtr = ta.sma(currentAtr, 100)
volatilityRatio = currentAtr / (avgAtr == 0 ? 1 : avgAtr)

// Is the market in a quiet, range-bound state?
isQuiet = volatilityRatio < volThreshold

// --- Liquidity Sweep Logic ---
// Sweep High: Price poked above upper band but closed inside
sweepHigh = high > upperBand and close < upperBand and open < upperBand

// Sweep Low: Price poked below lower band but closed inside
sweepLow = low < lowerBand and close > lowerBand and open > lowerBand

// --- Signals ---
fadeShort = isQuiet and sweepHigh
fadeLong = isQuiet and sweepLow

// --- Plotting ---
// Draw the range
plot(basis, color=color.new(color.gray, 50), title="Mean")
p1 = plot(upperBand, color=color.new(color.blue, 70), title="Upper Range")
p2 = plot(lowerBand, color=color.new(color.blue, 70), title="Lower Range")
fill(p1, p2, color=color.new(color.blue, 95), title="Range Background")

// Draw the signals
plotshape(fadeShort, title="Fade Short", location=location.abovebar, color=color.red, style=shape.triangledown, size=size.small, text="FADE")
plotshape(fadeLong, title="Fade Long", location=location.belowbar, color=color.green, style=shape.triangleup, size=size.small, text="FADE")

// Alert conditions for scalping
alertcondition(fadeShort, title="EURGBP Short Sweep", message="Bearish liquidity sweep in quiet market conditions.")
alertcondition(fadeLong, title="EURGBP Long Sweep", message="Bullish liquidity sweep in quiet market conditions.")
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Best forex pairs to scalp during london session

Post by PTScalper »

If you drop this onto a 5m or 15m EURGBP chart, you'll see it completely ignores the massive trend days (like when the Bank of England unexpectedly hikes) and strictly targets those slow, grinding, 15-pip sweeps you were talking about.

Would be curious to hear if you use time-of-day filters alongside your sweep logic. Do you find the Frankfurt/London overlap provides the best sweeps, or are you scalping it during the dead NY afternoon session when it's practically flatlining?
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Best forex pairs to scalp during london session

Post by PTScalper »

To operationalize your framework, I’ve engineered a more advanced Pine Script. Rather than relying on basic Bollinger Bands, this model utilizes a Rolling Z-Score to measure standard deviations from the mean, paired with a Volatility Regime Filter (VRF) and a Session Killzone constraint. This ensures we only fade sweeps when statistical extremes align with the appropriate market microstructure and time-of-day liquidity.

The Pine Script (v5): Statistical Sweep & Regime Filter

Code: Select all

//@version=5
indicator("EURGBP Statistical Mean-Reversion [Pro]", overlay=true, max_labels_count=500)

// =========================================================================
// INPUTS & PARAMETERS
// =========================================================================
grp_stat = "Statistical Mean-Reversion (Z-Score)"
zLength  = input.int(21, "Rolling Window Length", group=grp_stat)
zThresh  = input.float(2.0, "Z-Score Sweep Threshold", step=0.1, group=grp_stat)

grp_vol  = "Volatility Regime Filter (VRF)"
atrLen   = input.int(14, "ATR Length", group=grp_vol)
atrAvg   = input.int(100, "ATR Baseline Length", group=grp_vol)
volLimit = input.float(1.1, "Max Volatility Ratio", tooltip="Disables signals during structural breakouts", step=0.05, group=grp_vol)

grp_time = "Session Liquidity (Killzones)"
useTime  = input.bool(true, "Enable Session Filter", group=grp_time)
session  = input.session("0800-1600", "Trading Window (Exchange Time)", group=grp_time)

// =========================================================================
// CORE CALCULATIONS
// =========================================================================
// 1. Z-Score (Statistical Standard Deviation)
mean    = ta.sma(close, zLength)
stdDev  = ta.stdev(close, zLength)
zScore  = stdDev == 0 ? 0 : (close - mean) / stdDev
zHigh   = (high - mean) / stdDev
zLow    = (low - mean) / stdDev

// 2. Volatility Regime Filter
currentAtr = ta.atr(atrLen)
baselineAtr = ta.sma(currentAtr, atrAvg)
volRatio = currentAtr / (baselineAtr == 0 ? 1 : baselineAtr)
isRangeRegime = volRatio < volLimit

// 3. Time Session Filter
inSession = not useTime or not na(time(timeframe.period, session))

// =========================================================================
// SIGNAL LOGIC (LIQUIDITY SWEEPS)
// =========================================================================
// Bearish Sweep: High pierces upper Z-threshold, but closes below it
sweepShort = (zHigh > zThresh) and (zScore < zThresh) and isRangeRegime and inSession

// Bullish Sweep: Low pierces lower Z-threshold, but closes above it
sweepLong  = (zLow < -zThresh) and (zScore > -zThresh) and isRangeRegime and inSession

// =========================================================================
// VISUALIZATION & UI
// =========================================================================
// Plot dynamic mean and statistical extremes
upperBand = mean + (stdDev * zThresh)
lowerBand = mean - (stdDev * zThresh)

plot(mean, color=color.new(color.gray, 60), title="Fair Value (Mean)")
p1 = plot(upperBand, color=color.new(color.teal, 70), style=plot.style_cross, title="Upper Statistical Bound")
p2 = plot(lowerBand, color=color.new(color.teal, 70), style=plot.style_cross, title="Lower Statistical Bound")

// Signal Generation
plotshape(sweepShort, title="Institutional Fade Short", location=location.abovebar, color=color.rgb(255, 82, 82), style=shape.labeldown, size=size.tiny, text="FADE", textcolor=color.white)
plotshape(sweepLong, title="Institutional Fade Long", location=location.belowbar, color=color.rgb(38, 166, 154), style=shape.labelup, size=size.tiny, text="FADE", textcolor=color.white)

// Background highlight for active trading session
bgcolor(inSession ? color.new(color.blue, 97) : na, title="Session Highlight")

// =========================================================================
// ALERTS
// =========================================================================
alertcondition(sweepShort, title="EURGBP Z-Score Sweep Short", message="Statistical bearish sweep executed within structural range constraints.")
alertcondition(sweepLong, title="EURGBP Z-Score Sweep Long", message="Statistical bullish sweep executed within structural range constraints.")
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Best forex pairs to scalp during london session

Post by PTScalper »

Key Upgrades in this Model:

Statistical Z-Score vs. Bollinger Bands: Instead of standard bands, it calculates the Z-Score of the wick highs/lows. If a wick penetrates a 2.0 Standard Deviation extreme but the body closes back inside the distribution curve, it registers as a liquidity extraction event rather than a breakout.

Volatility Regime Filter (VRF): It compares a fast ATR to a 100-period baseline ATR. If volatility expands beyond a 1.1 ratio (indicating macro news, ECB/BOE divergence, or a true structural shift), the algorithm automatically suppresses all mean-reversion signals to prevent catching a falling knife.

Session Constraints: Range algorithms perform poorly during dead zones (Asian session for this pair) due to the aforementioned execution drag. The script includes a customizable killzone (defaulted to London/Frankfurt overlap into early NY) to ensure signals only fire when volume is sufficient to execute the reversion.

I'd be curious to hear your thoughts on order flow confirmation at these sweep levels. When you observe a sweep on the structural timeframes, are you utilizing footprint charts or delta divergence on the micro-timeframes to validate the absorption, or simply executing via limit orders based on the structural close?
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Best forex pairs to scalp during london session

Post by PTScalper »

Here are the production-ready MQL4 and MQL5 implementations of the EURGBP Statistical Mean-Reversion Model. Both versions preserve the exact mathematical structure: rolling Z-Score boundaries, ATR-based Volatility Regime Filtering (VRF) to block breakout signals, session killzone filtering, and non-repainting signal logic.

1. MetaTrader 4 (MQL4)
Save this file as EURGBP_Statistical_Sweeps.mq4 in your MQL4/Indicators/ directory.

Code: Select all

//+------------------------------------------------------------------+
//|                                  EURGBP_Statistical_Sweeps.mq4   |
//|                                      Pro Mean-Reversion Indicator|
//+------------------------------------------------------------------+
#property copyright "Quantitative Research"
#property link      ""
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 5

// --- Buffer Display Styles
#property indicator_color1 clrSilver
#property indicator_color2 clrDarkTurquoise
#property indicator_color3 clrDarkTurquoise
#property indicator_color4 clrMediumSeaGreen
#property indicator_color5 clrCrimson

#property indicator_width1 1
#property indicator_width2 1
#property indicator_width3 1
#property indicator_width4 2
#property indicator_width5 2

// --- Input Parameters
input string  InpHeader1     = "=== Statistical Framework ==="; // ---
input int     InpZLength     = 21;        // Rolling Window (SMA / StdDev)
input double  InpZThresh     = 2.0;       // Z-Score Sweep Threshold

input string  InpHeader2     = "=== Volatility Regime Filter ==="; // ---
input int     InpAtrLen      = 14;        // Fast ATR Period
input int     InpAtrAvg      = 100;       // ATR Baseline Period
input double  InpVolLimit    = 1.1;       // Max Volatility Ratio (Breakout Filter)

input string  InpHeader3     = "=== Session Killzone ==="; // ---
input bool    InpUseTime     = true;      // Enable Killzone Filter
input int     InpStartHour   = 8;         // Session Start Hour (Broker Time)
input int     InpStartMinute = 0;         // Session Start Minute
input int     InpEndHour     = 16;        // Session End Hour (Broker Time)
input int     InpEndMinute   = 0;         // Session End Minute

input string  InpHeader4     = "=== Notifications ==="; // ---
input bool    InpEnableAlert = true;      // Enable Terminal Alerts
input bool    InpEnablePush  = false;     // Enable Push Notifications

// --- Indicator Buffers
double BufferMean[];
double BufferUpper[];
double BufferLower[];
double BufferBuy[];
double BufferSell[];

// --- State Variables
datetime lastAlertTime = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   IndicatorDigits(Digits);

   SetIndexBuffer(0, BufferMean);
   SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 1);
   SetIndexLabel(0, "Fair Value (Mean)");

   SetIndexBuffer(1, BufferUpper);
   SetIndexStyle(1, DRAW_LINE, STYLE_DOT, 1);
   SetIndexLabel(1, "Upper Z-Bound");

   SetIndexBuffer(2, BufferLower);
   SetIndexStyle(2, DRAW_LINE, STYLE_DOT, 1);
   SetIndexLabel(2, "Lower Z-Bound");

   SetIndexBuffer(3, BufferBuy);
   SetIndexStyle(3, DRAW_ARROW);
   SetIndexArrow(3, 233); // Wingdings Up Arrow
   SetIndexLabel(3, "Fade Long");

   SetIndexBuffer(4, BufferSell);
   SetIndexStyle(4, DRAW_ARROW);
   SetIndexArrow(4, 234); // Wingdings Down Arrow
   SetIndexLabel(4, "Fade Short");

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 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 minBarsRequired = InpZLength + InpAtrAvg + 1;
   if(rates_total < minBarsRequired) return(0);

   int limit = rates_total - prev_calculated;
   if(prev_calculated > 0) limit++;
   if(limit > rates_total - minBarsRequired) limit = rates_total - minBarsRequired;

   for(int i = limit; i >= 0; i--)
   {
      // 1. Rolling Mean & Standard Deviation
      double mean   = iMA(NULL, 0, InpZLength, 0, MODE_SMA, PRICE_CLOSE, i);
      double stdDev = iStdDev(NULL, 0, InpZLength, 0, MODE_SMA, PRICE_CLOSE, i);
      double upper  = mean + (stdDev * InpZThresh);
      double lower  = mean - (stdDev * InpZThresh);

      BufferMean[i]  = mean;
      BufferUpper[i] = upper;
      BufferLower[i] = lower;
      BufferBuy[i]   = EMPTY_VALUE;
      BufferSell[i]  = EMPTY_VALUE;

      // 2. Volatility Regime Filter (VRF)
      double currentAtr = iATR(NULL, 0, InpAtrLen, i);
      double sumAtr = 0.0;
      for(int j = 0; j < InpAtrAvg; j++)
      {
         sumAtr += iATR(NULL, 0, InpAtrLen, i + j);
      }
      double baselineAtr = sumAtr / InpAtrAvg;
      double volRatio = (baselineAtr > 0.0) ? (currentAtr / baselineAtr) : 1.0;
      bool isRangeRegime = (volRatio < InpVolLimit);

      // 3. Time Filter
      bool inSession = true;
      if(InpUseTime)
      {
         MqlDateTime dt;
         TimeToStruct(time[i], dt);
         int curMinutes   = dt.hour * 60 + dt.min;
         int startMinutes = InpStartHour * 60 + InpStartMinute;
         int endMinutes   = InpEndHour * 60 + InpEndMinute;

         if(startMinutes <= endMinutes)
            inSession = (curMinutes >= startMinutes && curMinutes < endMinutes);
         else
            inSession = (curMinutes >= startMinutes || curMinutes < endMinutes);
      }

      // 4. Sweep Signal Execution (Candle body inside, wick penetrating)
      bool sweepShort = (high[i] > upper) && (close[i] < upper) && isRangeRegime && inSession;
      bool sweepLong  = (low[i] < lower)  && (close[i] > lower) && isRangeRegime && inSession;

      if(sweepLong)  BufferBuy[i]  = low[i] - (stdDev * 0.25);
      if(sweepShort) BufferSell[i] = high[i] + (stdDev * 0.25);
   }

   // 5. Alert Trigger on Bar Close (Bar 1 confirmed)
   if(InpEnableAlert && rates_total > 1)
   {
      datetime barTime = time[0];
      if(barTime != lastAlertTime)
      {
         if(BufferBuy[1] != EMPTY_VALUE && BufferBuy[1] != 0.0)
         {
            SendNotificationSignal("Bullish Mean-Reversion Sweep");
            lastAlertTime = barTime;
         }
         else if(BufferSell[1] != EMPTY_VALUE && BufferSell[1] != 0.0)
         {
            SendNotificationSignal("Bearish Mean-Reversion Sweep");
            lastAlertTime = barTime;
         }
      }
   }

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Helper: Send Alert Dispatch                                      |
//+------------------------------------------------------------------+
void SendNotificationSignal(string type)
{
   string msg = StringFormat("[%s] %s on %s, %s", Symbol(), type, EnumToString((ENUM_TIMEFRAMES)Period()), TimeToString(TimeCurrent(), TIME_SECONDS));
   Alert(msg);
   if(InpEnablePush) SendNotification(msg);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Best forex pairs to scalp during london session

Post by PTScalper »

2. MetaTrader 5 (MQL5)

Save this file as EURGBP_Statistical_Sweeps.mq5 in your MQL5/Indicators/ directory. It uses indicator handles and explicit memory-mapped series indexing.

Code: Select all

//+------------------------------------------------------------------+
//|                                  EURGBP_Statistical_Sweeps.mq5   |
//|                                      Pro Mean-Reversion Indicator|
//+------------------------------------------------------------------+
#property copyright "Quantitative Research"
#property link      ""
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_plots   5

// --- Plot Definitions
#property indicator_label1  "Mean"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrSilver
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

#property indicator_label2  "Upper Z-Bound"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDarkTurquoise
#property indicator_style2  STYLE_DOT
#property indicator_width2  1

#property indicator_label3  "Lower Z-Bound"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrDarkTurquoise
#property indicator_style3  STYLE_DOT
#property indicator_width3  1

#property indicator_label4  "Fade Long"
#property indicator_type4   DRAW_ARROW
#property indicator_color4  clrMediumSeaGreen
#property indicator_width4  2

#property indicator_label5  "Fade Short"
#property indicator_type5   DRAW_ARROW
#property indicator_color5  clrCrimson
#property indicator_width5  2

// --- Input Parameters
input string  InpHeader1     = "=== Statistical Framework ==="; // ---
input int     InpZLength     = 21;        // Rolling Window (SMA / StdDev)
input double  InpZThresh     = 2.0;       // Z-Score Sweep Threshold

input string  InpHeader2     = "=== Volatility Regime Filter ==="; // ---
input int     InpAtrLen      = 14;        // Fast ATR Period
input int     InpAtrAvg      = 100;       // ATR Baseline Period
input double  InpVolLimit    = 1.1;       // Max Volatility Ratio (Breakout Filter)

input string  InpHeader3     = "=== Session Killzone ==="; // ---
input bool    InpUseTime     = true;      // Enable Killzone Filter
input int     InpStartHour   = 8;         // Session Start Hour (Broker Time)
input int     InpStartMinute = 0;         // Session Start Minute
input int     InpEndHour     = 16;        // Session End Hour (Broker Time)
input int     InpEndMinute   = 0;         // Session End Minute

input string  InpHeader4     = "=== Notifications ==="; // ---
input bool    InpEnableAlert = true;      // Enable Terminal Alerts
input bool    InpEnablePush  = false;     // Enable Push Notifications

// --- Indicator Buffers
double BufferMean[];
double BufferUpper[];
double BufferLower[];
double BufferBuy[];
double BufferSell[];

// --- Core Indicator Handles
int handleMA;
int handleStdDev;
int handleATR;

// --- State Variables
datetime lastAlertTime = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, BufferMean, INDICATOR_DATA);
   SetIndexBuffer(1, BufferUpper, INDICATOR_DATA);
   SetIndexBuffer(2, BufferLower, INDICATOR_DATA);
   SetIndexBuffer(3, BufferBuy, INDICATOR_DATA);
   SetIndexBuffer(4, BufferSell, INDICATOR_DATA);

   PlotIndexSetInteger(3, PLOT_ARROW, 233); // Wingdings Up Arrow
   PlotIndexSetInteger(4, PLOT_ARROW, 234); // Wingdings Down Arrow

   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   // Configure Handles
   handleMA     = iMA(_Symbol, _Period, InpZLength, 0, MODE_SMA, PRICE_CLOSE);
   handleStdDev = iStdDev(_Symbol, _Period, InpZLength, 0, MODE_SMA, PRICE_CLOSE);
   handleATR    = iATR(_Symbol, _Period, InpAtrLen);

   if(handleMA == INVALID_HANDLE || handleStdDev == INVALID_HANDLE || handleATR == INVALID_HANDLE)
   {
      Print("Indicator initialization failed: Error initializing built-in handles.");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   IndicatorRelease(handleMA);
   IndicatorRelease(handleStdDev);
   IndicatorRelease(handleATR);
}

//+------------------------------------------------------------------+
//| 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 minRequired = InpZLength + InpAtrAvg + 5;
   if(rates_total < minRequired) return(0);

   // Map all arrays to timeseries logic (Index 0 = Most recent candle)
   ArraySetAsSeries(time, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(BufferMean, true);
   ArraySetAsSeries(BufferUpper, true);
   ArraySetAsSeries(BufferLower, true);
   ArraySetAsSeries(BufferBuy, true);
   ArraySetAsSeries(BufferSell, true);

   // Query Handle Buffers
   double maVal[], stdVal[], atrVal[];
   ArraySetAsSeries(maVal, true);
   ArraySetAsSeries(stdVal, true);
   ArraySetAsSeries(atrVal, true);

   int copyCount = rates_total;
   if(CopyBuffer(handleMA, 0, 0, copyCount, maVal) <= 0) return(0);
   if(CopyBuffer(handleStdDev, 0, 0, copyCount, stdVal) <= 0) return(0);
   if(CopyBuffer(handleATR, 0, 0, copyCount, atrVal) <= 0) return(0);

   int limit = rates_total - prev_calculated;
   if(prev_calculated > 0) limit++;
   if(limit > rates_total - minRequired) limit = rates_total - minRequired;

   for(int i = limit; i >= 0; i--)
   {
      double mean   = maVal[i];
      double stdDev = stdVal[i];
      double upper  = mean + (stdDev * InpZThresh);
      double lower  = mean - (stdDev * InpZThresh);

      BufferMean[i]  = mean;
      BufferUpper[i] = upper;
      BufferLower[i] = lower;
      BufferBuy[i]   = EMPTY_VALUE;
      BufferSell[i]  = EMPTY_VALUE;

      // Volatility Regime Filter (Rolling ATR Average)
      double currentAtr = atrVal[i];
      double sumAtr = 0.0;
      for(int j = 0; j < InpAtrAvg; j++)
      {
         sumAtr += atrVal[i + j];
      }
      double baselineAtr = sumAtr / (double)InpAtrAvg;
      double volRatio = (baselineAtr > 0.0) ? (currentAtr / baselineAtr) : 1.0;
      bool isRangeRegime = (volRatio < InpVolLimit);

      // Session Killzone
      bool inSession = true;
      if(InpUseTime)
      {
         MqlDateTime dt;
         TimeToStruct(time[i], dt);
         int curMinutes   = dt.hour * 60 + dt.min;
         int startMinutes = InpStartHour * 60 + InpStartMinute;
         int endMinutes   = InpEndHour * 60 + InpEndMinute;

         if(startMinutes <= endMinutes)
            inSession = (curMinutes >= startMinutes && curMinutes < endMinutes);
         else
            inSession = (curMinutes >= startMinutes || curMinutes < endMinutes);
      }

      // Signal Execution
      bool sweepShort = (high[i] > upper) && (close[i] < upper) && isRangeRegime && inSession;
      bool sweepLong  = (low[i] < lower)  && (close[i] > lower) && isRangeRegime && inSession;

      if(sweepLong)  BufferBuy[i]  = low[i] - (stdDev * 0.25);
      if(sweepShort) BufferSell[i] = high[i] + (stdDev * 0.25);
   }

   // Signal Notifications on Completed Bar
   if(InpEnableAlert && rates_total > 1)
   {
      datetime barTime = time[0];
      if(barTime != lastAlertTime)
      {
         if(BufferBuy[1] != EMPTY_VALUE)
         {
            SendNotificationSignal("Bullish Mean-Reversion Sweep");
            lastAlertTime = barTime;
         }
         else if(BufferSell[1] != EMPTY_VALUE)
         {
            SendNotificationSignal("Bearish Mean-Reversion Sweep");
            lastAlertTime = barTime;
         }
      }
   }

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Helper: Send Alert Dispatch                                      |
//+------------------------------------------------------------------+
void SendNotificationSignal(string type)
{
   string msg = StringFormat("[%s] %s on %s, %s", _Symbol, type, EnumToString(_Period), TimeToString(TimeCurrent(), TIME_SECONDS));
   Alert(msg);
   if(InpEnablePush) SendNotification(msg);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Best forex pairs to scalp during london session

Post by PTScalper »

Architectural Highlights for MetaTrader Platforms

Execution Non-Repainting: Alerts evaluate on confirmed closes (index 1) with an explicit lastAlertTime latch to prevent multi-triggering within the same bar interval.

Memory Safety & Pre-Allocations: Handlers in MQL5 are checked against INVALID_HANDLE and cleanly unloaded via OnDeinit() to eliminate client-terminal memory leaks.

Array As Series Synchronization: The MQL5 script unifies dynamic price buffers into reverse-chronological order (ArraySetAsSeries(..., true)), matching standard MQL4 array traversal and preventing off-by-one errors across timeframe calculations.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Best forex pairs to scalp during london session

Post by PTScalper »

To elevate this from a Retail tool to an Institutional-grade quantitative model, we must address the three primary vectors of alpha decay in mean-reversion systems: Adverse Selection, Micro-Trend Momentum, and Execution Drag.

Here is the architectural upgrade for the "Pro" tier:

Microstructure Exhaustion (Climactic Volume): A price sweep without a volume spike is just a drift, not a liquidity extraction. The upgraded models now calculate a Volume Z-Score. A valid setup requires the sweep candle to exhibit climactic tick volume (proving retail stops were triggered and institutionally absorbed).

Intra-Bar Rejection Profiling: A sweep is instantly invalidated if the candle doesn't close aggressively away from the extreme. The model now requires the candle close to be within the opposite quartile of the wick.

Dynamic Spread Filtering (MT4/MT5): As discussed, EURGBP spreads can kill a scalper. The MetaTrader models now actively poll the broker's live spread tick-by-tick. If the spread exceeds your hard threshold during the sweep, the signal is suppressed.

Real-Time HUD: The MetaTrader versions now include a quantitative Heads-Up Display to monitor regime states in real-time.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Best forex pairs to scalp during london session

Post by PTScalper »

Here are the ultimate, production-ready iterations.

1. Pine Script v5: Volume-Validated Statistical Sweeps
This version introduces Volume Standard Deviation and Intra-bar Rejection profiling.

Code: Select all

//@version=5
indicator("EURGBP Quant: Volume-Validated Sweeps", overlay=true, max_labels_count=500)

// =========================================================================
// INPUT PARAMETERS
// =========================================================================
grp1 = "Statistical Z-Boundaries"
zLen    = input.int(21, "Rolling Window", group=grp1)
zThresh = input.float(2.2, "Z-Score Extraction Level", step=0.1, group=grp1)

grp2 = "Volume & Microstructure (Absorption)"
volLen  = input.int(20, "Volume Baseline Window", group=grp2)
volZMin = input.float(1.5, "Min Volume Spike (Z-Score)", tooltip="Requires climactic tick volume to confirm stop-hunting.", step=0.1, group=grp2)

grp3 = "Regime Filter"
atrLen   = input.int(14, "Fast ATR", group=grp3)
atrAvg   = input.int(100, "Baseline ATR", group=grp3)
volLimit = input.float(1.15, "Max Volatility Threshold", group=grp3)

// =========================================================================
// QUANTITATIVE CALCULATIONS
// =========================================================================
// 1. Price Z-Score
mean   = ta.sma(close, zLen)
stdDev = ta.stdev(close, zLen)
zHigh  = stdDev == 0 ? 0 : (high - mean) / stdDev
zLow   = stdDev == 0 ? 0 : (low - mean) / stdDev

// 2. Volume Z-Score (Climactic Exhaustion)
volMean = ta.sma(volume, volLen)
volStd  = ta.stdev(volume, volLen)
volZ    = volStd == 0 ? 0 : (volume - volMean) / volStd
isVolClimactic = volZ >= volZMin

// 3. Volatility Regime
baselineAtr = ta.sma(ta.atr(atrLen), atrAvg)
volRatio    = ta.atr(atrLen) / (baselineAtr == 0 ? 1 : baselineAtr)
isRangeBound = volRatio < volLimit

// 4. Intra-Bar Rejection (Candle Anatomy)
candleSize = high - low
// Short sweep requires close in bottom 50% of the candle
validShortClose = close < (high - (candleSize * 0.5))
// Long sweep requires close in top 50% of the candle
validLongClose  = close > (low + (candleSize * 0.5))

// =========================================================================
// ALGORITHMIC SIGNAL GENERATION
// =========================================================================
upperBound = mean + (stdDev * zThresh)
lowerBound = mean - (stdDev * zThresh)

// Institutional Short: Price pierces Z-thresh, high volume proves absorption, closes weakly
sweepShort = (zHigh > zThresh) and (close < upperBound) and isVolClimactic and isRangeBound and validShortClose

// Institutional Long: Price pierces -Z-thresh, high volume proves absorption, closes strongly
sweepLong  = (zLow < -zThresh)  and (close > lowerBound) and isVolClimactic and isRangeBound and validLongClose

// =========================================================================
// VISUALIZATION
// =========================================================================
plot(mean, color=color.new(color.gray, 70), title="VWAP/Mean")
p1 = plot(upperBound, color=color.new(#ff5252, 70), title="Short Execution Band")
p2 = plot(lowerBound, color=color.new(#00e676, 70), title="Long Execution Band")
fill(p1, p2, color=color.new(color.gray, 95))

// Paint climactic volume candles for visual backtesting
barcolor(isVolClimactic ? color.new(color.yellow, 20) : na, title="Volume Anomaly")

plotshape(sweepShort, style=shape.labeldown, location=location.abovebar, color=color.new(#d50000, 0), size=size.small, text="ABSORB\nSHORT", textcolor=color.white)
plotshape(sweepLong, style=shape.labelup, location=location.belowbar, color=color.new(#00c853, 0), size=size.small, text="ABSORB\nLONG", textcolor=color.white)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Best forex pairs to scalp during london session

Post by PTScalper »

2. MetaTrader 5 (MQL5): The Institutional Execution Engine

To convert to MT4, the core logic is identical, but you replace CopyBuffer calls with standard iMA/iATR calls and SymbolInfoInteger with MarketInfo(Symbol(), MODE_SPREAD).

This MQL5 script includes dynamic Spread Protection (vital for EURGBP) and a Live Quantitative HUD overlay.

Code: Select all

//+------------------------------------------------------------------+
//|                             EURGBP_Quant_Execution_Engine.mq5    |
//|                         Institutional Mean-Reversion Framework   |
//+------------------------------------------------------------------+
#property copyright "Quantitative Research Desk"
#property version   "2.00"
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_plots   5

// --- Aesthetics
#property indicator_label1  "Fair Value (Mean)"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDimGray
#property indicator_style1  STYLE_SOLID

#property indicator_label2  "Upper Statistical Bound"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrCrimson
#property indicator_style2  STYLE_DASH

#property indicator_label3  "Lower Statistical Bound"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrMediumSeaGreen
#property indicator_style3  STYLE_DASH

#property indicator_label4  "Institutional Fade Long"
#property indicator_type4   DRAW_ARROW
#property indicator_color4  clrLimeGreen
#property indicator_width4  3

#property indicator_label5  "Institutional Fade Short"
#property indicator_type5   DRAW_ARROW
#property indicator_color5  clrRed
#property indicator_width5  3

// --- Quantitative Inputs
input string  InpGrp1        = "--- Statistical Engine ---";
input int     InpZLength     = 21;      // Baseline Window
input double  InpZThresh     = 2.2;     // Standard Deviation Extraction Target

input string  InpGrp2        = "--- Microstructure & Cost ---";
input double  InpVolZMin     = 1.5;     // Min Tick Volume Spike (Z-Score)
input int     InpMaxSpread   = 15;      // Max Allowable Spread in Points (1.5 pips)

input string  InpGrp3        = "--- Regime Constraints ---";
input int     InpAtrLen      = 14;      
input int     InpAtrAvg      = 100;     
input double  InpVolLimit    = 1.15;    // Suppress if Volatility expands > 15% above norm

// --- Buffers & Handles
double BufferMean[], BufferUpper[], BufferLower[], BufferBuy[], BufferSell[];
int handleMA, handleStdDev, handleATR, handleVolMA, handleVolStd;

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, BufferMean, INDICATOR_DATA);
   SetIndexBuffer(1, BufferUpper, INDICATOR_DATA);
   SetIndexBuffer(2, BufferLower, INDICATOR_DATA);
   SetIndexBuffer(3, BufferBuy, INDICATOR_DATA);
   SetIndexBuffer(4, BufferSell, INDICATOR_DATA);

   PlotIndexSetInteger(3, PLOT_ARROW, 233); 
   PlotIndexSetInteger(4, PLOT_ARROW, 234); 

   handleMA     = iMA(_Symbol, _Period, InpZLength, 0, MODE_SMA, PRICE_CLOSE);
   handleStdDev = iStdDev(_Symbol, _Period, InpZLength, 0, MODE_SMA, PRICE_CLOSE);
   handleATR    = iATR(_Symbol, _Period, InpAtrLen);
   
   // Volume handlers for Microstructure verification
   handleVolMA  = iMA(_Symbol, _Period, 20, 0, MODE_SMA, VOLUME_TICK);
   handleVolStd = iStdDev(_Symbol, _Period, 20, 0, MODE_SMA, VOLUME_TICK);

   EventSetMillisecondTimer(500); // UI Refresh Rate
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Deinitialization (Cleanup)                                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   IndicatorRelease(handleMA);
   IndicatorRelease(handleStdDev);
   IndicatorRelease(handleATR);
   IndicatorRelease(handleVolMA);
   IndicatorRelease(handleVolStd);
   EventKillTimer();
   Comment(""); // Clear HUD
}

//+------------------------------------------------------------------+
//| UI HUD Timer Update                                              |
//+------------------------------------------------------------------+
void OnTimer()
{
   int currentSpread = (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
   string spreadWarn = (currentSpread > InpMaxSpread) ? "[!] SPREAD HIGH - SIGNALS BLOCKED" : "SPREAD OPTIMAL";
   
   string hud = "\n=== EURGBP QUANT ENGINE ===\n" +
                "Live Spread: " + IntegerToString(currentSpread) + " pts | Limit: " + IntegerToString(InpMaxSpread) + "\n" +
                "Execution Status: " + spreadWarn + "\n" +
                "Algorithm: Z-Score Absorption Reversion";
   Comment(hud);
}

//+------------------------------------------------------------------+
//| Core Processing Engine                                           |
//+------------------------------------------------------------------+
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 < InpAtrAvg + InpZLength) return(0);

   ArraySetAsSeries(time, true);
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(tick_volume, true);
   
   ArraySetAsSeries(BufferMean, true);
   ArraySetAsSeries(BufferUpper, true);
   ArraySetAsSeries(BufferLower, true);
   ArraySetAsSeries(BufferBuy, true);
   ArraySetAsSeries(BufferSell, true);

   double maVal[], stdVal[], atrVal[], vMaVal[], vStdVal[];
   ArraySetAsSeries(maVal, true); ArraySetAsSeries(stdVal, true); ArraySetAsSeries(atrVal, true);
   ArraySetAsSeries(vMaVal, true); ArraySetAsSeries(vStdVal, true);

   int count = rates_total;
   if(CopyBuffer(handleMA, 0, 0, count, maVal) <= 0 || CopyBuffer(handleStdDev, 0, 0, count, stdVal) <= 0) return 0;
   if(CopyBuffer(handleATR, 0, 0, count, atrVal) <= 0) return 0;
   if(CopyBuffer(handleVolMA, 0, 0, count, vMaVal) <= 0 || CopyBuffer(handleVolStd, 0, 0, count, vStdVal) <= 0) return 0;

   int limit = (prev_calculated > 0) ? rates_total - prev_calculated + 1 : rates_total - InpAtrAvg;

   for(int i = limit; i >= 0; i--)
   {
      double mean   = maVal[i];
      double stdDev = stdVal[i];
      double upper  = mean + (stdDev * InpZThresh);
      double lower  = mean - (stdDev * InpZThresh);

      BufferMean[i]  = mean;
      BufferUpper[i] = upper;
      BufferLower[i] = lower;
      BufferBuy[i]   = EMPTY_VALUE;
      BufferSell[i]  = EMPTY_VALUE;

      // 1. Regime Filter
      double sumAtr = 0;
      for(int j = 0; j < InpAtrAvg; j++) sumAtr += atrVal[i+j];
      double volRatio = atrVal[i] / (sumAtr / InpAtrAvg);
      bool isRange = (volRatio < InpVolLimit);

      // 2. Volume Exhaustion Check (Tick Volume Anomaly)
      double volStdDev = vStdVal[i];
      double currentVolZ = (volStdDev == 0) ? 0 : ((double)tick_volume[i] - vMaVal[i]) / volStdDev;
      bool isVolClimactic = (currentVolZ >= InpVolZMin);

      // 3. Intra-Bar Rejection (Candle must close aggressively away from the sweep)
      double candleSize = high[i] - low[i];
      bool validShortClose = close[i] < (high[i] - (candleSize * 0.5));
      bool validLongClose  = close[i] > (low[i]  + (candleSize * 0.5));

      // 4. Spread Execution Filter (Historical via spread array, live via symbol query)
      bool validSpread = (spread[i] <= InpMaxSpread);

      // EXECUTION LOGIC
      bool sweepShort = (high[i] > upper) && (close[i] < upper) && isRange && isVolClimactic && validShortClose && validSpread;
      bool sweepLong  = (low[i] < lower)  && (close[i] > lower) && isRange && isVolClimactic && validLongClose && validSpread;

      if(sweepLong)  BufferBuy[i]  = low[i] - (stdDev * 0.3);
      if(sweepShort) BufferSell[i] = high[i] + (stdDev * 0.3);
   }
   
   return(rates_total);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply