Bollinger Bands MT5 Indicator: MQL5 Custom Code, Squeeze & EA
Use the built-in iBands indicator from an MQL5 Expert Advisor and inspect an educational squeeze-breakout rule. This page supplies EA source, not a custom plotted indicator: save it under MQL5/Experts. The example demonstrates buffers, completed-bar conditions and exit requests, without a verified profitability claim.
Implementation details & mechanics
Core implementation pattern
The example compares normalized bandwidth on bars 2 and 1. A possible entry requires the older width below the chosen threshold, a wider latest completed bar, and a close beyond the corresponding outer band. The 2% squeeze setting is an illustrative rule rather than evidence of a high-probability move. Fixed lots and the 30-point spread guard must be evaluated for the instrument.
Error handling & safety rules
Matching positions are submitted for closure when the last completed close is on the opposite side of the middle band; this is a level check, not necessarily a newly detected cross. Initial SL is measured from the requested ask or bid, not from a candle high or low. Optional trailing uses the current ATR distance, not stored initial risk. Gap risk and rejected requests remain possible, and the spread guard can skip management on a bar.
MQL5 Code Implementation
//+------------------------------------------------------------------+
//| BollingerBandsBreakoutEA.mq5 |
//| Bollinger Bands Squeeze Breakout EA for MetaTrader 5 |
//| Educational example; no verified performance results. |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property version "1.00"
#property strict
#include <Trade/Trade.mqh>
//--- Input parameters
input int BB_Period = 20; // Bollinger Bands period
input double BB_Deviation = 2.0; // Standard deviations
input int ATR_Period = 14; // ATR period for stop-loss
input double ATR_SLMultiple = 1.5; // ATR multiplier for stop-loss
input double SqueezePct = 0.02; // Bandwidth squeeze threshold (2%)
input double LotSize = 0.1; // Fixed lot size
input int MagicNumber = 20240101; // EA magic number
input bool UseTrailingStop = true; // Enable trailing stop after 1R
input int MaxTrades = 1; // Max concurrent trades per symbol
//--- Indicator handles
int hBB;
int hATR;
//--- Trade object
CTrade trade;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Create Bollinger Bands handle
hBB = iBands(_Symbol, _Period, BB_Period, 0, BB_Deviation, PRICE_CLOSE);
if(hBB == INVALID_HANDLE)
{
Print("ERROR: Failed to create Bollinger Bands handle. Code: ", GetLastError());
return INIT_FAILED;
}
//--- Create ATR handle
hATR = iATR(_Symbol, _Period, ATR_Period);
if(hATR == INVALID_HANDLE)
{
Print("ERROR: Failed to create ATR handle. Code: ", GetLastError());
return INIT_FAILED;
}
//--- Set magic number on trade object
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(10);
Print("BollingerBandsBreakoutEA initialized on ", _Symbol, " ", EnumToString(_Period));
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(hBB != INVALID_HANDLE) IndicatorRelease(hBB);
if(hATR != INVALID_HANDLE) IndicatorRelease(hATR);
Print("BollingerBandsBreakoutEA deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Count open positions for this EA on current symbol |
//+------------------------------------------------------------------+
int CountOpenPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
count++;
}
return count;
}
//+------------------------------------------------------------------+
//| Apply trailing stop to open positions |
//+------------------------------------------------------------------+
void ManageTrailingStop(double atr)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double trailDist = ATR_SLMultiple * atr;
if(posType == POSITION_TYPE_BUY)
{
double profitR = bid - openPrice;
if(profitR >= trailDist) // 1R profit reached
{
double newSL = NormalizeDouble(bid - trailDist, _Digits);
if(newSL > currentSL)
trade.PositionModify(ticket, newSL, currentTP);
}
}
else if(posType == POSITION_TYPE_SELL)
{
double profitR = openPrice - ask;
if(profitR >= trailDist)
{
double newSL = NormalizeDouble(ask + trailDist, _Digits);
if(newSL < currentSL || currentSL == 0)
trade.PositionModify(ticket, newSL, currentTP);
}
}
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Only act on new bar
static datetime lastBar = 0;
datetime currentBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
if(currentBar == lastBar) return;
lastBar = currentBar;
//--- Copy 3 bars: iBands middle=0, upper=1, lower=2
double upper[], middle[], lower[], atrBuf[];
ArraySetAsSeries(upper, true);
ArraySetAsSeries(middle, true);
ArraySetAsSeries(lower, true);
ArraySetAsSeries(atrBuf, true);
if(CopyBuffer(hBB, 1, 0, 3, upper) < 3) return;
if(CopyBuffer(hBB, 0, 0, 3, middle) < 3) return;
if(CopyBuffer(hBB, 2, 0, 3, lower) < 3) return;
if(CopyBuffer(hATR, 0, 0, 3, atrBuf) < 3) return;
double atr = atrBuf[1]; // previous completed bar ATR
if(atr<=0 || middle[1]==0 || middle[2]==0) return;
//--- Squeeze detection: bandwidth = (upper - lower) / middle
double bwPrev = (upper[2] - lower[2]) / middle[2];
double bwCurrent = (upper[1] - lower[1]) / middle[1];
bool inSqueeze = bwPrev < SqueezePct;
bool expanding = bwCurrent > bwPrev;
//--- Price of last closed bar
double closePrice[1];
if(CopyClose(_Symbol, _Period, 1, 1, closePrice) < 1) return;
double close1 = closePrice[0];
//--- Current spread guard
double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point;
if(spread > 3 * _Point * 10) return; // skip wide spread
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
//--- Manage trailing stops on existing positions
if(UseTrailingStop) ManageTrailingStop(atr);
//--- Exit: close long if price crosses back below middle band
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(posType == POSITION_TYPE_BUY && close1 < middle[1])
trade.PositionClose(ticket);
else if(posType == POSITION_TYPE_SELL && close1 > middle[1])
trade.PositionClose(ticket);
}
//--- Entry: only if no open trades and squeeze + expansion confirmed
if(CountOpenPositions() >= MaxTrades) return;
if(!inSqueeze || !expanding) return;
//--- Long breakout: close above upper band
if(close1 > upper[1])
{
double sl = NormalizeDouble(ask - ATR_SLMultiple * atr, _Digits);
double tp = 0; // exit via middle band cross
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = LotSize;
request.type = ORDER_TYPE_BUY;
request.price = ask;
request.sl = sl;
request.tp = tp;
request.magic = MagicNumber;
request.comment = "BB_Squeeze_Long";
request.deviation = 10;
if(!OrderSend(request, result) ||
(result.retcode!=TRADE_RETCODE_DONE && result.retcode!=TRADE_RETCODE_DONE_PARTIAL))
Print("BUY OrderSend failed: ", result.retcode, " - ", result.comment);
else
Print("BUY opened at ", ask, " SL=", sl);
}
//--- Short breakout: close below lower band
else if(close1 < lower[1])
{
double sl = NormalizeDouble(bid + ATR_SLMultiple * atr, _Digits);
double tp = 0;
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = LotSize;
request.type = ORDER_TYPE_SELL;
request.price = bid;
request.sl = sl;
request.tp = tp;
request.magic = MagicNumber;
request.comment = "BB_Squeeze_Short";
request.deviation = 10;
if(!OrderSend(request, result) ||
(result.retcode!=TRADE_RETCODE_DONE && result.retcode!=TRADE_RETCODE_DONE_PARTIAL))
Print("SELL OrderSend failed: ", result.retcode, " - ", result.comment);
else
Print("SELL opened at ", bid, " SL=", sl);
}
}
//+------------------------------------------------------------------+Copy this code into MetaEditor, save it in your MQL5 folder (Experts, Scripts, or Include), and compile with F7.
Generate custom MQL5 code with Pineify AI
Describe the trade execution, indicator calculations, risk management, or data processing logic you need. Pineify generates editable MQL5 source code. Review its logic, request a syntax check, and compile and test the final file in MetaEditor.
Pine Script vs MQL5: Same Strategy, Different Platforms
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Series evaluated on chart updates | Event handlers with explicit buffers |
| Deployment | Runs in TradingView with alerts | Runs in the MT5 terminal or on a VPS |
| Broker access | Strategy orders are simulated; live automation needs a separate integration | Direct broker connectivity |
| Backtesting | Strategy Tester for strategy scripts | Strategy Tester for Expert Advisors |
| Code complexity | Simpler, functional syntax | C++-like, more powerful |
The calculation can be implemented on either platform, but the runtime model and buffer APIs differ. Read the Pine Script reference guide before porting logic between them.
Frequently Asked Questions
Related MQL5 pages
Technical references
Risk and testing note
These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.