Summary: BTC Range Guardian EA is an MQL4 expert advisor for Bitcoin (BTCUSD) using Bollinger Bands mean reversion with RSI divergence confirmation and adaptive lot sizing for stable operation on H4.
BTC Range Guardian EA is purpose-built for Bitcoin's unique characteristics: high volatility, frequent mean reversion, and weekend gaps. The strategy identifies overextended price moves using Bollinger Bands (20,2) and confirms exhaustion with RSI divergence. Rather than fighting the trend, it waits for price to return inside the bands before entering. Dynamic lot sizing based on ATR volatility ensures smaller positions during turbulent periods. The EA includes a maximum daily trade limit, equity-based drawdown protection, and a weekend position closure to avoid Monday gaps.
Recommended Timeframe: H4
Trading Logic:
1. Mean Reversion Setup: Price closes outside Bollinger Bands (20,2) after at least 3 consecutive closes outside.
2. Divergence Confirmation: RSI(14) shows hidden or regular divergence against price (price makes lower low but RSI higher low for long).
3. Re-entry Trigger: Wait for price to close back inside the bands.
4. Risk Management: ATR-based dynamic stop loss (2.5x ATR), take profit at opposite band. Maximum 1 trade, daily loss limit 6%, maximum spread 200 points.
```mql4
//+------------------------------------------------------------------+
//| BTCRangeGuardianEA.mq4 |
//+------------------------------------------------------------------+
#property copyright ""
#property link ""
#property version "1.00"
#property strict
//--- input parameters with comments
input double BaseLotSize = 0.01; // Base lot size (0.01 BTC)
input int BBPPeriod = 20; // Bollinger Bands period
input double BBDeviation = 2.0; // Bollinger Bands deviation
input int RSIPeriod = 14; // RSI period for divergence detection
input int ConsecutiveBars = 3; // Required consecutive closes outside BB before considering
input double ATRStopMultiplier = 2.5; // Stop loss as multiple of ATR
input double ATRTakeMultiplier = 1.5; // Take profit as multiple of ATR (to opposite band approx)
input int MaxDailyTrades = 2; // Maximum number of trades per day
input double MaxDailyLoss = 6.0; // Maximum daily loss percentage
input int MagicNumber = 202417; // Unique EA identifier
input int MaxSpread = 200; // Maximum allowed spread in points (Bitcoin)
input bool CloseOnSunday = true; // Close all positions before Sunday 22:00 GMT
//--- global variables
double dailyStartBalance = 0;
int dailyTradeCount = 0;
datetime lastTradeDate = 0;
datetime lastBarTime = 0;
bool sundayCloseExecuted = false;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
dailyStartBalance = AccountBalance();
dailyTradeCount = 0;
lastTradeDate = 0;
lastBarTime = 0;
sundayCloseExecuted = false;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
}
//+------------------------------------------------------------------+
//| Check for RSI divergence (bullish hidden/regular) |
//+------------------------------------------------------------------+
bool BullishDivergence()
{
double priceNow = iClose(Symbol(), PERIOD_H4, 1);
double pricePrev = iClose(Symbol(), PERIOD_H4, 2);
double rsiNow = iRSI(Symbol(), PERIOD_H4, RSIPeriod, PRICE_CLOSE, 1);
double rsiPrev = iRSI(Symbol(), PERIOD_H4, RSIPeriod, PRICE_CLOSE, 2);
// Regular bullish divergence: price makes lower low, RSI makes higher low
if(priceNow < pricePrev && rsiNow > rsiPrev)
return true;
// Hidden bullish divergence: price makes higher low, RSI makes lower low (continuation)
if(priceNow > pricePrev && rsiNow < rsiPrev && rsiNow < 50)
return true;
return false;
}
//+------------------------------------------------------------------+
//| Check for RSI divergence (bearish hidden/regular) |
//+------------------------------------------------------------------+
bool BearishDivergence()
{
double priceNow = iClose(Symbol(), PERIOD_H4, 1);
double pricePrev = iClose(Symbol(), PERIOD_H4, 2);
double rsiNow = iRSI(Symbol(), PERIOD_H4, RSIPeriod, PRICE_CLOSE, 1);
double rsiPrev = iRSI(Symbol(), PERIOD_H4, RSIPeriod, PRICE_CLOSE, 2);
// Regular bearish divergence: price makes higher high, RSI makes lower high
if(priceNow > pricePrev && rsiNow < rsiPrev)
return true;
// Hidden bearish divergence: price makes lower high, RSI makes higher high (continuation)
if(priceNow < pricePrev && rsiNow > rsiPrev && rsiNow > 50)
return true;
return false;
}
//+------------------------------------------------------------------+
//| Check if price has been outside BB for consecutive bars |
//+------------------------------------------------------------------+
bool ConsecutiveOutsideBB(bool above)
{
int count = 0;
for(int i = 1; i <= ConsecutiveBars; i++)
{
double close = iClose(Symbol(), PERIOD_H4, i);
double upper = iBands(Symbol(), PERIOD_H4, BBPPeriod, BBDeviation, 0, PRICE_CLOSE, MODE_UPPER, i);
double lower = iBands(Symbol(), PERIOD_H4, BBPPeriod, BBDeviation, 0, PRICE_CLOSE, MODE_LOWER, i);
if(above && close > upper)
count++;
else if(!above && close < lower)
count++;
else
break;
}
return (count >= ConsecutiveBars);
}
//+------------------------------------------------------------------+
//| Check if price has returned inside Bollinger Bands |
//+------------------------------------------------------------------+
bool PriceInsideBB()
{
double close = iClose(Symbol(), PERIOD_H4, 1);
double upper = iBands(Symbol(), PERIOD_H4, BBPPeriod, BBDeviation, 0, PRICE_CLOSE, MODE_UPPER, 1);
double lower = iBands(Symbol(), PERIOD_H4, BBPPeriod, BBDeviation, 0, PRICE_CLOSE, MODE_LOWER, 1);
return (close <= upper && close >= lower);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Daily equity protection and trade count reset
datetime currentTime = TimeCurrent();
if(TimeDayOfYear(currentTime) != TimeDayOfYear(lastTradeDate))
{
dailyStartBalance = AccountBalance();
dailyTradeCount = 0;
lastTradeDate = currentTime;
}
double currentEquity = AccountEquity();
double lossPercent = (dailyStartBalance - currentEquity) / dailyStartBalance * 100;
if(lossPercent >= MaxDailyLoss)
{
Comment("Daily loss limit reached. No new trades.");
return;
}
// Sunday close before Monday gap
if(CloseOnSunday && !sundayCloseExecuted)
{
if(TimeDayOfWeek(currentTime) == 0 && TimeHour(currentTime) >= 22)
{
CloseAllOrders();
sundayCloseExecuted = true;
return;
}
if(TimeDayOfWeek(currentTime) != 0)
sundayCloseExecuted = false;
}
// Spread filter
if(MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread)
{
Comment("BTC spread too high: ", MarketInfo(Symbol(), MODE_SPREAD));
return;
}
// Daily trade limit
if(dailyTradeCount >= MaxDailyTrades)
{
Comment("Daily trade limit reached.");
return;
}
// New bar logic (H4)
if(Time[0] == lastBarTime)
return;
lastBarTime = Time[0];
// Check existing position
if(CountPositions() > 0)
return;
// Get ATR for dynamic sizing and stops
double atr = iATR(Symbol(), PERIOD_H4, 14, 1);
if(atr <= 0) atr = 300 * Point;
// Dynamic lot size: smaller when ATR is high (volatility)
double avgATR = iATR(Symbol(), PERIOD_H4, 50, 1);
if(avgATR <= 0) avgATR = atr;
double lotSize = BaseLotSize;
if(atr > avgATR)
lotSize = BaseLotSize * (avgATR / atr);
if(lotSize < 0.01) lotSize = 0.01;
if(lotSize > 0.5) lotSize = 0.5;
int cmd = -1;
double sl = 0, tp = 0;
double ask = Ask;
double bid = Bid;
// Check Bollinger Bands
double upperBand = iBands(Symbol(), PERIOD_H4, BBPPeriod, BBDeviation, 0, PRICE_CLOSE, MODE_UPPER, 1);
double lowerBand = iBands(Symbol(), PERIOD_H4, BBPPeriod, BBDeviation, 0, PRICE_CLOSE, MODE_LOWER, 1);
double close1 = iClose(Symbol(), PERIOD_H4, 1);
double close0 = iClose(Symbol(), PERIOD_H4, 0);
// Long setup: price was above upper band for consecutive bars, shows bullish divergence, now back inside
if(ConsecutiveOutsideBB(true) && PriceInsideBB() && BullishDivergence())
{
cmd = OP_BUY;
sl = bid - (atr * ATRStopMultiplier);
tp = bid + (atr * ATRTakeMultiplier);
}
// Short setup: price was below lower band for consecutive bars, shows bearish divergence, now back inside
else if(ConsecutiveOutsideBB(false) && PriceInsideBB() && BearishDivergence())
{
cmd = OP_SELL;
sl = ask + (atr * ATRStopMultiplier);
tp = ask - (atr * ATRTakeMultiplier);
}
if(cmd != -1)
{
int ticket = OrderSend(Symbol(), cmd, lotSize, (cmd==OP_BUY?ask:bid), 5, sl, tp, "BTC Range Guardian", MagicNumber, 0, clrNONE);
if(ticket > 0)
{
dailyTradeCount++;
Print("Trade opened. Daily count: ", dailyTradeCount);
}
else
Print("OrderSend failed: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Count open positions with this MagicNumber |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| Close all orders for this symbol and magic |
//+------------------------------------------------------------------+
void CloseAllOrders()
{
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY)
OrderClose(OrderTicket(), OrderLots(), Bid, 5, clrNONE);
else if(OrderType() == OP_SELL)
OrderClose(OrderTicket(), OrderLots(), Ask, 5, clrNONE);
}
}
}
}
//+------------------------------------------------------------------+
```
Reference: Original MQL4 code for educational purposes.
Disclaimer: Bitcoin trading carries extreme risk due to high volatility and potential exchange issues. This EA is provided as-is without any guarantee of profitability or stability. Always test on a demo account for a minimum of 3 months before live deployment. Past performance does not guarantee future results.