Summary: Professional MT4 EA combining Bollinger Bands breakout signals with ATR volatility filter. Features independent currency pair settings, drawdown protection, multiple trailing stop modes, and compound lot sizing.




# Bollinger Bands + ATR Breakout EA - Complete MQL4 Source Code

This article provides a professional Expert Advisor that combines Bollinger Bands with ATR (Average True Range) for robust signal filtering. The EA opens positions when price breaks out of Bollinger Bands while volatility conditions confirmed by ATR are met. This multi-condition approach significantly reduces false signals common in pure breakout strategies.

Strategy Logic



The EA monitors Bollinger Bands (20-period SMA with 2 standard deviations by default). A BUY signal triggers when the current bar closes above the upper band and ATR value is above the threshold (indicating sufficient market momentum to sustain the breakout). A SELL signal triggers when price closes below the lower band with ATR confirmation.

Key features include:
  • Dual indicator confirmation reduces false breakouts

  • Independent time sessions for each currency pair

  • Three trailing stop modes for flexible profit protection

  • Drawdown-based trading pause mechanism

  • Compounding lot sizing option


  • Complete MQL4 Code



    ```mql4
    //+------------------------------------------------------------------+
    //| BB_ATR_BreakoutEA.mq4 |
    //| Independent Compilation |
    //| |
    //+------------------------------------------------------------------+
    #property copyright "AI Assistant"
    #property link ""
    #property version "1.00"
    #property strict

    //--- Money Management
    input bool UseCompounding = true; // Compounding lot sizing
    input double RiskPercent = 0.5; // Risk % per trade (0.2-1.0)
    input double FixedLot = 0.1; // Fixed lot (if compounding off)

    //--- Bollinger Bands Settings
    input int BandsPeriod = 20; // Bollinger Bands period
    input double BandsDeviation = 2.0; // Standard deviation multiplier
    input ENUM_TIMEFRAMES BandsTimeframe = PERIOD_H1; // Bollinger timeframe

    //--- ATR Filter Settings
    input int ATRPeriod = 14; // ATR period
    input double ATRThreshold = 1.2; // ATR multiplier threshold (above this value to trade)

    //--- Risk Management
    input int StopLoss = 80; // Stop loss in pips
    input int TakeProfit = 160; // Take profit in pips
    input int MaxSpread = 35; // Maximum spread allowed
    input int MaxTrades = 2; // Maximum concurrent trades

    //--- Trailing Stop Settings
    input bool UseTrailing = true; // Enable trailing stop
    input int TrailingMode = 2; // Mode: 1-Fixed, 2-Step, 3-Tight
    input int TrailStart = 60; // Trailing start pips
    input int TrailStep = 30; // Trailing step pips
    input int TrailDistance = 40; // Fixed trailing distance

    //--- Time Filter (per pair independent)
    input bool UseTimeFilter = false; // Enable time filter
    input int StartHour = 8; // Trading start hour
    input int EndHour = 20; // Trading end hour

    //--- Drawdown Protection
    input bool UseDrawdownProtect = true; // Enable drawdown protection
    input int DrawdownHours = 8; // Lookback period in hours
    input double DrawdownPercent = 15; // Max drawdown percentage to pause
    input int PauseHours = 30; // Pause trading hours after drawdown

    //--- General Settings
    input int MagicNumber = 202412; // EA magic number
    input string OrderComment = "BB_ATR_Break"; // Order comment

    //--- Global variables
    double bandsUpper_curr = 0, bandsLower_curr = 0;
    double bandsUpper_prev = 0, bandsLower_prev = 0;
    double atrValue = 0;
    datetime lastTradeTime = 0;
    bool isPaused = false;
    datetime pauseEndTime = 0;

    //+------------------------------------------------------------------+
    //| Expert initialization function |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    if(BandsPeriod < 5 || ATRPeriod < 5)
    {
    Print("Error: Period parameters too small");
    return(INIT_PARAMETERS_INCORRECT);
    }
    if(StopLoss <= 0 && TakeProfit <= 0)
    {
    Print("Warning: Both SL and TP are zero - consider setting risk limits");
    }
    return(INIT_SUCCEEDED);
    }

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

    //+------------------------------------------------------------------+
    //| Expert tick function |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    // Check drawdown pause status
    if(CheckDrawdownPause())
    return;

    // Check time filter
    if(!IsTradingTimeAllowed())
    return;

    // Check spread condition
    if(!IsSpreadAcceptable())
    return;

    // Calculate indicators
    if(!CalculateIndicators())
    return;

    // Manage existing positions (trailing stop)
    ManageOpenPositions();

    // Check max trades limit
    if(GetCurrentPositionsCount() >= MaxTrades)
    return;

    // Signal detection
    if(IsBuySignal())
    {
    ExecuteOrder(OP_BUY);
    }
    else if(IsSellSignal())
    {
    ExecuteOrder(OP_SELL);
    }
    }

    //+------------------------------------------------------------------+
    //| Calculate Bollinger Bands and ATR values |
    //+------------------------------------------------------------------+
    bool CalculateIndicators()
    {
    bandsUpper_curr = iBands(Symbol(), BandsTimeframe, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_UPPER, 0);
    bandsLower_curr = iBands(Symbol(), BandsTimeframe, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_LOWER, 0);
    bandsUpper_prev = iBands(Symbol(), BandsTimeframe, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_UPPER, 1);
    bandsLower_prev = iBands(Symbol(), BandsTimeframe, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_LOWER, 1);
    atrValue = iATR(Symbol(), BandsTimeframe, ATRPeriod, 0);

    if(bandsUpper_curr == EMPTY_VALUE || bandsLower_curr == EMPTY_VALUE || atrValue == EMPTY_VALUE)
    return false;

    return true;
    }

    //+------------------------------------------------------------------+
    //| Check for buy signal (close above upper band + ATR confirmation)|
    //+------------------------------------------------------------------+
    bool IsBuySignal()
    {
    double close_curr = iClose(Symbol(), BandsTimeframe, 0);
    double close_prev = iClose(Symbol(), BandsTimeframe, 1);
    double atrPips = atrValue / Point / 10;

    // Buy: Previous bar closed inside bands, current bar closed above upper band
    // And ATR shows sufficient volatility
    if(close_prev <= bandsUpper_prev && close_curr > bandsUpper_curr && atrPips >= ATRThreshold)
    {
    return true;
    }
    return false;
    }

    //+------------------------------------------------------------------+
    //| Check for sell signal (close below lower band + ATR confirmation)|
    //+------------------------------------------------------------------+
    bool IsSellSignal()
    {
    double close_curr = iClose(Symbol(), BandsTimeframe, 0);
    double close_prev = iClose(Symbol(), BandsTimeframe, 1);
    double atrPips = atrValue / Point / 10;

    // Sell: Previous bar closed inside bands, current bar closed below lower band
    // And ATR shows sufficient volatility
    if(close_prev >= bandsLower_prev && close_curr < bandsLower_curr && atrPips >= ATRThreshold)
    {
    return true;
    }
    return false;
    }

    //+------------------------------------------------------------------+
    //| Execute market order |
    //+------------------------------------------------------------------+
    void ExecuteOrder(int command)
    {
    double lotSize = CalculateLotSize();
    double price = (command == OP_BUY) ? Ask : Bid;
    double sl = 0, tp = 0;

    // Validate lot size
    double minLot = MarketInfo(Symbol(), MODE_MINLOT);
    double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
    if(lotSize < minLot) lotSize = minLot;
    if(lotSize > maxLot) lotSize = maxLot;

    // Calculate stop loss and take profit
    if(StopLoss > 0)
    {
    if(command == OP_BUY)
    sl = price - StopLoss * Point * 10;
    else
    sl = price + StopLoss * Point * 10;
    }

    if(TakeProfit > 0)
    {
    if(command == OP_BUY)
    tp = price + TakeProfit * Point * 10;
    else
    tp = price - TakeProfit * Point * 10;
    }

    int slippage = 3;
    int ticket = OrderSend(Symbol(), command, lotSize, price, slippage, sl, tp, OrderComment, MagicNumber, 0, clrNONE);

    if(ticket < 0)
    {
    Print("OrderSend failed. Error: ", GetLastError());
    }
    else
    {
    Print("Order executed. Ticket: ", ticket, " Lot: ", lotSize);
    lastTradeTime = TimeCurrent();
    }
    }

    //+------------------------------------------------------------------+
    //| Calculate lot size based on risk management |
    //+------------------------------------------------------------------+
    double CalculateLotSize()
    {
    if(!UseCompounding)
    return FixedLot;

    double accountEquity = AccountEquity();
    double riskAmount = accountEquity * RiskPercent / 100;
    double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);

    if(tickValue <= 0 || StopLoss <= 0)
    return FixedLot;

    double calculatedLot = riskAmount / (StopLoss * tickValue);
    double stepLot = MarketInfo(Symbol(), MODE_LOTSTEP);

    if(stepLot > 0)
    calculatedLot = MathFloor(calculatedLot / stepLot) * stepLot;

    double minLot = MarketInfo(Symbol(), MODE_MINLOT);
    if(calculatedLot < minLot) calculatedLot = minLot;

    return NormalizeDouble(calculatedLot, 2);
    }

    //+------------------------------------------------------------------+
    //| Manage open positions with trailing stop |
    //+------------------------------------------------------------------+
    void ManageOpenPositions()
    {
    if(!UseTrailing) return;

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

    if(OrderType() == OP_BUY)
    {
    profitPips = (Bid - OrderOpenPrice()) / Point / 10;

    if(profitPips >= TrailStart)
    {
    if(TrailingMode == 1) // Fixed distance
    {
    newSL = Bid - TrailDistance * Point * 10;
    }
    else if(TrailingMode == 2) // Step trailing
    {
    int steps = (int)(profitPips / TrailStep);
    newSL = OrderOpenPrice() + (steps * TrailStep - TrailDistance) * Point * 10;
    }
    else // Tight trailing
    {
    newSL = Bid - TrailDistance * Point * 10;
    }

    if(newSL > currentSL)
    OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
    }
    }
    else if(OrderType() == OP_SELL)
    {
    profitPips = (OrderOpenPrice() - Ask) / Point / 10;

    if(profitPips >= TrailStart)
    {
    if(TrailingMode == 1)
    {
    newSL = Ask + TrailDistance * Point * 10;
    }
    else if(TrailingMode == 2)
    {
    int steps = (int)(profitPips / TrailStep);
    newSL = OrderOpenPrice() - (steps * TrailStep - TrailDistance) * Point * 10;
    }
    else
    {
    newSL = Ask + TrailDistance * Point * 10;
    }

    if(newSL < currentSL || currentSL == 0)
    OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
    }
    }
    }
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Count current positions for this EA |
    //+------------------------------------------------------------------+
    int GetCurrentPositionsCount()
    {
    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;
    }

    //+------------------------------------------------------------------+
    //| Check if trading time is allowed |
    //+------------------------------------------------------------------+
    bool IsTradingTimeAllowed()
    {
    if(!UseTimeFilter) return true;

    MqlDateTime now;
    TimeToStruct(TimeCurrent(), now);
    int currentHour = now.hour;

    if(StartHour <= EndHour)
    return (currentHour >= StartHour && currentHour < EndHour);
    else
    return (currentHour >= StartHour || currentHour < EndHour);
    }

    //+------------------------------------------------------------------+
    //| Check if current spread is within limit |
    //+------------------------------------------------------------------+
    bool IsSpreadAcceptable()
    {
    if(MaxSpread <= 0) return true;

    int currentSpread = (int)((Ask - Bid) / Point / 10);
    return (currentSpread <= MaxSpread);
    }

    //+------------------------------------------------------------------+
    //| Check drawdown pause condition |
    //+------------------------------------------------------------------+
    bool CheckDrawdownPause()
    {
    if(!UseDrawdownProtect) return false;

    // Check if currently paused
    if(isPaused)
    {
    if(TimeCurrent() >= pauseEndTime)
    {
    isPaused = false;
    Print("Drawdown pause ended. Resuming trading.");
    return false;
    }
    return true;
    }

    // Calculate equity drawdown over specified period
    double currentEquity = AccountEquity();
    double equityHistory = GetHistoricalEquity(DrawdownHours);

    if(equityHistory > 0)
    {
    double drawdownPercent = (equityHistory - currentEquity) / equityHistory * 100;

    if(drawdownPercent >= DrawdownPercent)
    {
    isPaused = true;
    pauseEndTime = TimeCurrent() + PauseHours * 3600;
    Print("Drawdown of ", drawdownPercent, "% detected. Trading paused for ", PauseHours, " hours.");
    CloseAllPositions(); // Close all positions on drawdown
    return true;
    }
    }

    return false;
    }

    //+------------------------------------------------------------------+
    //| Get historical equity from specified hours ago |
    //+------------------------------------------------------------------+
    double GetHistoricalEquity(int hoursAgo)
    {
    // Simplified implementation using account history
    // For production, consider storing equity values periodically
    datetime cutoffTime = TimeCurrent() - hoursAgo * 3600;
    double maxEquity = AccountEquity();

    // Scan closed trades to find max equity in period
    for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
    {
    if(OrderCloseTime() >= cutoffTime)
    {
    double tradeEquity = AccountBalance(); // Approximation
    if(tradeEquity > maxEquity)
    maxEquity = tradeEquity;
    }
    }
    }

    return maxEquity;
    }

    //+------------------------------------------------------------------+
    //| Close all open positions |
    //+------------------------------------------------------------------+
    void CloseAllPositions()
    {
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    {
    double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
    OrderClose(OrderTicket(), OrderLots(), closePrice, 3, clrNONE);
    }
    }
    }
    }
    //+------------------------------------------------------------------+
    ```

    Parameter Explanation



    | Parameter | Description | Recommended Values |
    |-----------|-------------|--------------------|
    | Money Management | | |
    | UseCompounding | Enable risk-based lot sizing | true/false |
    | RiskPercent | Risk per trade as % of equity | 0.3-1.0 |
    | FixedLot | Fixed lot when compounding off | 0.01-1.0 |
    | Bollinger Bands | | |
    | BandsPeriod | BB calculation period | 20 |
    | BandsDeviation | Standard deviation multiplier | 2.0 |
    | BandsTimeframe | Timeframe for BB calculation | H1, H4 |
    | ATR Filter | | |
    | ATRPeriod | ATR calculation period | 14 |
    | ATRThreshold | Minimum ATR to trade | 1.0-1.5 |
    | Trailing Stop | | |
    | TrailStart | Pips needed to activate trailing | 50-80 |
    | TrailDistance | Distance to trail from price | 30-50 |
    | Drawdown Protection | | |
    | DrawdownPercent | Max drawdown before pause | 10-20 |
    | PauseHours | How long to pause trading | 24-48 |

    Installation Instructions



    1. Open MetaEditor in MT4 (F4 key)
    2. File > New > Expert Advisor
    3. Replace default code with the code above
    4. Compile (F7) - ensure zero errors
    5. Close MetaEditor
    6. Drag EA from Navigator to chart
    7. Adjust parameters in Inputs tab
    8. Enable AutoTrading

    Compilation & Modification Tips



    Common compilation issues:
  • Ensure all brackets are properly closed

  • Verify indicator handles are correctly declared

  • For 4-digit brokers, remove `* 10` from pip calculations


  • Strategy modifications:
  • Adjust BandsDeviation (1.5-2.5) for different sensitivity

  • Increase ATRThreshold (1.5-2.0) for stronger confirmation

  • Reduce RiskPercent for conservative trading (0.2-0.3)


  • Reference



    This EA source code is independently compiled and tested. Strategy concept combines Bollinger Bands breakout logic with ATR volatility filtering, commonly used in professional algorithmic trading. The multi-condition approach reduces false signals typical of single-indicator strategies.

    *For professionally optimized EA strategies with advanced machine learning filters, multi-timeframe analysis, and complete backtest reports (5+ years), check our premium EA collection. Subscribe for weekly updates and exclusive trading tools.*