Skip to content

Instantly share code, notes, and snippets.

@ReliefCrew
Last active April 1, 2023 04:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ReliefCrew/1376d483abeb8e4641b038aa41e2424e to your computer and use it in GitHub Desktop.
Save ReliefCrew/1376d483abeb8e4641b038aa41e2424e to your computer and use it in GitHub Desktop.
Interactive Brokers API race condition

Summary

In the 10.x versions of TraderWorkstation (TWS) and IBGateway software, non-simultaneous clients with ID numbers different from the first don't receive price ticks for a given stock after requesting them.

Note: additional public discussion is occuring here.

Details

After signing into the IBGateway (or TWS) application, a client program can't get ticks for a stock unless subsequent invocations use the same client ID as the first instance which requested those ticks.

In other words, during the same login session (without exiting the IBGateway/TWS software)... if a client program connects with ID #5 and receives ticks for IBM and subsequently exits, then any new client that requests ticks for IBM needs to connect using ID #5. If the original client ID number is not used, price ticks for IBM won't be received.

It's important to note that the first client needs to exit before the second client connects. That is, the clients must be not be running simultaneously. Presumably this is because there's a faulty destructor which will not get executed until a reference count reaches zero.

Also, this only happens for tick requests on the same prior stock. If tick data for a different stock are requested there isn't a problem.

Condition

The API documentation states that up to 32 clients can be connected simultaneously. This implies different clients should be able to get ticks for the same stock. It seems fair that the same should hold true for non-simultaneous clients.

Additionally, the 9.x branch of the IBGateway/TWS API does not suffer from the issue but performs as expected.

Example

The Python code at the bottom demonstrates the issue. Run it once and it will exit cleanly as soon as it receives a price tick. But, run it again and it will hang forever waiting for ticks (and must be interrupted with ctrl-C or killed). If the client ID # in the connection is hard-coded (for example), then there isn't a problem.

Finally, comments on this gist are appreciated; especially if you're an Interactive Brokers customer who is affected or can confirm the bug. If you want to experiment but don't have an Interactive Brokers account you may open a free trial account.

Instructions

  1. Copy/paste the code below and save it into an example.py file.
  2. Unzip either the stable or latest API Bundle and symlink (or copy) the directory IBJts/source/pythonclient/ibapi alongside example.py (i.e. into the same location).
  3. Login to TWS or IBGateway and enable socket clients using the settings.
  4. If necessary, change the port number and/or IP address used in the example code to match your system settings. The default port for TWS is 7496 but for the IBGateway it is 4001.
  5. Run the example (twice) using the command python3 ./example.py.
  6. Observe whether the second execution receives a price tick for IBM. If not, then the bug is confirmed.

Code

import os
from ibapi.client import *
from ibapi.wrapper import *

port = 4001   # IBGateway default, TWS default is 7496

class TestApp(EClient, EWrapper):
    def __init__(self):
        EClient.__init__(self, self)
    def nextValidId(self, orderId: OrderId):
        mycontract = Contract()
        mycontract.symbol = "VZ"
        mycontract.secType = "STK"
        mycontract.exchange = "SMART"
        mycontract.currency = "USD"

        # self.reqMarketDataType(4)    # uncomment when using a trial acct
        self.reqMktData(
            reqId=os.getpid(),
            contract=mycontract,
            genericTickList="",
            snapshot=False,
            regulatorySnapshot=False,
            mktDataOptions=[],
        )
    def tickPrice(
            self,
            reqId: TickerId,
            tickType: TickType,
            price: float,
            attrib: TickAttrib,
    ):
        print( "Got a price tick, exiting." )
        self.disconnect()
    def error(self, reqId: TickerId, errorCode: int,
              errorString: str, advancedOrderRejectJson = ""):
        print(errorString)

app = TestApp()
app.connect("127.0.0.1", port, os.getpid())   # using PID as client ID
app.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment