Advertisement IC Markets

XAU/USD (Spot Gold) trading strategy PRO level

Share, develop, and backtest custom MQL4/MQL5 Expert Advisors, Python data-scraping scripts, trading bots, and automated market alert systems.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: XAU/USD (Spot Gold) trading strategy PRO level

Post by PTScalper »

1. MT5 Strategy Specification & Engine Logic

A common failure point in XAU/USD EAs is hardcoding "pips" or "points," which breaks across brokers that quote gold to 2 decimal places (0.01) versus 3 decimal places (0.001). This strategy normalizes all price distances directly into USD per Troy Ounce ($) and uses tick-value contract math for exact lot sizing.
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: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: XAU/USD (Spot Gold) trading strategy PRO level

Post by PTScalper »

2. Complete MQL5 Expert Advisor (XAUUSD_SweepScaler.mq5)

Open MetaEditor 5 (F4 in MT5), create a new Expert Advisor named XAUUSD_SweepScaler.mq5, and paste the complete source code below:

Code: Select all

//+------------------------------------------------------------------+
//|                                          XAUUSD_SweepScaler.mq5  |
//|        D1/H4/M15 Liquidity Sweep & House-Money Scaling Engine    |
//+------------------------------------------------------------------+
#property copyright "Custom MQL5 Institutional Gold Architecture"
#property version   "2.10"
#property strict

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

//--- Input Parameters: Execution Mode & Session Filters
input group "=== 1. Execution & Session Controls (GMT/Server) ==="
input ulong  InpMagicNumber        = 20261018; // EA Magic Number
input bool   InpEnableAutoEntries  = true;     // Enable Automated M15 Sweep Entries
input bool   InpManageManualTrades = true;     // Auto-Manage Manual / Mobile Entries (Magic = 0)
input int    InpSessionStartHour   = 8;        // Session Start Hour (Server Time, e.g., London Open)
input int    InpSessionEndHour     = 19;       // Session End Hour (Server Time, e.g., NY Mid-Session)
input double InpMaxSpreadUSD       = 0.35;     // Max Allowed Spread in USD ($0.35 = 3.5 pips)

//--- Input Parameters: D1 / H4 / M15 Price Action Structure
input group "=== 2. Multi-Timeframe Structure (Price in USD) ==="
input bool   InpUseD1TrendFilter   = true;     // Align with D1 EMA(20) Bias (unless PDH/PDL Sweep)
input int    InpH4LookbackBars     = 40;       // H4 Lookback Bars for Structural S/R Pivots
input int    InpM15SwingLookback   = 12;       // M15 Lookback Bars for Local Liquidity High/Low
input double InpH4ZoneToleranceUSD = 3.50;     // Proximity to H4 S/R or PDH/PDL ($ USD)
input double InpMinSweepUSD        = 0.40;     // Min Liquidity Sweep Piercing ($ USD)
input double InpMaxSweepUSD        = 4.50;     // Max Liquidity Sweep Piercing ($ USD)
input double InpMinWickRatio       = 0.42;     // Min Rejection Wick-to-Range Ratio (0.0 - 1.0)

//--- Input Parameters: Stop-Loss, R-Targets & Trade Management
input group "=== 3. Stop-Loss & Asymmetric Profit-Lock ==="
input double InpSLBufferUSD        = 0.90;     // Extra SL Buffer Beyond Sweep Wick ($ USD)
input double InpMinSLDistanceUSD   = 1.80;     // Minimum Allowed Stop-Loss Distance ($ USD)
input double InpMaxSLDistanceUSD   = 6.50;     // Maximum Allowed Stop-Loss Distance ($ USD)
input double InpTargetRMultiple    = 3.0;      // Final Take-Profit Target (R-Multiple)
input double InpPartialCloseAtR    = 1.0;      // Trigger Partial Close & Breakeven at +R
input double InpPartialCloseRatio  = 0.50;     // Volume Ratio to Close at +1R (0.50 = 50%)
input double InpBECommissionUSD    = 0.25;     // Breakeven Lock Buffer for ECN Commission ($ USD)
input double InpTrailTriggerR      = 2.0;      // Start M15 Structure Trailing after +R

//--- Input Parameters: Exponential House-Money Risk Model
input group "=== 4. House-Money Exponential Position Sizing ==="
input double InpBaseRiskPct        = 0.50;     // Baseline Capital Risk per Trade (%)
input double InpReinvestProfitPct  = 50.0;     // % of Today's Realized Profit to Scale into Next Trade
input double InpMaxTotalRiskPct    = 2.50;     // Hard Cap on Total Risk per Trade (% of Equity)
input int    InpMaxDailyLosses     = 2;        // Daily Circuit Breaker (Max Consecutive Losses)

//--- Global Objects & Handles
CTrade         ExtTrade;
CPositionInfo  ExtPosition;
CSymbolInfo    ExtSymbol;
int            ExtHandleD1Ema = INVALID_HANDLE;
datetime       ExtLastM15Bar  = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   if(!ExtSymbol.Name(_Symbol))
      return(INIT_FAILED);
   ExtSymbol.RefreshRates();

   ExtTrade.SetExpertMagicNumber(InpMagicNumber);
   ExtTrade.SetMarginMode();
   ExtTrade.SetTypeFillingBySymbol(_Symbol);
   ExtTrade.SetDeviationInPoints(20);

   ExtHandleD1Ema = iMA(_Symbol, PERIOD_D1, 20, 0, MODE_EMA, PRICE_CLOSE);
   if(ExtHandleD1Ema == INVALID_HANDLE)
   {
      Print("Error creating D1 EMA handle: ", GetLastError());
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(ExtHandleD1Ema != INVALID_HANDLE)
      IndicatorRelease(ExtHandleD1Ema);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   if(!ExtSymbol.RefreshRates())
      return;

   // 1. Tick-level active management (+1R partial close, BE lock, +2R trailing)
   ManageOpenPositions();

   // 2. Bar-level entry scanning on M15 close
   if(!InpEnableAutoEntries)
      return;

   datetime currentM15Bar = iTime(_Symbol, PERIOD_M15, 0);
   if(currentM15Bar == ExtLastM15Bar)
      return;

   // Verify trading session window & spread
   MqlDateTime dt;
   TimeCurrent(dt);
   if(dt.hour < InpSessionStartHour || dt.hour >= InpSessionEndHour)
      return;

   double currentSpreadUSD = ExtSymbol.Ask() - ExtSymbol.Bid();
   if(currentSpreadUSD > InpMaxSpreadUSD)
      return;

   // Mark bar as processed once inside valid session and spread
   ExtLastM15Bar = currentM15Bar;

   // Skip new auto entries if an EA position is already open on this symbol
   if(HasActiveRiskPosition())
      return;

   // Check Daily Circuit Breaker
   int consecutiveLossesToday = 0;
   double realizedPnLToday = GetTodayRealizedStats(consecutiveLossesToday);
   if(consecutiveLossesToday >= InpMaxDailyLosses)
      return;

   // 3. Evaluate D1 / H4 / M15 Liquidity Sweep Setup
   CheckForM15SweepEntry(realizedPnLToday);
}

