-
Notifications
You must be signed in to change notification settings - Fork 18
Ibkr Ws Client
The IbkrWsClient
class provides an interface to the IBKR WebSocket API.
It shares some basic configuration with the IbkrClient
. See IBind Configuration - Construction Parameters section for more.
IbkrWsClient
is a concrete implementation of the WsClient
abstract base class, which is a collection of features that facilitate utilising a WebSocket API:
- Asynchronicity - handling the communication with the WebSocket API on a separate worker thread.
- Connection - taking care of establishing and re-establishing the WebSocket connection.
- Subscriptions - managing WebSocket channel subscriptions and distributing messages accordingly.
- Queues - providing an asynchronous interface to consume the data received.
On top of these base functionalities, the IbkrWsClient
implements message processing and connectivity handling specific to the IBKR WebSocket API.
See API Reference - IbkrWsClient for more.
See examples "ws_01_basic" and "ws_02_intermediate" which demonstrate various types of the WebSocket setup.
from ibind import IbkrWsKey, IbkrWsClient
# Construct the client. Assumes IBIND_ACCOUNT_ID and IBIND_CACERT environment variables have been set.
ws_client = IbkrWsClient(start=True)
# Choose the WebSocket channel
ibkr_ws_key = IbkrWsKey.PNL
# Subscribe to the PNL channel
ws_client.subscribe(channel=ibkr_ws_key.channel)
# Wait for new items in the PNL queue.
while True:
while not ws_client.empty(ibkr_ws_key):
print(ws_client.get(ibkr_ws_key))
Using the IbkrWsClient
involves handling three areas:
- Managing its lifecycle. It is asynchronous and it will run on a separate thread, hence we need to construct it, start it and then manage its lifecycle on the originating thread.
- Subscribing and unsubscribing. It is subscription-based, hence we need to specify which channels we want to subscribe to and remember to unsubscribe later.
- Consuming data. It uses a queue system, hence we need to access these queues and consume their data.
WebSocket interface is implemented in an asynchronous manner. IbkrWsClient
manages a lifecycle of a worker thread that handles the connectivity and message processing. We can control this lifecycle by starting and stopping the IbkrWsClient
.
We construct the IbkrWsClient
as follows:
from ibind import IbkrClient, IbkrWsClient, ibind_logs_initialize
ibind_logs_initialize()
ws_client = IbkrWsClient()
Having done that, we can manage the IbkrWsClient
's lifecycle:
ws_client.start()
print(ws_client.running) # True
ws_client.shutdown()
print(ws_client.running) # False
Outputs:
2024-04-26 17:02:34,248|I| IbkrWsClient: Starting
2024-04-26 17:02:34,248|I| IbkrWsClient: Trying to connect
2024-04-26 17:02:36,344|I| IbkrWsClient: Connection open
True
2024-04-26 17:02:36,359|I| IbkrWsClient: Shutting down
2024-04-26 17:02:36,365|I| IbkrWsClient: on_close
2024-04-26 17:02:36,366|I| IbkrWsClient: Connection closed
2024-04-26 17:02:36,366|I| IbkrWsClient: Gracefully stopped
False
Note:
- Passing an optional constructor argument
start=True
will cause the worker thread to start automatically when constructed.
IBKR WebSocket API requires us to utilise the tickle
REST endpoint in order to authenticate when connecting. IbkrWsClient
accepts an instance of IbkrClient
as a parameter in order to use it to handle this authentication automatically.
For simple cases, an instance of IbkrClient
is created automatically upon construction using the same common parameters as the IbkrWsClient
if none is provided as a parameter. In production, it is recommended that IbkrClient
is created explicitly and passed as an argument.
If we check the IBKR WebSocket documentation, we learn that interacting with the IBKR WebSocket channels involves sending appropriate string payloads over the WebSocket API. These payloads usually:
- Start with
s
for subscription andu
for unsubscription, eg.smd
andumd
. - Have a two-letter channel identifier, eg.
md
for market data. - Have additional arguments defined after a
+
plus symbol, eg.smd+conid+...
IbkrWsClient
handles these three points as follows when calling either subscribe
or unsubscribe
:
- Adding the
s
andu
prefix is handled automatically. - The channel identifier is passed as the required
channel
argument. - Additional payload arguments are passed as an optional
data
argument.
For example, in order to subscribe to the Live Order Updates, the documentation indicates that we need to send the following message:
sor+{}
This translates to:
- The
's'
prefix is expected. - The
channel
is'or'
. - There is no additional arguments that we would provide through
data
.
We subscribe to this channel using IbkrWsClient
in the following fashion:
ws_client.subscribe(channel='or', data=None)
Let's look at another example, this time subscribing to the Trades Data channel which accepts additional arguments:
str+{
"realtimeUpdatesOnly":realtimeUpdatesOnly,
"days":days
}
This translates to:
- The
's'
prefix is expected. - The
channel
is'tr'
. - We can pass
data
dictionary containingrealtimeUpdatesOnly
anddays
fields.
We subscribe to this channel using IbkrWsClient
in the following fashion:
ws_client.subscribe(channel='tr', data={'realtimeUpdatesOnly': True, 'days': 5})
The subscribe
and unsubscribe
methods accept an optional needs_confirmation
parameter. It indicates whether the client should expect a confirmation from the API, which is used to aid the subscriptions' lifecycle management.
ws_client.subscribe(channel='tr', data={'realtimeUpdatesOnly': True, 'days': 5}, needs_confirmation=True)
IBKR WebSocket API sends information back directly after subscribing - but only for some of its channels. Hence we can set needs_confirmation=False
when subscribing to channels that don't send any confirmation.
Contrarily, for channels that do provide a confirmation, IbkrWsClient
will expect a confirmation and attempt to re-subscribe if it is not received. Some channels in fact require two subscription requests to be sent before starting to send data, which is also facilitated by this confirmation and re-attempt functionality.
The IbkrWsClient
will derive whether a channel's subscription or unsubscription requires a confirmation if you don't explicitly specify it using the needs_confirmation
argument.
When valid WebSocket messages are received, the worker thread of IbkrWsClient
processes these and stores them in appropriate FIFO Queue
objects.
The queues in IbkrWsClient
are identified using the IbkrWsKey
enum. Each IBKR WebSocket channel has a corresponding IbkrWsKey
. Therefore, requesting data for a particular channel involves identifying it by passing the appropriate IbkrWsKey
when calling the new_queue_accessor
method in order to acquire a new QueueAccessor
object.
QueueAccessor
is a read-only wrapper around the Queue
object, employed to ensure that the write access to queues is only given to the IbkrWsClient
. It exposes two read functions from the Queue
object:
-
get
- for accessing data. Note that unlike theQueue.get
, this method is non-blocking by default, and returns aNone
instead of raisingEmpty
exception if no items are available. -
empty
- for checking if queue has items available.
To summarise, consuming data involves:
- Identifying the channel using the
IbkrWsKey
. - Acquiring a new
QueueAccessor
by passing the appropriateIbkrWsKey
to thenew_queue_accessor
method. - Using
QueueAccessor.get
method to receive items from the queue.
# acquire a new QueueAccessor
qa = ws_client.new_queue_accessor(IbkrWsKey.MARKET_DATA)
while True:
# get data from the queue
item = qa.get()
# process data
print(item)
For simple cases, you can use the IbkrWsClient.get(ibkr_ws_key:IbkrWsKey)
and IbkrWsClient.empty(ibkr_ws_key:IbkrWsKey)
methods to utilise the IbkrWsClient
's default internal QueueAccessor
objects. For production code, it is recommended to acquire a separate copy of QueueAccessor
using new_queue_accessor
.
IBKR WebSocket channel messages can be split into two categories.
- Solicited - received after having explicitly subscribed to a channel.
- Unsolicited - received without having requested any subscription.
The IbkrWsKeys
enum represents these types of messages as follows:
Subscription-based:
ACCOUNT_SUMMARY
ACCOUNT_LEDGER
MARKET_DATA
MARKET_HISTORY
PRICE_LADDER
ORDERS
PNL
TRADES
Unsolicited:
ACCOUNT_UPDATES
AUTHENTICATION_STATUS
BULLETINS
ERROR
SYSTEM
NOTIFICATIONS
Note that by default the unsolicited messages are not redirected to Queue
objects, but used internally by IbkrWsClient
only.
To access the unsolicited messages using the QueueAccessor
interface, pass a list of IbkrWsKey
as unsolicited_channels_to_be_queued
argument when constructing the IbkrWsClient
. Eg.:
ws_client = IbkrWsClient(..., unsolicited_channels_to_be_queued=[IbkrWsKey.ERROR, IbkrWsKey.AUTHENTICATION_STATUS])
qa = ws_client.new_queue_accessor(IbkrWsKey.ERROR)
IbkrWsKey
enum also contain confirms_subscribing
and confirms_unsubscribing
properties that specify whether a particular channel sends confirmation data when subscribing and unsubscribing.
These values are hard coded, hence should be used with caution should the IBKR WebSocket API change.
They are used when automatically deriving the needs_confirmation
parameter if not provided to the subscribe
or unsubscribe
methods.
IbkrWsClient
automatically performs two type of health checks:
- pinging the WebSocket API on an interval
- observing the heartbeat data which is automatically sent by the API
If it detects unusual behaviour in either of these two metrics, it will automatically attempt to close and re-establish a connection to the API.
You can modify the following parameters to adjust this behaviour:
-
ping_interval
- how often to ping the WebSocket API, in seconds. -
max_ping_interval
- how many seconds to wait for the ping response and between heartbeats. If pings or heartbeats happen less frequently than this value, the connection is marked as unhealthy and will be re-established.
Learn about the Advanced WebSocket.
See any error on this page? Create an Issue and let us know.