← Back to Home
Enhancing Bollinger Bands Mean-Reversion with ADX and RSI Transforming $7000 Loss into $57000 Profit

Enhancing Bollinger Bands Mean-Reversion with ADX and RSI Transforming $7000 Loss into $57000 Profit

Basic Strategy

Mean-reversion is a popular trading strategy that assumes asset prices tend to revert to their historical mean. In the context of Bollinger Bands, the strategy often uses the lower and upper bands as triggers for trading:

Basic Strategy Code:

Here’s how I implemented the basic mean-reversion strategy using Backtrader:

class BBMeanReversion(bt.Strategy):
    params = (
        ('bb_period', 20),
        ('bb_devfactor', 2.0),
        ('printlog', True),
    )

    def __init__(self):
        self.dataclose = self.datas[0].close
        self.bband = bt.indicators.BollingerBands(
            self.datas[0],
            period=self.params.bb_period, 
            devfactor=self.params.bb_devfactor
        )

    def next(self):
        # Entry condition: Only enter if not in market
        if not self.position:
            if self.dataclose[0] < self.bband.lines.bot[0]:
                self.order = self.buy()
        # Exit condition: if in a long position and price crosses above the mid band
        else:
            if self.dataclose[0] > self.bband.lines.mid[0]:
                self.order = self.close()

Performance Observations:
When applied on a one-year dataset, the basic Bollinger Bands mean-reversion strategy can yield a high return. However, over other years the same strategy might result in modest returns or even losses. This inconsistency is common with straightforward mean-reversion approaches because markets can experience strong trends during which price “extremes” do not reliably reverse. For example let’s look at the cumulative performance from 2020 onward: Pasted image 20250412170459.png

let’s look at some statistics:

Starting Portfolio Value: 100000.00
2025-04-12 - (BB Period 20, BB DevFact 2.0) Ending Value 92930.49
Final Portfolio Value: 92930.49
Sharpe Ratio: 0.008
Total Return: -7.33%
Annualized Return: -0.95%
Max Drawdown: 53.27%
Total Trades: 28
Winning Trades: 18
Losing Trades: 10
Win Rate: 64.29%
Avg Winning Trade: 4947.69
Avg Losing Trade: -9612.79
Profit Factor: 0.9264571927698776

Enhancing the Strategy with Filters

To address the limitations of a basic mean-reversion strategy, multiple filters can be introduced:

These filters help ensure that trades are initiated only when the conditions favor mean-reversion rather than trend continuation.

Enhanced Strategy Code:

Below is the modified version of the basic strategy. Notice that both the entry and exit conditions now incorporate the ADX and RSI filters:

class EnhancedBBMeanReversion(bt.Strategy):
    params = (
        ('bb_period', 20),
        ('bb_devfactor', 2.0),
        ('adx_period', 14),
        ('adx_threshold', 25),
        ('rsi_period', 14),
        ('rsi_lower', 30),
        ('rsi_upper', 70),
        ('printlog', True),
    )

    def __init__(self):
        self.dataclose = self.datas[0].close
        self.bband = bt.indicators.BollingerBands(
            self.datas[0],
            period=self.params.bb_period,
            devfactor=self.params.bb_devfactor
        )
        # ADX Filter for non-trending conditions
        self.adx = bt.indicators.ADX(self.datas[0], period=self.params.adx_period)
        # RSI Filter for overbought/oversold conditions
        self.rsi = bt.indicators.RSI(self.datas[0], period=self.params.rsi_period)

    def next(self):
        # Skip if ADX indicates a trending market
        if self.adx[0] >= self.params.adx_threshold:
            self.log(f"Market trending (ADX {self.adx[0]:.2f} >= {self.params.adx_threshold}), skipping new entries.")
            return

        # Long Entry Condition with multiple confirmations:
        # Price below lower band and RSI indicates oversold conditions.
        if not self.position:
            if (self.dataclose[0] < self.bband.lines.bot[0] and 
                self.rsi[0] < self.params.rsi_lower):
                self.log(f'LONG ENTRY SIGNAL: Close: {self.dataclose[0]:.2f}, Lower BB: {self.bband.lines.bot[0]:.2f}, RSI: {self.rsi[0]:.2f}')
                self.order = self.buy()
        # Exit Condition: long positions exit when price crosses above the middle band.
        else:
            if self.position.size > 0 and self.dataclose[0] > self.bband.lines.mid[0]:
                self.log(f'EXIT LONG SIGNAL: Close: {self.dataclose[0]:.2f}, Mid BB: {self.bband.lines.mid[0]:.2f}')
                self.order = self.close()

Key Enhancements:

Overall Impact on Performance:
While the basic mean-reversion strategy can deliver high returns in periods conducive to reversion, it typically suffers from significant variability and higher risk during trending markets. The enhanced version, by integrating both ADX and RSI filters, may yield slightly lower returns during peak mean-reversion years, but it offers substantially improved stability. This results in smaller drawdowns and a more robust risk profile over time. Let’s examine the performance metrics from 2020 onward:

Pasted image 20250412171816.png

let’s check the new result statistics:

Starting Portfolio Value: 100000.00
2025-04-12 - (BB Period 20, BB DevFact 2.0, ADX Thresh 25, RSI [< 30 / > 70]) Ending Value 157725.55
Final Portfolio Value: 157725.55
Sharpe Ratio: 0.033
Total Return: 45.57%
Annualized Return: 6.13%
Max Drawdown: 14.98%
Total Trades: 6
Winning Trades: 4
Losing Trades: 2
Win Rate: 66.67%
Avg Winning Trade: 15023.39
Avg Losing Trade: -1184.00
Profit Factor: 25.377326687383313

Conclusion

Introducing filters such as ADX and RSI to the Bollinger Bands mean-reversion strategy provides a more refined trading signal—one that avoids unfavorable conditions and captures high-conviction entries. While the basic version might produce high returns in the right market environment, it can also suffer substantial losses in trending periods. The enhanced strategy, on the other hand, demonstrates that combining multiple indicators can lead to a more stable and attractive risk-adjusted performance.

By integrating both ADX and RSI filters, traders can achieve a balance between capturing high-probability mean-reversion trades and maintaining strict risk control—resulting in fewer trades but with significantly improved outcomes.