//+------------------------------------------------------------------+
//| Check if there is an open trade that is NOT yet locked at BE     |
//+------------------------------------------------------------------+
bool HasActiveRiskPosition()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i))
         continue;
      if(ExtPosition.Symbol() != _Symbol)
         continue;

      ulong magic = ExtPosition.Magic();
      if(magic != InpMagicNumber && !(InpManageManualTrades && magic == 0))
         continue;

      double openPrice = ExtPosition.PriceOpen();
      double sl        = ExtPosition.StopLoss();

      // If SL is not yet at Breakeven, capital is still at risk
      if(ExtPosition.PositionType() == POSITION_TYPE_BUY && (sl < openPrice || sl == 0.0))
         return(true);
      if(ExtPosition.PositionType() == POSITION_TYPE_SELL && (sl > openPrice || sl == 0.0))
         return(true);
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Calculate Today's Realized Net PnL and Consecutive Losses        |
//+------------------------------------------------------------------+
double GetTodayRealizedStats(int &outConsecutiveLosses)
{
   outConsecutiveLosses = 0;
   double netPnL = 0.0;

   datetime startOfDay = iTime(_Symbol, PERIOD_D1, 0);
   if(!HistorySelect(startOfDay, TimeCurrent()))
      return(0.0);

   int totalDeals = HistoryDealsTotal();
   for(int i = 0; i < totalDeals; i++)
   {
      ulong ticket = HistoryDealGetTicket(i);
      if(ticket == 0)
         continue;

      if(HistoryDealGetString(ticket, DEAL_SYMBOL) != _Symbol)
         continue;

      ENUM_DEAL_ENTRY entryType = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket, DEAL_ENTRY);
      if(entryType != DEAL_ENTRY_OUT && entryType != DEAL_ENTRY_INOUT)
         continue;

      double dealNet = HistoryDealGetDouble(ticket, DEAL_PROFIT)
                     + HistoryDealGetDouble(ticket, DEAL_COMMISSION)
                     + HistoryDealGetDouble(ticket, DEAL_SWAP);

      netPnL += dealNet;

      // Track streak (ignore tiny breakeven scratches within $2.00)
      if(dealNet < -2.0)
         outConsecutiveLosses++;
      else if(dealNet > 2.0)
         outConsecutiveLosses = 0;
   }
   return(netPnL);
}

