request to IB responds differently when in a loop

ⅰ亾dé卋堺 提交于 2021-02-08 08:26:20

问题


I just don't understand what is going on here. I'm a bit new to wrappers and IB. Here is my function that overrides and parses the XML. Should the XML be parsed in the main? I just want to get the fundamental data for all the symbols in my CSV file. When I comment out the loop and run an individual request it works, sometimes. What's going on here?

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

        self.csv = pd.read_csv('getfund.csv');

        self.symbols = self.csv['Symbol']
        self.exchanges = self.csv['Exchange']

    def nextValidId(self, reqId: int):
        reqs_do()

    def reqs_do():

        for i in range(len(symbols)):
            contract = Contract()
            contract.symbol = self.symbols[i]
            contract.secType = "STK"
            contract.exchange = self.exchanges[i]
            contract.currency = "USD"
            contract.primaryExchange = "NASDAQ"

            print(contract)
            print(symbols[i])
            print(exchanges[i])
            print(i)

            app.reqFundamentalData(8001 , contract, "ReportsFinSummary", [])

    def error(self, reqID:TickerId, errorCode:int, errorString:str):
        print("Error: ", reqID, " " , errorCode, " ", errorString)

    def fundamentalData(self, reqId: TickerId, data: str):
        super().fundamentalData(reqId, data)
        print("FundamentalData. ", reqId, data)

        e = ET.fromstring(data)

        e.tag

        e.attrib

        ##code to parse the XML file returned from the ib reqfundamentaldata function.
        ##there are multiple report types and I am uncertain what they mean, worth
        ##looking into later.

        row_list = []
        for atype in e.iter('TotalRevenue'):
            if atype.attrib['reportType']=="A"  and atype.attrib['period']=="3M":
                dict1 = {"Date": atype.attrib['asofDate'], 
                                      'Revenue':atype.text}
                row_list.append(dict1)

        columns = ['Date', 'Revenue']
        tr = pd.DataFrame(row_list, columns=columns)


        row_list = []
        for atype in e.iter('DividendPerShare'):
            if atype.attrib['reportType']=="A" and atype.attrib['period']=="3M":
                dict1 = {"Date": atype.attrib['asofDate'],
                                      'Dividend':atype.text}
                row_list.append(dict1)

        columns = ['Date', 'Dividend']
        dps = pd.DataFrame(row_list, columns=columns)


        row_list = []
        for atype in e.iter('EPS'):
            if atype.attrib['reportType']=="A" and atype.attrib['period']=="3M":
                dict1 = {"Date": atype.attrib['asofDate'],
                                      'EPS':atype.text}
                row_list.append(dict1)

        columns = ['Date', 'EPS']
        eps = pd.DataFrame(row_list, columns=columns)

        temp = tr.merge(dps, left_on='Date', right_on='Date', how='left')
        fin = temp.merge(eps, left_on='Date', right_on='Date', how='left')

        print(fin)
        #fin.to_csv("fin.csv", sep=',')

        if done: app.disconnect()


def main():
    app = TestApp()
    app.connect("127.0.0.1", 4001, clientId=123)
    print("serverVersion:%s connectionTime:%s" % (app.serverVersion(),
                                                app.twsConnectionTime()))
    app.run()


if __name__ == "__main__":
    main()

回答1:


Your logic has you connecting and then requesting all the data while waiting 60 secs in between requests....and then listening for a response (app.run).

app.run() starts a loop reading from the socket for responses. Without some threading, you can't do anything after this call. You can just write the program to do things in an asynchronous manner.

Once you've got a connection with a read loop running, you can start asking for data. nextValidId is used for this purpose as it's the first response sent by TWS(IBG). So in nextValidId you really start your program.

Your code will never get to disconnect since app.run() is blocking the thread. You need to use async logic to decide when to disconnect. In this simple case just count requests.

Not a real program, just to illustrate the logic flow.

class TestApp(EWrapper, EClient):
    def __init__(self):
        #other stuff
        #make the following members instead of global
        self.csv = pd.read_csv('getfund.csv');
        self.symbols = csv['Symbol']
        self.exchanges = csv['Exchange']

    def nextValidId(self, reqId: int):
        reqs_do()

    def reqs_do():
        for i in range(len(symbols)):
            # same, just use self.symbols etc..

    def fundamentalData(self, reqId: TickerId, data: str):
        # similar but disconnect after everything has been rec'd 
        if done: app.disconnect()

def main():
    app = TestApp()
    app.connect("127.0.0.1", 4001, clientId=123)
    app.run()

if __name__ == "__main__": main()
  • The program will work by first creating TestApp and reading the csv file.
  • Then you will connect via socket to IB Gateway but not be reading until app.run().
  • As soon as you start reading you will rec'v nextValidId and some informational error messages about the connection.
  • Once nextValidiD is rec'd you start asking for data.
  • It can come at anytime but usually right away except maybe friday nights when everything is closed.
  • If rec'd it will print in the data callback you have.


来源:https://stackoverflow.com/questions/49329139/request-to-ib-responds-differently-when-in-a-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!