Advertisement IC Markets

💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
FTtrader
Posts: 567
Joined: Mon Aug 03, 2026 2:43 pm

Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping

Post by FTtrader »

MQL4 (Pro_Liquidity_Sweep.mq4)

For standard MT4 environments. It handles arrays and time series inherently differently, utilizing built-in functions like iHighest, iLowest, and TimeHour/TimeMinute for leaner legacy execution.

Code: Select all

//+------------------------------------------------------------------+
//|                                        Pro_Liquidity_Sweep.mq4   |
//|                         Institutional PA & AAE Tracker           |
//+------------------------------------------------------------------+
#property strict
#property indicator_chart_window
#property indicator_buffers 2

#property indicator_label1  "Bullish Sweep"
#property indicator_type1   DRAW_ARROW
#property indicator_color1  MediumSeaGreen
#property indicator_width1  2

#property indicator_label2  "Bearish Sweep"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  Crimson
#property indicator_width2  2

extern string  InpSessionStart = "08:00"; 
extern string  InpSessionEnd   = "16:30"; 
extern int     InpSwingLen     = 15;      
extern double  InpRRTarget     = 2.0;     
extern double  InpStopBuffer   = 0.2;     
extern int     InpAtrPeriod    = 14;      

double BullBuffer[];
double BearBuffer[];

int OnInit()
{
    SetIndexBuffer(0, BullBuffer);
    SetIndexBuffer(1, BearBuffer);
    
    SetIndexArrow(0, 233);
    SetIndexArrow(1, 234);
    
    return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
    Comment("");
}

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 < InpSwingLen * 2) return 0;

    int limit = rates_total - prev_calculated;
    if(limit > rates_total - InpSwingLen - 1) limit = rates_total - InpSwingLen - 1;
    if(prev_calculated == 0) 
    {
        ArrayInitialize(BullBuffer, EMPTY_VALUE);
        ArrayInitialize(BearBuffer, EMPTY_VALUE);
    }

    int totalTrades = 0, wins = 0;
    double sumAAE = 0.0;

    for(int i = limit; i >= 1; i--)
    {
        // 1. Session Filter
        string currentHour = IntegerToString(TimeHour(time[i]), 2, '0');
        string currentMin  = IntegerToString(TimeMinute(time[i]), 2, '0');
        string currentTime = currentHour + ":" + currentMin;
        
        bool inSession = (currentTime >= InpSessionStart && currentTime <= InpSessionEnd);
        if(!inSession) continue;

        // 2. HTF Alignment
        double dailyEMA = iMA(NULL, PERIOD_D1, 20, 0, MODE_EMA, PRICE_CLOSE, iBarShift(NULL, PERIOD_D1, time[i]));
        bool htfBullish = close[i] > dailyEMA;
        bool htfBearish = close[i] < dailyEMA;

        // 3. Find Liquidity Pools
        int highestIdx = iHighest(NULL, 0, MODE_HIGH, InpSwingLen, i + 1);
        int lowestIdx  = iLowest(NULL, 0, MODE_LOW, InpSwingLen, i + 1);
        double liqHigh = high[highestIdx];
        double liqLow  = low[lowestIdx];

        double atr = iATR(NULL, 0, InpAtrPeriod, i);
        
        // 4. Sweep Logic
        bool bullSweep = htfBullish && (low[i] < liqLow) && (close[i] > liqLow) && (close[i] > open[i]);
        bool bearSweep = htfBearish && (high[i] > liqHigh) && (close[i] < liqHigh) && (close[i] < open[i]);

        if(bullSweep)
        {
            BullBuffer[i] = low[i] - (atr * 0.5);
            
            double entryPx = close[i];
            double slPx = low[i] - (atr * InpStopBuffer);
            double tpPx = entryPx + ((entryPx - slPx) * InpRRTarget);
            double maxDrawdown = 0.0;
            
            for(int j = i - 1; j >= 0; j--)
            {
                double currentDD = (entryPx - low[j]) / atr;
                if(currentDD > maxDrawdown) maxDrawdown = currentDD;
                
                if(high[j] >= tpPx) { totalTrades++; wins++; sumAAE += maxDrawdown; break; }
                if(low[j] <= slPx)  { totalTrades++; sumAAE += maxDrawdown; break; }
            }
        }
        else if(bearSweep)
        {
            BearBuffer[i] = high[i] + (atr * 0.5);
            
            double entryPx = close[i];
            double slPx = high[i] + (atr * InpStopBuffer);
            double tpPx = entryPx - ((slPx - entryPx) * InpRRTarget);
            double maxDrawdown = 0.0;
            
            for(int j = i - 1; j >= 0; j--)
            {
                double currentDD = (high[j] - entryPx) / atr;
                if(currentDD > maxDrawdown) maxDrawdown = currentDD;
                
                if(low[j] <= tpPx)  { totalTrades++; wins++; sumAAE += maxDrawdown; break; }
                if(high[j] >= slPx) { totalTrades++; sumAAE += maxDrawdown; break; }
            }
        }
    }

    // 5. Dashboard Output
    if(totalTrades > 0)
    {
        double winRate = ((double)wins / totalTrades) * 100.0;
        double avgAAE = sumAAE / totalTrades;
        string dash = "--- SYSTEM ANALYTICS ---\n";
        dash += "Total Setups: " + IntegerToString(totalTrades) + "\n";
        dash += "Win Rate (" + DoubleToString(InpRRTarget, 1) + "R): " + DoubleToString(winRate, 2) + "%\n";
        dash += "Avg Adverse Excursion: " + DoubleToString(avgAAE, 2) + " ATR";
        Comment(dash);
    }

    return rates_total;
}
Recommended broker for automated trading & scalping IC Markets
FTtrader
Posts: 567
Joined: Mon Aug 03, 2026 2:43 pm

Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping

