Quant500 Academy

5. Un caso completo: Bandas de Bollinger con RSI

Nivel 12: Backtesting y Métricas de Riesgo Reales

Vamos a construir una estrategia algo más compleja combinando Bandas de Bollinger y el Índice de Fuerza Relativa (RSI) con Backtrader. La lógica es de reversión a la media con confirmación:

La idea de exigir dos condiciones simultáneas es reducir falsos positivos: el precio puede perforar la banda inferior en plena tendencia bajista sin que haya nada que revertir, y el RSI actúa de filtro.

import backtrader as bt
import yfinance as yf
import datetime

class BollingerRSIStrategy(bt.Strategy):
    params = (
        ('bband_period', 20),
        ('rsi_period', 14),
        ('overbought', 70),
        ('oversold', 30),
        ('stddev', 2),
    )

    def __init__(self):
        self.bband = bt.indicators.BollingerBands(
            self.data.close,
            period=self.params.bband_period,
            devfactor=self.params.stddev
        )
        self.rsi = bt.indicators.RelativeStrengthIndex(
            period=self.params.rsi_period
        )

    def next(self):
        if self.data.close < self.bband.lines.bot and self.rsi < self.params.oversold:
            self.buy()
        elif self.data.close > self.bband.lines.top and self.rsi > self.params.overbought:
            self.sell()

data_df = yf.download('AAPL', start='2020-01-01', end='2022-01-01')
data = bt.feeds.PandasData(dataname=data_df)

cerebro = bt.Cerebro(stdstats=False)
cerebro.addstrategy(BollingerRSIStrategy)
cerebro.adddata(data)
cerebro.broker.set_cash(100000.0)
cerebro.addsizer(bt.sizers.FixedSize, stake=1000)
cerebro.broker.setcommission(commission=0.001)

print('Valor inicial de la cartera: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Valor final de la cartera: %.2f' % cerebro.broker.getvalue())

cerebro.plot(iplot=True)

Qué hace cada pieza

BollingerBands construye tres líneas: mid (la media móvil simple del periodo indicado), top y bot, situadas a devfactor desviaciones típicas por encima y por debajo de la media. Con devfactor=2 y retornos aproximadamente normales, alrededor del 5% de los cierres quedarían fuera de las bandas. Los retornos financieros no son normales y tienen colas gruesas, así que en la práctica se sale de las bandas bastante más a menudo, sobre todo en episodios de estrés.

RelativeStrengthIndex con periodo 14 es la configuración estándar de Wilder. Ojo con una trampa habitual: en un activo con tendencia alcista fuerte y sostenida, el RSI de 14 puede pasar meses sin bajar de 40. La estrategia simplemente no comprará, y el backtest devolverá un resultado plano que se puede confundir con una estrategia prudente cuando en realidad es una estrategia inactiva.

stdstats=False en el constructor de Cerebro desactiva los observadores estándar (valor de cartera, operaciones y compraventas) del gráfico. Sirve para que el dibujo quede limpio cuando ya tienes varios paneles de indicadores, pero significa que pierdes de vista la curva de capital, que es justamente lo que más deberías mirar. Úsalo con conocimiento de causa.

Advertencias del autor, que conviene tomarse en serio

Sobre este código concreto hay dos deficiencias que ya deberías detectar tú solo. La primera: no hay stop-loss ni objetivo de beneficio, así que una posición perdedora puede quedarse abierta indefinidamente. La segunda: como no se comprueba self.position, varias señales de compra consecutivas acumulan posición sin control. Añadir if not self.position antes de comprar y usar self.close() para salir es el mínimo exigible antes de tomarse en serio cualquier número que salga de aquí.

El resto de Quant500

Lo que estás aprendiendo, aplicado al índice todos los días:

La cartera del modelo, cada semana en tu correo

Cada sábado, su semana en un minuto: cómo fue, lo mejor y lo peor, y qué directivos compraron con su dinero. Y el día que el modelo se rehace, qué entra y qué sale. Es una simulación, no un consejo. Como mucho un correo a la semana, sin publicidad y sin cuenta; te das de baja con un clic. Lo hace una persona: Pol Gracia.

Cartera modelo con resultados simulados, no una recomendación personalizada. Tu correo se usa solo para esto: privacidad.

Detrás de la Academia y del resto de Quant500 hay una sola persona: Pol Gracia Serrahima.