Page 2 of 2
Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping
Posted: Sun Sep 20, 2026 9:07 am
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;
}
Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping
Posted: Sun Sep 20, 2026 9:08 am
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.
Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping
Posted: Sun Sep 20, 2026 9:08 am
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");
}
}
Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping
Posted: Sun Sep 20, 2026 9:09 am
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.
Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping
Posted: Sun Sep 20, 2026 9:10 am
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());
}
}
Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping
Posted: Sun Sep 20, 2026 9:10 am
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.
Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping
Posted: Sun Sep 20, 2026 9:11 am
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.
Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping
Posted: Sun Sep 20, 2026 9:11 am
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);
}
}
}
}
Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping
Posted: Sun Sep 20, 2026 9:11 am
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.
Re: 💎 The "Silver Squeeze" Strategy: Master XAG/USD Scalping
Posted: Sun Sep 20, 2026 11:48 am
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.