Post by FTtrader »

Transitioning from an indicator OnCalculate loop to an Expert Advisor OnTick environment requires shifting from historical array processing to a real-time, stateful execution model.

Because your strategy relies on candlestick structure (specifically, the rejection close back inside the liquidity pool), the EA must execute strictly on the opening tick of a new bar to evaluate the previously closed bar. Attempting to evaluate this intra-bar will result in repainting false signals as price probes the liquidity pool.
FTtrader
Posts: 567
Joined: Mon Aug 03, 2026 2:43 pm

Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping

Post by FTtrader »

Here is the production-ready MQL5 Expert Advisor.

It utilizes the standard <Trade\Trade.mqh> library for robust OrderSend execution and includes strict point normalization for stop loss and take profit calculations—a mandatory step when trading metals like XAGUSD to avoid invalid stops.

Code: Select all

//+------------------------------------------------------------------+
//|                                     Pro_Liquidity_Sweep_EA.mq5   |
//|                     Institutional PA Execution & Trade Manager   |
//+------------------------------------------------------------------+
#property strict

#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\PositionInfo.mqh>

CTrade         trade;
CSymbolInfo    symInfo;
CPositionInfo  posInfo;

// =========================================================================
// INPUTS
// =========================================================================
input group "Execution & Risk"
input double   InpLotSize      = 0.1;         // Fixed Lot Size
input ulong    InpMagicNumber  = 777123;      // EA Magic Number
input double   InpRRTarget     = 2.0;         // Target R:R
input double   InpStopBuffer   = 0.2;         // Stop Buffer (ATR)

input group "Context & Time"
input string   InpSessionStart = "08:00";     // Session Start (Broker Time)
input string   InpSessionEnd   = "16:30";     // Session End (Broker Time)
input int      InpSwingLen     = 15;          // Liquidity Pool Lookback
input int      InpAtrPeriod    = 14;          // ATR Period

int atrHandle, emaHandle;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    symInfo.Name(_Symbol);
    trade.SetExpertMagicNumber(InpMagicNumber);
    trade.SetMarginMode();
    trade.SetTypeFillingBySymbol(_Symbol);

    atrHandle = iATR(_Symbol, _Period, InpAtrPeriod);
    emaHandle = iMA(_Symbol, PERIOD_D1, 20, 0, MODE_EMA, PRICE_CLOSE);
    
    if(atrHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE) 
    {
        Print("Failed to initialize indicator handles.");
        return INIT_FAILED;
    }
    
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| New Bar Detection (Wait for candle close to confirm sweep)       |
//+------------------------------------------------------------------+
bool IsNewBar()
{
    static datetime lastBarTime = 0;
    datetime currentBarTime = iTime(_Symbol, _Period, 0);
    
    if(lastBarTime != currentBarTime)
    {
        lastBarTime = currentBarTime;
        return true;
    }
    return false;
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // 1. Only execute on the open of a new bar (evaluating closed bar [1])
    if(!IsNewBar()) return;

    // 2. Prevent concurrent entries if we already hold a position
    if(PositionsTotal() > 0)
    {
        for(int i = PositionsTotal() - 1; i >= 0; i--)
        {
            if(posInfo.SelectByIndex(i))
            {
                if(posInfo.Symbol() == _Symbol && posInfo.Magic() == InpMagicNumber)
                    return; // Wait for current trade to resolve
            }
        }
    }

    // 3. Session Filter Check
    MqlDateTime dt;
    TimeCurrent(dt);
    string currentTime = StringFormat("%02d:%02d", dt.hour, dt.min);
    if(currentTime < InpSessionStart || currentTime > InpSessionEnd) return;

    // 4. Retrieve Data for Bar [1] (The completed setup candle)
    double atrArr[], emaArr[];
    if(CopyBuffer(atrHandle, 0, 1, 1, atrArr) <= 0) return;
    if(CopyBuffer(emaHandle, 0, 1, 1, emaArr) <= 0) return;

    double open  = iOpen(_Symbol, _Period, 1);
    double high  = iHigh(_Symbol, _Period, 1);
    double low   = iLow(_Symbol, _Period, 1);
    double close = iClose(_Symbol, _Period, 1);
    
    double atr = atrArr[0];
    double htfEma = emaArr[0];

    // 5. Find Liquidity Pools (Swing Highs/Lows from Bar [2] backwards)
    double highPool[], lowPool[];
    if(CopyHigh(_Symbol, _Period, 2, InpSwingLen, highPool) <= 0) return;
    if(CopyLow(_Symbol, _Period, 2, InpSwingLen, lowPool) <= 0) return;

    int highestIdx = ArrayMaximum(highPool);
    int lowestIdx  = ArrayMinimum(lowPool);
    
    double liqHigh = highPool[highestIdx];
    double liqLow  = lowPool[lowestIdx];

    // 6. Evaluate Structural Context
    bool htfBullish = close > htfEma;
    bool htfBearish = close < htfEma;

    bool bullSweep = htfBullish && (low < liqLow) && (close > liqLow) && (close > open) && ((liqLow - low) <= atr * 1.5);
    bool bearSweep = htfBearish && (high > liqHigh) && (close < liqHigh) && (close < open) && ((high - liqHigh) <= atr * 1.5);

    // 7. Execution & Risk Calculations
    symInfo.RefreshRates();
    double ask = symInfo.Ask();
    double bid = symInfo.Bid();
    
    if(bullSweep)
    {
        double sl = low - (atr * InpStopBuffer);
        double slDist = ask - sl;
        double tp = ask + (slDist * InpRRTarget);
        
        // Normalize for broker server digits
        sl = symInfo.NormalizePrice(sl);
        tp = symInfo.NormalizePrice(tp);

        PrintFormat("Bullish Sweep Detected: Ask: %f | SL: %f | TP: %f", ask, sl, tp);
        trade.Buy(InpLotSize, _Symbol, ask, sl, tp, "PA Bull Sweep");
    }
    else if(bearSweep)
    {
        double sl = high + (atr * InpStopBuffer);
        double slDist = sl - bid;
        double tp = bid - (slDist * InpRRTarget);
        
        sl = symInfo.NormalizePrice(sl);
        tp = symInfo.NormalizePrice(tp);

        PrintFormat("Bearish Sweep Detected: Bid: %f | SL: %f | TP: %f", bid, sl, tp);
        trade.Sell(InpLotSize, _Symbol, bid, sl, tp, "PA Bear Sweep");
    }
}
FTtrader
Posts: 567
Joined: Mon Aug 03, 2026 2:43 pm

Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping

