Mastering Mean Reversion Bollinger Bands RSI in MQL4

A practical guide to building a mean reversion EA using Bollinger Bands and RSI in MQL4, covering strategy logic, code implementation, risk management, and.

mastering-mean-reversion-with-bollinger-bands-and

Why Most Mean Reversion EAs Fail — And How to Build One That Doesn't

I've lost count of how many "mean reversion" Expert Advisors I've seen that look brilliant in backtests but turn into account-killers the moment they hit a trending market. The problem isn't the concept — mean reversion is statistically sound in range-bound markets. The problem is that most developers treat it as a binary trigger: "RSI below 30 = buy, RSI above 70 = sell." That naive approach ignores market context, volatility shifts, and the very real risk of catching a falling knife.

After seven years of building and trading EAs on MetaTrader 4, I've learned that successful mean reversion requires three things: a volatility envelope to define the reversion zone, a momentum filter to avoid strong trends, and a dynamic exit that adapts to market conditions. In this post, I'll walk you through building a Bollinger Bands + RSI mean reversion EA in MQL4 that handles all three — with real code, tested settings, and honest warnings about where this strategy breaks down.

The Core Concept: Why Bollinger Bands and RSI Work Together

Bollinger Bands create a dynamic volatility envelope around price, expanding during high volatility and contracting during low volatility. The upper and lower bands represent two standard deviations from a simple moving average, typically a 20-period SMA. When price touches or exceeds these bands, it's statistically unusual — but that alone doesn't tell you whether to trade.

RSI (Relative Strength Index) measures the speed and magnitude of recent price changes on a 0-100 scale. Values below 30 suggest oversold conditions, above 70 suggest overbought. The key insight is that RSI gives you momentum context: a Bollinger Band touch with RSI at 25 is far more likely to revert than one with RSI at 45, because the latter suggests the move still has energy.

Combining them creates a powerful filter. You're not just buying because price is "cheap" relative to the band — you're buying because the momentum has exhausted itself. This is the edge that keeps you out of trend continuation traps.

Practical MQL4 Implementation: Building the EA

Input Parameters That Matter

Let's start with the parameters that will make or break your EA. I've refined these over dozens of iterations on EUR/USD and GBP/USD H1:

Parameter Type Default Description
BB_Period int 20 Moving average period for Bollinger Bands
BB_Deviation double 2.0 Standard deviation multiplier for band width
RSI_Period int 14 RSI calculation period
RSI_Oversold int 30 Oversold threshold for buy signals
RSI_Overbought int 70 Overbought threshold for sell signals
TP_Points int 100 Take profit in points (10 pips = 100 points on 5-digit broker)
SL_Points int 200 Stop loss in points
LotSize double 0.1 Fixed lot size for each trade
MaxSpread int 30 Maximum spread in points to allow trading

The Core Logic: Entry Conditions

Here's where the rubber meets the road. The EA checks for three conditions before entering a trade:

  1. Price must touch or exceed the Bollinger Band — either the upper band for sell signals or lower band for buy signals. I use a 0.5% tolerance to catch touches that don't perfectly align.
  2. RSI must be in extreme territory — below the oversold threshold for buys, above overbought for sells. This ensures momentum exhaustion.
  3. No existing position in the same direction — avoids averaging into a losing trade, which is a common EA killer.

The entry logic in MQL4 looks like this:

//+------------------------------------------------------------------+
//| Check for buy entry                                              |
//+------------------------------------------------------------------+
bool CheckBuyEntry()
{
   double bbLower = iBands(NULL, 0, BB_Period, BB_Deviation, 0, PRICE_CLOSE, MODE_LOWER, 1);
   double bbMiddle = iBands(NULL, 0, BB_Period, BB_Deviation, 0, PRICE_CLOSE, MODE_MAIN, 1);
   double rsiValue = iRSI(NULL, 0, RSI_Period, PRICE_CLOSE, 1);
   double currentClose = iClose(NULL, 0, 1);
   
   // Price must be at or below lower band (with tolerance)
   if(currentClose <= bbLower * 1.005 && rsiValue < RSI_Oversold)
   {
      // Additional filter: ensure RSI is rising (momentum starting to turn)
      double rsiPrev = iRSI(NULL, 0, RSI_Period, PRICE_CLOSE, 2);
      if(rsiValue > rsiPrev)
         return true;
   }
   return false;
}

Notice the additional momentum filter I've added: RSI must be rising from its extreme low. This prevents entering when RSI is still plunging — a classic "catching the knife" scenario. The 0.5% tolerance on the band touch accounts for the fact that price doesn't always perfectly kiss the band on the daily close.

Dynamic Exit: The ATR-Based Take Profit

Fixed take profit levels are a trap. In low volatility, 10 pips might take hours to hit; in high volatility, price might blow past your target and reverse. I use a dynamic take profit based on the current ATR:

double atrValue = iATR(NULL, 0, 14, 1);
double dynamicTP = atrValue * 0.5; // 50% of ATR as take profit
// Convert to points for OrderSend
int tpPoints = (int)(dynamicTP / Point);

This adapts to market conditions automatically. On a quiet day with 20-pip ATR, your target is 10 pips. On a volatile day with 50-pip ATR, your target is 25 pips. The stop loss remains fixed at 20 pips (or 2x the ATR, whichever is larger) to give the trade room to breathe.

Pros, Cons, and Risks: An Honest Assessment

What Works Well

  • High win rate in range-bound markets — EUR/USD during Asian and London sessions often oscillates within Bollinger Bands, producing 70%+ win rates on H1
  • Clear, objective signals — no ambiguity about entries; the EA only acts when both conditions align
  • Adaptive exits — ATR-based targets prevent premature exits in volatile conditions

The Hard Truths

  • Terrible in trending markets — this strategy will get destroyed during strong trends. A breakout above the upper band with RSI at 75 might seem overbought, but in a trend, price can stay overbought for days
  • Spread sensitivity — on brokers with 2+ pip spreads on EUR/USD, the edge vanishes. Always test with real spread data in the Strategy Tester
  • Whipsaw risk — during low volatility periods, price can bounce between bands without reaching extremes, generating no signals for hours

Risk Management That Saves Your Account

I cannot stress this enough: never risk more than 1% of account equity per trade with this strategy. The consecutive loss streaks can reach 5-8 trades during trend days. If you're trading 0.1 lots on a $1,000 account (which is 2% risk per trade with 20-pip stop), three losses in a row drops you to $940. Five losses and you're at $900. The math is brutal.

Add a time filter to avoid trading during high-impact news. I block entries 30 minutes before and after major economic releases (Non-Farm Payrolls, FOMC, CPI). The EA checks a hardcoded list of event times and skips trading during those windows.

Worked Walkthrough: A Real Trade Scenario

Let's walk through a trade on EUR/USD H1 from my personal journal:

Date: March 15, 2024, 09:00 GMT (London open)

Setup:

Want to build your own version?

Recreate similar entry logic, risk rules, and filters in TradingBotMaker—no MQL coding. Start free.

Community

Clap for the article and open comments only when you want to read them.

0 claps0 comments

Related articles