IB API Python sample not using Ibpy

前端 未结 4 1591
我寻月下人不归
我寻月下人不归 2020-12-13 22:27

Can someone help me to figure out how to do basic request by using IB API Python socket? (I am using the latest IB API and it seems it support Python so should not need the

相关标签:
4条回答
  • 2020-12-13 22:46

    This is example how to process API messages using multithreading. The app.run() is started as separate thread and it is listening to TWS API responses. The main program then sends 5 requests for ContractDetails and then main program is 10 seconds waiting for responses. TWS API messages are stored within app instance and simple semaphore signals when the response is ready to be processed.

    It is my first multithreading program, any comments are welcome.

    from ibapi import wrapper
    from ibapi.client import EClient
    from ibapi.utils import iswrapper #just for decorator
    from ibapi.common import *
    from ibapi.contract import *
    from ibapi.ticktype import *
    #from OrderSamples import OrderSamples
    import threading
    import time
    
    class myThread (threading.Thread):
       def __init__(self, app, threadID, name):
          threading.Thread.__init__(self)
          self.threadID = threadID
          self.name = name
          self.app = app
    
       def run(self):
          print ("Starting application in separate thread:", self.name,     "threadID:", self.threadID  )
          self.app.run()
          print ("Exiting " + self.name)
    
    class TestApp(wrapper.EWrapper, EClient):
        def __init__(self):
            wrapper.EWrapper.__init__(self)
            EClient.__init__(self, wrapper=self)
            self.started = False
            self.nextValidOrderId = 0
            self.reqData = {}       # store data returned by requests
            self.reqStatus = {}     # semaphore of requests - status End will indicate request is finished
    
    
    @iswrapper
    def nextValidId(self, orderId:int):
        print("setting nextValidOrderId: %d", orderId)
        self.nextValidOrderId = orderId
    
    
    @iswrapper
    def error(self, reqId:TickerId, errorCode:int, errorString:str):
        print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)
    
    @iswrapper
    # ! [contractdetails]
    def contractDetails(self, reqId: int, contractDetails: ContractDetails):
        super().contractDetails(reqId, contractDetails)
        # store response in reqData dict, for each request several objects are appended into list
        if not reqId in self.reqData:
            self.reqData[reqId] = []
        self.reqData[reqId].append(contractDetails) # put returned data into data storage dict
        # ! [contractdetails]
    
    @iswrapper
    # ! [contractdetailsend]
    def contractDetailsEnd(self, reqId: int):
        super().contractDetailsEnd(reqId)
        print("ContractDetailsEnd. ", reqId, "\n")  # just info
        self.reqStatus[reqId] = 'End'               # indicates the response is ready for further processing
        # ! [contractdetailsend]
    
    
    
    def main():
    
        app = TestApp()
        app.connect("127.0.0.1", 4001, clientId=123)
        print("serverVersion:%s connectionTime:%s" % (app.serverVersion(),
                                                app.twsConnectionTime()))
    
        thread1App = myThread(app, 1, "Thread-1")  # define thread for sunning app
        thread1App.start()                         # start app.run(] as infitnite loop in separate thread
    
        print('Requesting MSFT contract details:')
        contract = Contract()
        contract.symbol = "MSFT"
        contract.secType = "STK"
        contract.currency = "USD"
        contract.exchange = ""
        app.reqStatus[210] = 'Sent'   # set request status to "sent to TWS"
        app.reqContractDetails(210, contract)
    
        print('Requesting IBM contract details:')
        contract.symbol = "IBM"
        app.reqStatus[211] = 'Sent'          
        app.reqContractDetails(211, contract)
    
        print('Requesting IBM contract details:')
        contract.symbol = "GE"
        app.reqStatus[212] = 'Sent'
        app.reqContractDetails(212, contract)
    
        print('Requesting IBM contract details:')
        contract.symbol = "GM"
        app.reqStatus[213] = 'Sent'
        app.reqContractDetails(213, contract)
    
        print('Requesting IBM contract details:')
        contract.symbol = "BAC"
        app.reqStatus[214] = 'Sent'
        app.reqContractDetails(214, contract)
    
        i = 0
        while i < 100:         # exit loop after 10 sec (100 x time.sleep(0.1)
            i = i+1
            for reqId in app.reqStatus:
                if app.reqStatus[reqId] == 'End':
                    for contractDetails in app.reqData[reqId]:
                        print("ContractDetails. ReqId:", reqId, contractDetails.summary.symbol,
                      contractDetails.summary.secType, "ConId:", contractDetails.summary.conId,
                      "@", contractDetails.summary.exchange)
                    app.reqStatus[reqId] = 'Processed'
            time.sleep(0.1)
        app.done = True             # this stops app.run() loop
    
    if __name__ == "__main__":
        main()
    
    0 讨论(0)
  • 2020-12-13 22:49

    You have to subclass/override/implement the wrapper.EWrapper. That's where you're telling EClient to send the data received from TWS.

    I removed almost everything from the sample program and this runs.

    from ibapi import wrapper
    from ibapi.client import EClient
    from ibapi.utils import iswrapper #just for decorator
    from ibapi.common import *
    from ibapi.contract import *
    from ibapi.ticktype import *
    
    class TestApp(wrapper.EWrapper, EClient):
        def __init__(self):
            wrapper.EWrapper.__init__(self)
            EClient.__init__(self, wrapper=self)
    
        @iswrapper
        def nextValidId(self, orderId:int):
            print("setting nextValidOrderId: %d", orderId)
            self.nextValidOrderId = orderId
            #here is where you start using api
            contract = Contract()
            contract.symbol = "AAPL"
            contract.secType = "STK"
            contract.currency = "USD"
            contract.exchange = "SMART"
            self.reqMktData(1101, contract, "", False, None)
    
        @iswrapper
        def error(self, reqId:TickerId, errorCode:int, errorString:str):
            print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)
    
        @iswrapper
        def tickPrice(self, reqId: TickerId , tickType: TickType, price: float,
                      attrib:TickAttrib):
            print("Tick Price. Ticker Id:", reqId, "tickType:", tickType, "Price:", price)
            #this will disconnect and end this program because loop finishes
            self.done = True
    
    def main():
        app = TestApp()
        app.connect("127.0.0.1", 7496, clientId=123)
        print("serverVersion:%s connectionTime:%s" % (app.serverVersion(),
                                                    app.twsConnectionTime()))
        app.run()
    
    if __name__ == "__main__":
        main()
    

    Once you call app.run() the program starts an almost infinite loop reading messages so you'll need some other way to structure your program since the loop must be started.

    0 讨论(0)
  • 2020-12-13 22:55

    I have looked for the way how to process sequence of requests outside of app object.

    This is my little modification of Brian's code (thanks Brian for the introduction how to work with it), which gets two contract details, first it requests contractdetails for MSFT, then for IBM.

    • app.run() is finished after receiving all contractDetails messages by setting app.done = True in contractDetailsEnd() method
    • when app.done is set to True, the client disconnects in EClient.run() method. I did not figured out how to quit the EClient.run() method without disconnecting, so I changed exiting in the source code in the client.py EClient.run() method:

      finally:
          #self.disconnect() # Myk prevent disconnect
          return  #Myk instead of disconnect return
      
    • after that app.run() is finished without disconnection and can be called again, but you must set app.done to False first, otherwise the run() method exits
    • you must disconnect at the end bye yourself
    • disconnect() method throws error, but as someone said it seems you can ignore it, especially if you disconnect at the end of your code

    If someone knows better way without changing API source code, I'll be glad if you give me an advice.

    Here is the code:

    from ibapi import wrapper
    from ibapi.client import EClient
    from ibapi.utils import iswrapper #just for decorator
    from ibapi.common import *
    from ibapi.contract import *
    from ibapi.ticktype import *
    
    class TestApp(wrapper.EWrapper, EClient):
        def __init__(self):
            wrapper.EWrapper.__init__(self)
            EClient.__init__(self, wrapper=self)
            self.reqIsFinished = True
            self.started = False
            self.nextValidOrderId = 0
    
        @iswrapper
        def nextValidId(self, orderId:int):
            print("setting nextValidOrderId: %d", orderId)
            self.nextValidOrderId = orderId
            # we can start now
    
        @iswrapper
        def error(self, reqId:TickerId, errorCode:int, errorString:str):
            print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " ,     errorString)
    
        @iswrapper
        # ! [contractdetails]
        def contractDetails(self, reqId: int, contractDetails: ContractDetails):
            super().contractDetails(reqId, contractDetails)
            print("ContractDetails. ReqId:", reqId, contractDetails.summary.symbol,
                  contractDetails.summary.secType, "ConId:", contractDetails.summary.conId,
              "@", contractDetails.summary.exchange)
            # ! [contractdetails]
    
        @iswrapper
        # ! [contractdetailsend]
        def contractDetailsEnd(self, reqId: int):
            super().contractDetailsEnd(reqId)
            print("ContractDetailsEnd. ", reqId, "\n")
            self.done = True  # This ends the messages loop
            # ! [contractdetailsend]
    
    def main():
        app = TestApp()
        app.connect("127.0.0.1", 4001, clientId=123)
        print("serverVersion:%s connectionTime:%s" % (app.serverVersion(),
                                                app.twsConnectionTime()))
    
        print('MSFT contract details:')
        contract = Contract()
        contract.symbol = "MSFT"
        contract.secType = "STK"
        contract.currency = "USD"
        contract.exchange = ""
        app.reqContractDetails(210, contract)
        app.run()
    
        print('IBM contract details:')
        contract.symbol = "IBM"
        app.done = False # must be set before next run
        app.reqContractDetails(210, contract)
        app.run()
    
        app.disconnect() 
    
    if __name__ == "__main__":
        main()
    
    0 讨论(0)
  • 2020-12-13 22:57

    There is a new project which simplifies work with Python TWS Api.

    It is called IB-insync and it allows both sync and async processing. It looks very great for newbies in TWS API. Link to Project Page

    Example of requesting historical data using IB-insync:

    from ib_insync import *
    
    ib = IB()
    ib.connect('127.0.0.1', 7497, clientId=1)
    
    contract = Forex('EURUSD')
    bars = ib.reqHistoricalData(contract, endDateTime='', durationStr='30 D',
        barSizeSetting='1 hour', whatToShow='MIDPOINT', useRTH=True)
    
    # convert to pandas dataframe:
    df = util.df(bars)
    print(df[['date', 'open', 'high', 'low', 'close']])
    
    0 讨论(0)
提交回复
热议问题