IC Markets

Forex scalping EURUSD London sesion - Judas Swing

Optimize MetaTrader 4, MetaTrader 5, cTrader, and TradingView for speed. Discuss Level II Market Depth (DOM), custom hotkeys, and volume indicators.
Post Reply
PTScalper
Site Admin
Posts: 210
Joined: Mon Jul 20, 2026 1:28 pm

Forex scalping EURUSD London sesion - Judas Swing

Post by PTScalper »

Hi Scalpers,

today i would like to share some of my forex scalping setup, its called Judas Swing.

The EURUSD London Open is notorious for the "Judas Swing"—a deliberate move by institutions to sweep the liquidity resting above and below the tight Asian session consolidation before committing to the true daily trend.

This is how i programmed that:

Installation & CompilationCopy this source code into MetaEditor (F4), click New $\rightarrow$ Custom Indicator, name it London_Liquidity_Sweep, and paste the code below.

Code: Select all

//+------------------------------------------------------------------+
//|                                     London_Liquidity_Sweep.mq4   |
//|                                     Custom Indicator for EURUSD  |
//+------------------------------------------------------------------+
#property copyright "Forex Scalping Tools"
#property strict
#property indicator_chart_window

//--- Expose Buffers for the Arrows
#property indicator_buffers 2
#property indicator_color1 clrDodgerBlue  // Buy Arrow
#property indicator_color2 clrCrimson     // Sell Arrow
#property indicator_width1 2
#property indicator_width2 2

//--- User Inputs
input int    AsianStartHour  = 0;       // Broker Hour: Asian Session Start
input int    AsianEndHour    = 8;       // Broker Hour: Asian Session End
input int    LondonEndHour   = 12;      // Broker Hour: Stop looking for breakouts
input int    AtrPeriod       = 7;       // Fast ATR for volume spike detection
input double AtrMultiplier   = 1.3;     // Minimum ATR expansion required for valid breakout

//--- Global Buffers & Variables
double BuyBuffer[];
double SellBuffer[];
double currentAsianHigh = 0;
double currentAsianLow  = 0;
int    activeDay        = -1;
bool   breakoutFired    = false;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
    SetIndexBuffer(0, BuyBuffer);
    SetIndexStyle(0, DRAW_ARROW);
    SetIndexArrow(0, 233); // Up Arrow symbol

    SetIndexBuffer(1, SellBuffer);
    SetIndexStyle(1, DRAW_ARROW);
    SetIndexArrow(1, 234); // Down Arrow symbol

    IndicatorDigits(Digits);
    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[])
{
    if (rates_total < AtrPeriod * 2) return(0);

    // Limit calculation to uncalculated bars to optimize CPU
    int limit = rates_total - prev_calculated;
    if (limit == 0) limit = 1; // Always check the current forming bar

    for(int i = limit - 1; i >= 0; i--)
    {
        int currentHour = TimeHour(time[i]);
        int dayOfYear   = TimeDayOfYear(time[i]);

        // 1. Reset state for a new trading day
        if(dayOfYear != activeDay)
        {
            activeDay = dayOfYear;
            currentAsianHigh = 0;
            currentAsianLow  = 99999;
            breakoutFired    = false;
        }

        // 2. Map the Asian Session Range
        if(currentHour >= AsianStartHour && currentHour < AsianEndHour)
        {
            if(high[i] > currentAsianHigh) currentAsianHigh = high[i];
            if(low[i] < currentAsianLow)   currentAsianLow  = low[i];
            
            // Clean buffers during the Asian session
            BuyBuffer[i]  = EMPTY_VALUE;
            SellBuffer[i] = EMPTY_VALUE;
            continue; 
        }

        // 3. Evaluate the London Breakout Window
        BuyBuffer[i]  = EMPTY_VALUE;
        SellBuffer[i] = EMPTY_VALUE;

        if(currentHour >= AsianEndHour && currentHour < LondonEndHour)
        {
            // Only fire one valid signal per day to avoid overtrading the chop
            if(breakoutFired) continue;

            // Fetch ATR data
            double atrCurrent = iATR(NULL, 0, AtrPeriod, i);
            double atrPrev    = iATR(NULL, 0, AtrPeriod, i+1);

            // Volatility Filter: Current bar's ATR must be heavily expanding
            bool hasInstitutionalVolume = (atrCurrent > (atrPrev * AtrMultiplier));

            // Long Breakout: Close above Asian High with volume
            if(close[i] > currentAsianHigh && hasInstitutionalVolume)
            {
                BuyBuffer[i] = low[i] - (10 * Point); // Place arrow below the candle
                breakoutFired = true;
            }
            // Short Breakout: Close below Asian Low with volume
            else if(close[i] < currentAsianLow && hasInstitutionalVolume)
            {
                SellBuffer[i] = high[i] + (10 * Point); // Place arrow above the candle
                breakoutFired = true;
            }
        }
    }

    return(rates_total);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 210
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping EURUSD London sesion - Judas Swing

Post by PTScalper »

To draw the Asian Range dynamically, we need to leverage MT4's Object API—specifically OBJ_RECTANGLE.

Because indicators recalculate on every tick, we can't just spawn a new rectangle constantly. We need to create a unique string identifier for each day's box (e.g., AsianBox_145 for the 145th day of the year) and then continuously update its coordinates in memory as the session's high, low, and time expand.

Here is the complete, updated indicator. It now draws a filled background box that forms in real-time during the Asian session and locks in place once London opens.

Code: Select all

//+------------------------------------------------------------------+
//|                                     London_Liquidity_Sweep.mq4   |
//|                                     Custom Indicator for EURUSD  |
//+------------------------------------------------------------------+
#property copyright "Forex Scalping Tools"
#property strict
#property indicator_chart_window

//--- Expose Buffers for the Arrows
#property indicator_buffers 2
#property indicator_color1 clrDodgerBlue  // Buy Arrow
#property indicator_color2 clrCrimson     // Sell Arrow
#property indicator_width1 2
#property indicator_width2 2

//--- User Inputs
input int    AsianStartHour  = 0;       // Broker Hour: Asian Session Start
input int    AsianEndHour    = 8;       // Broker Hour: Asian Session End
input int    LondonEndHour   = 12;      // Broker Hour: Stop looking for breakouts
input int    AtrPeriod       = 7;       // Fast ATR for volume spike detection
input double AtrMultiplier   = 1.3;     // Minimum ATR expansion required for valid breakout
input color  BoxColor        = clrDarkSlateGray; // Asian Range Box Color

//--- Global Buffers & Variables
double BuyBuffer[];
double SellBuffer[];
double currentAsianHigh = 0;
double currentAsianLow  = 0;
datetime asianStartTime = 0;
datetime asianEndTime   = 0;
int    activeDay        = -1;
bool   breakoutFired    = false;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
    SetIndexBuffer(0, BuyBuffer);
    SetIndexStyle(0, DRAW_ARROW);
    SetIndexArrow(0, 233); // Up Arrow symbol

    SetIndexBuffer(1, SellBuffer);
    SetIndexStyle(1, DRAW_ARROW);
    SetIndexArrow(1, 234); // Down Arrow symbol

    IndicatorDigits(Digits);
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // Clean up all drawn rectangles from the chart when removing the indicator
    ObjectsDeleteAll(0, "AsianBox_");
}

//+------------------------------------------------------------------+
//| 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[])
{
    if (rates_total < AtrPeriod * 2) return(0);

    // Limit calculation to uncalculated bars to optimize CPU
    int limit = rates_total - prev_calculated;
    if (limit == 0) limit = 1; // Always check the current forming bar

    for(int i = limit - 1; i >= 0; i--)
    {
        int currentHour = TimeHour(time[i]);
        int dayOfYear   = TimeDayOfYear(time[i]);

        // 1. Reset state for a new trading day
        if(dayOfYear != activeDay)
        {
            activeDay = dayOfYear;
            currentAsianHigh = 0;
            currentAsianLow  = 99999;
            asianStartTime   = 0;
            breakoutFired    = false;
        }

        // 2. Map the Asian Session Range & Draw Rectangle
        if(currentHour >= AsianStartHour && currentHour < AsianEndHour)
        {
            // Track Highs and Lows
            if(high[i] > currentAsianHigh) currentAsianHigh = high[i];
            if(low[i] < currentAsianLow)   currentAsianLow  = low[i];
            
            // Track Times
            if(asianStartTime == 0) asianStartTime = time[i];
            asianEndTime = time[i]; // Push the right side of the box forward
            
            // Draw or Update the Rectangle Object
            string boxName = "AsianBox_" + IntegerToString(activeDay);
            
            if(ObjectFind(0, boxName) < 0) // If box doesn't exist yet, create it
            {
                ObjectCreate(0, boxName, OBJ_RECTANGLE, 0, asianStartTime, currentAsianHigh, asianEndTime, currentAsianLow);
                ObjectSetInteger(0, boxName, OBJPROP_COLOR, BoxColor);
                ObjectSetInteger(0, boxName, OBJPROP_STYLE, STYLE_SOLID);
                ObjectSetInteger(0, boxName, OBJPROP_BACK, true); // True = filled box, False = outline only
                ObjectSetInteger(0, boxName, OBJPROP_SELECTABLE, false);
                ObjectSetInteger(0, boxName, OBJPROP_HIDDEN, true); // Hide from object list to prevent clutter
            }
            else // If box exists, dynamically stretch it
            {
                ObjectSetDouble(0, boxName, OBJPROP_PRICE1, currentAsianHigh);
                ObjectSetDouble(0, boxName, OBJPROP_PRICE2, currentAsianLow);
                ObjectSetInteger(0, boxName, OBJPROP_TIME2, asianEndTime);
            }
            
            // Clean buffers during the Asian session
            BuyBuffer[i]  = EMPTY_VALUE;
            SellBuffer[i] = EMPTY_VALUE;
            continue; 
        }

        // 3. Evaluate the London Breakout Window
        BuyBuffer[i]  = EMPTY_VALUE;
        SellBuffer[i] = EMPTY_VALUE;

        if(currentHour >= AsianEndHour && currentHour < LondonEndHour)
        {
            if(breakoutFired) continue;

            double atrCurrent = iATR(NULL, 0, AtrPeriod, i);
            double atrPrev    = iATR(NULL, 0, AtrPeriod, i+1);

            bool hasInstitutionalVolume = (atrCurrent > (atrPrev * AtrMultiplier));

            // Long Breakout
            if(close[i] > currentAsianHigh && hasInstitutionalVolume)
            {
                BuyBuffer[i] = low[i] - (10 * Point); 
                breakoutFired = true;
            }
            // Short Breakout
            else if(close[i] < currentAsianLow && hasInstitutionalVolume)
            {
                SellBuffer[i] = high[i] + (10 * Point); 
                breakoutFired = true;
            }
        }
    }

    return(rates_total);
}
Architectural Additions
OBJ_RECTANGLE State Management: Notice how the boxName string is generated dynamically per day ("AsianBox_" + activeDay). MT4 will throw errors if you try to create two objects with the exact same name, so this guarantees uniqueness across the chart's entire history.

OnDeinit() Cleanup: When you remove the indicator or change timeframes, MT4 fires the OnDeinit() function. I added ObjectsDeleteAll(0, "AsianBox_"); so the script wipes out all the generated rectangles automatically, preventing your chart from becoming permanently cluttered with orphaned graphical objects.

OBJPROP_BACK: This property is set to true, which draws the rectangle behind the candlesticks as a filled semi-transparent zone (depending on your chart properties). If you prefer an empty, hollow outline instead, just change that property to false in the code.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply