Summary: Professional Bollinger Bands breakout EA for MT4. Trades when price closes outside the bands and re-enters. Includes ATR volatility filter, trailing stop, and position sizing.




# Bollinger Bands Breakout EA - Complete MQL4 Source Code

This Expert Advisor implements a classic volatility-based breakout strategy using Bollinger Bands. When price closes outside the upper or lower band and then re-enters the band, the EA opens a position in the breakout direction.

Strategy Logic



The Bollinger Bands consist of a middle SMA and two standard deviation channels. When price breaks above the upper band and closes back inside, a BUY signal is generated. When price breaks below the lower band and closes back inside, a SELL signal is generated. This captures momentum continuation after volatility expansion.

Complete MQL4 Code



```mql4
//+------------------------------------------------------------------+
//| BBreakoutEA.mq4 |
//| Version 2.0 |
//+------------------------------------------------------------------+
#property copyright "Forex Strategy Builder"
#property link ""
#property version "2.00"
#property strict

//--- Input Parameters
input double LotSize = 0.1; // Fixed lot size
input int BBPeriod = 20; // Bollinger Bands period
input double BBDeviation = 2.0; // Standard deviation multiplier
input int MAPrice = PRICE_CLOSE; // Price type for bands
input int ATRPeriod = 14; // ATR period for filter
input double ATRFilter = 1.5; // Minimum ATR multiplier for entry
input int StopLoss = 0; // Stop loss in pips (0 = ATR based)
input int TakeProfit = 0; // Take profit in pips (0 = ATR based)
input double RiskPercent = 2.0; // Risk percentage per trade (0=use fixed lot)
input int TrailingStart = 25; // Trailing stop activation in pips
input int TrailingStep = 15; // Trailing stop distance in pips
input int MaxSpread = 35; // Maximum allowed spread in pips
input int MagicNumber = 202412; // EA magic number
input bool CloseOpposite = true; // Close opposite position on new signal

//--- Global Variables
double upperBand = 0, lowerBand = 0, middleBand = 0;
double atrValue = 0;

//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(BBPeriod < 5)
{
Print("Error: Bollinger Bands period must be at least 5");
return(INIT_PARAMETERS_INCORRECT);
}
Print("Bollinger Bands Breakout EA initialized");
return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA removed. Reason code: ", reason);
}

//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Safety checks
if(!IsTradeAllowed()) return;
if(IsNewBar() == false) return;

// Check spread
if(MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread * Point * 10) return;

// Calculate indicators
CalculateBollingerBands();
atrValue = iATR(Symbol(), 0, ATRPeriod, 1);

// Validate ATR filter
if(atrValue <= 0) return;
if(ATRFilter > 0 && atrValue / Point < ATRFilter * 10) return;

// Get current and previous close prices
double closeCurr = Close[0];
double closePrev = Close[1];
double upperPrev = iBands(Symbol(), 0, BBPeriod, BBDeviation, 0, MAPrice, MODE_UPPER, 1);
double lowerPrev = iBands(Symbol(), 0, BBPeriod, BBDeviation, 0, MAPrice, MODE_LOWER, 1);

// Check for existing positions
int posCount = CountPositions();

// BUY Signal: Price closed above upper band on previous bar, now inside
if(closePrev > upperPrev && closeCurr <= upperBand)
{
if(posCount > 0 && CloseOpposite) CloseAllPositions();
if(CountPositions() == 0) OpenBuy();
}

// SELL Signal: Price closed below lower band on previous bar, now inside
else if(closePrev < lowerPrev && closeCurr >= lowerBand)
{
if(posCount > 0 && CloseOpposite) CloseAllPositions();
if(CountPositions() == 0) OpenSell();
}

// Trailing stop management
if(posCount > 0) ManageTrailingStop();
}

//+------------------------------------------------------------------+
//| Calculate current Bollinger Bands values |
//+------------------------------------------------------------------+
void CalculateBollingerBands()
{
upperBand = iBands(Symbol(), 0, BBPeriod, BBDeviation, 0, MAPrice, MODE_UPPER, 0);
lowerBand = iBands(Symbol(), 0, BBPeriod, BBDeviation, 0, MAPrice, MODE_LOWER, 0);
middleBand = iBands(Symbol(), 0, BBPeriod, BBDeviation, 0, MAPrice, MODE_MAIN, 0);
}

//+------------------------------------------------------------------+
//| Open Buy Order |
//+------------------------------------------------------------------+
void OpenBuy()
{
double lot = CalculateLotSize();
double sl = 0, tp = 0;
double price = Ask;

// Calculate dynamic SL/TP based on ATR
if(StopLoss > 0)
{
sl = price - StopLoss * Point * 10;
}
else if(ATRPeriod > 0)
{
sl = price - atrValue * 1.5;
}

if(TakeProfit > 0)
{
tp = price + TakeProfit * Point * 10;
}
else if(ATRPeriod > 0)
{
tp = price + atrValue * 2.5;
}

int ticket = OrderSend(Symbol(), OP_BUY, lot, price, 3, sl, tp, "BB Breakout BUY", MagicNumber, 0, clrDodgerBlue);

if(ticket > 0)
Print("Buy order opened. Ticket: ", ticket);
else
Print("Buy order failed. Error: ", GetLastError());
}

//+------------------------------------------------------------------+
//| Open Sell Order |
//+------------------------------------------------------------------+
void OpenSell()
{
double lot = CalculateLotSize();
double sl = 0, tp = 0;
double price = Bid;

if(StopLoss > 0)
{
sl = price + StopLoss * Point * 10;
}
else if(ATRPeriod > 0)
{
sl = price + atrValue * 1.5;
}

if(TakeProfit > 0)
{
tp = price - TakeProfit * Point * 10;
}
else if(ATRPeriod > 0)
{
tp = price - atrValue * 2.5;
}

int ticket = OrderSend(Symbol(), OP_SELL, lot, price, 3, sl, tp, "BB Breakout SELL", MagicNumber, 0, clrCrimson);

if(ticket > 0)
Print("Sell order opened. Ticket: ", ticket);
else
Print("Sell order failed. Error: ", GetLastError());
}

//+------------------------------------------------------------------+
//| Calculate lot size based on risk percentage |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
if(RiskPercent <= 0 || StopLoss == 0 && ATRPeriod == 0)
return LotSize;

double riskMoney = AccountBalance() * RiskPercent / 100.0;
double stopPoints;

if(StopLoss > 0)
stopPoints = StopLoss;
else
stopPoints = (atrValue * 1.5) / (Point * 10);

double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);

double calculatedLot = riskMoney / (stopPoints * tickValue);
calculatedLot = MathFloor(calculatedLot / lotStep) * lotStep;
calculatedLot = MathMax(minLot, MathMin(maxLot, calculatedLot));

return NormalizeDouble(calculatedLot, 2);
}

//+------------------------------------------------------------------+
//| Count positions with this EA's magic number |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
count++;
}
}
return count;
}

//+------------------------------------------------------------------+
//| Close all positions |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
bool result = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 3, clrWhite);
if(!result)
Print("Close failed: ", GetLastError());
}
}
}
}

//+------------------------------------------------------------------+
//| Trailing stop management |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
if(TrailingStart <= 0) return;

for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double newSL = 0;
double currentSL = OrderStopLoss();

if(OrderType() == OP_BUY)
{
double profitPoints = (Bid - OrderOpenPrice()) / (Point * 10);
if(profitPoints >= TrailingStart)
{
newSL = Bid - TrailingStep * Point * 10;
if(newSL > currentSL || currentSL == 0)
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrWhite);
}
}
else if(OrderType() == OP_SELL)
{
double profitPoints = (OrderOpenPrice() - Ask) / (Point * 10);
if(profitPoints >= TrailingStart)
{
newSL = Ask + TrailingStep * Point * 10;
if(newSL < currentSL || currentSL == 0)
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrWhite);
}
}
}
}
}
}

//+------------------------------------------------------------------+
//| Check if new bar has formed |
//+------------------------------------------------------------------+
bool IsNewBar()
{
static datetime lastBarTime = 0;
datetime currentBarTime = Time[0];
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
return true;
}
return false;
}
//+------------------------------------------------------------------+
```

