Summary: Complete MT4 EA implementing Bollinger Bands mean reversion with ATR volatility confirmation. Entries at band extremes, exits at midline. Features spread control, break-even management, and time filters. Compiles on any MT4 platform.




# Bollinger Bands Mean Reversion EA - Complete MQL4 Source Code

This article provides a fully functional Expert Advisor that trades based on Bollinger Bands mean reversion principle with ATR volatility confirmation. The EA opens a SELL when price touches or exceeds the upper band and opens a BUY when price touches or falls below the lower band, anticipating a reversion to the middle band.

Strategy Logic



The strategy operates on the mean reversion principle - when price deviates significantly from its moving average (the middle band), it tends to revert. The Bollinger Bands measure this deviation using standard deviations. The EA includes ATR filtering to avoid trading in low-volatility environments where mean reversion may not provide sufficient movement. Additional features include break-even stop management, spread control, and trading hour filters.

Key Features



  • Bollinger Band signals: Enter on upper/lower band touch

  • ATR volatility filter: Prevents trading during low volatility

  • Break-even management: Moves stop to entry after configurable profit

  • Spread protection: Blocks trading when spreads exceed threshold

  • Time filter: Restricts trading to specified hours

  • Single position per direction: Avoids overexposure


  • Complete MQL4 Code



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

    //--- Input Parameters
    input double LotSize = 0.1; // Fixed lot size
    input int BandsPeriod = 20; // Bollinger Bands period
    input double BandsDeviation = 2.0; // Standard deviation multiplier
    input int ATRPeriod = 14; // ATR period for filter
    input double MinATR = 10; // Minimum ATR value (points) - low volatility filter
    input int StopLoss = 60; // Stop loss in pips
    input int TakeProfit = 120; // Take profit in pips
    input bool UseBreakEven = true; // Enable break-even stop
    input int BreakEvenPips = 25; // Pips needed to trigger break-even
    input int MaxSpread = 30; // Maximum allowed spread in pips
    input bool UseTimeFilter = false; // Enable trading time filter
    input int StartHour = 8; // Trading start hour (server time)
    input int EndHour = 20; // Trading end hour (server time)
    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;
    int pointMultiplier = 10; // For 5-digit brokers

    //+------------------------------------------------------------------+
    //| Expert initialization function |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    // Check for 4-digit broker
    if(Digits == 4 || Digits == 3)
    pointMultiplier = 1;
    else if(Digits == 5 || Digits == 2)
    pointMultiplier = 10;

    if(BandsPeriod < 2)
    {
    Print("Error: Bollinger Bands period must be at least 2");
    return(INIT_PARAMETERS_INCORRECT);
    }

    if(ATRPeriod < 2)
    {
    Print("Error: ATR period must be at least 2");
    return(INIT_PARAMETERS_INCORRECT);
    }

    Print("EA initialized successfully on ", Symbol());
    Print("Point multiplier: ", pointMultiplier);
    return(INIT_SUCCEEDED);
    }

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

    //+------------------------------------------------------------------+
    //| Expert tick function |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    // Check time filter
    if(!IsTradingTime())
    return;

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

    // Get Bollinger Bands values
    upperBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_UPPER, 0);
    lowerBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_LOWER, 0);
    middleBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_MAIN, 0);

    // Get ATR value for volatility filter
    atrValue = iATR(Symbol(), 0, ATRPeriod, 0);

    // Validate data
    if(upperBand == EMPTY_VALUE || lowerBand == EMPTY_VALUE || atrValue == EMPTY_VALUE)
    return;

    // Convert ATR to points for comparison
    double atrPoints = atrValue / Point / pointMultiplier;

    // Low volatility filter - skip trading
    if(atrPoints < MinATR)
    return;

    // Manage existing positions
    ManageBreakEven();

    // Signal detection
    if(IsSellSignal())
    {
    if(CloseOpposite) CloseBuyPositions();
    if(CountSellPositions() == 0)
    OpenOrder(OP_SELL);
    }
    else if(IsBuySignal())
    {
    if(CloseOpposite) CloseSellPositions();
    if(CountBuyPositions() == 0)
    OpenOrder(OP_BUY);
    }
    }

    //+------------------------------------------------------------------+
    //| Check for buy signal - price touches or breaks lower band |
    //+------------------------------------------------------------------+
    bool IsBuySignal()
    {
    // Using low price for confirmation to avoid repainting
    double lowPrice = Low[0];
    return (lowPrice <= lowerBand);
    }

    //+------------------------------------------------------------------+
    //| Check for sell signal - price touches or breaks upper band |
    //+------------------------------------------------------------------+
    bool IsSellSignal()
    {
    // Using high price for confirmation to avoid repainting
    double highPrice = High[0];
    return (highPrice >= upperBand);
    }

    //+------------------------------------------------------------------+
    //| Open market order |
    //+------------------------------------------------------------------+
    void OpenOrder(int cmd)
    {
    double price = (cmd == OP_BUY) ? Ask : Bid;
    double sl = 0, tp = 0;
    double slPoints = StopLoss * pointMultiplier * Point;
    double tpPoints = TakeProfit * pointMultiplier * Point;

    if(StopLoss > 0)
    {
    if(cmd == OP_BUY)
    sl = price - slPoints;
    else
    sl = price + slPoints;
    }

    if(TakeProfit > 0)
    {
    if(cmd == OP_BUY)
    tp = price + tpPoints;
    else
    tp = price - tpPoints;
    }

    // Check margin before sending order
    double marginRequired = MarketInfo(Symbol(), MODE_MARGINREQUIRED);
    double freeMargin = AccountFreeMargin();

    if(freeMargin < marginRequired * LotSize * 1.5)
    {
    Print("Insufficient margin. Required: ", marginRequired * LotSize, " Free: ", freeMargin);
    return;
    }

    int ticket = OrderSend(Symbol(), cmd, LotSize, price, 3, sl, tp, "BB MeanRev", MagicNumber, 0, clrNONE);

    if(ticket < 0)
    Print("OrderSend failed. Error: ", GetLastError());
    else
    Print("Order opened. Ticket: ", ticket, " Type: ", (cmd == OP_BUY ? "BUY" : "SELL"));
    }

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

    datetime now = TimeCurrent();
    MqlDateTime dt;
    TimeToStruct(now, dt);

    int hour = dt.hour;

    if(hour >= StartHour && hour < EndHour)
    return true;

    return false;
    }

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

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

    //+------------------------------------------------------------------+
    //| Break-even management - move stop to entry after profit target |
    //+------------------------------------------------------------------+
    void ManageBreakEven()
    {
    if(!UseBreakEven) return;

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

    if(OrderType() == OP_BUY)
    {
    double profitPoints = (Bid - OrderOpenPrice()) / Point / pointMultiplier;
    if(profitPoints >= BreakEvenPips && (currentSL == 0 || currentSL < OrderOpenPrice()))
    {
    bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, clrNONE);
    if(modified)
    Print("Break-even set for BUY #", OrderTicket());
    }
    }
    else if(OrderType() == OP_SELL)
    {
    double profitPoints = (OrderOpenPrice() - Ask) / Point / pointMultiplier;
    if(profitPoints >= BreakEvenPips && (currentSL == 0 || currentSL > OrderOpenPrice()))
    {
    bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, clrNONE);
    if(modified)
    Print("Break-even set for SELL #", OrderTicket());
    }
    }
    }
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Count buy positions |
    //+------------------------------------------------------------------+
    int CountBuyPositions()
    {
    int count = 0;
    for(int i = 0; i < OrdersTotal(); i++)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_BUY)
    count++;
    }
    }
    return count;
    }

    //+------------------------------------------------------------------+
    //| Count sell positions |
    //+------------------------------------------------------------------+
    int CountSellPositions()
    {
    int count = 0;
    for(int i = 0; i < OrdersTotal(); i++)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_SELL)
    count++;
    }
    }
    return count;
    }

    //+------------------------------------------------------------------+
    //| Close all buy positions |
    //+------------------------------------------------------------------+
    void CloseBuyPositions()
    {
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_BUY)
    {
    if(OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrNONE))
    Print("Closed BUY #", OrderTicket());
    else
    Print("Failed to close BUY #", OrderTicket(), " Error: ", GetLastError());
    }
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Close all sell positions |
    //+------------------------------------------------------------------+
    void CloseSellPositions()
    {
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_SELL)
    {
    if(OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrNONE))
    Print("Closed SELL #", OrderTicket());
    else
    Print("Failed to close SELL #", OrderTicket(), " Error: ", GetLastError());
    }
    }
    }
    }
    //+------------------------------------------------------------------+
    ```

    Parameter Explanation



    | Parameter | Description | Recommended Values |
    |-----------|-------------|--------------------|
    | LotSize | Fixed trading volume | 0.01 - 1.0 |
    | BandsPeriod | Bollinger Bands calculation period | 20 (standard) |
    | BandsDeviation | Standard deviation multiplier | 2.0 - 2.5 |
    | ATRPeriod | ATR calculation period for filter | 14 |
    | MinATR | Minimum ATR points - filters low volatility | 8-15 |
    | StopLoss | Stop loss distance in pips | 50-80 |
    | TakeProfit | Take profit distance in pips | 100-160 |
    | UseBreakEven | Enable break-even stop management | true |
    | BreakEvenPips | Pips needed to trigger break-even | 20-30 |
    | MaxSpread | Maximum allowed spread | 20-50 |
    | UseTimeFilter | Restrict trading to specific hours | false |
    | StartHour | Trading start hour (server time) | 8 |
    | EndHour | Trading end hour | 20 |
    | MagicNumber | Unique EA identifier | any unique |
    | CloseOpposite | Close opposite positions on new signal | true |

    Installation Instructions



    1. Open MetaEditor in MT4 (press F4)
    2. Create new Expert Advisor (File > New > Expert Advisor)
    3. Delete all default code and paste the complete code above
    4. Press Compile (F7) - verify 0 errors in the Errors tab
    5. Close MetaEditor and drag EA from Navigator to chart
    6. Adjust parameters in Inputs tab
    7. Enable AutoTrading (green triangle button)

    Compilation Tips



    Common fixes:
  • For 4-digit brokers, EA auto-detects and adjusts Point multiplier

  • Ensure MagicNumber does not conflict with other EAs

  • Test on demo account for at least 2 weeks before live trading

  • Use VPS for stable 24/5 operation


  • Optimization suggestions:
  • BandsDeviation = 2.0 works best for most pairs

  • MinATR should be adjusted based on pair's average volatility

  • Consider turning on UseTimeFilter to avoid low-liquidity sessions


  • Reference



    This EA source code is independently compiled based on mean reversion principles. Strategy design references standard Bollinger Bands methodology.

    *For advanced EA strategies with multi-timeframe confirmation, machine learning filters, and comprehensive backtest reports, check our premium EA collection. Subscribe for weekly updates and professional trading tools.*