Trading Automation with Interactive Broker API, Python and Docker

Crypto trading has been all the rage over the past few years, and people tend to forget that there are many many more trading opportunities in the real world than in the crypto world. Unfortunately, the real world is often using outdated tech and it can be a real pain to finally manage to perform automatic actions on the stock exchanges.

Here, we are going to quickly set up docker containers that will allow you to automate your trading strategy in the real world, using Interactive Broker API. Interactive Broker is one of the most famous and versatile platforms for trading. Its API is quite clunky but thanks to the software popularity, people have developed nice wrappers around it.

An important note: I’ve tried many systems and after a few days of exploration, I found out that Interactive Broker is the most practical one, allows you to perform all kinds of actions, is available in the USA and in Europe. All other tools were either USA-only, either limited in terms of features scope (ex: limited to Forex).

So, how do we go about it? The first thing is to get Interactive Broker (IB) API to run in headless mode, inside a docker container. Originally, IB is a trading platform with a GUI, and the API is online when the GUI is open. If you want to automate stuff, it’s better to have something running headless in a Docker container on a Linux server. In order to achieve that, we used Dockerfile from https://github.com/ryankennedyio/ib-docker and we then modified 1 or 2 things such as using a docker-compose.yml, and adding a few comments.

Then, we use PostgreSQL to store data we retrieve from the API, and Pgweb to visualise the DB content (useful for debugging). We then use Python Celery to run periodic tasks (fetch stock market data every X min), Celery flower to visualise the queue, and Grafana to explore our data and get nice charts.

Here is the full docker-compose.yml :


version: '2'
services:
#RabbitMQ broker, for Celery
rabbit:
hostname: rabbit
image: rabbitmq:3.7.3
environment:
– RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER}
– RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS}
#Flower UI for Celery queue
ui:
build: ./celery-ib
command: flower -A worker –port=5555 –broker=amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbit
environment:
– BROKER=amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbit
volumes:
– ./worker:/worker
– ./ibapi:/ibapi
ports:
– "127.0.0.1:5010:5555"
links:
– rabbit
depends_on:
– rabbit
#Python Celery worker
worker-1:
build: ./celery-ib
command: celery -A worker worker –loglevel=info -Ofair –concurrency=1
environment:
– BROKER=amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbit
– ECHEANCE_CAC40=${ECHEANCE_CAC40}
– ECHEANCE_DAX=${ECHEANCE_DAX}
– ECHEANCE_EUROSTOXX=${ECHEANCE_EUROSTOXX}
– ECHEANCE_FTSEMIB=${ECHEANCE_FTSEMIB}
volumes:
– ./worker:/worker
– ./ibapi:/ibapi
– ./logs:/logs
links:
– tws:tws
#Celery beat, to schedule periodic tasks
beat:
build: ./celery-ib
command: celery -A worker beat –loglevel=info
environment:
– BROKER=amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbit
links:
– rabbit
depends_on:
– postgres
– rabbit
volumes:
– ./worker:/worker
– ./ibapi:/ibapi
#Interactive Broker containerized API
tws:
build: ./ib-docker
ports:
– "127.0.0.1:4003:4003"
– "127.0.0.1:5900:5900"
volumes:
– ./ib-docker/IBController.ini:/opt/IBController/IBController.ini
– ./ib-docker/jts.ini:/root/Jts/jts.ini
environment:
– TZ=France/Paris
# Variables pulled from /IBController/IBControllerGatewayStart.sh
# Normally this script sets those environment variables and then launches DisplayBannerAndLaunch.sh.
# But now it's our runscript.sh that launches DisplayBannerAndLaunch.sh, so we set ourselves the environment variables
– VNC_PASSWORD=password
– TWS_MAJOR_VRSN=974
– IBC_INI=/opt/IBController/IBController.ini
– TRADING_MODE=paper
– IBC_PATH=/opt/IBController
– TWS_PATH=/root/Jts
– TWS_CONFIG_PATH=/root/Jts
– LOG_PATH=/opt/IBController/Logs
– TWSUSERID=XXX
– TWSPASSWORD=XXX
– FIXUSERID=
– FIXPASSWORD=
– JAVA_PATH=
– APP=GATEWAY
#Database
postgres:
image: postgres:9.6
restart: always
environment:
POSTGRES_DB: ${PG_DATABASE}
POSTGRES_USER: ${PG_USER}
POSTGRES_PASSWORD: ${PG_PASSWORD}
volumes:
– ./pgdata:/var/lib/postgresql/data
– ./db:/init
#Database web client
pgweb:
image: sosedoff/pgweb
command: pgweb –readonly –bind=0.0.0.0 –listen=8081
ports: ["127.0.0.1:8080:8081"]
links:
– postgres:postgres
environment:
– DATABASE_URL=postgres://${PG_USER}:${PG_PASSWORD}@postgres:5432/${PG_DATABASE}?sslmode=disable
depends_on:
– postgres
#Grafana to explore and visualise data
grafana:
image: grafana/grafana:5.0.3
restart: always
ports:
– "127.0.0.1:7000:3000"
volumes:
– ./grafana:/var/lib/grafana
environment:
– GF_SECURITY_ADMIN_USER=admin
– GF_SECURITY_ADMIN_PASSWORD=password

You’ll find the full github repo here, with instructions to get started:

https://github.com/francoisruty/fruty_trading-automation

Important disclaimer:

  • You cannot perform high-frequency trading with this setup. Interactive Broker is a broker, which means you’re not talking directly to the exchanges. Moreover, your internet connection is probably too slow compared to the big boys, and none of this code is optimized for speed.
  • You will probably not get rich with automated trading! Brokers usually sell their customers order books to bigger fishes before executing them! The optimal use-case for this tutorial is to automate mundane and repetitive trading actions in a context of a long-term trading strategy. If I reformulate: if you don’t already have a winning trading strategy, automation won’t give you one!

Leave a comment