๐Ÿš€ AI One-Click Generation of mql4 Strategy Code

How to Create a Bollinger Bandsยฎ Breakout Expert Advisor in Mql4

mql4 | Published on: 2025/11/26
The following content is generated by EasyQuant .

Problem Description

Can you code an EA that buys when the price breaks above the upper Bollinger Band and sells when it breaks below the lower band?

Solution

Here is a complete, fully commented Expert Advisor (EA) for MetaTrader 4 that implements a Bollinger Bands breakout strategy.

Strategy Logic

  1. Buy Signal: The previous candle's Close price is higher than the Upper Bollinger Band.
  2. Sell Signal: The previous candle's Close price is lower than the Lower Bollinger Band.
  3. Execution: The EA checks conditions only once per bar (on the open of a new candle) to prevent false signals during price fluctuations within the candle duration.
  4. Trade Management: If a Buy signal occurs while a Sell trade is open, the Sell trade is closed immediately, and vice versa.

MQL4 Code

//+------------------------------------------------------------------+
//|                                       BollingerBreakout_EA.mq4   |
//|                        Copyright 2023, Quantitative Trading AI   |
//|                                             https://mql4.com     |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative Trading AI"
#property link      "https://mql4.com"
#property version   "1.00"
#property strict

//--- Input Parameters
input double   InpLots          = 0.1;      // Lot size
input int      InpStopLoss      = 50;       // Stop Loss in points (0 = no SL)
input int      InpTakeProfit    = 100;      // Take Profit in points (0 = no TP)
input int      InpMagicNum      = 123456;   // Magic Number to identify orders
input int      InpSlippage      = 3;        // Maximum slippage in points

//--- Bollinger Bands Settings
input int      InpBandsPeriod   = 20;       // Bollinger Bands Period
input int      InpBandsShift    = 0;        // Bollinger Bands Shift
input double   InpBandsDev      = 2.0;      // Bollinger Bands Deviation

//--- Global Variables
datetime glbLastBarTime = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Basic validation
   if(InpBandsPeriod <= 0)
   {
      Print("Error: Bands Period must be greater than 0");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // 1. Check for New Bar (Run logic only once per candle close)
   if(glbLastBarTime == Time[0]) return;
   glbLastBarTime = Time[0];

   // 2. Get Indicator Values (Shift 1 = Previous closed candle)
   double upperBand = iBands(_Symbol, _Period, InpBandsPeriod, InpBandsDev, InpBandsShift, PRICE_CLOSE, MODE_UPPER, 1);
   double lowerBand = iBands(_Symbol, _Period, InpBandsPeriod, InpBandsDev, InpBandsShift, PRICE_CLOSE, MODE_LOWER, 1);
   double prevClose = Close[1];

   // 3. Check for Buy Signal (Breakout Above)
   if(prevClose > upperBand)
   {
      // Close existing Sell positions first
      ClosePositions(OP_SELL);
      
      // Open Buy if no Buy exists
      if(CountOrders(OP_BUY) == 0)
      {
         OpenTrade(OP_BUY);
      }
   }

   // 4. Check for Sell Signal (Breakout Below)
   if(prevClose < lowerBand)
   {
      // Close existing Buy positions first
      ClosePositions(OP_BUY);
      
      // Open Sell if no Sell exists
      if(CountOrders(OP_SELL) == 0)
      {
         OpenTrade(OP_SELL);
      }
   }
}

//+------------------------------------------------------------------+
//| Helper: Open a Trade                                             |
//+------------------------------------------------------------------+
void OpenTrade(int type)
{
   double price, sl, tp;
   color cmdColor;

   if(type == OP_BUY)
   {
      price = Ask;
      // Calculate SL/TP for Buy
      sl = (InpStopLoss > 0) ? price - InpStopLoss * _Point : 0;
      tp = (InpTakeProfit > 0) ? price + InpTakeProfit * _Point : 0;
      cmdColor = clrBlue;
   }
   else
   {
      price = Bid;
      // Calculate SL/TP for Sell
      sl = (InpStopLoss > 0) ? price + InpStopLoss * _Point : 0;
      tp = (InpTakeProfit > 0) ? price - InpTakeProfit * _Point : 0;
      cmdColor = clrRed;
   }

   // Normalize prices to avoid Error 130 (Invalid Stops)
   price = NormalizeDouble(price, _Digits);
   sl    = NormalizeDouble(sl, _Digits);
   tp    = NormalizeDouble(tp, _Digits);

   int ticket = OrderSend(_Symbol, type, InpLots, price, InpSlippage, sl, tp, "BB Breakout", InpMagicNum, 0, cmdColor);

   if(ticket < 0)
   {
      Print("OrderSend failed with error #", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| Helper: Close specific position types                            |
//+------------------------------------------------------------------+
void ClosePositions(int type)
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagicNum)
         {
            if(OrderType() == type)
            {
               bool result = false;
               if(type == OP_BUY)
                  result = OrderClose(OrderTicket(), OrderLots(), Bid, InpSlippage, clrNONE);
               else if(type == OP_SELL)
                  result = OrderClose(OrderTicket(), OrderLots(), Ask, InpSlippage, clrNONE);
               
               if(!result) Print("Failed to close order: ", GetLastError());
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Helper: Count specific order types                               |
//+------------------------------------------------------------------+
int CountOrders(int type)
{
   int count = 0;
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagicNum)
         {
            if(OrderType() == type) count++;
         }
      }
   }
   return count;
}
//+------------------------------------------------------------------+

How to Install and Use

  1. Open MetaTrader 4.
  2. Press F4 to open the MetaEditor.
  3. Click New -> Expert Advisor (template) -> Name it "BollingerBreakout".
  4. Delete everything in the new file and paste the code provided above.
  5. Press F7 to Compile. Ensure there are no errors in the "Errors" tab at the bottom.
  6. Return to the main MT4 window.
  7. Find "BollingerBreakout" in the Navigator panel under Expert Advisors.
  8. Drag it onto a chart (e.g., EURUSD H1).
  9. Ensure AutoTrading is enabled in the top toolbar.

Key Features of this Code

  • New Bar Check: The logic if(glbLastBarTime == Time[0]) return; ensures the EA only trades once per candle. This is crucial for breakout strategies to confirm the candle actually closed outside the band.
  • Risk Management: It includes inputs for Stop Loss and Take Profit. If you set them to 0, the EA will rely solely on the opposite signal to close the trade.
  • Magic Number: Uses a unique ID (InpMagicNum) so it doesn't accidentally close trades opened by you manually or by other EAs.
  • Normalization: Uses NormalizeDouble on prices to prevent "Invalid Price" errors common with 5-digit brokers.

Q&A: Bollinger Bands Breakout Strategy

Q: Why does the EA wait for the candle to close before trading?
A: Waiting for the close confirms the breakout. If the price spikes above the band but retraces before the candle closes, it leaves a "wick." Trading immediately on the touch (tick-by-tick) often results in false breakouts and buying at the very top before a reversal.

Q: Can I use this on any timeframe?
A: Yes. The EA uses _Period, which automatically detects the timeframe of the chart you attach it to. Bollinger Band breakouts are typically more reliable on H1, H4, or Daily charts.

Q: What happens if the price stays outside the bands for multiple candles?
A: The code checks if(CountOrders(OP_BUY) == 0). If a Buy trade is already open and the price stays above the upper band, it will simply hold the existing position and will not open multiple trades for the same trend.

Q: How do I optimize the parameters?
A: In the MT4 Strategy Tester, you can select "Optimization". You can then set ranges for InpBandsPeriod (e.g., 10 to 50) and InpBandsDev (e.g., 1.5 to 3.0) to find the best combination for a specific currency pair.