Summary: Gold Cascade EA is an MQL4 expert advisor for XAUUSD that implements a controlled multi-layer grid system in trend direction only. Uses EMA trend filter, ADX strength validation, dynamic lot progression, and basket take profit. Designed for H1 stable operation.




Gold Cascade EA is engineered for stable gold trading using a disciplined "trend grid" approach. Unlike traditional grid systems that trade blindly in both directions, this EA only grids in the direction of the primary trend (H1 EMA50). It uses a multi-layer position structure (max 5 layers) with controlled dynamic lot sizing that increases gradually (not martingale). The EA includes ADX(14) trend strength filter (>25) to avoid ranging markets, ATR-based volatility guard, spread control, daily loss limit, and Friday close protection. The basket take profit mechanism closes all positions when the total floating profit reaches a configurable target, locking in accumulated gains.

Recommended Timeframe: H1
Trading Logic:
1. Trend Direction: H1 EMA(50) determines grid direction (price above = long only grid; below = short only grid).
2. Trend Strength: ADX(14) must be > 25 to confirm trending market.
3. First Entry: When price pulls back to EMA(50) with a confirmatory candlestick.
4. Grid Layering: Add positions every `GridStepPoints` (default 200 points) in trend direction, lot size increases by `LotIncrement` (default 0.01 per level).
5. Basket Take Profit: When total floating profit of all grid positions reaches `BasketTargetProfit` (default $15 for 0.01 starting lot), close all positions.

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

//--- input parameters with comments
input double StartLot = 0.01; // Starting lot size (0.01 for XAUUSD)
input double LotIncrement = 0.01; // Lot increment per grid level (0.01 add each level)
input int MaxGridLevels = 5; // Maximum grid levels (positions)
input int GridStepPoints = 200; // Grid step in points (200 points between levels)
input int TrendMAPeriod = 50; // EMA period for trend direction filter
input int ADXPeriod = 14; // ADX period for trend strength
input int ADXThreshold = 25; // Minimum ADX for trending market
input double ATRStopMultiplier = 1.5; // Stop loss as multiple of ATR
input double BasketTargetProfit = 15.0; // Basket take profit in dollars (for 0.01 start lot)
input double DailyLossLimit = 5.0; // Daily loss limit as percentage of balance
input int MaxSpread = 35; // Maximum allowed spread in points
input bool UseFridayClose = true; // Close trades before Friday 21:00 GMT
input int MagicNumber = 202502; // Unique EA identifier

//--- global variables
double dailyStartBalance = 0;
datetime lastBarTime = 0;
bool fridayCloseExecuted = false;
double avgATR = 0;
int consecutiveLosses = 0;

//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
dailyStartBalance = AccountBalance();
lastBarTime = 0;
fridayCloseExecuted = false;
avgATR = iATR(Symbol(), PERIOD_H1, 14, 1);
if(avgATR <= 0) avgATR = 250 * Point;
return(INIT_SUCCEEDED);
}

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

//+------------------------------------------------------------------+
//| Get trend direction from EMA |
//+------------------------------------------------------------------+
int GetTrendDirection()
{
double close1 = iClose(Symbol(), PERIOD_H1, 1);
double ema = iMA(Symbol(), PERIOD_H1, TrendMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
if(close1 > ema) return 1; // Bullish trend
if(close1 < ema) return -1; // Bearish trend
return 0;
}

//+------------------------------------------------------------------+
//| Check ADX trend strength |
//+------------------------------------------------------------------+
bool IsTrendStrong()
{
double adx = iADX(Symbol(), PERIOD_H1, ADXPeriod, PRICE_CLOSE, MODE_MAIN, 1);
return (adx >= ADXThreshold);
}

//+------------------------------------------------------------------+
//| Calculate current grid level count for given direction |
//+------------------------------------------------------------------+
int CountGridPositions(int direction)
{
int count = 0;
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if((direction == 1 && OrderType() == OP_BUY) ||
(direction == -1 && OrderType() == OP_SELL))
count++;
}
}
}
return count;
}

//+------------------------------------------------------------------+
//| Get last order price for grid direction |
//+------------------------------------------------------------------+
double GetLastOrderPrice(int direction)
{
double lastPrice = 0;
datetime lastTime = 0;
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if((direction == 1 && OrderType() == OP_BUY) ||
(direction == -1 && OrderType() == OP_SELL))
{
if(OrderOpenTime() > lastTime)
{
lastTime = OrderOpenTime();
lastPrice = OrderOpenPrice();
}
}
}
}
}
return lastPrice;
}

//+------------------------------------------------------------------+
//| Calculate total floating profit of all grid positions |
//+------------------------------------------------------------------+
double CalculateBasketProfit()
{
double totalProfit = 0;
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
totalProfit += OrderProfit() + OrderSwap() + OrderCommission();
}
}
}
return totalProfit;
}

//+------------------------------------------------------------------+
//| Close all grid positions (basket close) |
//+------------------------------------------------------------------+
void CloseAllGridOrders()
{
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, 5, clrNONE);
else if(OrderType() == OP_SELL)
OrderClose(OrderTicket(), OrderLots(), Ask, 5, clrNONE);
}
}
}
}

//+------------------------------------------------------------------+
//| Calculate lot size for next grid level |
//+------------------------------------------------------------------+
double GetNextLotSize(int currentLevel)
{
// Lot = StartLot + (currentLevel * LotIncrement)
// Level 0 (first position): StartLot
// Level 1: StartLot + LotIncrement
// Level 2: StartLot + 2 * LotIncrement
double lot = StartLot + (currentLevel * LotIncrement);
if(lot < 0.01) lot = 0.01;
if(lot > 1.0) lot = 1.0;
return NormalizeDouble(lot, 2);
}

//+------------------------------------------------------------------+
//| Check if we should add a new grid layer |
//+------------------------------------------------------------------+
bool ShouldAddGridLayer(int direction, int currentLevel, double lastPrice)
{
if(currentLevel >= MaxGridLevels) return false;

double currentPrice = (direction == 1) ? Bid : Ask;
double step = GridStepPoints * Point;
double distance = MathAbs(currentPrice - lastPrice);

return (distance >= step);
}

//+------------------------------------------------------------------+
//| Open first position (trend entry) |
//+------------------------------------------------------------------+
bool OpenFirstPosition(int direction, double atr)
{
double entryPrice = 0, sl = 0, tp = 0;
int cmd = (direction == 1) ? OP_BUY : OP_SELL;

if(direction == 1)
{
entryPrice = Ask;
sl = entryPrice - (atr * ATRStopMultiplier);
// No take profit for individual positions - basket take profit manages exits
tp = 0;
}
else
{
entryPrice = Bid;
sl = entryPrice + (atr * ATRStopMultiplier);
tp = 0;
}

double lot = StartLot;
int ticket = OrderSend(Symbol(), cmd, lot, entryPrice, 5, sl, tp, "Cascade L1", MagicNumber, 0, clrNONE);
if(ticket > 0)
{
Print("First position opened. Direction: ", direction==1?"BUY":"SELL");
return true;
}
else
{
Print("Failed to open first position. Error: ", GetLastError());
return false;
}
}

//+------------------------------------------------------------------+
//| Add grid layer position |
//+------------------------------------------------------------------+
bool AddGridLayer(int direction, int currentLevel)
{
double entryPrice = 0, sl = 0;
int cmd = (direction == 1) ? OP_BUY : OP_SELL;
double atr = iATR(Symbol(), PERIOD_H1, 14, 1);
if(atr <= 0) atr = avgATR;

if(direction == 1)
{
entryPrice = Ask;
sl = entryPrice - (atr * ATRStopMultiplier);
}
else
{
entryPrice = Bid;
sl = entryPrice + (atr * ATRStopMultiplier);
}

double lot = GetNextLotSize(currentLevel);
int ticket = OrderSend(Symbol(), cmd, lot, entryPrice, 5, sl, 0, "Cascade L" + string(currentLevel+1), MagicNumber, 0, clrNONE);
if(ticket > 0)
{
Print("Grid layer ", currentLevel+1, " added. Lot: ", lot);
return true;
}
else
{
Print("Failed to add grid layer. Error: ", GetLastError());
return false;
}
}

//+------------------------------------------------------------------+
//| Check for pullback to EMA for first entry |
//+------------------------------------------------------------------+
bool IsPullbackEntry(int direction)
{
double close1 = iClose(Symbol(), PERIOD_H1, 1);
double low1 = iLow(Symbol(), PERIOD_H1, 1);
double high1 = iHigh(Symbol(), PERIOD_H1, 1);
double ema = iMA(Symbol(), PERIOD_H1, TrendMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
double open1 = iOpen(Symbol(), PERIOD_H1, 1);

if(direction == 1) // Bullish: price pulled back to or below EMA, then closed above
{
bool touchedEMA = (low1 <= ema);
bool closedAbove = (close1 > ema);
bool bullishCandle = (close1 > open1);
return (touchedEMA && closedAbove && bullishCandle);
}
else if(direction == -1) // Bearish: price pulled back to or above EMA, then closed below
{
bool touchedEMA = (high1 >= ema);
bool closedBelow = (close1 < ema);
bool bearishCandle = (close1 < open1);
return (touchedEMA && closedBelow && bearishCandle);
}
return false;
}

//+------------------------------------------------------------------+
//| Check daily loss limit |
//+------------------------------------------------------------------+
bool IsDailyLossExceeded()
{
double currentEquity = AccountEquity();
double lossPercent = (dailyStartBalance - currentEquity) / dailyStartBalance * 100;
return (lossPercent >= DailyLossLimit);
}

//+------------------------------------------------------------------+
//| Manage basket take profit |
//+------------------------------------------------------------------+
bool CheckBasketTakeProfit()
{
double basketProfit = CalculateBasketProfit();
if(basketProfit >= BasketTargetProfit)
{
CloseAllGridOrders();
Print("Basket take profit triggered. Profit: $", basketProfit);
return true;
}
return false;
}

//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Daily loss protection
if(IsDailyLossExceeded())
{
Comment("Daily loss limit reached. No new trades.");
return;
}

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

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

// New bar logic (H1)
if(Time[0] == lastBarTime)
return;
lastBarTime = Time[0];

// Update average ATR
double atr = iATR(Symbol(), PERIOD_H1, 14, 1);
if(atr > 0) avgATR = (avgATR * 0.95) + (atr * 0.05);

// Check existing positions
int posCount = CountGridPositions(1) + CountGridPositions(-1);

if(posCount > 0)
{
// Check basket take profit first
if(CheckBasketTakeProfit())
return;

// Determine direction from existing positions
int direction = 0;
if(CountGridPositions(1) > 0) direction = 1;
else if(CountGridPositions(-1) > 0) direction = -1;

if(direction != 0)
{
int currentLevel = CountGridPositions(direction);
double lastPrice = GetLastOrderPrice(direction);

if(ShouldAddGridLayer(direction, currentLevel, lastPrice))
{
AddGridLayer(direction, currentLevel);
}
}
return;
}

// No positions - check for new entry
// Get trend direction and strength
int trendDir = GetTrendDirection();
if(trendDir == 0)
{
Comment("No clear trend direction");
return;
}

if(!IsTrendStrong())
{
Comment("ADX below threshold. No strong trend.");
return;
}

// Check pullback entry
if(IsPullbackEntry(trendDir))
{
OpenFirstPosition(trendDir, atr);
}
}

//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
```
Reference: Original MQL4 code inspired by institutional trend grid principles observed in professional gold trading systems.
Disclaimer: Gold trading involves significant risk due to high volatility and leverage. Grid strategies can amplify losses in strong 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 deployment. Past performance does not guarantee future results.