Summary: Gold Nova Surge EA is a high-performance MQL4 expert advisor for XAUUSD. It uses momentum breakout entries with a controlled anti-martingale position sizing and dynamic profit trailing. Suitable for H1 timeframe.




Gold Nova Surge EA is engineered for gold (XAUUSD) to deliver high profit potential by capturing strong momentum moves. The EA identifies sudden volatility expansion using Bollinger Bands squeeze detection, then enters on the first strong directional candle. Instead of grid/martingale which adds risk, this EA uses an anti-martingale approach: increases lot size only after profitable trades (positive reinforcement). A dynamic breakeven stop and profit trailing lock in gains. Daily loss limits and session filters provide stability even in adverse gold conditions.

Recommended Timeframe: H1
Trading Logic:
1. Squeeze Detection: Bollinger Bands (20,2) width < 0.5 * average width of last 50 bars → volatility contraction.
2. Momentum Entry: After squeeze, first H1 candle with body > 1.5x ATR(14) in either direction triggers entry.
3. Anti-Martingale Sizing: Base lot 0.01. After each winning trade, next trade lot increases by 0.01 (capped at 0.1). After losing trade, resets to base lot.
4. Risk Control: Fixed SL = 2.5x ATR, TP = 5x ATR (2:1). Trailing start at 3x ATR. Daily loss limit 6%. Maximum 2 concurrent trades.

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

//--- input parameters with comments
input double BaseLotSize = 0.01; // Base lot size (minimum 0.01)
input double MaxLotSize = 0.10; // Maximum lot size cap
input int BBPeriod = 20; // Bollinger Bands period
input double BBDeviation = 2.0; // Bollinger Bands deviation
input double SqueezeRatio = 0.5; // Squeeze threshold (band width / avg width)
input int ATRPeriod = 14; // ATR period for SL/TP and momentum filter
input double MomentumBodyRatio = 1.5; // Minimum candle body multiple of ATR for entry
input double StopLossATRMult = 2.5; // Stop loss as multiple of ATR
input double TakeProfitATRMult = 5.0; // Take profit as multiple of ATR (2:1 ratio)
input int TrailingStartATR = 3; // Start trailing when profit reaches this ATR multiple
input int TrailingStepATR = 1; // Trailing step in ATR multiples
input double DailyLossLimit = 6.0; // Daily loss limit in percentage of balance
input int MaxConcurrentTrades = 2; // Maximum concurrent trades allowed
input int MagicNumber = 202415; // Unique EA identifier
input int MaxSpread = 35; // Maximum allowed spread in points

//--- global variables
double dailyStartBalance = 0;
datetime lastBarTime = 0;
double lastTradeResult = 0; // 0=no result, 1=win, -1=loss
double currentLotSize = 0.01;
int consecutiveWins = 0;
double avgBBWidth = 0;

//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
dailyStartBalance = AccountBalance();
lastBarTime = 0;
lastTradeResult = 0;
consecutiveWins = 0;
currentLotSize = BaseLotSize;
avgBBWidth = 0;
return(INIT_SUCCEEDED);
}

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

//+------------------------------------------------------------------+
//| Check and update anti-martingale lot size based on trade history|
//+------------------------------------------------------------------+
void UpdateLotSizeFromLastTrade()
{
// Find last closed trade with this magic number
datetime lastCloseTime = 0;
double lastProfit = 0;

for(int i = OrdersHistoryTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderCloseTime() > lastCloseTime)
{
lastCloseTime = OrderCloseTime();
lastProfit = OrderProfit() + OrderSwap() + OrderCommission();
}
}
}
}

if(lastCloseTime > 0)
{
if(lastProfit > 0)
{
consecutiveWins++;
// Anti-martingale: increase lot after win
currentLotSize = BaseLotSize + (consecutiveWins * 0.01);
if(currentLotSize > MaxLotSize) currentLotSize = MaxLotSize;
lastTradeResult = 1;
}
else
{
consecutiveWins = 0;
currentLotSize = BaseLotSize;
lastTradeResult = -1;
}
}
}

//+------------------------------------------------------------------+
//| Calculate Bollinger Bands width |
//+------------------------------------------------------------------+
double GetBBWidth(int shift)
{
double upper = iBands(Symbol(), PERIOD_H1, BBPeriod, BBDeviation, 0, PRICE_CLOSE, MODE_UPPER, shift);
double lower = iBands(Symbol(), PERIOD_H1, BBPeriod, BBDeviation, 0, PRICE_CLOSE, MODE_LOWER, shift);
if(upper > 0 && lower > 0)
return (upper - lower) / iClose(Symbol(), PERIOD_H1, shift);
return 0;
}

//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Daily equity protection
double currentEquity = AccountEquity();
double lossPercent = (dailyStartBalance - currentEquity) / dailyStartBalance * 100;
if(lossPercent >= DailyLossLimit)
{
Comment("Daily loss limit reached. No new trades.");
return;
}

// 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 lot size based on last trade result (called once per bar)
UpdateLotSizeFromLastTrade();

// Count current open positions
int currentPositions = CountPositions();

// Trailing stop management for existing positions
if(currentPositions > 0)
{
ManageTrailingStop();
}

// Entry conditions - only if below max concurrent trades
if(currentPositions >= MaxConcurrentTrades)
return;

// Bollinger Bands squeeze detection
double currentWidth = GetBBWidth(1);
double widthSum = 0;
int widthCount = 0;
for(int i = 2; i <= 51; i++)
{
double w = GetBBWidth(i);
if(w > 0)
{
widthSum += w;
widthCount++;
}
}
double avgWidth = (widthCount > 0) ? widthSum / widthCount : currentWidth;
if(avgWidth > 0)
avgBBWidth = (avgBBWidth == 0) ? avgWidth : (avgBBWidth * 0.9 + avgWidth * 0.1);

bool isSqueeze = (currentWidth < avgBBWidth * SqueezeRatio);
if(!isSqueeze)
{
Comment("No squeeze detected. BB width ratio: ", currentWidth/avgBBWidth);
return;
}

// Momentum candle detection
double atr = iATR(Symbol(), PERIOD_H1, ATRPeriod, 1);
if(atr <= 0) return;

double body1 = MathAbs(iClose(Symbol(), PERIOD_H1, 1) - iOpen(Symbol(), PERIOD_H1, 1));
double body2 = MathAbs(iClose(Symbol(), PERIOD_H1, 2) - iOpen(Symbol(), PERIOD_H1, 2));

bool strongMomentum = (body1 > atr * MomentumBodyRatio);
if(!strongMomentum)
{
Comment("No strong momentum candle. Body: ", body1/Point, " ATR: ", atr/Point);
return;
}

// Determine direction
double close1 = iClose(Symbol(), PERIOD_H1, 1);
double open1 = iOpen(Symbol(), PERIOD_H1, 1);
double close2 = iClose(Symbol(), PERIOD_H1, 2);

int cmd = -1;
double entryPrice = 0;
double sl = 0, tp = 0;

// Bullish momentum: close > open and close > previous close
if(close1 > open1 && close1 > close2)
{
cmd = OP_BUY;
entryPrice = Ask;
sl = entryPrice - (atr * StopLossATRMult);
tp = entryPrice + (atr * TakeProfitATRMult);
}
// Bearish momentum: close < open and close < previous close
else if(close1 < open1 && close1 < close2)
{
cmd = OP_SELL;
entryPrice = Bid;
sl = entryPrice + (atr * StopLossATRMult);
tp = entryPrice - (atr * TakeProfitATRMult);
}

if(cmd != -1)
{
double lotToUse = currentLotSize;
if(lotToUse < BaseLotSize) lotToUse = BaseLotSize;
if(lotToUse > MaxLotSize) lotToUse = MaxLotSize;

int ticket = OrderSend(Symbol(), cmd, lotToUse, entryPrice, 3, sl, tp, "Gold Nova Surge", MagicNumber, 0, clrNONE);
if(ticket > 0)
{
Print("Entry triggered. Lot: ", lotToUse, " Direction: ", (cmd==OP_BUY?"BUY":"SELL"));
}
else
{
Print("OrderSend failed: ", GetLastError());
}
}
}

//+------------------------------------------------------------------+
//| Manage trailing stop for all open positions |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double atr = iATR(Symbol(), PERIOD_H1, ATRPeriod, 1);
if(atr <= 0) continue;

double newSL = 0;
double profitInPoints = 0;

if(OrderType() == OP_BUY)
{
profitInPoints = (Bid - OrderOpenPrice()) / Point;
if(profitInPoints >= TrailingStartATR * (atr/Point))
{
newSL = Bid - (TrailingStepATR * atr);
if(newSL > OrderStopLoss())
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE))
Print("Trailing updated for BUY #", OrderTicket());
}
}
}
else if(OrderType() == OP_SELL)
{
profitInPoints = (OrderOpenPrice() - Ask) / Point;
if(profitInPoints >= TrailingStartATR * (atr/Point))
{
newSL = Ask + (TrailingStepATR * atr);
if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE))
Print("Trailing updated for SELL #", OrderTicket());
}
}
}
}
}
}
}

//+------------------------------------------------------------------+
//| Count open positions with this MagicNumber |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
```
Reference: Original MQL4 code for educational purposes.
Disclaimer: Trading Forex and Gold involves substantial risk. This high-performance EA uses anti-martingale position sizing which can lead to larger losses during losing streaks. This EA is provided as-is without any guarantee of profit. Test thoroughly on demo for at least 3 months before live trading. Past performance does not guarantee future results.