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:
1. Entry Filter: RSI(14) on closed bar must reach ≥70 (short) or ≤30 (long) before first order.
2. Grid Spacing: ATR(14) × multiplier calculates dynamic distance between grid levels.
3. Profit Decay: Target profit decreases as grid levels increase (safer exit during deep drawdown).
4. Smart Recovery: When levels exceed threshold, lot size calculation allows 10% retracement to breakeven.
5. 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.