Post by FTtrader »

Key Architectural Choices for Live Execution

IsNewBar() State Gate: The code strictly prevents intra-bar execution. It logs the time of the current candle and blocks the evaluation block until iTime registers a shift. This ensures your sweeping wick is permanently locked into the closed structure.

Active Trade Blocking: The loop checks PositionsTotal() and specifically filters for your InpMagicNumber and _Symbol. If a trade is currently floating, it blocks new executions to prevent stacking overlapping trades during prolonged consolidations around a liquidity level.

Price Normalization (CSymbolInfo): Calculating stops using raw ATR math on XAG/USD or spot forex often produces floating-point prices that the broker's matching engine rejects (e.g., Error 4756: ERR_TRADE_INVALID_STOPS). Using symInfo.NormalizePrice() snaps your calculated SL/TP to valid tick sizes before the payload leaves your terminal.
FTtrader
Posts: 567
Joined: Mon Aug 03, 2026 2:43 pm

Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping

Post by FTtrader »

Here is the MQL4 version of the Expert Advisor.

While the underlying logic remains identical to the MT5 version, transitioning this to the MT4 environment requires navigating its older order management system (OrderSend vs the CTrade class) and its legacy time-series array architecture.

I’ve structured it with strict typings and included specific MT4 error handling, particularly checking the return values of iHighest and iLowest to prevent silent execution failures.

MQL4 (Pro_Liquidity_Sweep_EA.mq4)

Code: Select all

//+------------------------------------------------------------------+
//|                                     Pro_Liquidity_Sweep_EA.mq4   |
//|                     Institutional PA Execution & Trade Manager   |
//+------------------------------------------------------------------+
#property strict

// =========================================================================
// INPUTS
// =========================================================================
input double   InpLotSize      = 0.1;         // Fixed Lot Size
input int      InpMagicNumber  = 777123;      // EA Magic Number
input double   InpRRTarget     = 2.0;         // Target R:R
input double   InpStopBuffer   = 0.2;         // Stop Buffer (ATR)

input string   InpSessionStart = "08:00";     // Session Start (Broker Time)
input string   InpSessionEnd   = "16:30";     // Session End (Broker Time)
input int      InpSwingLen     = 15;          // Liquidity Pool Lookback
input int      InpAtrPeriod    = 14;          // ATR Period

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    Print("Liquidity Sweep EA Initialized on ", Symbol());
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| New Bar Detection (Wait for candle close to confirm sweep)       |
//+------------------------------------------------------------------+
bool IsNewBar()
{
    static datetime lastBarTime = 0;
    datetime currentBarTime = Time[0];
    
    if(lastBarTime != currentBarTime)
    {
        lastBarTime = currentBarTime;
        return true;
    }
    return false;
}

