Skip to content

Ibkr Ws Client

voyz edited this page May 2, 2024 · 21 revisions

IbkrWsClient

The IbkrWsClient class provides an interface to the IBKR WebSocket API.

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.

Basic Example

See examples "ws_01_basic" and "ws_02_intermediate" which demonstrate various types of the WebSocket setup.

from ibind import IbkrWsKey, IbkrClient, IbkrWsClient, ibind_logs_initialize

ibind_logs_initialize()

# create the clients
client = IbkrClient()
ws_client = IbkrWsClient(ibkr_client=client)

# start the IbkrWsClient's thread
ws_client.start()

# acquire a queue accessor
qa = ws_client.new_queue_accessor(IbkrWsKey.ORDERS)

# subscribe to a channel
ws_client.subscribe(channel='or', data=None, needs_confirmation=False)

# consume the data
while True:
    try:
        if qa.empty():
            continue

        print(qa.get())

    except KeyboardInterrupt:
        break

# tidy up
ws_client.unsubscribe(channel='or', data=None, needs_confirmation=False)

# shut down the worker thread
ws_client.shutdown()

Using IbkrWsClient

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.

Managing the Lifecycle

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()

client = IbkrClient()
ws_client = IbkrWsClient(ibkr_client=client)

Note that we pass an instance of IbkrClient class as an argument. This is due to the fact that IBKR WebSocket API requires us to utilise the tickle REST endpoint in order to authenticate when connecting. IbkrWsClient uses the IbkrClient to handle this automatically.

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

Subscribing and Unsubscribing

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 and u for unsubscription, eg. smd and umd.
  • 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 and u prefix is handled automatically.
  • The channel identifier is passed as the required channel argument.
  • Additional payload arguments are passed as an optional data argument.

Live Order Updates example

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, needs_confirmation=False)

Trades Data example

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 containing realtimeUpdatesOnly and days fields.

We subscribe to this channel using IbkrWsClient in the following fashion:

ws_client.subscribe(channel='tr', data={'realtimeUpdatesOnly': True, 'days': 5}, needs_confirmation=False)

Receiving Confirmation

The subscribe and unsubscribe methods accept needs_confirmation parameter (True by default). It indicates whether the client should expect a confirmation from the API, which is used to aid the subscriptions' lifecycle management.

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.

Consuming Data

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 the Queue.get, this method is non-blocking by default, and returns a None instead of raising Empty 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 appropriate IbkrWsKey to the new_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)

IbkrWsKeys

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.:

IbkrWsClient(..., unsolicited_channels_to_be_queued=[IbkrWsKey.ERROR, IbkrWsKey.AUTHENTICATION_STATUS])

Health Monitoring

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.

Next

Learn about the Advanced WebSocket.