← Back to Home
Mastering Technical Indicators in backtrader RSI, MACD, Bollinger Bands

Mastering Technical Indicators in backtrader RSI, MACD, Bollinger Bands

Technical analysis is a cornerstone of quantitative trading, and backtrader provides an excellent framework for implementing and backtesting strategies based on key technical indicators. Visualizing these indicators alongside price action is crucial for understanding strategy behavior and results.

In this article, we will delve into three of the most popular and widely used indicators – Relative Strength Index (RSI), Moving Average Convergence Divergence (MACD), and Bollinger Bands – and demonstrate how to implement and plot them using backtrader.

Visualizing Indicators with backtrader Plotting

One of backtrader’s most powerful features is its integrated plotting system, built on top of matplotlib. After running a backtest using the Cerebro engine, a single command (cerebro.plot()) generates comprehensive charts visualizing:

Crucially, standard indicators declared within your strategy are typically plotted automatically without needing extra plotting code within the strategy itself. backtrader intelligently places oscillators like RSI and MACD in separate subplots, while overlays like Moving Averages and Bollinger Bands are drawn directly on the main price chart.

1. Relative Strength Index (RSI)

The Relative Strength Index (RSI) is a momentum oscillator measuring the magnitude of recent price changes to evaluate overbought or oversold conditions (typically above 70 or below 30).

Implementing RSI in backtrader:

Python

import backtrader as bt

class RSIStrategy(bt.Strategy):
    params = (
        ('rsi_period', 14),  # Period for RSI calculation
        ('rsi_overbought', 70),
        ('rsi_oversold', 30),
    )

    def __init__(self):
        # Store the indicator reference - backtrader plots it automatically
        self.rsi = bt.indicators.RelativeStrengthIndex(
            self.data, period=self.params.rsi_period)
        # Optional: Add horizontal lines to the RSI plot
        self.rsi.plotinfo.plotyhlines = [self.params.rsi_oversold, self.params.rsi_oversold]

    def next(self):
        if not self.position:  # Not in the market
            # Potential buy signal
            if self.rsi < self.params.rsi_oversold:
                self.log(f'RSI OVERSOLD, BUY CREATE {self.data.close[0]:.2f}')
                self.order = self.buy()
        else:  # In the market
            # Potential sell signal
            if self.rsi > self.params.rsi_overbought:
                self.log(f'RSI OVERBOUGHT, SELL CREATE {self.data.close[0]:.2f}')
                self.order = self.sell()

    def log(self, txt, dt=None):
        ''' Logging function for this strategy'''
        dt = dt or self.datas[0].datetime.date(0)
        print(f'{dt.isoformat()} {txt}') # Print date and log message

Plotting Behavior: When plotted using cerebro.plot(), the RSI typically appears in its own subplot below the main price chart. backtrader often automatically draws horizontal lines at the 70 and 30 levels if detected, or you can explicitly add them using plotinfo.plotyhlines as shown above.

2. Moving Average Convergence Divergence (MACD)

The Moving Average Convergence Divergence (MACD) shows the relationship between two EMAs. It consists of the MACD line, a Signal line (EMA of MACD), and a Histogram (MACD - Signal). Crossovers are often used as signals.

Implementing MACD in backtrader:

Python

import backtrader as bt

class MACDStrategy(bt.Strategy):
    params = (
        ('fast_period', 12),
        ('slow_period', 26),
        ('signal_period', 9),
    )

    def __init__(self):
        # Store indicator references - backtrader plots them automatically
        self.macd_obj = bt.indicators.MACD(self.data,
                                           period_me1=self.params.fast_period,
                                           period_me2=self.params.slow_period,
                                           period_signal=self.params.signal_period)
        # Optional: Define a crossover signal indicator for cleaner logic
        self.macd_crossover = bt.indicators.CrossOver(self.macd_obj.macd, self.macd_obj.signal)

    def next(self):
        if not self.position:
            # Buy signal: MACD line crosses above Signal line
            if self.macd_crossover > 0: # Value is 1 for upward crossover
                self.log(f'MACD Crossover BUY, {self.data.close[0]:.2f}')
                self.order = self.buy()
        else:
            # Sell signal: MACD line crosses below Signal line
            if self.macd_crossover < 0: # Value is -1 for downward crossover
                self.log(f'MACD Crossover SELL, {self.data.close[0]:.2f}')
                self.order = self.sell()

    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print(f'{dt.isoformat()} {txt}')

Plotting Behavior: In backtrader plots, the MACD line, signal line, and histogram are usually displayed together in a separate subplot beneath the price data, making it easy to visualize crossovers and histogram divergence.

3. Bollinger Bands

Bollinger Bands® consist of an SMA (middle band) and two outer bands plotted at +/- standard deviations. They help visualize volatility and potential price extremes relative to the recent trend.

Implementing Bollinger Bands in backtrader:

Python

import backtrader as bt

class BollingerBandsStrategy(bt.Strategy):
    params = (
        ('period', 20),    # Period for the SMA middle band
        ('devfactor', 2.0) # Standard Deviations for upper/lower bands
    )

    def __init__(self):
        # Store indicator reference - backtrader plots it automatically
        self.bbands = bt.indicators.BollingerBands(
            self.data, period=self.params.period, devfactor=self.params.devfactor)
        # Optional: Store individual lines if needed for complex logic
        # self.mid_band = self.bbands.mid
        # self.top_band = self.bbands.top
        # self.bot_band = self.bbands.bot

    def next(self):
        if not self.position:
            # Buy signal: Price closes below the lower band
            if self.data.close[0] < self.bbands.lines.bot[0]:
                self.log(f'BBands Low, BUY CREATE {self.data.close[0]:.2f}')
                self.order = self.buy()
        else:
            # Sell signal: Price closes above the upper band
            if self.data.close[0] > self.bbands.lines.top[0]:
                self.log(f'BBands High, SELL CREATE {self.data.close[0]:.2f}')
                self.order = self.sell()
            # Alternative Exit: Close crosses back below middle band (example)
            # elif self.data.close[0] < self.bbands.lines.mid[0]:
            #     self.log(f'BBands Mid Cross, SELL CREATE {self.data.close[0]:.2f}')
            #     self.order = self.sell()

    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print(f'{dt.isoformat()} {txt}')

Important Considerations:

This article provides a basic overview of implementing RSI, MACD, and Bollinger Bands within the backtrader framework. By mastering these fundamental indicators, you can build a solid foundation for developing and backtesting more complex algorithmic trading strategies.

Further Exploration:

For a deeper dive into technical analysis and quantitative trading with backtrader, I recommend exploring:

Remember, consistent learning and practice are key to becoming a successful quantitative trader.

Disclaimer: This information is for educational purposes only and should not be considered financial advice. Investing in financial markets carries significant risks, and you could lose all of your invested capital.