//+------------------------------------------------------------------+
//| Verify if Price is at an H4 Structural Zone or PDH / PDL         |
//+------------------------------------------------------------------+
bool IsNearH4OrDailyStructure(double priceLevel, bool isBullishSetup, bool &outSweptPDH_PDL)
{
   outSweptPDH_PDL = false;

   double pdh = iHigh(_Symbol, PERIOD_D1, 1);
   double pdl = iLow(_Symbol, PERIOD_D1, 1);

   // Check Previous Day High / Low confluence first
   if(isBullishSetup && MathAbs(priceLevel - pdl) <= InpH4ZoneToleranceUSD)
   {
      outSweptPDH_PDL = true;
      return(true);
   }
   if(!isBullishSetup && MathAbs(priceLevel - pdh) <= InpH4ZoneToleranceUSD)
   {
      outSweptPDH_PDL = true;
      return(true);
   }

   // Scan H4 Swing Pivots (3-bar fractal pivots over InpH4LookbackBars)
   for(int i = 2; i <= InpH4LookbackBars; i++)
   {
      if(isBullishSetup)
      {
         double h4Low  = iLow(_Symbol, PERIOD_H4, i);
         double prevL  = iLow(_Symbol, PERIOD_H4, i + 1);
         double nextL  = iLow(_Symbol, PERIOD_H4, i - 1);
         if(h4Low <= prevL && h4Low <= nextL)
         {
            if(MathAbs(priceLevel - h4Low) <= InpH4ZoneToleranceUSD)
               return(true);
         }
      }
      else
      {
         double h4High = iHigh(_Symbol, PERIOD_H4, i);
         double prevH  = iHigh(_Symbol, PERIOD_H4, i + 1);
         double nextH  = iHigh(_Symbol, PERIOD_H4, i - 1);
         if(h4High >= prevH && h4High >= nextH)
         {
            if(MathAbs(priceLevel - h4High) <= InpH4ZoneToleranceUSD)
               return(true);
         }
      }
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Evaluate M15 Liquidity Sweep + Displacement Entry                |
//+------------------------------------------------------------------+
void CheckForM15SweepEntry(double realizedPnLToday)
{
   // M15 Closed Signal Candle = Bar 1
   double open1  = iOpen(_Symbol, PERIOD_M15, 1);
   double high1  = iHigh(_Symbol, PERIOD_M15, 1);
   double low1   = iLow(_Symbol, PERIOD_M15, 1);
   double close1 = iClose(_Symbol, PERIOD_M15, 1);
   double range1 = high1 - low1;

   if(range1 <= 0.40)
      return;

   // Find prior M15 Liquidity High and Low (Bars 2 to InpM15SwingLookback + 1)
   double priorLiquidityLow  = DBL_MAX;
   double priorLiquidityHigh = 0.0;

   for(int i = 2; i <= InpM15SwingLookback + 1; i++)
   {
      double l = iLow(_Symbol, PERIOD_M15, i);
      double h = iHigh(_Symbol, PERIOD_M15, i);
      if(l < priorLiquidityLow)  priorLiquidityLow  = l;
      if(h > priorLiquidityHigh) priorLiquidityHigh = h;
   }

   // Check D1 Trend Filter (20 EMA on Daily)
   double d1EmaBuf[];
   ArraySetAsSeries(d1EmaBuf, true);
   bool d1Bullish = true;
   bool d1Bearish = true;

   if(InpUseD1TrendFilter && CopyBuffer(ExtHandleD1Ema, 0, 0, 1, d1EmaBuf) == 1)
   {
      d1Bullish = (close1 >= d1EmaBuf[0]);
      d1Bearish = (close1 <= d1EmaBuf[0]);
   }

   // --- 1. BULLISH LIQUIDITY SWEEP CHECK ---
   double sweepDepthBull = priorLiquidityLow - low1;
   double lowerWick      = MathMin(open1, close1) - low1;

   if(sweepDepthBull >= InpMinSweepUSD && sweepDepthBull <= InpMaxSweepUSD &&
      close1 > priorLiquidityLow && close1 > open1 &&
      (lowerWick / range1) >= InpMinWickRatio)
   {
      bool sweptDailyExtreme = false;
      if(IsNearH4OrDailyStructure(low1, true, sweptDailyExtreme))
      {
         if(d1Bullish || sweptDailyExtreme)
         {
            double entryPrice = ExtSymbol.Ask();
            double slPrice    = NormalizeDouble(low1 - InpSLBufferUSD, _Digits);
            double slDistUSD  = entryPrice - slPrice;

            if(slDistUSD >= InpMinSLDistanceUSD && slDistUSD <= InpMaxSLDistanceUSD)
            {
               double tpPrice = NormalizeDouble(entryPrice + (slDistUSD * InpTargetRMultiple), _Digits);
               double lots    = CalculateHouseMoneyLots(slDistUSD, realizedPnLToday);
               if(lots > 0.0)
                  ExtTrade.Buy(lots, _Symbol, entryPrice, slPrice, tpPrice, "XAU_Sweep_Long");
            }
         }
      }
   }

   // --- 2. BEARISH LIQUIDITY SWEEP CHECK ---
   double sweepDepthBear = high1 - priorLiquidityHigh;
   double upperWick      = high1 - MathMax(open1, close1);

   if(sweepDepthBear >= InpMinSweepUSD && sweepDepthBear <= InpMaxSweepUSD &&
      close1 < priorLiquidityHigh && close1 < open1 &&
      (upperWick / range1) >= InpMinWickRatio)
   {
      bool sweptDailyExtreme = false;
      if(IsNearH4OrDailyStructure(high1, false, sweptDailyExtreme))
      {
         if(d1Bearish || sweptDailyExtreme)
         {
            double entryPrice = ExtSymbol.Bid();
            double slPrice    = NormalizeDouble(high1 + InpSLBufferUSD, _Digits);
            double slDistUSD  = slPrice - entryPrice;

            if(slDistUSD >= InpMinSLDistanceUSD && slDistUSD <= InpMaxSLDistanceUSD)
            {
               double tpPrice = NormalizeDouble(entryPrice - (slDistUSD * InpTargetRMultiple), _Digits);
               double lots    = CalculateHouseMoneyLots(slDistUSD, realizedPnLToday);
               if(lots > 0.0)
                  ExtTrade.Sell(lots, _Symbol, entryPrice, slPrice, tpPrice, "XAU_Sweep_Short");
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Calculate Position Size Using Base Risk + Locked-In Daily Profit |
//+------------------------------------------------------------------+
double CalculateHouseMoneyLots(double slDistanceUSD, double realizedPnLToday)
{
   if(slDistanceUSD <= 0.0)
      return(0.0);

   double balance       = AccountInfoDouble(ACCOUNT_BALANCE);
   double equity        = AccountInfoDouble(ACCOUNT_EQUITY);
   double baseRiskMoney = balance * (InpBaseRiskPct / 100.0);
   double totalRiskUSD  = baseRiskMoney;

   // Add House-Money scaling strictly from positive realized gains today
   if(realizedPnLToday > 0.0 && InpReinvestProfitPct > 0.0)
   {
      double houseMoneyAddon = realizedPnLToday * (InpReinvestProfitPct / 100.0);
      totalRiskUSD += houseMoneyAddon;
   }

   // Enforce hard cap on total trade risk
   double maxAllowedRiskUSD = equity * (InpMaxTotalRiskPct / 100.0);
   if(totalRiskUSD > maxAllowedRiskUSD)
      totalRiskUSD = maxAllowedRiskUSD;

   // Exact MT5 Contract Tick Value Calculation
   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   if(tickSize <= 0.0 || tickValue <= 0.0)
      return(0.0);

   double lossPerLot = (slDistanceUSD / tickSize) * tickValue;
   if(lossPerLot <= 0.0)
      return(0.0);

   double rawLots = totalRiskUSD / lossPerLot;

   // Normalize to broker volume step & limits
   double volStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double volMin  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double volMax  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);

   double normalizedLots = MathFloor(rawLots / volStep) * volStep;

   // Ensure volume can be cleanly split for partial close if above 2x min lot
   if(normalizedLots < volMin)
      return(0.0);
   if(normalizedLots > volMax)
      normalizedLots = volMax;

   return(NormalizeDouble(normalizedLots, 2));
}

//+------------------------------------------------------------------+
//| Manage Open Positions: +1R Partial & BE Lock, +2R M15 Trailing   |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
   double volStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double volMin  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i))
         continue;
      if(ExtPosition.Symbol() != _Symbol)
         continue;

      ulong magic = ExtPosition.Magic();
      if(magic != InpMagicNumber && !(InpManageManualTrades && magic == 0))
         continue;

      ulong  ticket    = ExtPosition.Ticket();
      double openPrice = ExtPosition.PriceOpen();
      double currentSL = ExtPosition.StopLoss();
      double currentTP = ExtPosition.TakeProfit();
      double volume    = ExtPosition.Volume();
      bool   isBuy     = (ExtPosition.PositionType() == POSITION_TYPE_BUY);

      if(currentSL == 0.0)
         continue;

      // Determine whether position is still in Phase 1 (initial risk) or Phase 2 (locked at BE+)
      bool isLockedAtBE = isBuy ? (currentSL >= openPrice) : (currentSL <= openPrice);

      // --- PHASE 1: Monitor for +1R Partial Close & Breakeven Lock ---
      if(!isLockedAtBE)
      {
         double initialRiskUSD = isBuy ? (openPrice - currentSL) : (currentSL - openPrice);
         if(initialRiskUSD <= 0.0)
            continue;

         double currentGainUSD = isBuy ? (ExtSymbol.Bid() - openPrice) : (openPrice - ExtSymbol.Ask());

         if(currentGainUSD >= (initialRiskUSD * InpPartialCloseAtR))
         {
            // Calculate partial volume to close
            double closeVol = MathFloor((volume * InpPartialCloseRatio) / volStep) * volStep;
            double remVol   = volume - closeVol;

            if(closeVol >= volMin && remVol >= volMin)
               ExtTrade.PositionClosePartial(ticket, NormalizeDouble(closeVol, 2));

            // Move SL on remaining position to Breakeven + ECN Commission Buffer
            double newSL = isBuy ? NormalizeDouble(openPrice + InpBECommissionUSD, _Digits)
                                 : NormalizeDouble(openPrice - InpBECommissionUSD, _Digits);

            ExtTrade.PositionModify(ticket, newSL, currentTP);
         }
      }
      // --- PHASE 2: Structural M15 Trailing Stop once trade is Risk-Free ---
      else
      {
         // Estimate 1R from TP distance if TP exists, else use $2.50 baseline
         double estOneR = (currentTP > 0.0)
                        ? (MathAbs(currentTP - openPrice) / InpTargetRMultiple)
                        : 2.50;

         double currentGainUSD = isBuy ? (ExtSymbol.Bid() - openPrice) : (openPrice - ExtSymbol.Ask());
         if(currentGainUSD < (estOneR * InpTrailTriggerR))
            continue;

         // Trail behind the lowest low / highest high of the last 3 closed M15 bars
         if(isBuy)
         {
            double m15StructureLow = MathMin(iLow(_Symbol, PERIOD_M15, 1),
                                     MathMin(iLow(_Symbol, PERIOD_M15, 2), iLow(_Symbol, PERIOD_M15, 3)));
            double candidateSL     = NormalizeDouble(m15StructureLow - 0.30, _Digits);

            if(candidateSL > currentSL + 0.10 && candidateSL < ExtSymbol.Bid() - InpMinSLDistanceUSD)
               ExtTrade.PositionModify(ticket, candidateSL, currentTP);
         }
         else
         {
            double m15StructureHigh = MathMax(iHigh(_Symbol, PERIOD_M15, 1),
                                      MathMax(iHigh(_Symbol, PERIOD_M15, 2), iHigh(_Symbol, PERIOD_M15, 3)));
            double candidateSL      = NormalizeDouble(m15StructureHigh + 0.30, _Digits);

            if(candidateSL < currentSL - 0.10 && candidateSL > ExtSymbol.Ask() + InpMinSLDistanceUSD)
               ExtTrade.PositionModify(ticket, candidateSL, currentTP);
         }
      }
   }
}
//+------------------------------------------------------------------+
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: XAU/USD (Spot Gold) trading strategy PRO level

Post by PTScalper »

3. How the Mobile + Desktop Hybrid Workflow Operates

Automated Desktop / VPS Execution (InpEnableAutoEntries = true):

Attach the EA to an M15 XAUUSD chart. On every closed M15 candle during the London and New York sessions, the EA checks whether price swept a 12-bar M15 high/low inside an H4 structural pivot (or PDH/PDL) and closed back inside with a strong rejection wick.

Mobile Discretionary Execution (InpManageManualTrades = true):

When you spot a setup on your phone and execute a market or limit order via the MT5 mobile app, you only need to set your initial Stop-Loss (and optionally Take-Profit). Because manual mobile trades carry MagicNumber = 0, the EA running on your workstation/VPS immediately adopts the ticket:

As soon as price moves +1R in your favor (equal to your initial Stop-Loss distance), the EA closes 50% of your lot size and moves your Stop-Loss to Entry Price + $0.25 to cover Raw ECN round-turn commissions.

Once the trade reaches +2R, the EA begins trailing the remaining 50% runner behind the 3-bar M15 structural swing low/high (± $0.30 buffer).

Once the trade closes in profit, the realized gain is automatically added to GetTodayRealizedStats(), scaling up the lot size of your next setup using house money while keeping your starting daily balance protected.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: XAU/USD (Spot Gold) trading strategy PRO level

Post by PTScalper »

4. MT5 Strategy Tester (Backtesting) Configuration

To validate and calibrate this EA against historical Raw ECN data in the MT5 Strategy Tester (Ctrl + R):

Symbol & Timeframe: Select XAUUSD on M15. The EA automatically pulls H4 and D1 series in the background.

Modelling Quality: Select "Every tick based on real ticks". Never use "1 minute OHLC" for gold liquidity-sweep strategies, as synthetic ticks distort intra-bar wick extensions and trigger +1R partial closes inaccurately.

Execution Delay (Latency Stress Test): Set Execution Delay to 50 ms – 120 ms to simulate realistic ECN order routing during London/NY volatility spikes.

Server Time Alignment (InpSessionStartHour / InpSessionEndHour): On brokers using EET / GMT+2 (GMT+3 in DST) such as IC Markets, London Open (07:00 GMT) corresponds to 10:00 Server Time, and late NY (17:00 GMT) corresponds to 20:00 Server Time. Adjust InpSessionStartHour = 10 and InpSessionEndHour = 20 to match your broker's Market Watch clock.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: XAU/USD (Spot Gold) trading strategy PRO level

Post by PTScalper »

To make the XAU/USD strategy 100% autonomous (XAUUSD_AutonomousEngine.mq5) for unattended 24/5 VPS execution, every discretionary step—session clock alignment, H4 Fair Value Gap and Order Block mapping, two-step M15 Break-of-Structure confirmation, pullback limit order placement and cancellation, high-impact USD news blackouts, and Friday rollover protection—must be governed by an internal state machine.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: XAU/USD (Spot Gold) trading strategy PRO level

Post by PTScalper »

2. Complete Autonomous MQL5 Code (XAUUSD_AutonomousEngine.mq5)

Open MetaEditor 5 (F4), create a new Expert Advisor named XAUUSD_AutonomousEngine.mq5, and paste the complete source code below:

Code: Select all

//+------------------------------------------------------------------+
//|                                      XAUUSD_AutonomousEngine.mq5 |
//|     100% Autonomous D1/H4/M15 Liquidity Sweep & Scaling Engine   |
//+------------------------------------------------------------------+
#property copyright "Autonomous Institutional XAUUSD Engine v3.0"
#property version   "3.00"
#property strict

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

//--- 1. Autonomous GMT Session & Friday Flat Controls
input group "=== 1. Autonomous GMT Clock & Session Windows ==="
input ulong  InpMagicNumber          = 20261099; // Autonomous EA Magic Number
input bool   InpUseLondonSession     = true;     // Trade London Window (07:00 - 10:30 GMT)
input int    InpLondonStartHourGMT   = 7;        // London Start Hour (GMT)
input int    InpLondonEndHourGMT     = 11;       // London End Hour (GMT)
input bool   InpUseNYOverlapSession  = true;     // Trade NY Overlap Window (12:00 - 16:30 GMT)
input int    InpNYStartHourGMT       = 12;       // NY Start Hour (GMT)
input int    InpNYEndHourGMT         = 17;       // NY End Hour (GMT)
input bool   InpFridayAutoFlat       = true;     // Auto-Close All Trades & Orders Friday Evening
input int    InpFridayFlatHourGMT    = 20;       // Friday Flat Hour (GMT)
input int    InpFridayFlatMinuteGMT  = 30;       // Friday Flat Minute (GMT)
input double InpMaxSpreadUSD         = 0.35;     // Max Allowed ECN Spread in USD ($0.35 = 3.5 pips)

//--- 2. Native MQL5 Economic Calendar News Filter
input group "=== 2. Built-In High-Impact USD News Shield ==="
input bool   InpUseNewsFilter        = true;     // Enable Native MT5 Calendar USD News Filter
input int    InpNewsBlockBeforeMin   = 15;       // Minutes Before High-Impact USD News to Block/Cancel
input int    InpNewsBlockAfterMin    = 15;       // Minutes After High-Impact USD News to Resume
input bool   InpLockBEBeforeNews     = true;     // Auto-Lock Profitable Trades (>= 0.5R) to BE Before News

//--- 3. Multi-Timeframe D1 / H4 / M15 Institutional Structure
input group "=== 3. D1 / H4 / M15 Structural & Sweep Parameters ==="
input bool   InpUseD1TrendFilter     = true;     // Require D1 EMA(20) Bias (Overridden on PDH/PDL Sweep)
input int    InpH4LookbackBars       = 45;       // H4 Lookback Bars for S/R Pivots & Unmitigated FVGs
input int    InpM15SwingLookback     = 12;       // M15 Lookback Bars for Liquidity High/Low
input double InpH4ZoneToleranceUSD   = 3.50;     // Proximity Tolerance to H4 Zone / PDH / PDL ($ USD)
input double InpMinSweepUSD          = 0.40;     // Min M15 Liquidity Sweep Depth ($ USD)
input double InpMaxSweepUSD          = 4.80;     // Max M15 Liquidity Sweep Depth ($ USD)
input double InpMinWickRatio         = 0.40;     // Min Rejection Wick Ratio for 1-Bar Sweep (0.0 - 1.0)

//--- 4. Pullback Limit Execution & Asymmetric Trade Management
input group "=== 4. Pullback Limit Order & +1R/+2R Management ==="
input bool   InpUsePullbackLimit     = true;     // Place BuyLimit/SellLimit at Retracement (False = Market)
input double InpRetraceEntryRatio    = 0.50;     // Displacement Candle Retracement Entry Ratio (0.50 = 50%)
input int    InpLimitExpiryM15Bars   = 3;        // Auto-Expire Unfilled Limit Orders After N M15 Bars
input double InpCancelLimitAtR       = 1.50;     // Auto-Cancel Unfilled Limit If Price Runs to +R
input double InpSLBufferUSD          = 0.85;     // Hard SL Buffer Beyond Sweep Wick ($ USD)
input double InpMinSLDistanceUSD     = 1.80;     // Min Allowed Stop-Loss Distance ($ USD)
input double InpMaxSLDistanceUSD     = 6.50;     // Max Allowed Stop-Loss Distance ($ USD)
input double InpTargetRMultiple      = 3.00;     // Final Take-Profit Target (R-Multiple)
input double InpPartialCloseAtR      = 1.00;     // Trigger Partial Close & Breakeven Lock at +R
input double InpPartialCloseRatio    = 0.50;     // Fraction of Volume to Bank at +1R (0.50 = 50%)
input double InpBECommissionUSD      = 0.25;     // Breakeven Plus ECN Commission Buffer ($ USD)
input double InpTrailTriggerR        = 2.00;     // Activate 3-Bar M15 Structural Trailing Stop at +R

//--- 5. House-Money Scaling & Equity Circuit Breakers
input group "=== 5. House-Money Scaling & Circuit Breakers ==="
input double InpBaseRiskPct          = 0.50;     // Base Risk Per Trade (% of Daily Starting Balance)
input double InpReinvestProfitPct    = 50.0;     // % of Today's Realized Net Profit Added to Next Risk
input double InpMaxTotalRiskPct      = 2.50;     // Max Cap on Risk Per Trade (% of Account Equity)
input double InpMaxDailyDrawdownPct  = 2.00;     // Hard Daily Equity Drawdown Breaker (%)
input int    InpMaxConsecutiveLosses = 2;        // Max Consecutive Losses Per Day Before Shutdown
input bool   InpShowChartHUD         = true;     // Render Real-Time Autonomous Telemetry HUD on Chart

//--- Global Engine State
CTrade         ExtTrade;
CPositionInfo  ExtPosition;
COrderInfo     ExtOrder;
CSymbolInfo    ExtSymbol;

int            ExtHandleD1Ema        = INVALID_HANDLE;
datetime       ExtLastM15BarTime     = 0;
datetime       ExtCurrentDayStart    = 0;
double         ExtDayStartBalance    = 0.0;
bool           ExtDailyBreakerTripped= false;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   if(!ExtSymbol.Name(_Symbol))
      return(INIT_FAILED);
   ExtSymbol.RefreshRates();

   ExtTrade.SetExpertMagicNumber(InpMagicNumber);
   ExtTrade.SetMarginMode();
   ExtTrade.SetTypeFillingBySymbol(_Symbol);
   ExtTrade.SetDeviationInPoints(25);

   ExtHandleD1Ema = iMA(_Symbol, PERIOD_D1, 20, 0, MODE_EMA, PRICE_CLOSE);
   if(ExtHandleD1Ema == INVALID_HANDLE)
   {
      Print("Failed to initialize D1 EMA(20) handle. Error: ", GetLastError());
      return(INIT_FAILED);
   }

   ExtCurrentDayStart     = iTime(_Symbol, PERIOD_D1, 0);
   ExtDayStartBalance     = AccountInfoDouble(ACCOUNT_BALANCE);
   ExtDailyBreakerTripped = false;

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(ExtHandleD1Ema != INVALID_HANDLE)
      IndicatorRelease(ExtHandleD1Ema);
   Comment("");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   if(!ExtSymbol.RefreshRates())
      return;

   // 1. Check Daily Rollover Reset & Hard Equity Drawdown Circuit Breaker
   UpdateDailyBaselineAndCheckDrawdown();
   if(ExtDailyBreakerTripped)
   {
      if(InpShowChartHUD)
         RenderTelemetryHUD("DAILY DRAWDOWN BREAKER TRIPPED - LOCKED UNTIL D1 ROLLOVER", false, 0.0, 0);
      return;
   }

   // 2. Check Friday Evening Autonomous Flat Protection
   if(IsFridayCutoffReached())
   {
      CloseAllPositionsAndDeleteOrders("Friday GMT Cutoff");
      if(InpShowChartHUD)
         RenderTelemetryHUD("FRIDAY EVENING GMT CUTOFF - FLAT FOR WEEKEND", false, 0.0, 0);
      return;
   }

   // 3. Tick-Level Management of Open Positions (+1R Partial, BE Lock, +2R Trail)
   ManageOpenPositions();

   // 4. Tick-Level Management of Pending Pullback Limit Orders
   ManagePendingLimitOrders();

   // 5. Evaluate Built-In High-Impact USD News Filter
   string activeNewsName = "";
   bool isNewsBlackout = IsHighImpactUSDNewsWindow(activeNewsName);
   if(isNewsBlackout)
   {
      DeleteAllPendingOrders();
      if(InpLockBEBeforeNews)
         HardenStopsBeforeNews();

      int losses = 0;
      double pnl = GetTodayRealizedStats(losses);
      if(InpShowChartHUD)
         RenderTelemetryHUD("USD HIGH-IMPACT NEWS SHIELD: " + activeNewsName, true, pnl, losses);
      return;
   }

   // 6. Verify Active GMT Session Window (London or NY Overlap)
   string sessionStatus = "";
   bool inSession = IsInsideGMTSession(sessionStatus);

   int consecutiveLosses = 0;
   double realizedPnLToday = GetTodayRealizedStats(consecutiveLosses);

   if(InpShowChartHUD)
      RenderTelemetryHUD(sessionStatus, false, realizedPnLToday, consecutiveLosses);

   if(!inSession)
      return;

   if(consecutiveLosses >= InpMaxConsecutiveLosses)
      return;

   // 7. New M15 Closed Bar Evaluation for Autonomous Entries
   datetime currentM15Bar = iTime(_Symbol, PERIOD_M15, 0);
   if(currentM15Bar == ExtLastM15BarTime)
      return;

   // Check ECN Spread Filter before marking bar as evaluated
   double currentSpreadUSD = ExtSymbol.Ask() - ExtSymbol.Bid();
   if(currentSpreadUSD > InpMaxSpreadUSD)
      return;

   ExtLastM15BarTime = currentM15Bar;

   // Only allow a new setup (or House-Money Pyramid) when no unprotected trade or pending order exists
   if(HasUnprotectedPositionOrPendingOrder())
      return;

   // 8. Run 2-Stage M15 Liquidity Sweep + Break of Structure Engine
   EvaluateM15InstitutionalTrap(realizedPnLToday);
}

//+------------------------------------------------------------------+
//| Track New Trading Day & Enforce Hard Daily Drawdown Breaker      |
//+------------------------------------------------------------------+
void UpdateDailyBaselineAndCheckDrawdown()
{
   datetime todayStart = iTime(_Symbol, PERIOD_D1, 0);
   if(todayStart != ExtCurrentDayStart)
   {
      ExtCurrentDayStart     = todayStart;
      ExtDayStartBalance     = AccountInfoDouble(ACCOUNT_BALANCE);
      ExtDailyBreakerTripped = false;
   }

   if(ExtDayStartBalance <= 0.0)
      return;

   double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   double ddPct = ((ExtDayStartBalance - currentEquity) / ExtDayStartBalance) * 100.0;

   if(ddPct >= InpMaxDailyDrawdownPct)
   {
      CloseAllPositionsAndDeleteOrders("Max Daily Drawdown Breaker");
      ExtDailyBreakerTripped = true;
   }
}

//+------------------------------------------------------------------+
//| Check if Friday GMT Flat Window is Reached                       |
//+------------------------------------------------------------------+
bool IsFridayCutoffReached()
{
   if(!InpFridayAutoFlat)
      return(false);

   MqlDateTime gmt;
   TimeGMT(gmt);

   if(gmt.day_of_week == 5)
   {
      if(gmt.hour > InpFridayFlatHourGMT ||
        (gmt.hour == InpFridayFlatHourGMT && gmt.min >= InpFridayFlatMinuteGMT))
         return(true);
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Check if Current GMT Time is Inside London or NY Overlap         |
//+------------------------------------------------------------------+
bool IsInsideGMTSession(string &outStatus)
{
   MqlDateTime gmt;
   TimeGMT(gmt);

   // Block late NY rollover (21:00 - 23:59 GMT) and Weekend
   if(gmt.day_of_week == 0 || gmt.day_of_week == 6)
   {
      outStatus = "WEEKEND STANDBY";
      return(false);
   }

   bool inLondon = (InpUseLondonSession && gmt.hour >= InpLondonStartHourGMT && gmt.hour < InpLondonEndHourGMT);
   bool inNY     = (InpUseNYOverlapSession && gmt.hour >= InpNYStartHourGMT && gmt.hour < InpNYEndHourGMT);

   if(inLondon && inNY)
   {
      outStatus = "ACTIVE: LONDON + NY OVERLAP (GMT)";
      return(true);
   }
   if(inLondon)
   {
      outStatus = "ACTIVE: LONDON SESSION (GMT)";
      return(true);
   }
   if(inNY)
   {
      outStatus = "ACTIVE: NEW YORK SESSION (GMT)";
      return(true);
   }

   outStatus = "STANDBY: OUTSIDE PEAK LIQUIDITY WINDOWS (GMT)";
   return(false);
}

//+------------------------------------------------------------------+
//| Built-In MQL5 Economic Calendar High-Impact USD News Filter      |
//+------------------------------------------------------------------+
bool IsHighImpactUSDNewsWindow(string &outEventName)
{
   outEventName = "";
   if(!InpUseNewsFilter)
      return(false);

   // Strategy Tester does not always populate live CalendarValueHistory on all brokers
   if(MQLInfoInteger(MQL_TESTER))
      return(false);

   datetime nowServer  = TimeCurrent();
   datetime fromWindow = nowServer - (InpNewsBlockAfterMin * 60);
   datetime toWindow   = nowServer + (InpNewsBlockBeforeMin * 60);

   MqlCalendarValue values[];
   int count = CalendarValueHistory(values, fromWindow, toWindow, NULL, "USD");
   if(count <= 0)
      return(false);

   for(int i = 0; i < count; i++)
   {
      MqlCalendarEvent eventInfo;
      if(CalendarEventById(values[i].event_id, eventInfo))
      {
         if(eventInfo.importance == CALENDAR_IMPORTANCE_HIGH)
         {
            outEventName = eventInfo.name;
            return(true);
         }
      }
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Protect Open Profitable Positions (>= 0.5R) Ahead of Tier-1 News |
//+------------------------------------------------------------------+
void HardenStopsBeforeNews()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i))
         continue;
      if(ExtPosition.Symbol() != _Symbol || ExtPosition.Magic() != InpMagicNumber)
         continue;

      double openPrice = ExtPosition.PriceOpen();
      double currentSL = ExtPosition.StopLoss();
      double currentTP = ExtPosition.TakeProfit();
      bool   isBuy     = (ExtPosition.PositionType() == POSITION_TYPE_BUY);

      bool isLockedAtBE = isBuy ? (currentSL >= openPrice) : (currentSL <= openPrice && currentSL > 0.0);
      if(isLockedAtBE)
         continue;

      double oneR      = ExtractInitialOneR(openPrice, currentSL, currentTP, ExtPosition.Comment());
      double gainUSD   = isBuy ? (ExtSymbol.Bid() - openPrice) : (openPrice - ExtSymbol.Ask());

      if(oneR > 0.0 && gainUSD >= (oneR * 0.50))
      {
         double safeSL = isBuy ? NormalizeDouble(openPrice + InpBECommissionUSD, _Digits)
                               : NormalizeDouble(openPrice - InpBECommissionUSD, _Digits);
         ExtTrade.PositionModify(ExtPosition.Ticket(), safeSL, currentTP);
      }
   }
}

//+------------------------------------------------------------------+
//| Check for Unprotected Open Position or Active Pending Order      |
//+------------------------------------------------------------------+
bool HasUnprotectedPositionOrPendingOrder()
{
   // 1. Check active pending orders
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!ExtOrder.SelectByIndex(i))
         continue;
      if(ExtOrder.Symbol() == _Symbol && ExtOrder.Magic() == InpMagicNumber)
         return(true);
   }

   // 2. Check open positions whose SL is not yet locked at Breakeven+
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i))
         continue;
      if(ExtPosition.Symbol() != _Symbol || ExtPosition.Magic() != InpMagicNumber)
         continue;

      double openPrice = ExtPosition.PriceOpen();
      double sl        = ExtPosition.StopLoss();

      if(ExtPosition.PositionType() == POSITION_TYPE_BUY && (sl < openPrice || sl == 0.0))
         return(true);
      if(ExtPosition.PositionType() == POSITION_TYPE_SELL && (sl > openPrice || sl == 0.0))
         return(true);
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Close All EA Positions & Delete Pending Orders                   |
//+------------------------------------------------------------------+
void CloseAllPositionsAndDeleteOrders(string reason)
{
   DeleteAllPendingOrders();

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i))
         continue;
      if(ExtPosition.Symbol() == _Symbol && ExtPosition.Magic() == InpMagicNumber)
         ExtTrade.PositionClose(ExtPosition.Ticket());
   }
}

//+------------------------------------------------------------------+
//| Delete All Active Pending Limit Orders for This EA               |
//+------------------------------------------------------------------+
void DeleteAllPendingOrders()
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!ExtOrder.SelectByIndex(i))
         continue;
      if(ExtOrder.Symbol() == _Symbol && ExtOrder.Magic() == InpMagicNumber)
         ExtTrade.OrderDelete(ExtOrder.Ticket());
   }
}

//+------------------------------------------------------------------+
//| Manage Pending Limit Orders (Auto-Cancel If Target Reached Early)|
//+------------------------------------------------------------------+
void ManagePendingLimitOrders()
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!ExtOrder.SelectByIndex(i))
         continue;
      if(ExtOrder.Symbol() != _Symbol || ExtOrder.Magic() != InpMagicNumber)
         continue;

      double orderPrice = ExtOrder.PriceOpen();
      double orderSL    = ExtOrder.StopLoss();
      if(orderSL <= 0.0)
         continue;

      double oneR = MathAbs(orderPrice - orderSL);
      ENUM_ORDER_TYPE type = ExtOrder.OrderType();

      // If price runs +1.5R away from limit price before filling, thesis is front-run -> cancel
      if(type == ORDER_TYPE_BUY_LIMIT)
      {
         if(ExtSymbol.Bid() >= orderPrice + (oneR * InpCancelLimitAtR))
            ExtTrade.OrderDelete(ExtOrder.Ticket());
      }
      else if(type == ORDER_TYPE_SELL_LIMIT)
      {
         if(ExtSymbol.Ask() <= orderPrice - (oneR * InpCancelLimitAtR))
            ExtTrade.OrderDelete(ExtOrder.Ticket());
      }
   }
}

//+------------------------------------------------------------------+
//| Scan H4 Swing S/R, H4 Unmitigated FVG, and D1 PDH/PDL            |
//+------------------------------------------------------------------+
bool IsNearH4OrDailyStructure(double testPrice, bool isBullish, bool &outSweptDailyExtreme)
{
   outSweptDailyExtreme = false;

   double pdh = iHigh(_Symbol, PERIOD_D1, 1);
   double pdl = iLow(_Symbol, PERIOD_D1, 1);

   // 1. Check Previous Day High / Low Liquidity Pool
   if(isBullish && MathAbs(testPrice - pdl) <= InpH4ZoneToleranceUSD)
   {
      outSweptDailyExtreme = true;
      return(true);
   }
   if(!isBullish && MathAbs(testPrice - pdh) <= InpH4ZoneToleranceUSD)
   {
      outSweptDailyExtreme = true;
      return(true);
   }

   // 2. Scan H4 Fractal Swing Pivots and H4 3-Candle Fair Value Gaps (FVG)
   for(int i = 2; i <= InpH4LookbackBars; i++)
   {
      double h4HighPrev = iHigh(_Symbol, PERIOD_H4, i + 1);
      double h4LowPrev  = iLow(_Symbol, PERIOD_H4, i + 1);
      double h4HighCurr = iHigh(_Symbol, PERIOD_H4, i);
      double h4LowCurr  = iLow(_Symbol, PERIOD_H4, i);
      double h4HighNext = iHigh(_Symbol, PERIOD_H4, i - 1);
      double h4LowNext  = iLow(_Symbol, PERIOD_H4, i - 1);

      if(isBullish)
      {
         // A. H4 Swing Low Pivot
         if(h4LowCurr <= h4LowPrev && h4LowCurr <= h4LowNext)
         {
            if(MathAbs(testPrice - h4LowCurr) <= InpH4ZoneToleranceUSD)
               return(true);
         }
         // B. Bullish H4 Fair Value Gap (Imbalance between Bar i+1 High and Bar i-1 Low)
         if(h4LowNext > h4HighPrev)
         {
            if(testPrice >= (h4HighPrev - InpH4ZoneToleranceUSD) &&
               testPrice <= (h4LowNext + InpH4ZoneToleranceUSD))
               return(true);
         }
      }
      else
      {
         // A. H4 Swing High Pivot
         if(h4HighCurr >= h4HighPrev && h4HighCurr >= h4HighNext)
         {
            if(MathAbs(testPrice - h4HighCurr) <= InpH4ZoneToleranceUSD)
               return(true);
         }
         // B. Bearish H4 Fair Value Gap (Imbalance between Bar i+1 Low and Bar i-1 High)
         if(h4HighNext < h4LowPrev)
         {
            if(testPrice <= (h4LowPrev + InpH4ZoneToleranceUSD) &&
               testPrice >= (h4HighNext - InpH4ZoneToleranceUSD))
               return(true);
         }
      }
   }
   return(false);
}

//+------------------------------------------------------------------+
//| 2-Stage M15 Sweep + Displacement / Micro-BoS Detection Engine    |
//+------------------------------------------------------------------+
void EvaluateM15InstitutionalTrap(double realizedPnLToday)
{
   // Inspect M15 Bar 1 (just closed) and Bar 2 (prior closed bar)
   double open1  = iOpen(_Symbol, PERIOD_M15, 1);
   double high1  = iHigh(_Symbol, PERIOD_M15, 1);
   double low1   = iLow(_Symbol, PERIOD_M15, 1);
   double close1 = iClose(_Symbol, PERIOD_M15, 1);
   double range1 = high1 - low1;

   double open2  = iOpen(_Symbol, PERIOD_M15, 2);
   double high2  = iHigh(_Symbol, PERIOD_M15, 2);
   double low2   = iLow(_Symbol, PERIOD_M15, 2);
   double close2 = iClose(_Symbol, PERIOD_M15, 2);

   if(range1 < 0.45)
      return;

   // Identify prior M15 Liquidity Pool (Bars 3 to InpM15SwingLookback + 2)
   double priorLiquidityLow  = DBL_MAX;
   double priorLiquidityHigh = 0.0;

   for(int i = 3; i <= InpM15SwingLookback + 2; i++)
   {
      double l = iLow(_Symbol, PERIOD_M15, i);
      double h = iHigh(_Symbol, PERIOD_M15, i);
      if(l < priorLiquidityLow)  priorLiquidityLow  = l;
      if(h > priorLiquidityHigh) priorLiquidityHigh = h;
   }

   // D1 Trend Filter via 20-day EMA
   double d1Ema[];
   ArraySetAsSeries(d1Ema, true);
   bool d1Bullish = true;
   bool d1Bearish = true;

   if(InpUseD1TrendFilter && CopyBuffer(ExtHandleD1Ema, 0, 0, 1, d1Ema) == 1)
   {
      d1Bullish = (close1 >= d1Ema[0]);
      d1Bearish = (close1 <= d1Ema[0]);
   }

   // =================================================================
   // 1. BULLISH INSTITUTIONAL SWEEP + DISPLACEMENT / MICRO-BOS
   // =================================================================
   double bullTrapLow   = MathMin(low1, low2);
   double bullSweepSize = priorLiquidityLow - bullTrapLow;

   // Pattern A: 1-Bar Sweep & Engulfing Rejection on Bar 1
   double lowerWick1    = MathMin(open1, close1) - low1;
   bool   bullPattern1  = (low1 < priorLiquidityLow &&
                           close1 > priorLiquidityLow &&
                           close1 > open1 &&
                           (lowerWick1 / range1) >= InpMinWickRatio);

   // Pattern B: 2-Bar Sweep (Bar 2 sweeps, Bar 1 confirms Micro-BoS & Engulfs Bar 2)
   bool   bullPattern2  = (low2 < priorLiquidityLow &&
                           close1 > priorLiquidityLow &&
                           close1 > high2 &&
                           close1 > open1);

   if((bullPattern1 || bullPattern2) &&
      bullSweepSize >= InpMinSweepUSD && bullSweepSize <= InpMaxSweepUSD)
   {
      bool sweptDaily = false;
      if(IsNearH4OrDailyStructure(bullTrapLow, true, sweptDaily))
      {
         if(d1Bullish || sweptDaily)
         {
            ExecuteAutonomousSetup(true, bullTrapLow, low1, high1, realizedPnLToday);
            return;
         }
      }
   }

   // =================================================================
   // 2. BEARISH INSTITUTIONAL SWEEP + DISPLACEMENT / MICRO-BOS
   // =================================================================
   double bearTrapHigh  = MathMax(high1, high2);
   double bearSweepSize = bearTrapHigh - priorLiquidityHigh;

   // Pattern A: 1-Bar Sweep & Engulfing Rejection on Bar 1
   double upperWick1    = high1 - MathMax(open1, close1);
   bool   bearPattern1  = (high1 > priorLiquidityHigh &&
                           close1 < priorLiquidityHigh &&
                           close1 < open1 &&
                           (upperWick1 / range1) >= InpMinWickRatio);

   // Pattern B: 2-Bar Sweep (Bar 2 sweeps, Bar 1 confirms Micro-BoS & Breaks Bar 2 Low)
   bool   bearPattern2  = (high2 > priorLiquidityHigh &&
                           close1 < priorLiquidityHigh &&
                           close1 < low2 &&
                           close1 < open1);

   if((bearPattern1 || bearPattern2) &&
      bearSweepSize >= InpMinSweepUSD && bearSweepSize <= InpMaxSweepUSD)
   {
      bool sweptDaily = false;
      if(IsNearH4OrDailyStructure(bearTrapHigh, false, sweptDaily))
      {
         if(d1Bearish || sweptDaily)
         {
            ExecuteAutonomousSetup(false, bearTrapHigh, low1, high1, realizedPnLToday);
            return;
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Place Pullback Limit Order (or Market Order) with Exact 1R Tag   |
//+------------------------------------------------------------------+
void ExecuteAutonomousSetup(bool isBuy, double trapExtremePrice, double bar1Low, double bar1High, double realizedPnLToday)
{
   double ask        = ExtSymbol.Ask();
   double bid        = ExtSymbol.Bid();
   double stopsLevel = MathMax(SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point, 0.20);

   double slPrice    = isBuy ? NormalizeDouble(trapExtremePrice - InpSLBufferUSD, _Digits)
                             : NormalizeDouble(trapExtremePrice + InpSLBufferUSD, _Digits);

   // Calculate ideal 50% displacement pullback entry price
   double dispRange  = bar1High - bar1Low;
   double limitPrice = isBuy ? NormalizeDouble(bar1High - (dispRange * InpRetraceEntryRatio), _Digits)
                             : NormalizeDouble(bar1Low  + (dispRange * InpRetraceEntryRatio), _Digits);

   bool useLimitOrder = InpUsePullbackLimit;

   // Verify whether limitPrice is valid relative to current market price & stops level
   if(isBuy && limitPrice >= (ask - stopsLevel))
      useLimitOrder = false;
   if(!isBuy && limitPrice <= (bid + stopsLevel))
      useLimitOrder = false;

   double execPrice  = useLimitOrder ? limitPrice : (isBuy ? ask : bid);
   double slDistUSD  = isBuy ? (execPrice - slPrice) : (slPrice - execPrice);

   if(slDistUSD < InpMinSLDistanceUSD || slDistUSD > InpMaxSLDistanceUSD)
      return;

   double tpPrice    = isBuy ? NormalizeDouble(execPrice + (slDistUSD * InpTargetRMultiple), _Digits)
                             : NormalizeDouble(execPrice - (slDistUSD * InpTargetRMultiple), _Digits);

   double lots       = CalculateHouseMoneyLots(slDistUSD, realizedPnLToday);
   if(lots <= 0.0)
      return;

   // Encode exact 1R USD distance in comment for state-persistent Phase 2 management
   string orderComment = StringFormat("XAU_AUTO|R:%.2f", slDistUSD);
   datetime expiryTime = TimeCurrent() + (InpLimitExpiryM15Bars * 15 * 60);

   if(useLimitOrder)
   {
      if(isBuy)
         ExtTrade.BuyLimit(lots, execPrice, _Symbol, slPrice, tpPrice, ORDER_TIME_SPECIFIED, expiryTime, orderComment);
      else
         ExtTrade.SellLimit(lots, execPrice, _Symbol, slPrice, tpPrice, ORDER_TIME_SPECIFIED, expiryTime, orderComment);
   }
   else
   {
      if(isBuy)
         ExtTrade.Buy(lots, _Symbol, execPrice, slPrice, tpPrice, orderComment);
      else
         ExtTrade.Sell(lots, _Symbol, execPrice, slPrice, tpPrice, orderComment);
   }
}

//+------------------------------------------------------------------+
//| House-Money Position Sizing (Base Risk + Realized Daily Profit)  |
//+------------------------------------------------------------------+
double CalculateHouseMoneyLots(double slDistanceUSD, double realizedPnLToday)
{
   if(slDistanceUSD <= 0.0)
      return(0.0);

   double baseBalance = (ExtDayStartBalance > 0.0) ? ExtDayStartBalance : AccountInfoDouble(ACCOUNT_BALANCE);
   double equity      = AccountInfoDouble(ACCOUNT_EQUITY);

   double totalRiskUSD = baseBalance * (InpBaseRiskPct / 100.0);

   // Scale position size strictly from today's locked-in realized profits
   if(realizedPnLToday > 0.0 && InpReinvestProfitPct > 0.0)
      totalRiskUSD += (realizedPnLToday * (InpReinvestProfitPct / 100.0));

   double maxCapUSD = equity * (InpMaxTotalRiskPct / 100.0);
   if(totalRiskUSD > maxCapUSD)
      totalRiskUSD = maxCapUSD;

   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   if(tickSize <= 0.0 || tickValue <= 0.0)
      return(0.0);

   double lossPerLot = (slDistanceUSD / tickSize) * tickValue;
   if(lossPerLot <= 0.0)
      return(0.0);

   double rawLots = totalRiskUSD / lossPerLot;

   double volStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double volMin  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double volMax  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);

   double normLots = MathFloor(rawLots / volStep) * volStep;
   if(normLots < volMin)
      return(0.0);
   if(normLots > volMax)
      normLots = volMax;

   return(NormalizeDouble(normLots, 2));
}

//+------------------------------------------------------------------+
//| Extract Exact 1R Distance from Order Comment or TP Distance      |
//+------------------------------------------------------------------+
double ExtractInitialOneR(double openPrice, double currentSL, double currentTP, string comment)
{
   int pos = StringFind(comment, "R:");
   if(pos >= 0)
   {
      string rStr = StringSubstr(comment, pos + 2);
      double parsedR = StringToDouble(rStr);
      if(parsedR >= 0.50)
         return(parsedR);
   }

   if(currentTP > 0.0 && InpTargetRMultiple > 0.0)
      return(MathAbs(currentTP - openPrice) / InpTargetRMultiple);

   if(currentSL > 0.0)
      return(MathAbs(openPrice - currentSL));

   return(2.50);
}

//+------------------------------------------------------------------+
//| Autonomous Position Management (+1R Partial/BE & +2R M15 Trail)  |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
   double volStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double volMin  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i))
         continue;
      if(ExtPosition.Symbol() != _Symbol || ExtPosition.Magic() != InpMagicNumber)
         continue;

      ulong  ticket    = ExtPosition.Ticket();
      double openPrice = ExtPosition.PriceOpen();
      double currentSL = ExtPosition.StopLoss();
      double currentTP = ExtPosition.TakeProfit();
      double volume    = ExtPosition.Volume();
      bool   isBuy     = (ExtPosition.PositionType() == POSITION_TYPE_BUY);

      if(currentSL <= 0.0)
         continue;

      double oneR          = ExtractInitialOneR(openPrice, currentSL, currentTP, ExtPosition.Comment());
      double currentGainUSD= isBuy ? (ExtSymbol.Bid() - openPrice) : (openPrice - ExtSymbol.Ask());
      bool   isLockedAtBE  = isBuy ? (currentSL >= openPrice) : (currentSL <= openPrice);

      // --- STAGE 1: +1R Partial Profit & Breakeven+Commission Lock ---
      if(!isLockedAtBE)
      {
         if(currentGainUSD >= (oneR * InpPartialCloseAtR))
         {
            double closeVol = MathFloor((volume * InpPartialCloseRatio) / volStep) * volStep;
            double remVol   = volume - closeVol;

            if(closeVol >= volMin && remVol >= volMin)
               ExtTrade.PositionClosePartial(ticket, NormalizeDouble(closeVol, 2));

            double newSL = isBuy ? NormalizeDouble(openPrice + InpBECommissionUSD, _Digits)
                                 : NormalizeDouble(openPrice - InpBECommissionUSD, _Digits);

            ExtTrade.PositionModify(ticket, newSL, currentTP);
         }
      }
      // --- STAGE 2: +2R Structural 3-Bar M15 Swing Trailing Stop ---
      else
      {
         if(currentGainUSD < (oneR * InpTrailTriggerR))
            continue;

         if(isBuy)
         {
            double structureLow = MathMin(iLow(_Symbol, PERIOD_M15, 1),
                                  MathMin(iLow(_Symbol, PERIOD_M15, 2), iLow(_Symbol, PERIOD_M15, 3)));
            double candidateSL  = NormalizeDouble(structureLow - 0.30, _Digits);

            if(candidateSL > currentSL + 0.10 && candidateSL < (ExtSymbol.Bid() - InpMinSLDistanceUSD))
               ExtTrade.PositionModify(ticket, candidateSL, currentTP);
         }
         else
         {
            double structureHigh = MathMax(iHigh(_Symbol, PERIOD_M15, 1),
                                   MathMax(iHigh(_Symbol, PERIOD_M15, 2), iHigh(_Symbol, PERIOD_M15, 3)));
            double candidateSL   = NormalizeDouble(structureHigh + 0.30, _Digits);

            if(candidateSL < currentSL - 0.10 && candidateSL > (ExtSymbol.Ask() + InpMinSLDistanceUSD))
               ExtTrade.PositionModify(ticket, candidateSL, currentTP);
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Calculate Today's Realized Net PnL and Consecutive Loss Streak   |
//+------------------------------------------------------------------+
double GetTodayRealizedStats(int &outConsecutiveLosses)
{
   outConsecutiveLosses = 0;
   double netPnL = 0.0;

   datetime startOfDay = iTime(_Symbol, PERIOD_D1, 0);
   if(!HistorySelect(startOfDay, TimeCurrent()))
      return(0.0);

   int totalDeals = HistoryDealsTotal();
   for(int i = 0; i < totalDeals; i++)
   {
      ulong dealTicket = HistoryDealGetTicket(i);
      if(dealTicket == 0)
         continue;
      if(HistoryDealGetString(dealTicket, DEAL_SYMBOL) != _Symbol)
         continue;
      if((ulong)HistoryDealGetInteger(dealTicket, DEAL_MAGIC) != InpMagicNumber)
         continue;

      ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(dealTicket, DEAL_ENTRY);
      if(entry != DEAL_ENTRY_OUT && entry != DEAL_ENTRY_INOUT)
         continue;

      double dealNet = HistoryDealGetDouble(dealTicket, DEAL_PROFIT)
                     + HistoryDealGetDouble(dealTicket, DEAL_COMMISSION)
                     + HistoryDealGetDouble(dealTicket, DEAL_SWAP);

      netPnL += dealNet;

      if(dealNet < -2.50)
         outConsecutiveLosses++;
      else if(dealNet > 2.50)
         outConsecutiveLosses = 0;
   }
   return(netPnL);
}

//+------------------------------------------------------------------+
//| On-Chart Telemetry HUD for Unattended VPS Monitoring             |
//+------------------------------------------------------------------+
void RenderTelemetryHUD(string statusLine, bool newsActive, double realizedPnLToday, int lossStreak)
{
   MqlDateTime gmt;
   TimeGMT(gmt);

   double baseBalance = (ExtDayStartBalance > 0.0) ? ExtDayStartBalance : AccountInfoDouble(ACCOUNT_BALANCE);
   double nextRiskUSD = baseBalance * (InpBaseRiskPct / 100.0);
   if(realizedPnLToday > 0.0 && InpReinvestProfitPct > 0.0)
      nextRiskUSD += (realizedPnLToday * (InpReinvestProfitPct / 100.0));

   string hud = StringFormat(
      "========================================================\n"
      " XAUUSD AUTONOMOUS INSTITUTIONAL ENGINE v3.0 (M15/H4/D1)\n"
      "========================================================\n"
      " GMT Clock         : %02d:%02d:%02d UTC\n"
      " Engine Status     : %s\n"
      " USD News Shield   : %s\n"
      " Current Spread    : $%.2f (Max Allowed: $%.2f)\n"
      "--------------------------------------------------------\n"
      " Day Start Balance : $%.2f\n"
      " Today Realized PnL: $%.2f (Loss Streak: %d / %d)\n"
      " Next Setup Risk $ : $%.2f (Base %.2f%% + %.0f%% House-Money)\n"
      "========================================================",
      gmt.hour, gmt.min, gmt.sec,
      statusLine,
      newsActive ? "BLOCKING (HIGH-IMPACT USD EVENT)" : "CLEAR",
      ExtSymbol.Ask() - ExtSymbol.Bid(), InpMaxSpreadUSD,
      baseBalance,
      realizedPnLToday, lossStreak, InpMaxConsecutiveLosses,
      nextRiskUSD, InpBaseRiskPct, InpReinvestProfitPct
   );

   Comment(hud);
}
//+------------------------------------------------------------------+
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: XAU/USD (Spot Gold) trading strategy PRO level

Post by PTScalper »

3. Deployment Checklist for Unattended 24/5 VPS Operation

Compile & Attach Once: Compile XAUUSD_AutonomousEngine.mq5 in MetaEditor (F7), open a single XAUUSD M15 chart in MT5, enable Algo Trading on the top toolbar, and drag the EA onto the chart.

Zero Timezone Calibration Required: Because IsInsideGMTSession() and IsFridayCutoffReached() read TimeGMT() directly from the operating system/server UTC clock, you do not need to adjust session hours when your broker shifts between GMT+2 and GMT+3.

Strategy Tester vs. Live News Filter Behavior: During historical backtesting (MQL_TESTER), the EA automatically bypasses CalendarValueHistory() so tick simulations run at maximum speed without missing-calendar warnings. On a live or demo ECN account, it queries MT5's native economic calendar in real time. For a quick refresher on how MQL5 handles pending limit orders via the standard library, watch this MQL5 Sell Limit tutorial, which demonstrates programmatic limit order execution using the CTrade class.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: XAU/USD (Spot Gold) trading strategy PRO level

Post by PTScalper »

I hope that you will find core of this trading strategy usefull.

If you will try, please let me know your feedback.

Next week i will try to share here some results and optimal setups from my backtesting.

Take a care and have a lot of good trades :-)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply