This package exposes the Interactive Brokers TWS Python API as service. Two options are provided:
- An asyncio API is provided which can be directly used to implement a service
- A GraphQL endpoint that internally uses the asyncio API is provided, that allows running the TWS API as a GraphQL server.
This work is distinct from other projects in that it doesn't attempt to re-implement the TWS API. Instead, the TWS API is used as is, and the service is implemented through code generation and supporting classes.
Testing has been performed with TWS API 9.85.1.
The following section describes the Interactive Brokers TWS API setup and notes that describe the TWS API, which were useful in the implementation of this package.
Interactive Brokers does not allow redistribution of the TWS API so it needs to be setup by accepting the license agreement via the following steps:
-
After unzipping the downloaded file, follow the steps in the
source/pythonclient/README.md
. At the time of the writing the API can be setup by running the following commands in thesource/pythonclient
directory of the unzipped folder:- Install the wheel module via
pip3 install wheel
- python3 setup.py bdist_wheel
- python3 -m pip install --user --upgrade dist/ibapi-*-py3-none-any.whl
- Install the wheel module via
The following are notes that characterize the TWS API to help with the design of this project.
The TWS API uses the following threading model:
- Messages from TWS are read from the socket via the
EReader
class in a dedicated thread and pushed into a queue. - Messages to TWS can be sent from any thread via the
EClient
class, but is a blocking operation. - The main event loop for the
EClient
class, is responsible for decoding messages sent by TWS by taking them out of the queue used byEReader
. The dequeue operation is blocking, so theEClient
event loop is typically run in a dedicated thread.
The TWS API uses the following request/response patterns:
-
Queries that have a single item response. These have a single method to make a request, an optional method to cancel the request and a single callback for the response.
Example:
EClient.reqCurrentTime
andEWrapper.currentTime
-
Queries that have a response consisting of a list of items. These have a single method to make a request, an optional method to cancel the request, and one or more callbacks for each item. Additionally, there's a method or flag in the callbacks to signal the end of the list.
Example:
EClient.reqPositions
,EClient.cancelPositions
,EWrapper.position
,EWrapper.positionEnd
-
Subscriptions. These have a single method to start the subscription, a method to stop the subscription, and one or more callbacks for status updates.
Example"
EClient.reqTickByTickData
,EClient.tickByTickAllLast
,EWrapper.tickByTickBidAsk
EClient.cancelTickByTickData
-
A variant of this pattern are requests that take a requestId parameter. This allows the same type of request to be issued with different parameters
Example:
EClient.reqPositionsMulti
,EClient.cancelPositionsMulti
,EWrapper.positionMulti
,EWrapper.positionMultiEnd
-
Fire and forget requests. These have a single request method.
Example:
EClient.setServerLogLevel
. -
A one-off pattern that wraps both a subscription and query in a single request call controlled by a flag.
Example
EClient.reqHistoricalTicks
,EWrapper.historicalTicks
,EWrapper.historicalTicksBidAsk
,EWrapper.historicalTicksLast
The code generation is implemented as part of the codegen
module and can be run via ib_tws_server/codegen/main.py.
The code generator assumes the TWS API is available as part of the python module search path. The TWS API version that the generator uses can be changed by modifying the module path via overriding the PYTHONPATH
environment variable, using Python virtual environments, etc.
The code generator uses definitions captured in ib_tws_server/api_definition.py. These definitions describe the TWS API in terms of patterns described in the TWS API Patterns section.
The generated files are in the ib_tws_server/gen
directory.
The following files are generated:
gen/client_responses.py
:- Contains classes used for responses from the TWS API. The following types/classes are generated:
- Top-Level Unions:
- For requests that have more than one callback, and have complex responses that return more than one parameter, a Union type is generated to encapsulate all the different return types for a request
- Callback Classes:
- A top-level class is generated for every request that has one or more callbacks that return more than one value.
- For callbacks for queries the response class has the name
{RequestName}Response
- Additional classes are generated that encapsulate the parameters for each of the callbacks when the callbacks return one or more parameters
gen/asyncio_client.py
:- Contains the AsyncioClient class which subclasses the
ibapi.client.EClient
to provide an asyncio API around the TWS API - All request methods are asynchronous and declared using
async
- Subscriptions return a
SubscriptionGenerator
instance is anAsyncGenerator
- Request ids of the original TWS API are implicitly managed.
- Currently only subscriptions can be cancelled. Even though TWS API allows cancelling queries with multiple responses this is not exposed as part of the API.
- Contains the AsyncioClient class which subclasses the
- Other improvements
- Errors from TWS are propagated via exceptions
- To avoid blocking the asyncio running loop, an
IBWriter
class to send messages to TWS in a separate thread.
gen/asyncio_wrapper.py
:- Subclasses
ibapi.client.EWrapper
and used internally by theAsyncioClient
class
- Subclasses
gen/schema.graphql
: The GraphQL schemagen/graphql_resolver.py
: GraphQL resolvers
- TWS API
- ib_insync library
- Posts by Juri Sarbach to deploy TWS as a microservice:
- Guide to Interactive Brokers API Code
- Build a GraphQL API with Subscriptions using Python, Asyncio and Ariadne
- Introduction to Generators
- GraphQL Configuration
- Resolving Union Types in Ariadne