Saltar al contenido

Descarga datos históricos desde interactive brokers usando Python

    portada

    La conexión con Interactive Brokers usando Python siempre ha sido un tema bastante complejo. La forma de conectarse a ellos desde Python no es lo fácil que es con otros brokers, y también requiere software corriendo en nuestras máquinas.

    Además, programar utilizando la librería que nos ofrece el acceso fácil a esta plataforma tampoco es sencilla, ya que se tiene que realizar a través de un wrapper. Esta forma de conexión para algunos programadores es compleja de entender y mucho más de manejar.

    En este artículo espero dejar claro cómo hacer la conexión con este bróker y cómo descargar datos históricos (poner órdenes y demás ya lo dejo para otros artículos). Así que espero que a partir de ahora le perdáis un poco el miedo a este bróker, que nos ofrece mucha información (la descarga de datos es solo raspar la superficie de todo lo que nos puede dar).

    Vamos a lio.

    Crear cuenta demo en IB

    Lógicamente, lo primero es tener una cuenta creada con IB, no vamos a ningún lado sin ello. No voy a entrar en todo el detalle de como crear una cuenta en Interactive Brokers, ya que hay mucha información sobre ello (como por ejemplo este vídeo de wowate). Solo voy a poner como, ya teniendo una cuenta, os podéis crear una cuenta demo (o simulada como ellos dicen) para la descarga de los datos.

    Una vez que abrimos nuestra cuenta normal de IB tendremos que ir a ajustes, dando sobre el icono de nuestro usuario, tal y como vemos en la imagen:

    Menú de usuario

    Una vez dentro de los ajustes hay una sección con el nombre de “Cuenta de negociación simulada” sobre el cual deberemos de pulsar para crear la cuenta demo que tanto deseamos.

    Cuenta negociada simulada

    Aquí deberemos rellenar los datos que nos piden para crear esta cuenta. En este caso es simplemente es un nombre de usuario y contraseña para la cuenta:

    Datos para la creación de la cuenta demo

    Una vez que la hemos creado, ya podemos entrar con nuestra cuenta demo y ver algo similar a esta pantalla:

    Cuenta demo creada

    Como ves de principio no sale una aviso de que que el estado de la solicitud está en curso, con lo cual todavía no lo podemos utilizar. La aceptación de la solicitud suele tardar un máximo de un día, así que ahora toca tener paciencia y esperar al día siguiente para poder entrar en nuestra cuenta y que nos muestre los datos como si fuera una cuenta real.

    IB Gateway

    El segundo paso es descargar desde la propia página el software necesario de IB para poder conectar. En este caso usaremos IB Gateway, es un programilla que se quedará en nuestro ordenador funcionando sin molestar y sin casi consumir recursos (lo cual es genial para poner el futuro robot en una nube). Así que iremos a esta web y nos lo descargaremos desde aquí.

    Un vez descargado lo ejecutamos y veremos como los pasos muy simples:

    Instalación del IB Gateway

    Solo hay que configurar la ruta en la que queremos que se instale, después ya se puede ejecutar sin problemas como si fuera un programa normal.

    Configuración de IB Gateway

    Una vez instalado, cuando lo ejecutemos nos encontraremos con esta pantalla:

    Pantalla de inicio del IB Gateway

    Tenemos que fijarnos en que esté marcado IB API y negociación simulada, para meter los datos de nuestra cuenta demo. Una vez abierta la sesión ya nos informan que es una cuenta demo:

    Inicio del IB Gateway

    Vale, parece que ya está todo abierto y funcionando, así ahora vamos a configurar tres cosas para que podamos conectarnos sin problemas desde Python. Para acceder a al configuración hay que ir en el menú a “Configuración” y luego otra vez al submenú “Configuración”, y acto seguido veremos una pantalla como esta:

    Configuración del IB Gateway

    Las opciones que hay que tocar son:

    • API de solo lectura: Normalmente está marcado y hay que dejarlo sin la marca, ya que sino no nos permitirá hacer operaciones (ya nos sirve para el futuro).
    • Socket Port: El puerto es importante para saber nosotros hacia donde tenemos que conectar. Lógicamente podemos cambiarlo, pero hay que tenerlo en cuenta para cuando hagamos la conexión y poner el mismo puerto que hemos configurado.
    • Permitir conexiones solo de localhost: Es importante para la seguridad del entorno y que no se pueda conectar alguien externo.

    Instalando la librería Ibapi

    La librería para implementar la conexión con Python se llama Ibapi. Es una librería muy completa que nos permite hacer todo lo que nos muestra IB en su web. Si quereis profundizar más sobre esta librería podéis encontrar la información técnica sobre ella en su web. La manera fácil de instalar la librería es así:

    pip install ibapi

    Aunque como os he comentado, la información sobre la librería está muy bien, la mayor fuente de conocimiento sobre qué es cada llamada a cada función está en la web de IB, donde se explica cada punto de la conexión y además viene para varios lenguajes de programación. Lógicamente para ver la documentación para nuestros propósitos hay que poner Python en el menú:

    Documentación de Python

    Desde ahí podéis ya ver las múltiples opciones que nos da la API de Interactive Brokers usando Python, que es realmente potente. Os recomiendo que gastéis unos minutos observando todo lo que se puede hacer con ella.

    Las clases de Ibapi

    La forma de trabajar con la librería ibapi es poco común ya que se hace creando un wrapper de la clase EWrapper. Es decir, tenemos que crear una clase que a su vez herede todo lo de esta clase. Lo se, suena un poco lio, así que creo que es mejor verlo con el ejemplo.

    Lo primero es que vamos a importar las librerías que vamos a necesitar para la descarga de datos históricos:

    from ibapi.client import EClient
    from ibapi.wrapper import EWrapper
    from ibapi.contract import Contract
    import pandas as pd

    Como veis hemos importado 3 librerías:

    • EClient: Esta es la clase con la que se hace la conexión con el IB Gateway, así que puede ser la más importante. La documentación la tenéis aquí.
    • EWrapper: Esta es la clase que realiza la comunicación, y todos los eventos que se generan por el IB Gateway son recogidos por esta clase. Si queréis más información sobre lo mucho que se puede hacer, lo tenéis aquí.
    • Contract: La clase en la que se definen los contratos. La documentación la podéis consultar aquí.

    Como veis con estas tres librerías tenemos lo imprescindible para hacer una conexión, definir el contrato que queremos y esperar todas las respuestas del IB Gateway. Ahora que ya lo tenemos listo todo, vamos a implementar la forma más sencilla de conexión y de descargar los datos (Para complicarnos ya tenemos tiempo más adelante).

    Entendiendo el wrapper

    Para hacer la descarga de los datos necesitamos una clase que tenga las siguientes funciones:

    • nextValidId: La función donde se comienza las peticiones de cualquier comando que se pasa a IB Gateway.
    • historicalData: En esta función llega cada nueva vela y si queremos hacer algún cálculo o añadir información sobre cada vela deberemos de hacerlo en esta función.
    • historicalDataEnd: Una vez que finaliza la descarga de datos se llama a esta función así que aquí es donde debemos de hacer las operaciones finales sobre los datos.
    • Error: Creo que no es el mejor método para llamar a esto porque aquí es donde vienen todos los mensajes ya sean de error o no, así que importante tenerlo en cuenta para ver lo que pasa en todo momento que intentamos conectar al IB Gateway.

    Además de esto tenemos que crear un tipo de dato “Contrato” para definir los datos que queremos y además hay que llamar a la clase “reqHistoricalData” para hacer la llamada al IB Gateway y así descarga ya los datos desde Interactive Brokers usando Python.

    Vamos a ver como hacemos la clase con todo esto.

    Creando el wrapper

    Vamos a poner el código del wrapper y lo vamos comentando:

    class MyWrapper(EWrapper):
        def __init__(self):
            self.datos_list = []
            self.datos = None
            
        def nextValidId(self, orderId:int):
            print("Comienza la petición con Id:", orderId)
            self.start()
        
        def historicalData(self, reqId, bar):
            # Por cada linea vamos sacando las variables y
            # añadiéndola a la lista
            self.datos_list.append(vars(bar));
            
        def historicalDataEnd(self, reqId: int, start: str, end: str):
            # Al finalizar ponemos los datos en formato dataframe,
            # la fecha en formato datetime y la ponemos como indice
            print("Final. ReqId:", reqId, "(", start, "/", end,")")
            self.datos = pd.DataFrame(self.datos_list)
            self.datos['date'] = pd.to_datetime(self.datos['date'])
            self.datos.set_index('date', inplace=True)
            app.disconnect()
            
        def error(self, reqId, errorCode, errorString):
            # Mostramos los mensajes que devuelve el IB Gateway
            print(errorString," (Id:", reqId, " Code:", errorCode,")")
    
        def start(self):        
            # Creamos en contrato
            contrato = Contract()
            contrato.symbol = "SPY"
            contrato.secType = "STK" 
            contrato.currency = "USD"
            contrato.exchange = "ARCA"
            
            # Hacemos la petición de los datos
            app.reqHistoricalData(1, contrato, "", "1 D", "1 min", "MIDPOINT", 0, 1, False, [])

    Creo que las funciones que ya hemos explicado antes se explican por si solas así que voy a comentar la función start, que es la donde se define el contrato y se hace la petición.

    Como se puede ver lo primero que se hace es la instanciación de la clase contrato, en este caso hemos descargado los datos del SP500, pero se pueden descargar los datos de muchos tipos de contrato diferentes: Forex, Criptos, Stocks, Indices, CFDs, Futuros, Opciones, Bonos, Warrants, etc. Entrar aquí para ver como configurar cada una de ellas, como poner las diferentes opciones y así no tenéis que liaros mucho más.

    Sobre la forma de descargar los datos con reqHistoricalData también tenéis toda la información en la web. Pero resumiendo mucho los campos que hay que pasarle son los siguientes:

    • tickerId: Un identificador para identificar las operaciones.
    • contract, El contrato que se ha definido.
    • endDateTime: La fecha y hora de la solicitud (si está vacio toma el momento de la petición).
    • durationString, La cantidad de tiempo para volver atrás y empezar a descargar los datos.
    • barSizeSetting, El tamaño de la barra.
    • whatToShow, El tipo de datos para recibir.
    • useRTH, 1 recibe los datos solos dentro de las horas regulares de trading, 0 recibe todos los datos.
    • formatDate, Formato de la fech
    • keepUpToDate, True para suscribirse a las actualizaciones cuando estén disponibles (que habría que controlar con la función historicalDataUpdate), False para recibir solo los datos que has solicitado.

    Recibiendo los datos

    Para recibir los datos, lógicamente, tenemos que realizar la conexión y, una vez conectado, recoger los resultados obtenidos. No es muy complicado ya que solo hay que instanciar nuestra clase pasársela al cliente, y hacer la conexión:

    wrap = MyWrapper()
    app = EClient(wrap)
    app.connect("127.0.0.1", 4002, clientId=999)
    app.run()

    Una vez lanzado esto veremos que nos devuelve algo así:

    10

    Como veis aunque la clase de los mensajes se llama “error” está claro que los mensajes que nos devuelve son de conexiones exitosas, por eso es importante también que veamos estos mensajes para ver que todo va correctamente.

    Una vez que finalice esta celda (doy por sentado que lo estamos haciendo desde una celda de jupyter), podemos ver directamente los datos que hemos descargado así:

    print(wrap.datos)

    Y ya veremos que los datos que hemos descargado:

    Datos descargados

    Si queremos guardar los datos en un csv es tan sencillo como:

    wrap.datos.to_csv("SP500.csv")

    Y para ver lo datos que hemos descargado de manera simple lo podemos hacer así:

    %matplotlib inline 
    wrap.datos.close.plot()

    Y veremos el gráfico de salida correspondiente a los precios de cierre (aquí, como siempre, ya os podéis complicar la vida para que os muestre velas o lo que queráis usando otras librerías):

    gráfica de los datos descargados desde Interactive Brokers usando Python

    Conclusiones

    La descarga de los datos desde Interactive Brokers usando Python no es tan sencilla como desde otros brokers que tienen una API más directa y que hace esta tarea mucho más sencilla. Aun así hemos visto como instalando el IB Gateway y la librería Ibapi se puede lograr la descarga de datos históricos con unas pocas líneas de código.

    Aunque el tema de hacer la programación a través de un wrapper no es tan intuitiva como puede ser hacerlo con otros métodos, es verdad que la documentación que ofrece Interactive Brokers en su web es muy completa y se puede seguir con bastante facilidad. Gracias a este nivel de detalle en la documentación han conseguido que te puedas poner a programar tu propio wrapper en poco tiempo.

    Si miráis la documentación veréis que hay mucha más chicha que rascar para descargar los datos así que si estáis interesados tenéis mucho donde mirar.

    Como siempre, si tenéis cualquier duda o mejora del artículo no dudéis en poneros en contacto conmigo y os contestaré lo antes posible.

    Deja una respuesta

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