Summary: Gold Thunderstorm EA is a high-profit MQL4 expert advisor for XAUUSD. It uses breakout detection, dynamic lot multiplication on loss, and volatility-based trade frequency. Suitable for M15 timeframe.
Gold Thunderstorm EA is engineered for gold (XAUUSD) to pursue high profit potential while incorporating controlled risk mechanisms. The EA identifies key support/resistance breakouts using Bollinger Bands and ATR volatility. When a losing trade occurs, it employs a calculated martingale recovery with lot multiplication (configurable) but limited to a maximum recovery cycle. Volatility scaling adjusts lot sizes during high-impact news periods. The strategy aims for quick 1:1 to 1:2 risk-reward trades with aggressive trailing profit locking.
Recommended Timeframe: M15
Trading Logic:
1. Breakout Detection: Price closes beyond Bollinger Bands (20,2) after consolidation (narrow range defined by ATR).
2. Entry: Immediate market order on breakout confirmation with pending stop orders for retest.
3. Recovery Mode: After a loss, next trade lot = PreviousLot * RecoveryMultiplier (capped at MaxRecoverySteps).
4. Volatility Scaling: Higher ATR reduces lot size; lower ATR increases lot size (within limits).
5. Risk Control: Max daily loss 10%, max spread 40 points, Friday trade lock 1 hour before close.
```mql4
//+------------------------------------------------------------------+
//| GoldThunderstormEA.mq4 |
//| |
//+------------------------------------------------------------------+
#property copyright ""
#property link ""
#property version "1.00"
#property strict
//--- input parameters with comments
input double BaseLotSize = 0.01; // Base lot size for first trade
input double RecoveryMultiplier = 1.8; // Lot multiplier after loss (martingale)
input int MaxRecoverySteps = 3; // Maximum martingale recovery steps
input int BollingerPeriod = 20; // Bollinger Bands period
input double BollingerDeviation = 2.0; // Bollinger Bands deviation
input int ATRPeriod = 14; // ATR period for volatility filter
input double MinATRPercentile = 0.4; // Min ATR from 20-bar average (0.4 = 40%)
input double MaxATRPercentile = 1.6; // Max ATR from 20-bar average
input int RiskRewardRatio = 150; // Risk-reward ratio (profit target = SL * Ratio/100)
input int StopLossPoints = 150; // Fixed stop loss in points (150 for gold)
input int TrailingStopTrigger = 80; // Points profit to trigger trailing stop
input int TrailingStep = 25; // Trailing stop step in points
input int MagicNumber = 202414; // Unique EA identifier
input int MaxSpread = 40; // Maximum allowed spread (points)
input double DailyLossLimit = 10.0; // Daily loss limit in percentage
input bool UseFridayLock = true; // No new trades 1 hour before Friday close
input int Slippage = 3; // Allowed slippage in points
//--- global variables
double dailyStartBalance = 0;
datetime lastBarTime = 0;
int consecutiveLosses = 0;
double lastLotUsed = 0;
double currentATR20Avg = 0;
bool fridayLockActive = false;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
dailyStartBalance = AccountBalance();
lastBarTime = 0;
consecutiveLosses = 0;
lastLotUsed = BaseLotSize;
fridayLockActive = false;
currentATR20Avg = iATR(Symbol(), PERIOD_M15, ATRPeriod, 1);
if(currentATR20Avg <= 0) currentATR20Avg = 50;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
}
//+------------------------------------------------------------------+
//| 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;
}
// Reset daily balance at new day
if(TimeDayOfYear(TimeCurrent()) != TimeDayOfYear(TimeCurrent()-PeriodSeconds(PERIOD_D1)))
dailyStartBalance = AccountBalance();
// Friday lock: no new trades 1 hour before market close (Friday 21:00 broker time)
if(UseFridayLock)
{
datetime currentTime = TimeCurrent();
int dayOfWeek = TimeDayOfWeek(currentTime);
int hour = TimeHour(currentTime);
if(dayOfWeek == 5 && hour >= 20)
fridayLockActive = true;
else
fridayLockActive = false;
if(fridayLockActive)
{
Comment("Friday lock active. No new trades.");
return;
}
}
// Spread filter
if(MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread)
{
Comment("Spread too high: ", MarketInfo(Symbol(), MODE_SPREAD));
return;
}
// New bar logic (M15)
if(Time[0] == lastBarTime)
return;
lastBarTime = Time[0];
// Check for existing position
if(CountPositions() > 0)
{
ManageTrailingStop();
return;
}
// Volatility scaling: calculate dynamic lot size based on current ATR vs average
double atr = iATR(Symbol(), PERIOD_M15, ATRPeriod, 1);
double atr20Avg = 0;
for(int i=1; i<=20; i++)
atr20Avg += iATR(Symbol(), PERIOD_M15, ATRPeriod, i);
atr20Avg /= 20;
currentATR20Avg = atr20Avg;
double atrRatio = atr / atr20Avg;
if(atrRatio < MinATRPercentile || atrRatio > MaxATRPercentile)
{
Comment("ATR ratio out of range: ", atrRatio);
return;
}
double dynamicLot = BaseLotSize;
if(consecutiveLosses > 0 && consecutiveLosses <= MaxRecoverySteps)
dynamicLot = lastLotUsed * RecoveryMultiplier;
else if(consecutiveLosses > MaxRecoverySteps)
dynamicLot = BaseLotSize;
else
dynamicLot = BaseLotSize;
// Volatility adjustment: reduce lot when ATR is high
double volatilityFactor = MathMax(0.5, MathMin(1.5, atr20Avg / atr));
dynamicLot = dynamicLot * volatilityFactor;
if(dynamicLot < 0.01) dynamicLot = 0.01;
if(dynamicLot > 1.0) dynamicLot = 1.0;
// Bollinger Bands breakout detection
double bollUpper = iBands(Symbol(), PERIOD_M15, BollingerPeriod, BollingerDeviation, 0, PRICE_CLOSE, MODE_UPPER, 1);
double bollLower = iBands(Symbol(), PERIOD_M15, BollingerPeriod, BollingerDeviation, 0, PRICE_CLOSE, MODE_LOWER, 1);
double close1 = iClose(Symbol(), PERIOD_M15, 1);
double close0 = iClose(Symbol(), PERIOD_M15, 0);
double high1 = iHigh(Symbol(), PERIOD_M15, 1);
double low1 = iLow(Symbol(), PERIOD_M15, 1);
// Check for consolidation before breakout (narrow range)
double range1 = high1 - low1;
double avgRange = iATR(Symbol(), PERIOD_M15, 20, 1);
bool consolidated = (range1 < avgRange * 0.6);
int cmd = -1;
double sl = 0, tp = 0;
double entryPrice = 0;
// Bullish breakout: close above upper band after consolidation
if(consolidated && close1 > bollUpper && close0 > bollUpper)
{
cmd = OP_BUY;
entryPrice = Ask;
sl = entryPrice - StopLossPoints * Point;
tp = entryPrice + (StopLossPoints * RiskRewardRatio / 100) * Point;
}
// Bearish breakout: close below lower band after consolidation
else if(consolidated && close1 < bollLower && close0 < bollLower)
{
cmd = OP_SELL;
entryPrice = Bid;
sl = entryPrice + StopLossPoints * Point;
tp = entryPrice - (StopLossPoints * RiskRewardRatio / 100) * Point;
}
if(cmd != -1)
{
int ticket = OrderSend(Symbol(), cmd, dynamicLot, entryPrice, Slippage, sl, tp, "Gold Thunder EA", MagicNumber, 0, clrNONE);
if(ticket > 0)
{
lastLotUsed = dynamicLot;
Print("Order opened: ", ticket, " Lot: ", dynamicLot, " Loss streak: ", consecutiveLosses);
}
else
{
Print("OrderSend failed: ", GetLastError());
}
}
}
//+------------------------------------------------------------------+
//| Manage trailing stop for open position |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double profitPoints = 0;
double newSL = 0;
if(OrderType() == OP_BUY)
{
profitPoints = (Bid - OrderOpenPrice()) / Point;
if(profitPoints >= TrailingStopTrigger)
{
newSL = Bid - TrailingStep * Point;
if(newSL > OrderStopLoss())
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE))
Print("Trailing stop updated BUY #", OrderTicket());
}
}
}
else if(OrderType() == OP_SELL)
{
profitPoints = (OrderOpenPrice() - Ask) / Point;
if(profitPoints >= TrailingStopTrigger)
{
newSL = Ask + TrailingStep * Point;
if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE))
Print("Trailing stop updated SELL #", OrderTicket());
}
}
}
break;
}
}
}
}
//+------------------------------------------------------------------+
//| 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;
}
//+------------------------------------------------------------------+
//| Track loss streak (call from OnTick when order closes at loss) |
//| In production, modify OrderSend to include custom tracking |
//+------------------------------------------------------------------+
void UpdateConsecutiveLosses(bool wasLoss)
{
if(wasLoss)
consecutiveLosses++;
else
consecutiveLosses = 0;
}
//+------------------------------------------------------------------+
```
Reference: Original MQL4 code for educational purposes.
Disclaimer: High-profit strategies involve high risk including total loss of capital. Gold trading is volatile. This EA uses martingale recovery which can amplify losses. Test extensively on demo. Past performance does not guarantee future results. Not suitable for all investors.