Saltar al contenido

Backtesting.py: Otra forma de probar tus estrategias de trading

    portada backtesting

    Aunque backtrader es una herramienta potente para hacer backtesting, hay otras que han evolucionado a partir de esta herramienta. Y una de las más potentes es backtesting.py, un framework ligero, potente y muy fácil de usar.

    Una de las características que nos puede sorprender de esta herramienta es que no tiene una librería técnica pero está completamente integrado con librerías de análisis técnico como TA-Lib, Tulipy, PyAlgoTrade, NumPy, SciPy, con lo que nos permite hacer estrategias muy completas utilizando estas dos librerías.

    Instalación

    La instalación de este paquete es igual no tienen ninguna complicación y se instala igual que el resto de paquetes de python:

    pip install backtesting

    Una vez, y como siempre, se debe de importar la librería para poder empezar a utilizarla, pero esto ya lo vamos a ir viendo poco a poco para conocer cuales son las clases que tiene esta librería y como podemos utilizarlas para poder sacar el mayor provecho posible.

    Datos

    Para utilizar esta librería tenemos que tener los datos en formato “dataframe” y que tengan los datos ohlc, es decir las columnas open, high, low y close. En este caso nosotros hemos optado por conseguir los datos a través de la librería yfinance, que nos proporciona los datos que tiene yahoo finance, y la cual nos da los datos que necesitamos para realizar el backtesting con backtesting.py.

    La forma de conseguir los datos que he utilizado es esta:

    Conseguir datos con yfinance

    Como vemos ya tenemos los datos que necesitamos, incluso alguno más que en este caso no vamos a utilizar.

    Estrategias

    Ahora que ya tenemos los datos que vamos a necesitar, lo siguiente es pensar la estrategia que vamos a utilizar. En este caso no nos vamos a complicar mucho con la estrategia (ya que no es el objetivo de esta entrada) y crearemos una estrategia simple de cruce de medias utilizando la librería Ta-lib para realizar los cálculos.

    La estrategia que vamos a utilizar para esta técnica es la del cruce dorado que ya hemos visto cuando hemos probado backtrader, y a la vez hacemos una comparativa con el mismo caso en ambas librerías. El cruce dorado es el cruce de medias de la media de 50 y la media de 200, así que implementarlo es sencillo.

    Como novedad y por ver la sencillez de esta librería vamos a hacer que cuando la media de 50 cruce la media de 200 opere en largo, y cuando la media de 50 cruce la de 200 opere en corto.

    from backtesting import Strategy
    from backtesting.lib import crossover
    import pandas as pd
    import talib as ta
    
    class GoldenCross(Strategy):
        # Definimos las medias que vamos a utilizar
        n1 = 50
        n2 = 200
        
        def init(self):
            # Configuramos las dos medias
            self.sma50 = self.I(ta.MA, self.data.Close, self.n1)
            self.sma200 = self.I(ta.MA, self.data.Close, self.n2)
    
        def next(self):
            # Si la media de 50 cruza hacia arriba la media de 200
            # cierra cualquier posicion que estuviera abierta compra
            if crossover(self.sma50, self.sma200):
                self.position.close()
                self.buy()
    
            # si la media de 50 cruza hacía abajo la media de 200
            # cerramos cualquier posicion anterior y vendemos
            elif crossover(self.sma200, self.sma50):
                self.position.close()
                self.sell()

    Como se puede ver al principio se puede que importamos varias librerías. La clase “Strategy” que es la que luego usaremos como base para nuestra propia clase, y la “crossover” que es la que utilizaremos para comprobar si una media cruza a otra.

    El trozo que puede ser más complejo es la configuración de las medias, donde como vemos utilizamos el método “self.I”. Para este método hay que pasarle como primer argumento la función que vamos a llamar para para hacer los cálculos (en este caso la media simple que nos da la librería Ta-lib) y los siguientes parámetros son los argumentos que le vamos a pasar a la función.

    Si quisiéramos poner stop-loss, take profit u ordenes de límite, tanto la orden buy como la orden sell tiene parámetros para hacer esto y podríamos ponerlo así:

    buy(size=1, limit=None, stop=None, sl=self.data.close-3, tp=self.data.close+10)

    Backtesting.py trae más funciones que pueden ayudarnos a crear estrategias más rápido o mucho más complejas. Todo esto puede verse en su amplia documentación.

    Lanzar las pruebas

    La forma de lanzar las pruebas también es muy sencilla con Backtesting.py. Simplemente hay que importar la clase “Backtest” que es la que hará la magia. Si miramos la definición de la clase en la documentación de backtesting.py le podemos pasar varios parámetros:

    class Backtest(data, strategy, *, cash=10000, 
    commission=0.0, margin=1.0, trade_on_close=False, 
    hedging=False, exclusive_orders=False)

    Las opciones son las siguientes:

    • data: Los datos que le tenemos que pasar de tipo ohlc para que haga el backtesting.
    • strategy: La clase de la estrategia que hemos definido anteriormente
    • cash: Dinero inicial
    • commision: Las comisiones del brocker por operaciones
    • trade_on_close: si es True la orden de mercado se hará se hará sobre el precio de la barra de cierre actual y no sobre la apertura de la siguiente barra, lo cual hará si es False.
    • hedging: Si es True permitirá operaciones simultaneas en ambas direcciones, si es False cerrará las operaciones contrarias antes de hacer la actual.
    • exclusive_orders: si es True solo permitirá una operación a la vez y cerrará la anterior automáticamente.

    Nosotros vamos a ejecutarlo de la siguiente manera:

    from backtesting import Backtest
    
    bt = Backtest(data, GoldenCross, cash=10000, commission=.005)
    stats = bt.run()
    stats

    Como se puede ver en la llamada a esta clase podemos pasarle como parámetros varios datos. En este caso le pasamos la cantidad de dinero con la que empezará a realizar el trading y la comisión que nos cobrará el broker. Luego haremos la llamada a “run()” de backtesting.py, la cual nos devolverá las estadísticas de todas las operaciones realizadas:

    stats de backtesting.py

    Si queremos hacer operaciones con alguna de estas estadísticas para sacar las nuestra propias se puede acceder a cada valor de “stats” utilizando el nombre de la estadística que deseamos obtener:

    Conseguir un stat fácilmente

    Gráficos

    Si queremos ver en un gráfico las operaciones que se han realizado utilizando esta librería es extremadamente sencillo con backtesting.py. Solo tendremos que hacer la llamada a la función “plot()”:

    bt.plot()

    Y la salida que tendremos será así:

    Modo gráfico de backtesting.py

    Donde, como vemos en la imagen, en la parte izquierda tenemos unas herramientas con las cuales podemos manejar el gráfico completamente haciendo zoom y otras muchas herramientas para ver mejora las operaciones:

    Zoom del modo gráfico de backtesting.py

    Utilizar modelos de Machine learning

    Si estamos utilizando modelos también podemos hacer backtesting con backtesting.py, Solo habría que cargar el modelo en la clase estrategia y a partir de ese momento.

    Si por ejemplo tenemos un modelo muy simple al que le pasamos los datos ohlc y nos devuelve 1 si hay que comprar y 2 si hay que cerrar la posición, la estrategia sería así:

    from backtesting import Strategy
    from backtesting.lib import crossover
    import pandas as pd
    import talib as ta
    import joblib
    
    class MLmodel(Strategy):
        def init(self):
            # Cargamos el modelo
            loaded_model = joblib.load('modelo.joblib')
    
        def next(self):
            # Aquí es donde tendréis que hacer la extracción de datos, normalizar o
            # cualquier operación que necesitéis hacer con los datos antes de pasarlos
            if self.loaded_model.predict(data) == 1:
                self.buy()
            if self.loaded_model.predict(data) == 2:
                self.position.close()

    Como se puede ver es muy sencillo hacer backtesting de modelos de aprendizaje automático utilizando backtesting.py.

    Conclusiones

    Como se puede la utilización de esta librería nos facilita muchísimo la labor de hacer el backtesting y obtener resultados de forma muy sencilla, con unas estadísticas que nos puede ayudar mucho a la hora de probar si nuestra estrategia (ya sea de ML o no) es buena o hay que mejorarla de alguna forma.

    La documentación que trae esta herramienta también está muy completa y echándole un ojo podemos llegar a hacer estrategias muy complejas con muy pocas líneas de código (aunque lamentablemente no hay muchos ejemplos prácticos para probar.

    Como siempre cualquier duda o aclaración no dudéis en ponerla en los comentarios o enviarme un mensaje y os contestaré lo antes posible.

    7 comentarios en «Backtesting.py: Otra forma de probar tus estrategias de trading»

    1. Hola Ivan, buen día

      Un saludo desde Colombia. Navegando en Internett me encontré con tu web la cual me ha parecido fantástica, ya que la información brindada es de calidad y lo mejor, en Español!. Estoy desarrollando un bot para realizar probar mi estrategia de trading y una de las partes fundamentales es el de realizar el backtest de la estrategia. Estoy copiando al pie de la letra el código mostrado utilizando backtesting.py y me aparece el siguiente error:
      “TypeError: Can’t instantiate abstract class GoldenCross with abstract method init”. No sé si es la version de python (utilizo la 3.10.6), o la version del matplotlib (3.2.2). Toda la retroalimentacion que me puedes brindar seria de mucha ayuda para mí.

      De antemano gracias y quedo atento a tus comentarios.

      Saludos

      GUIDO A. MARQUEZ PALOMINO
      [email protected]

    2. Hola Guido,

      Gracias por tus palabras, espero mantener la calidad en próximas entradas.

      Sobre tu pregunta, lo acabo de ejecutar y me ha funcionado perfectamente. Pero he pensado por el error que te está dando que igual al definir el init en la clase lo has puesto como “def __init__(self)” y no como “def init(self)”. He hecho la prueba yo de ponerlo así y me sale el mismo error que a ti, así que igual es simplemente eso.

      Sino te funciona en cuanto me contestes te escribo por correo e intercambiamos el código.

      Un saludo.

      1. Hola Ivan, buenas tardes
        Me funcionó con el código mostrado en la pagina oficial de Python.org.(https://pypi.org/project/Backtesting/) Las diferencias están en estas líneas con referencia al código mostrado en este bloc:
        self.ma1 = self.I(SMA, price, 10)
        self.ma2 = self.I(SMA, price, 20)

        Aprovechando la oportunidad, tengo una nueva consulta: donde o como puedo revisar la estructura de la función crossover? Quiero revisar al detalle como funciona (o como maneja las listas), ya que he intentado probar mi propia estrategia (te la puedo explicar en datalle al correo)y no he logrado que se muestre adecuadamente en las graficas. Si gustas te puedo enviar el codigo completo junto con la explicación (a que correo lo puedo enviar) para que puedes validar en que puedo estar fallado. Yo adquiero los datos desde MT5.

        Quedo atento a tu apoyo. Saludos.

        GAMP

      2. Hola Ivan, buenas tardes
        Me funcionó con el código mostrado en la pagina oficial de Python.org.(https://pypi.org/project/Backtesting/) Las diferencias están en estas líneas con referencia al código mostrado en este bloc:
        self.ma1 = self.I(SMA, price, 10)
        self.ma2 = self.I(SMA, price, 20)

        Aprovechando la oportunidad, tengo una nueva consulta: donde o como puedo revisar la estructura de la función crossover? Quiero revisar al detalle como funciona (o como maneja las listas), ya que he intentado probar mi propia estrategia (te la puedo explicar en datalle al correo)y no he logrado que se muestre adecuadamente en las graficas. Si gustas te puedo enviar el codigo completo junto con la explicación (a que correo lo puedo enviar) para que puedes validar en que puedo estar fallado. Yo adquiero los datos desde MT5.

        Quedo atento a tu apoyo. Saludos.

    3. Hola Iván. Buenos días.

      En primer lugar, quería felicitarte por tu activo y valioso trabajo que desarrollas en este Blog, resulta muy útil para entusiastas como nosotros que estamos en el camino de desarrollar un bot de trading utilizando los fundamentos del aprendizaje automático.

      Tengo una duda sobre esta librería de Backtesting. Con qué nivel de resolución hace los cálculos? Es posible llegar al nivel de Tickchart? Mi duda viene dada porque estoy queriendo implementar estrategias de trailing stop Loss sobre estrategias optimizadas mediante algoritmos de machine learning y deep learning. Hemos desarrollado una función backtesting propia, pero encontramos siempre anomalías y no estamos seguros de estar por el camino correcto.

      Muchas gracias!

      1. Hola Matias,

        Gracias por tus palabras sobre el blog, me gusta mucho saber que estoy ayudando a gente a entrar en este mundo que a mí me parece apasionante.

        Sobre tu duda con la librería de Backtesting.py, sí que es posible ya que los datos se los pasas tu en un dataframe, supongo que si le pasas el dataframe con los precios de open, low, high y close con el precio del tick debería de funcionar. Personalmente nunca he bajado a tan poco tiempo con python, porque luego el problema es la implementación de la estrategia en tiempo real, que hay que tener mucha máquina y el código muy fino, en plan vectorizar todo, utilizar la librería Numba o meter código en C para que todo vaya más rápido.

        De todas formas si haces pruebas a nivel de tick con la librería de backtesting.py escríbeme desde la página de contacto y comentamos.

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *