Summary: Gold Dynamic Decay Grid EA is an MQL4 expert advisor for XAUUSD that combines RSI extreme filtering, ATR dynamic spacing, and a smart 10% retracement recovery model. Designed for stable operation on M15.




Gold Dynamic Decay Grid EA is designed specifically for XAUUSD, addressing the limitations of traditional fixed-interval grid systems. It uses RSI extreme values to filter entry timing, ATR to calculate dynamic grid spacing, and a smart decay model to reduce drawdown duration. The system includes dual momentum protection to prevent overloading during explosive news-driven moves.

Recommended Timeframe: M15
Trading Logic:
  • Entry Filter: RSI(14) on closed bar must reach ≥70 (short) or ≤30 (long) before first order.

  • Grid Spacing: ATR(14) × multiplier calculates dynamic distance between grid levels.

  • Profit Decay: Target profit decreases as grid levels increase (safer exit during deep drawdown).

  • Smart Recovery: When levels exceed threshold, lot size calculation allows 10% retracement to breakeven.

  • Momentum Guard: Single-bar max 1 order; if candle range > 2×ATR, pause grid adding.


  • ``mql4
    //+------------------------------------------------------------------+
    //| GoldDynamicDecayGrid.mq4 |
    //+------------------------------------------------------------------+
    #property copyright ""
    #property link ""
    #property version "1.00"
    #property strict

    //+------------------------------------------------------------------+
    //| Input Parameters with Comments |
    //+------------------------------------------------------------------+
    //=== Basic Settings ===
    input string comment = "GoldDecayGrid"; // Order comment identifier
    input int MagicNumber = 202419; // Unique EA magic number
    input double InitialLotSize = 0.01; // Initial lot size (0.01 for gold)
    input int MaxSpread = 35; // Maximum allowed spread in points

    //=== RSI Entry Filter ===
    input int RSIPeriod = 14; // RSI calculation period
    input int RSIOverbought = 70; // RSI overbought level (sell trigger)
    input int RSIOversold = 30; // RSI oversold level (buy trigger)
    input bool UseRSIFilter = true; // Enable RSI extreme filter

    //=== ATR Dynamic Grid Spacing ===
    input int ATRPeriod = 14; // ATR period for volatility calculation
    input double ATRGridMultiplier = 1.2; // Grid spacing = ATR × this multiplier

    //=== Profit Decay Settings ===
    input double BaseTargetProfit = 30.0; // Base profit target in USD (0.01 lot)
    input double MinTargetProfit = 15.0; // Minimum profit target at max levels
    input int DecayStartLevel = 3; // Level where profit decay begins

    //=== Smart Recovery (10% Retracement) ===
    input bool UseSmartRecovery = true; // Enable smart recovery calculation
    input int SmartRecoveryStart = 4; // Minimum levels to activate smart recovery
    input double RecoveryRetracePercent = 0.10; // Retracement % to breakeven (0.10 = 10%)

    //=== Grid Trading Parameters ===
    input double LotMultiplier = 1.3; // Lot multiplier per grid level (1.3×)
    input int MaxGridLevels = 10; // Maximum grid levels per direction
    input int MaxTotalPositions = 20; // Maximum total positions across both directions

    //=== Momentum Protection ===
    input bool UseMomentumFilter = true; // Enable high-volatility protection
    input double MaxCandleVolatility = 2.0; // Max candle range as multiple of ATR

    //=== Risk Management ===
    input double DailyLossLimit = 8.0; // Daily loss limit as % of balance
    input double MaxDrawdownPercent = 25.0; // Max equity drawdown to stop EA
    input bool UseFridayClose = true; // Close all trades before Friday 21:00 GMT

    //=== Time Filter (Optional) ===
    input bool UseTimeFilter = false; // Enable trading time filter
    input string StartTime = "08:00"; // Trading start time (GMT)
    input string EndTime = "20:00"; // Trading end time (GMT)

    //+------------------------------------------------------------------+
    //| Global Variables |
    //+------------------------------------------------------------------+
    double dailyStartBalance = 0;
    double equityPeak = 0;
    datetime lastBarTime = 0;
    bool fridayCloseExecuted = false;
    bool drawdownStop = false;
    double atrValue = 0;
    double lastGridPriceBuy = 0;
    double lastGridPriceSell = 0;
    int buyLevelCount = 0;
    int sellLevelCount = 0;

    //+------------------------------------------------------------------+
    //| Expert Initialization Function |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    dailyStartBalance = AccountBalance();
    equityPeak = AccountEquity();
    lastBarTime = 0;
    fridayCloseExecuted = false;
    drawdownStop = false;
    buyLevelCount = 0;
    sellLevelCount = 0;
    lastGridPriceBuy = 0;
    lastGridPriceSell = 0;
    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| Expert Deinitialization Function |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
    Comment("");
    }

    //+------------------------------------------------------------------+
    //| Calculate Current ATR Value |
    //+------------------------------------------------------------------+
    double GetATR()
    {
    double atr = iATR(Symbol(), PERIOD_M15, ATRPeriod, 1);
    if(atr <= 0) atr = 200 Point;
    return atr;
    }

    //+------------------------------------------------------------------+
    //| Get RSI Value from Closed Bar |
    //+------------------------------------------------------------------+
    double GetRSI(int shift)
    {
    return iRSI(Symbol(), PERIOD_M15, RSIPeriod, PRICE_CLOSE, shift);
    }

    //+------------------------------------------------------------------+
    //| Check if Within Trading Hours |
    //+------------------------------------------------------------------+
    bool IsTradingTime()
    {
    if(!UseTimeFilter) return true;
    datetime now = TimeCurrent();
    int currentHour = TimeHour(now);
    int currentMinute = TimeMinute(now);
    int startHour, startMin, endHour, endMin;
    startHour = (int)StringToInteger(StringSubstr(StartTime, 0, 2));
    startMin = (int)StringToInteger(StringSubstr(StartTime, 3, 2));
    endHour = (int)StringToInteger(StringSubstr(EndTime, 0, 2));
    endMin = (int)StringToInteger(StringSubstr(EndTime, 3, 2));
    int currentTotal = currentHour
    60 + currentMinute;
    int startTotal = startHour 60 + startMin;
    int endTotal = endHour
    60 + endMin;
    if(startTotal <= endTotal)
    return (currentTotal >= startTotal && currentTotal <= endTotal);
    else
    return (currentTotal >= startTotal || currentTotal <= endTotal);
    }

    //+------------------------------------------------------------------+
    //| Calculate Dynamic Profit Target Based on Grid Level |
    //+------------------------------------------------------------------+
    double GetDynamicProfitTarget(int level)
    {
    if(level <= DecayStartLevel)
    return BaseTargetProfit;
    double decayRatio = (double)(MaxGridLevels - level) / (MaxGridLevels - DecayStartLevel);
    double target = BaseTargetProfit - (BaseTargetProfit - MinTargetProfit) (1 - decayRatio);
    if(target < MinTargetProfit) target = MinTargetProfit;
    return target;
    }

    //+------------------------------------------------------------------+
    //| Calculate Grid Spacing Based on ATR |
    //+------------------------------------------------------------------+
    double GetGridSpacing()
    {
    atrValue = GetATR();
    return atrValue
    ATRGridMultiplier;
    }

    //+------------------------------------------------------------------+
    //| Calculate Smart Recovery Lot Size |
    //+------------------------------------------------------------------+
    double CalculateRecoveryLot(int level, double currentPrice, double avgPrice, double totalLots)
    {
    if(!UseSmartRecovery || level < SmartRecoveryStart)
    return InitialLotSize MathPow(LotMultiplier, level - 1);
    double retraceTarget = RecoveryRetracePercent;
    double priceDistance = MathAbs(currentPrice - avgPrice);
    double requiredDistance = priceDistance
    retraceTarget / (1 - retraceTarget);
    double lotNeeded = (totalLots priceDistance) / requiredDistance;
    lotNeeded = lotNeeded - totalLots;
    if(lotNeeded < InitialLotSize) lotNeeded = InitialLotSize;
    if(lotNeeded > InitialLotSize
    MathPow(LotMultiplier, MaxGridLevels))
    lotNeeded = InitialLotSize MathPow(LotMultiplier, MaxGridLevels);
    return NormalizeDouble(lotNeeded, 2);
    }

    //+------------------------------------------------------------------+
    //| Check Momentum Protection |
    //+------------------------------------------------------------------+
    bool IsMomentumSafe()
    {
    if(!UseMomentumFilter) return true;
    double currentRange = iHigh(Symbol(), PERIOD_M15, 0) - iLow(Symbol(), PERIOD_M15, 0);
    if(currentRange > GetATR()
    MaxCandleVolatility)
    return false;
    return true;
    }

    //+------------------------------------------------------------------+
    //| Close All Orders |
    //+------------------------------------------------------------------+
    void CloseAllOrders()
    {
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    {
    if(OrderType() == OP_BUY)
    OrderClose(OrderTicket(), OrderLots(), Bid, 30, clrNONE);
    else if(OrderType() == OP_SELL)
    OrderClose(OrderTicket(), OrderLots(), Ask, 30, clrNONE);
    }
    }
    }
    buyLevelCount = 0;
    sellLevelCount = 0;
    lastGridPriceBuy = 0;
    lastGridPriceSell = 0;
    }

    //+------------------------------------------------------------------+
    //| Count Positions by Direction |
    //+------------------------------------------------------------------+
    void CountPositions()
    {
    buyLevelCount = 0;
    sellLevelCount = 0;
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    {
    if(OrderType() == OP_BUY)
    buyLevelCount++;
    else if(OrderType() == OP_SELL)
    sellLevelCount++;
    }
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Calculate Total Floating Profit |
    //+------------------------------------------------------------------+
    double CalculateFloatingProfit()
    {
    double profit = 0;
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    profit += OrderProfit() + OrderSwap() + OrderCommission();
    }
    }
    return profit;
    }

    //+------------------------------------------------------------------+
    //| Check if Profit Target Reached |
    //+------------------------------------------------------------------+
    bool IsProfitTargetReached()
    {
    double floatingProfit = CalculateFloatingProfit();
    int totalLevels = buyLevelCount + sellLevelCount;
    double target = GetDynamicProfitTarget(totalLevels);
    if(floatingProfit >= target)
    return true;
    return false;
    }

    //+------------------------------------------------------------------+
    //| Open Buy Grid Order |
    //+------------------------------------------------------------------+
    void OpenBuyOrder()
    {
    double spacing = GetGridSpacing();
    double entryPrice = Ask;
    if(buyLevelCount > 0 && lastGridPriceBuy > 0)
    {
    double expectedPrice = lastGridPriceBuy - spacing;
    if(entryPrice > expectedPrice)
    return;
    }
    double lotSize = InitialLotSize;
    if(buyLevelCount > 0)
    {
    if(UseSmartRecovery && buyLevelCount >= SmartRecoveryStart)
    {
    double avgPrice = 0, totalLots = 0;
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_BUY)
    { avgPrice += OrderOpenPrice() OrderLots(); totalLots += OrderLots(); }
    }
    if(totalLots > 0) avgPrice /= totalLots;
    lotSize = CalculateRecoveryLot(buyLevelCount + 1, entryPrice, avgPrice, totalLots);
    }
    else
    lotSize = InitialLotSize
    MathPow(LotMultiplier, buyLevelCount);
    }
    if(lotSize < 0.01) lotSize = 0.01;
    if(lotSize > 1.0) lotSize = 1.0;
    int ticket = OrderSend(Symbol(), OP_BUY, lotSize, entryPrice, 30, 0, 0, comment, MagicNumber, 0, clrNONE);
    if(ticket > 0)
    {
    lastGridPriceBuy = entryPrice;
    buyLevelCount++;
    }
    }

    //+------------------------------------------------------------------+
    //| Open Sell Grid Order |
    //+------------------------------------------------------------------+
    void OpenSellOrder()
    {
    double spacing = GetGridSpacing();
    double entryPrice = Bid;
    if(sellLevelCount > 0 && lastGridPriceSell > 0)
    {
    double expectedPrice = lastGridPriceSell + spacing;
    if(entryPrice < expectedPrice)
    return;
    }
    double lotSize = InitialLotSize;
    if(sellLevelCount > 0)
    {
    if(UseSmartRecovery && sellLevelCount >= SmartRecoveryStart)
    {
    double avgPrice = 0, totalLots = 0;
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_SELL)
    { avgPrice += OrderOpenPrice() OrderLots(); totalLots += OrderLots(); }
    }
    if(totalLots > 0) avgPrice /= totalLots;
    lotSize = CalculateRecoveryLot(sellLevelCount + 1, entryPrice, avgPrice, totalLots);
    }
    else
    lotSize = InitialLotSize
    MathPow(LotMultiplier, sellLevelCount);
    }
    if(lotSize < 0.01) lotSize = 0.01;
    if(lotSize > 1.0) lotSize = 1.0;
    int ticket = OrderSend(Symbol(), OP_SELL, lotSize, entryPrice, 30, 0, 0, comment, MagicNumber, 0, clrNONE);
    if(ticket > 0)
    {
    lastGridPriceSell = entryPrice;
    sellLevelCount++;
    }
    }

    //+------------------------------------------------------------------+
    //| Expert Tick Function |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    // Daily loss protection
    double currentEquity = AccountEquity();
    double lossPercent = (dailyStartBalance - currentEquity) / dailyStartBalance 100;
    if(lossPercent >= DailyLossLimit)
    {
    Comment("Daily loss limit reached");
    return;
    }

    // Max drawdown protection
    if(currentEquity > equityPeak) equityPeak = currentEquity;
    double drawdownPercent = (equityPeak - currentEquity) / equityPeak
    100;
    if(drawdownPercent >= MaxDrawdownPercent && !drawdownStop)
    {
    CloseAllOrders();
    drawdownStop = true;
    Comment("Max drawdown reached, EA stopped");
    return;
    }

    // Friday close
    if(UseFridayClose && !fridayCloseExecuted)
    {
    datetime now = TimeCurrent();
    if(TimeDayOfWeek(now) == 5 && TimeHour(now) >= 21)
    {
    CloseAllOrders();
    fridayCloseExecuted = true;
    return;
    }
    if(TimeDayOfWeek(now) != 5) fridayCloseExecuted = false;
    }

    // Spread filter
    if(MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread)
    {
    Comment("Spread too high: ", MarketInfo(Symbol(), MODE_SPREAD));
    return;
    }

    // Time filter
    if(!IsTradingTime())
    {
    Comment("Outside trading hours");
    return;
    }

    // New bar detection (M15)
    if(Time[0] == lastBarTime) return;
    lastBarTime = Time[0];

    // Count current positions
    CountPositions();

    // Check profit target
    if((buyLevelCount > 0 || sellLevelCount > 0) && IsProfitTargetReached())
    {
    CloseAllOrders();
    Print("Profit target reached. Closed all positions.");
    return;
    }

    // Check total position limit
    if(buyLevelCount + sellLevelCount >= MaxTotalPositions)
    return;

    // Momentum protection
    if(!IsMomentumSafe())
    {
    Comment("High momentum detected, grid paused");
    return;
    }

    // RSI signal for new grid direction
    double rsi1 = GetRSI(1);
    double rsi2 = GetRSI(2);
    bool rsiValid = (rsi1 > 0 && rsi1 < 100);

    // Buy signal: RSI oversold condition
    if(UseRSIFilter)
    {
    if(rsiValid && rsi1 <= RSIOversold && buyLevelCount == 0 && buyLevelCount + sellLevelCount < MaxTotalPositions)
    {
    OpenBuyOrder();
    }
    // Sell signal: RSI overbought condition
    else if(rsiValid && rsi1 >= RSIOverbought && sellLevelCount == 0 && buyLevelCount + sellLevelCount < MaxTotalPositions)
    {
    OpenSellOrder();
    }
    }

    // Grid continuation logic - buy side
    if(buyLevelCount > 0 && buyLevelCount < MaxGridLevels)
    {
    double spacing = GetGridSpacing();
    double expectedNextPrice = lastGridPriceBuy - spacing;
    if(Bid <= expectedNextPrice)
    OpenBuyOrder();
    }

    // Grid continuation logic - sell side
    if(sellLevelCount > 0 && sellLevelCount < MaxGridLevels)
    {
    double spacing = GetGridSpacing();
    double expectedNextPrice = lastGridPriceSell + spacing;
    if(Ask >= expectedNextPrice)
    OpenSellOrder();
    }

    // Display info
    Comment("Buy Levels: ", buyLevelCount, " | Sell Levels: ", sellLevelCount,
    "\nATR: ", DoubleToStr(atrValue, 2), " | Spacing: ", DoubleToStr(GetGridSpacing(), 2),
    "\nFloating P/L: $", DoubleToStr(CalculateFloatingProfit(), 2));
    }
    //+------------------------------------------------------------------+
    ``
    Reference: Original MQL4 code inspired by modern grid trading concepts for gold .
    Disclaimer: Grid and martingale strategies carry inherent risk of large drawdowns during prolonged trending markets. This EA is provided as-is without any guarantee of profit. Test thoroughly on a demo account for at least 3 months before live trading. Past performance does not guarantee future results.