Summary: BTC Volatility Navigator EA is an MQL4 expert advisor for Bitcoin (BTCUSD). It uses volatility contraction breakout with RSI divergence and dynamic position sizing. Suitable for H4 timeframe.
BTC Volatility Navigator EA is built specifically for Bitcoin's unique market behavior: high volatility, frequent gaps, and strong trend persistence. The EA detects periods of volatility contraction (Bollinger Bands squeeze) and enters on breakout with RSI momentum confirmation. A dynamic position sizing mechanism reduces exposure during extreme volatility. The system includes a hard stop-loss based on ATR, trailing stop for profit protection, and a weekly drawdown limiter to preserve capital during adverse conditions.
Recommended Timeframe: H4
Trading Logic:
1. Volatility Contraction: Bollinger Bands (20,2) bandwidth falls below 0.3 * average bandwidth over 50 bars.
2. Breakout Entry: Price closes above upper band for long, or below lower band for short, with RSI(14) > 55 for long or < 45 for short.
3. Dynamic Lot Sizing: Base lot = 0.01 per $2000 balance. Reduced by up to 50% when ATR is above 1.5x average.
4. Risk Management: Stop loss at 2.5x ATR, take profit at 5x ATR. Trailing stop activates after 2.5x ATR profit. Weekly loss limit 10%.
```mql4
//+------------------------------------------------------------------+
//| BTCVolatilityNavigator.mq4 |
//+------------------------------------------------------------------+
#property copyright ""
#property link ""
#property version "1.00"
#property strict
//--- input parameters with comments
input double RiskPerEquity = 1.0; // Risk per trade as % of equity (1%)
input int BandsPeriod = 20; // Bollinger Bands period
input double BandsDeviation = 2.0; // Bollinger Bands deviation
input double BandwidthThreshold = 0.3; // Bandwidth contraction threshold (0.3 = 30% of avg)
input int RSIPeriod = 14; // RSI period for momentum confirmation
input int ATRPeriod = 14; // ATR period for stop loss
input double ATRStopMultiplier = 2.5; // Stop loss as multiple of ATR
input double ATRTakeMultiplier = 5.0; // Take profit as multiple of ATR
input double TrailingStart = 2.5; // Trailing start in ATR multiples
input double TrailingStep = 1.0; // Trailing step in ATR multiples
input int MagicNumber = 202417; // Unique EA identifier
input int MaxSpread = 200; // Maximum allowed spread in points (BTC has wider spread)
input double WeeklyLossLimit = 10.0; // Weekly loss limit as percentage of balance
input bool UseWeekendClose = true; // Close all trades before Sunday 22:00 GMT
//--- global variables
double weeklyStartBalance = 0;
datetime lastBarTime = 0;
bool weekendCloseExecuted = false;
double avgBandwidth = 0;
double avgATR = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
weeklyStartBalance = AccountBalance();
lastBarTime = 0;
weekendCloseExecuted = false;
avgATR = iATR(Symbol(), PERIOD_H4, ATRPeriod, 1);
if(avgATR <= 0) avgATR = 500 * Point;
avgBandwidth = CalculateBandwidth();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
}
//+------------------------------------------------------------------+
//| Calculate Bollinger Bands bandwidth |
//+------------------------------------------------------------------+
double CalculateBandwidth()
{
double upper = iBands(Symbol(), PERIOD_H4, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_UPPER, 1);
double lower = iBands(Symbol(), PERIOD_H4, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_LOWER, 1);
double middle = iBands(Symbol(), PERIOD_H4, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_MAIN, 1);
if(middle <= 0) return 0;
return (upper - lower) / middle;
}
//+------------------------------------------------------------------+
//| Calculate average bandwidth over last 50 bars |
//+------------------------------------------------------------------+
double GetAvgBandwidth()
{
double sum = 0;
int count = 0;
for(int i = 1; i <= 50; i++)
{
double upper = iBands(Symbol(), PERIOD_H4, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_UPPER, i);
double lower = iBands(Symbol(), PERIOD_H4, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_LOWER, i);
double middle = iBands(Symbol(), PERIOD_H4, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_MAIN, i);
if(middle > 0)
{
sum += (upper - lower) / middle;
count++;
}
}
if(count == 0) return 0.5;
return sum / count;
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Weekly equity protection
double currentEquity = AccountEquity();
double lossPercent = (weeklyStartBalance - currentEquity) / weeklyStartBalance * 100;
if(lossPercent >= WeeklyLossLimit)
{
Comment("Weekly loss limit reached. No new trades.");
return;
}
// Weekend close before Sunday night (broker gap risk)
if(UseWeekendClose && !weekendCloseExecuted)
{
datetime currentTime = TimeCurrent();
if(TimeDayOfWeek(currentTime) == 0 && TimeHour(currentTime) >= 22)
{
CloseAllOrders();
weekendCloseExecuted = true;
return;
}
if(TimeDayOfWeek(currentTime) != 0)
weekendCloseExecuted = false;
}
// Spread filter for Bitcoin
if(MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread)
{
Comment("Spread too high for BTC: ", MarketInfo(Symbol(), MODE_SPREAD));
return;
}
// New bar logic (H4)
if(Time[0] == lastBarTime)
return;
lastBarTime = Time[0];
// Update average ATR
double atr = iATR(Symbol(), PERIOD_H4, ATRPeriod, 1);
if(atr > 0) avgATR = (avgATR * 0.95) + (atr * 0.05);
// Check existing position
if(CountPositions() > 0)
{
ManageTrailingStop();
return;
}
// Volatility contraction detection
double currentBW = CalculateBandwidth();
double avgBW = GetAvgBandwidth();
if(avgBW > 0 && currentBW > avgBW * BandwidthThreshold)
{
Comment("No volatility contraction. Bandwidth: ", currentBW);
return;
}
// Bollinger Bands values
double upperBand = iBands(Symbol(), PERIOD_H4, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_UPPER, 1);
double lowerBand = iBands(Symbol(), PERIOD_H4, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_LOWER, 1);
double close1 = iClose(Symbol(), PERIOD_H4, 1);
double rsi = iRSI(Symbol(), PERIOD_H4, RSIPeriod, PRICE_CLOSE, 1);
// Dynamic lot sizing based on balance and volatility
double baseLot = NormalizeDouble(AccountBalance() * RiskPerEquity / 10000.0, 2);
if(baseLot < 0.01) baseLot = 0.01;
if(baseLot > 2.0) baseLot = 2.0;
// Reduce lot size during high volatility
double volatilityFactor = 1.0;
if(atr > avgATR * 1.5 && avgATR > 0)
volatilityFactor = 0.5;
double finalLot = NormalizeDouble(baseLot * volatilityFactor, 2);
if(finalLot < 0.01) finalLot = 0.01;
int cmd = -1;
double sl = 0, tp = 0;
double ask = Ask;
double bid = Bid;
// Long breakout: price closes above upper band, RSI > 55 confirming momentum
if(close1 > upperBand && rsi > 55)
{
cmd = OP_BUY;
sl = bid - (atr * ATRStopMultiplier);
tp = bid + (atr * ATRTakeMultiplier);
}
// Short breakout: price closes below lower band, RSI < 45 confirming momentum
else if(close1 < lowerBand && rsi < 45)
{
cmd = OP_SELL;
sl = ask + (atr * ATRStopMultiplier);
tp = ask - (atr * ATRTakeMultiplier);
}
if(cmd != -1)
{
int ticket = OrderSend(Symbol(), cmd, finalLot, (cmd==OP_BUY?ask:bid), 5, sl, tp, "BTC Navigator", MagicNumber, 0, clrNONE);
if(ticket < 0)
Print("OrderSend failed: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Manage trailing stop for open position |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double atr = iATR(Symbol(), PERIOD_H4, ATRPeriod, 1);
if(atr <= 0) atr = avgATR;
double newSL = 0;
double trailTrigger = atr * TrailingStart;
if(OrderType() == OP_BUY)
{
double profitPoints = (Bid - OrderOpenPrice()) / Point;
if(profitPoints >= trailTrigger / Point)
{
newSL = Bid - (atr * TrailingStep);
if(newSL > OrderStopLoss())
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE))
Print("Trailing stop updated for BUY #", OrderTicket());
}
}
}
else if(OrderType() == OP_SELL)
{
double profitPoints = (OrderOpenPrice() - Ask) / Point;
if(profitPoints >= trailTrigger / Point)
{
newSL = Ask + (atr * TrailingStep);
if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE))
Print("Trailing stop updated for SELL #", OrderTicket());
}
}
}
break;
}
}
}
}
//+------------------------------------------------------------------+
//| 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 extremely high risk due to volatility, liquidity gaps, and broker differences. This EA is provided as-is without any guarantee of profit. Test extensively on a demo account for at least 3 months before live trading. Past performance does not guarantee future results.