//+------------------------------------------------------------------+
//| Check if we already have an open trade for this EA               |
//+------------------------------------------------------------------+
bool HasOpenPosition()
{
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
            {
                return true;
            }
        }
    }
    return false;
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // 1. Only execute on the open of a new bar (evaluating closed bar [1])
    if(!IsNewBar()) return;

    // 2. Prevent concurrent entries if we already hold a position
    if(HasOpenPosition()) return;

    // 3. Session Filter Check
    string currentHour = IntegerToString(TimeHour(TimeCurrent()), 2, '0');
    string currentMin  = IntegerToString(TimeMinute(TimeCurrent()), 2, '0');
    string currentTime = currentHour + ":" + currentMin;
    
    if(currentTime < InpSessionStart || currentTime > InpSessionEnd) return;

    // 4. Retrieve Data for Bar [1] (The completed setup candle)
    double open  = Open[1];
    double high  = High[1];
    double low   = Low[1];
    double close = Close[1];
    
    double atr = iATR(NULL, 0, InpAtrPeriod, 1);
    
    // Shift Daily EMA to align with the current chart time
    int d1Index = iBarShift(NULL, PERIOD_D1, Time[1]);
    double htfEma = iMA(NULL, PERIOD_D1, 20, 0, MODE_EMA, PRICE_CLOSE, d1Index);

    // 5. Find Liquidity Pools (Swing Highs/Lows from Bar [2] backwards)
    int highestIdx = iHighest(NULL, 0, MODE_HIGH, InpSwingLen, 2);
    int lowestIdx  = iLowest(NULL, 0, MODE_LOW, InpSwingLen, 2);
    
    // Fallback if iHighest/iLowest fails to find a valid index
    if(highestIdx == -1 || lowestIdx == -1) return; 
    
    double liqHigh = High[highestIdx];
    double liqLow  = Low[lowestIdx];

    // 6. Evaluate Structural Context
    bool htfBullish = close > htfEma;
    bool htfBearish = close < htfEma;

    bool bullSweep = htfBullish && (low < liqLow) && (close > liqLow) && (close > open) && ((liqLow - low) <= atr * 1.5);
    bool bearSweep = htfBearish && (high > liqHigh) && (close < liqHigh) && (close < open) && ((high - liqHigh) <= atr * 1.5);

    // 7. Execution & Risk Calculations
    if(bullSweep)
    {
        double sl = low - (atr * InpStopBuffer);
        double slDist = Ask - sl;
        double tp = Ask + (slDist * InpRRTarget);
        
        // Normalize price for broker server requirements
        sl = NormalizeDouble(sl, Digits);
        tp = NormalizeDouble(tp, Digits);

        PrintFormat("Bullish Sweep Detected: Ask: %f | SL: %f | TP: %f", Ask, sl, tp);
        
        // Slippage set to 3 pips max
        int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, Ask, 3, sl, tp, "PA Bull Sweep", InpMagicNumber, 0, clrMediumSeaGreen);
        if(ticket < 0) Print("OrderSend failed with error #", GetLastError());
    }
    else if(bearSweep)
    {
        double sl = high + (atr * InpStopBuffer);
        double slDist = sl - Bid;
        double tp = Bid - (slDist * InpRRTarget);
        
        sl = NormalizeDouble(sl, Digits);
        tp = NormalizeDouble(tp, Digits);

        PrintFormat("Bearish Sweep Detected: Bid: %f | SL: %f | TP: %f", Bid, sl, tp);
        
        int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, Bid, 3, sl, tp, "PA Bear Sweep", InpMagicNumber, 0, clrCrimson);
        if(ticket < 0) Print("OrderSend failed with error #", GetLastError());
    }
}
FTtrader
Posts: 567
Joined: Mon Aug 03, 2026 2:43 pm

Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping

Post by FTtrader »

MT4 Specific Differences to Note:

NormalizeDouble: MT4 relies heavily on NormalizeDouble(price, Digits) rather than MT5's CSymbolInfo::NormalizePrice(). This is critical here because floating-point precision on mathematically calculated stops will instantly trigger Error 130 (Invalid Stops) on most MT4 servers.

OrderSend Architecture: MT4 processes trades through a single OrderSend command rather than class methods. The slippage parameter is hardcoded to 3 (points/pips depending on broker digits setup), which is generally safe for active session metals and forex.

Array Indexing: Because MT4 defaults arrays as time series, we pull the setup candle using direct High[1] and Low[1], and start scanning for the liquidity pool from candle [2] using iHighest and iLowest.
FTtrader
Posts: 567
Joined: Mon Aug 03, 2026 2:43 pm

Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping

Post by FTtrader »

Porting this to C# for cTrader (cAlgo) is a much cleaner experience than MQL. Because you are working in a modern .NET environment, we can ditch the clunky IsNewBar() tick-gate and leverage the native OnBar() event handler. We can also replace the MQL for loops for position management with a single LINQ query.

