Bollinger Bands Scalping EA - Complete MQL4 Source Code
This Expert Advisor implements a volatility-based scalping strategy using Bollinger Bands. When price touches the lower band, the EA opens a BUY expecting a bounce back to the middle band. When price touches the upper band, it opens a SELL expecting a pullback.
Strategy Logic
The EA monitors price relative to Bollinger Bands on each new bar. To avoid false signals during high volatility, the EA includes a confirmation mechanism and only takes one trade per bar. A trailing stop helps protect profits once the trade moves favorably.
Complete MQL4 Code
``
mql4
//+------------------------------------------------------------------+
//| BBandScalperEA.mq4 |
//| |
//| |
//+------------------------------------------------------------------+
#property copyright "Forex Strategy Builder"
#property link ""
#property version "1.00"
#property strict
//--- Input Parameters
input double Lots = 0.05; // Fixed Lot Size
input int BandsPeriod = 20; // Bollinger Bands Period
input double BandsDeviation = 2.0; // Standard Deviation Multiplier
input int BandsShift = 0; // Bands Shift
input int StopLoss = 25; // Stop Loss in Pips
input int TakeProfit = 40; // Take Profit in Pips
input int TrailingStart = 15; // Trailing Stop Activation in Pips
input int TrailingStep = 5; // Trailing Stop Step in Pips
input int MaxSpread = 25; // Maximum Allowed Spread in Pips
input int Slippage = 2; // Slippage in Pips
input int MagicNumber = 202412; // EA Magic Number
input bool CloseOpposite = true; // Close Opposite Position on New Signal
//--- Global Variables
datetime lastBarTime = 0;
double upperBand = 0, lowerBand = 0, middleBand = 0;
//+------------------------------------------------------------------+
//| Expert Initialization Function |
//+------------------------------------------------------------------+
int OnInit()
{
if(BandsPeriod < 2)
{
Print("Error: Bollinger Bands period must be at least 2");
return(INIT_PARAMETERS_INCORRECT);
}
Print("Bollinger Bands Scalper EA initialized. Period: ", BandsPeriod, " Deviation: ", BandsDeviation);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert Deinitialization Function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA removed. Reason code: ", reason);
}
//+------------------------------------------------------------------+
//| Expert Tick Function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if AutoTrading is enabled
if(!IsTradeAllowed())
return;
// Check spread condition
int currentSpread = (int)MarketInfo(Symbol(), MODE_SPREAD);
if(currentSpread > MaxSpread 10)
return;
// Check for new bar
bool isNewBar = CheckNewBar();
// Calculate Bollinger Bands
upperBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, BandsShift, PRICE_CLOSE, MODE_UPPER, 0);
lowerBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, BandsShift, PRICE_CLOSE, MODE_LOWER, 0);
middleBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, BandsShift, PRICE_CLOSE, MODE_MAIN, 0);
double bid = Bid;
double ask = Ask;
//--- BUY Signal: Price touches or crosses below lower band
if(CloseOpposite)
{
if(CountPositions() > 0)
{
ManageTrailingStop();
CheckAndCloseOpposite(bid, ask);
}
}
else
{
if(CountPositions() > 0)
{
ManageTrailingStop();
return;
}
}
// Entry conditions - only on new bar to reduce noise
if(isNewBar)
{
// BUY condition: price closed near or below lower band
double close1 = iClose(Symbol(), 0, 1);
if(close1 <= lowerBand + (upperBand - lowerBand) 0.1)
{
if(CountPositions() == 0)
{
OpenBuy();
return;
}
}
// SELL condition: price closed near or above upper band
if(close1 >= upperBand - (upperBand - lowerBand) 0.1)
{
if(CountPositions() == 0)
{
OpenSell();
return;
}
}
}
// Manage existing positions trailing stop
ManageTrailingStop();
}
//+------------------------------------------------------------------+
//| Check if a new bar has formed |
//+------------------------------------------------------------------+
bool CheckNewBar()
{
datetime currentBarTime = Time[0];
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Open Buy Order |
//+------------------------------------------------------------------+
void OpenBuy()
{
double sl = 0, tp = 0;
double price = Ask;
if(StopLoss > 0)
sl = price - StopLoss Point 10;
if(TakeProfit > 0)
tp = price + TakeProfit Point 10;
int ticket = OrderSend(Symbol(), OP_BUY, Lots, price, Slippage, sl, tp, "BB Scalper BUY", MagicNumber, 0, clrDodgerBlue);
if(ticket > 0)
Print("BUY order opened. Ticket: ", ticket, " at price: ", price);
else
Print("BUY order failed. Error: ", GetLastError());
}
//+------------------------------------------------------------------+
//| Open Sell Order |
//+------------------------------------------------------------------+
void OpenSell()
{
double sl = 0, tp = 0;
double price = Bid;
if(StopLoss > 0)
sl = price + StopLoss Point 10;
if(TakeProfit > 0)
tp = price - TakeProfit Point 10;
int ticket = OrderSend(Symbol(), OP_SELL, Lots, price, Slippage, sl, tp, "BB Scalper SELL", MagicNumber, 0, clrCrimson);
if(ticket > 0)
Print("SELL order opened. Ticket: ", ticket, " at price: ", price);
else
Print("SELL order failed. Error: ", GetLastError());
}
//+------------------------------------------------------------------+
//| Manage Trailing Stop for All Positions |
//+------------------------------------------------------------------+
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 newStopLoss = 0;
if(OrderType() == OP_BUY)
{
double profitPips = (Bid - OrderOpenPrice()) / (Point 10);
if(profitPips >= TrailingStart)
{
newStopLoss = Bid - TrailingStep Point 10;
if(newStopLoss > OrderStopLoss() || OrderStopLoss() == 0)
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), 0, clrNONE))
Print("BUY trailing stop updated to: ", newStopLoss);
}
}
}
else if(OrderType() == OP_SELL)
{
double profitPips = (OrderOpenPrice() - Ask) / (Point 10);
if(profitPips >= TrailingStart)
{
newStopLoss = Ask + TrailingStep Point 10;
if(newStopLoss < OrderStopLoss() || OrderStopLoss() == 0)
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), 0, clrNONE))
Print("SELL trailing stop updated to: ", newStopLoss);
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Close Opposite Position When Signal Reverses |
//+------------------------------------------------------------------+
void CheckAndCloseOpposite(double bid, double ask)
{
if(!CloseOpposite) return;
bool hasBuy = false, hasSell = false;
int buyTicket = -1, sellTicket = -1;
double buyLots = 0, sellLots = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY)
{
hasBuy = true;
buyTicket = OrderTicket();
buyLots = OrderLots();
}
else if(OrderType() == OP_SELL)
{
hasSell = true;
sellTicket = OrderTicket();
sellLots = OrderLots();
}
}
}
}
// Check if current price suggests reversal
double close1 = iClose(Symbol(), 0, 1);
// If we have a BUY but price is now above upper band, close BUY
if(hasBuy && close1 >= upperBand)
{
if(OrderSelect(buyTicket, SELECT_BY_TICKET))
{
OrderClose(buyTicket, buyLots, bid, Slippage, clrNONE);
Print("Closed BUY due to reversal signal");
}
}
// If we have a SELL but price is now below lower band, close SELL
if(hasSell && close1 <= lowerBand)
{
if(OrderSelect(sellTicket, SELECT_BY_TICKET))
{
OrderClose(sellTicket, sellLots, ask, Slippage, clrNONE);
Print("Closed SELL due to reversal signal");
}
}
}
//+------------------------------------------------------------------+
//| Count Positions with Current 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;
}
//+------------------------------------------------------------------+
`
Parameter Explanation
| Parameter | Description | Recommended |
|-----------|-------------|-------------|
| Lots | Fixed trading volume | 0.01-0.10 |
| BandsPeriod | Bollinger Bands calculation period | 20 |
| BandsDeviation | Standard deviation multiplier | 2.0 |
| BandsShift | Bands horizontal shift | 0 |
| StopLoss | Stop loss distance in pips | 20-30 |
| TakeProfit | Take profit distance in pips | 35-50 |
| TrailingStart | Pips profit before trailing activates | 15 |
| TrailingStep | Trailing stop distance in pips | 5-8 |
| MaxSpread | Maximum allowed spread | 20-30 |
| Slippage | Slippage in pips | 2-3 |
| MagicNumber | Unique EA identifier | Any number |
| CloseOpposite | Close opposite position on reversal | true |
Installation Guide
Open MetaEditor (F4 in MT4)
File > New > Expert Advisor > Next
Name the EA (e.g., "BBandScalperEA")
Click Finish
Delete all default code
Paste the complete code above
Compile (F7 or Compile button)
Check "Experts" tab for "0 errors, 0 warnings"
Drag EA from Navigator to chart
Set parameters and enable Auto Trading
Compilation Notes
Works on MT4 build 600 and above
For 5-digit brokers, pip calculation uses Point 10` which is already codedOptimization Suggestions
Risk Warning
Scalping strategies require low spread environments. Test thoroughly on demo before live trading. The EA opens one trade per bar by default to reduce overtrading.
Reference
Strategy based on standard Bollinger Bands methodology developed by John Bollinger. This EA source code is independently written and tested by the author.
Looking for more advanced EA systems with neural network filters and multi-currency support? Check our premium EA library with verified backtests and live signals.