Search code examples
pythonib-api

Why am I getting "ERROR 508 322 Error processing request.-'bW' : cause - Duplicate ticker id" in IB API / TWS (Python)


I'm trying to print the opening price of SPY (hence the tick of 14), similar to printing the account balance in USD. Printing the account balance worked prior to adding code trying to print the SPY price. However, I'm now getting "ERROR 508 322 Error processing request.-'bW' : cause - Duplicate ticker id". I don't understand why as I don't appear to have a duplicate ticker id in my code and I have restarted TWS and PyCharm.

from ibapi.client import EClient  # handles outgoing requests
from ibapi.wrapper import EWrapper  # handles incoming messages
from ibapi.contract import Contract
import threading
import time

class IBapi(EWrapper, EClient):
    def __init__(self):
        EClient.__init__(self, self)
        self.contract_details = {}
        self.bardata = {}  # initialise directory to store bar data
        self.USD_cash_balance = 0
        self.spyprice = 0

    def tickPrice(self, reqId, tickType, price, attrib):  # this function prints the price
        if tickType == 2 and reqId == 1:  # tickType 2 is ask price: lowest price offer on the contract
            print(price)
        if tickType == 14 and reqId == 508:
            self.spyprice = price

    def nextValidId(self, orderId: int):
        super().nextValidId(orderId)
        self.nextorderId = orderId
        print('The next valid order id is: ', self.nextorderId)

    def accountSummary(self, reqId: int, account: str, tag: str, value: str,
                       currency: str):
        if tag == "CashBalance" and reqId == 131:
            self.USD_cash_balance = value

def run_loop():
    app.run()  # starts communication with TWS


app = IBapi()
app.nextorderId = None
app.connect('127.0.0.1', 7497, 123)

# Start the socket in a thread
api_thread = threading.Thread(target=run_loop, daemon=True)  # Algo doesn't have "daemon=True"
api_thread.start()

# Check if the API is connected via orderid
while True:
    if isinstance(app.nextorderId,
                  int):  # the IB API sends out the next available order ID as soon as connection is made
        # The isinstance() function returns True if the specified object is of the specified type, otherwise False.
        print('connected')
        break
    else:
        print('waiting for connection')
        time.sleep(2)

# getting strike prices
spystock_contract = Contract()
spystock_contract.symbol = "SPY"
spystock_contract.secType = "STK"
spystock_contract.exchange = "SMART"
spystock_contract.currency = "USD"

while not getattr(app, 'spyprice', None):
    app.reqMktData(508, spystock_contract, "", False, False, [])
    time.sleep(0.5)

print("The opening price of SPY is $" + str(app.spyprice))

while not getattr(app, 'USD_cash_balance', None):
    app.reqAccountSummary(131, "All", "$LEDGER:USD")
    time.sleep(0.5)

print("My account balance in USD is: " + str(app.USD_cash_balance))

time.sleep(3)
print("Disconnecting.")
app.disconnect()

Solution

  • You can only call this app.reqMktData(508, spystock_contract, "", False, False, []) once, not every .5 seconds. Put it in the nextVaildId method so it only gets called once and it's called after connection.

    There is no need for sleep, just do things in an asynchronous manner like the API is intended to be used. One way is to do things after nextValidId is called by the API.

    def nextValidId(self, orderId: int):
        super().nextValidId(orderId)
        self.nextorderId = orderId
        print('The next valid order id is: ', self.nextorderId)
        spystock_contract = Contract()
        spystock_contract.symbol = "SPY"
        spystock_contract.secType = "STK"
        spystock_contract.exchange = "SMART"
        spystock_contract.currency = "USD"
        app.reqMktData(508, spystock_contract, "", False, False, [])