Parameter Explanation



| Parameter | Description | Recommended Value |
|-----------|-------------|-------------------|
| LotSize | Fixed lot size when risk management disabled | 0.01-0.1 |
| BBPeriod | Bollinger Bands calculation period | 20 |
| BBDeviation | Standard deviation multiplier | 2.0 |
| MAPrice | Price type for band calculation | PRICE_CLOSE |
| ATRPeriod | ATR period for volatility filter | 14 |
| ATRFilter | Minimum ATR multiplier to allow trades | 1.0-2.0 |
| StopLoss | Fixed stop loss in pips (0 = ATR based) | 0 |
| TakeProfit | Fixed take profit in pips (0 = ATR based) | 0 |
| RiskPercent | Risk per trade (% of balance, 0=use fixed lot) | 1.0-2.0 |
| TrailingStart | Pips profit to activate trailing | 25 |
| TrailingStep | Trailing stop distance in pips | 15 |
| MaxSpread | Maximum spread to open trades | 30-40 |
| MagicNumber | Unique identifier for EA trades | Any number |
| CloseOpposite | Close existing position on opposite signal | true |

Installation Guide



1. Launch MetaEditor in MT4 (F4)
2. Create new Expert Advisor (File > New > Expert Advisor)
3. Replace default code with the complete MQL4 code above
4. Press F7 or click Compile button
5. Check "Experts" tab for successful compilation
6. Drag EA from Navigator to chart
7. Configure parameters in Inputs tab
8. Enable AutoTrading (Alt+T)

Compilation Tips



  • For 5-digit brokers, the EA uses `Point * 10` for pip calculation automatically

  • Ensure `#property strict` is present to avoid hidden errors

  • Test on demo account with default parameters first

  • Use H1 or H4 timeframe for best results with this strategy


  • Strategy Optimization Suggestions



    1. Volatility Filter: Adjust ATRFilter based on market conditions. Higher values filter out low-volatility whipsaws.
    2. Bollinger Parameters: BBPeriod 20 with 2.0 deviation works well on most forex pairs. For trending markets, try 1.5-1.8 deviation.
    3. Timeframe: This EA performs best on H1 and H4 charts. Avoid M1 and M5 due to noise.
    4. Risk Management: Set RiskPercent to 1-2% for conservative trading. Use fixed lot for testing.

    Reference



    This EA source code is independently developed and tested based on standard Bollinger Bands breakout methodology described in John Bollinger's "Bollinger on Bollinger Bands".

    *For advanced EA collection including multi-strategy systems and neural network based filters, check our premium package with verified backtest reports.*