pythontws

Return price as an object/variable using Interactive Brokers TWS API - Python


As the title indicates, I am trying to get the price of a given security from TWS API and use it as a variable in my program elsewhere. The below code (straight from one of Interactive Broker's tutorials) will run and print the price out on the screen but I haven't been able to alter it in a way that works in getting to create a variable/object that contains the price. The code also only works about once in every ten tries, if there is something I'm doing wrong there, please let me know.

from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
from ibapi.ticktype import TickTypeEnum


class TestApp(EWrapper, EClient):
    def __init__(self):
        EClient.__init__(self, self)

    def error(self, reqId, errorCode, errorString):
        print('Error: ', reqId, ' ', errorCode, ' ', errorString)

    def tickPrice(self, reqId, tickType, price, attrib):
            print('Tick Price. Ticker Id:', reqId, 'tickType:', TickTypeEnum.to_str(tickType), 
            'Price:', price, end=' ')


def main():
    app = TestApp()

    app.connect('127.0.0.1', 7496, 0)

    contract = Contract()
    contract.symbol = 'AAPL'
    contract.secType = 'STK'
    contract.currency = 'USD'
    contract.exchange = 'SMART'
    
    app.reqMarketDataType(1)
    app.reqMktData(1, contract, '', False, False, [])

    app.run()


if __name__ == '__main__':
    main()

Solution

  • The program doesn't take into account the asynchronous nature of the api.

    #here you are asking to connect, you must wait for a connection
    app.connect('127.0.0.1', 7496, 0)
    
    contract...
    # you may not have a connection and you're not listeneing for responses yet.
    app.reqMarketDataType(1)
    app.reqMktData(1, contract, '', False, False, [])
    
    # here is where you start listening for responses, that's what run() does
    app.run()
    

    I would write it like this

    from ibapi.client import EClient
    from ibapi.wrapper import EWrapper
    from ibapi.contract import Contract
    from ibapi.ticktype import TickTypeEnum
    from ibapi.common import *
    
    class TestApp(EWrapper, EClient):
        def __init__(self):
            EClient.__init__(self, self)
            # here you can add variables to the TestApp class, just use self.var in the class
            self.last = 0;
            
        # ib suggests waiting for this response to know that you have a connection
        def nextValidId(self, orderId:int):    
            self.reqMarketDataType(MarketDataTypeEnum.REALTIME) # or DELAYED
            contract = Contract()
            contract.symbol = "AAPL"
            contract.secType = "STK"
            contract.currency = "USD"
            contract.exchange = "SMART"
            self.reqMktData(1, contract, "", False, False, None)
    
        def error(self, reqId, errorCode, errorString):
            print('Error: ', reqId, ' ', errorCode, ' ', errorString)
    
        def tickPrice(self, reqId, tickType, price, attrib):
                print('Tick Price. Ticker Id:', reqId, 'tickType:', TickTypeEnum.to_str(tickType), 
                'Price:', price)
                if tickType == TickTypeEnum.LAST or tickType == TickTypeEnum.DELAYED_LAST :
                    self.last = price;
                    print("disconnecting")
                    self.disconnect() # just for testing, normally the program would do something
    
    def main():
        app = TestApp()
        app.connect('127.0.0.1', 7497, 123)
        app.run() # this blocks the program until disconnect()
        print("app.last:", app.last) # now you refer to the variable by class.var
    
    if __name__ == '__main__':
        main()