A key paradigm shift in the cTrader API is how it handles SL and TP. While MQL's OrderSend expects exact price levels (which you have to manually normalize), cTrader's ExecuteMarketOrder expects the distance in pips. The API handles the normalization and point-to-pip conversion for you behind the scenes.
FTtrader
Posts: 567
Joined: Mon Aug 03, 2026 2:43 pm

Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping

Post by FTtrader »

Here is the production-ready C# cBot:

Code: Select all

using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ProLiquiditySweep : Robot
    {
        // =========================================================================
        // INPUTS
        // =========================================================================
        [Parameter("Volume (Lots)", DefaultValue = 0.1, Group = "Execution & Risk")]
        public double LotSize { get; set; }

        [Parameter("Target R:R", DefaultValue = 2.0, Group = "Execution & Risk")]
        public double RRTarget { get; set; }

        [Parameter("Stop Buffer (ATR)", DefaultValue = 0.2, Group = "Execution & Risk")]
        public double StopBuffer { get; set; }

        [Parameter("Session Start (HH:mm)", DefaultValue = "08:00", Group = "Context & Time")]
        public string SessionStartStr { get; set; }

        [Parameter("Session End (HH:mm)", DefaultValue = "16:30", Group = "Context & Time")]
        public string SessionEndStr { get; set; }

        [Parameter("Liquidity Pool Lookback", DefaultValue = 15, MinValue = 5, Group = "Context & Time")]
        public int SwingLength { get; set; }

        [Parameter("ATR Period", DefaultValue = 14, Group = "Context & Time")]
        public int AtrPeriod { get; set; }

        // =========================================================================
        // GLOBALS
        // =========================================================================
        private AverageTrueRange _atr;
        private ExponentialMovingAverage _dailyEma;
        private Bars _dailyBars;
        
        private TimeSpan _sessionStart;
        private TimeSpan _sessionEnd;
        private const string TradeLabel = "PA_Sweep_Pro";

        protected override void OnStart()
        {
            // Parse session boundaries
            if (!TimeSpan.TryParse(SessionStartStr, out _sessionStart) || !TimeSpan.TryParse(SessionEndStr, out _sessionEnd))
            {
                Print("Invalid session time format. Use HH:mm.");
                Stop();
            }

            // Initialize Indicators
            _atr = Indicators.AverageTrueRange(AtrPeriod, MovingAverageType.Simple);

            // Fetch HTF Daily Series for the Trend Filter
            _dailyBars = MarketData.GetBars(TimeFrame.Daily);
            _dailyEma = Indicators.ExponentialMovingAverage(_dailyBars.ClosePrices, 20);
        }

        protected override void OnBar()
        {
            // 1. Prevent concurrent entries using LINQ
            if (Positions.Count(p => p.Label == TradeLabel && p.SymbolName == SymbolName) > 0)
                return;

            // 2. Session Filter Check (Using Server Time to ensure consistency)
            var currentTime = Server.Time.TimeOfDay;
            if (currentTime < _sessionStart || currentTime > _sessionEnd)
                return;

            // cAlgo OnBar fires exactly when the new bar opens. 
            // Bars.Count - 1 is the currently open, empty tick.
            // We evaluate the just-completed setup candle at Bars.Count - 2.
            var closedBarIndex = Bars.Count - 2;
            
            // Liquidity search starts from the candle before the setup (Bars.Count - 3)
            var lookbackStartIndex = closedBarIndex - 1;
            if (lookbackStartIndex - SwingLength < 0) return;

            // 3. Find Liquidity Pools (Manual iteration is faster than LINQ for price series arrays)
            double liqHigh = double.MinValue;
            double liqLow = double.MaxValue;

            for (int i = 0; i < SwingLength; i++)
            {
                double h = Bars.HighPrices[lookbackStartIndex - i];
                double l = Bars.LowPrices[lookbackStartIndex - i];
                if (h > liqHigh) liqHigh = h;
                if (l < liqLow) liqLow = l;
            }

            // 4. Retrieve Setup Candle Data
            double open = Bars.OpenPrices[closedBarIndex];
            double high = Bars.HighPrices[closedBarIndex];
            double low = Bars.LowPrices[closedBarIndex];
            double close = Bars.ClosePrices[closedBarIndex];
            double currentAtr = _atr.Result[closedBarIndex];

            // 5. HTF Alignment Sync
            // We must find the correct Daily EMA value that corresponds to the closed bar's timestamp
            var dailyIndex = _dailyBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[closedBarIndex]);
            double htfEmaValue = _dailyEma.Result[dailyIndex];

            bool htfBullish = close > htfEmaValue;
            bool htfBearish = close < htfEmaValue;

            // 6. Evaluate Structural Context
            bool bullSweep = htfBullish && (low < liqLow) && (close > liqLow) && (close > open) && ((liqLow - low) <= currentAtr * 1.5);
            bool bearSweep = htfBearish && (high > liqHigh) && (close < liqHigh) && (close < open) && ((high - liqHigh) <= currentAtr * 1.5);

            // 7. Execution & Risk Calculations
            // Note: Metals (like XAGUSD) may require checking if the broker uses Lots or Units
            double volumeInUnits = Symbol.QuantityToVolumeInUnits(LotSize);

            if (bullSweep)
            {
                // Calculate raw prices to derive Pip distance for cTrader API
                double slPrice = low - (currentAtr * StopBuffer);
                double slPips = (Ask - slPrice) / Symbol.PipSize;
                double tpPips = slPips * RRTarget;

                Print("Bullish Sweep Detected - Executing Buy.");
                ExecuteMarketOrder(TradeType.Buy, SymbolName, volumeInUnits, TradeLabel, slPips, tpPips);
            }
            else if (bearSweep)
            {
                double slPrice = high + (currentAtr * StopBuffer);
                double slPips = (slPrice - Bid) / Symbol.PipSize;
                double tpPips = slPips * RRTarget;

                Print("Bearish Sweep Detected - Executing Sell.");
                ExecuteMarketOrder(TradeType.Sell, SymbolName, volumeInUnits, TradeLabel, slPips, tpPips);
            }
        }
    }
}
FTtrader
Posts: 567
Joined: Mon Aug 03, 2026 2:43 pm

Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping

Post by FTtrader »

Key Differences from MQL in cTrader

Time Series Syncing: To get the Daily EMA alignment accurately, you cannot just use iBarShift. Instead, we retrieve a separate Bars object for the Daily timeframe in OnStart(), and sync the indexes precisely using _dailyBars.OpenTimes.GetIndexByTime().

Index Positioning: In MT4/MT5, array index [0] is the current bar, and [1] is the previous closed bar. In cTrader, arrays are chronologically ordered. Bars.Count - 1 is the current, incomplete bar. We evaluate Bars.Count - 2 as the closed setup candle, and scan the liquidity pool starting from Bars.Count - 3.

Volume Standardization: The Symbol.QuantityToVolumeInUnits(LotSize) method safely bridges the gap between lot sizes and actual contract units, which prevents order rejections across different brokers.
LondonScalper
Posts: 615
Joined: Sat Sep 05, 2026 7:54 am

Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping

Post by LondonScalper »

FTtrader wrote:I never rely purely on oscillator extremes. For an exhaustion or squeeze setup to be valid, I absolutely require a prior structural impulse to fade, followed by a clear liquidity sweep.
That is the answer I was after. If silver has not taken a prior high or low and closed back inside it, there is no fade, whatever RSI is shouting.

ATR displacement is a fair way to demand an impulse. ATR is a trailing average, so a slow grind can clear the multiple without looking like a clean drive. I still want the move in a handful of honest bars, not two hours of drip.

The 08:00–16:30 UTC box is a sensible London-through-overlap window. The first minutes of London cash, and the mess around the New York fix, are still thin enough on silver that a textbook M5 sweep can be a vacuum. The stop beyond the sweep wick, plus a buffer that is a known slice of the day's risk, is the right shape.

A Pine adverse-excursion figure is homework. Bar highs are not the fill path, so reconcile it to a tick log before you trust the win rate.
Post Reply