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:
``
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.