Search code examples
pythonapiinteractive-brokerstws

TWS API frezee when receiving errors


I am writing a service to work through the TWS API based on the Python language. Faced a problem when getting historical data. The bottom line is that when you request app.reqHistoricalData() with the correct parameters, the script runs without problems and exits after execution. If false parameters are passed to app.reqHistoricalData() (for example, contract.currency = 'US'), then in this case I get an error in the console "ERROR 123 321 Request validation failed. -'bS' : cause - US currency is not allowed" . The script does not end and hangs in this state until it is stopped manually. Perhaps you have encountered such a problem and can advise how to handle such situations? Below is the code for my script.

from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
from ibapi.order import Order
import pandas as pd
import threading
import time


class IBapi(EWrapper, EClient):
    def __init__(self):
        EClient.__init__(self, self)
        self.open_orders = list()
        self.historical_data = list()
        self.df = None
        self.data_end = False

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

    def openOrder(self, orderId, contract, order, orderState):
        # print('OpenOrder ID:', orderId, contract.symbol, contract.secType, '@', contract.exchange, ':', order.action,
        #       order.orderType, order.totalQuantity, orderState.status)
        self.open_orders.append(('OpenOrder ID:', orderId, contract.symbol, contract.secType, '@',
                                 contract.exchange, ':', order.action, order.orderType, order.totalQuantity,
                                 orderState.status))

    def historicalData(self, reqId, bar):
        self.historical_data.append(vars(bar))

    def historicalDataUpdate(self, reqId, bar):
        line = vars(bar)
        self.df.loc[pd.to_datetime(line.pop('date'))] = line

    def historicalDataEnd(self, reqId: int, start: str, end: str):
        self.df = pd.DataFrame(self.historical_data)
        self.data_end = True


class Connect:
    app = None

    def __init__(self):
        self.app = IBapi()
        self.app.connect('127.0.0.1', 7497, 123)
        # self.app.nextorderId = None

        # Start the socket in a thread
        api_thread = threading.Thread(target=self.run_loop, daemon=True)
        api_thread.start()

        time.sleep(1)  # Sleep interval to allow time for connection to server
        print('Соединились')

    def run_loop(self):
        self.app.run()


def get_historical_data(symbol, sectype, exchange, currency, duration=None, barsize=None,
                        whattoshow=None, enddatetime=None, userth=None, format_date=None) -> list:
    print('\nGET HISTORY\n-------------------------------')

    app = Connect().app

    contract = Contract()  # Создание объекта Contract, который описывает интересующий актив

    contract.symbol = symbol  # символ актива
    contract.secType = sectype  # 'CASH' # тип ценной бумаги
    contract.exchange = exchange  # 'IDEALPRO'  # биржа
    contract.currency = currency  # 'USD'  # валюта базового актива

    if format_date is None:
        format_date = 1  # 1- datetime,  2 - unix

    if enddatetime is None:
        enddatetime = ''

    if barsize is None:
        barsize = '1 hour'

    if duration is None:
        duration = '1 D'

    if whattoshow is None:
        whattoshow = 'BID'

    if userth is None:
        userth = 1  # 1 - обычные торговые часы, 0 - предторговля

    app.reqHistoricalData(reqId=123, contract=contract, endDateTime=enddatetime, durationStr=duration,
                          barSizeSetting=barsize, whatToShow=whattoshow, useRTH=userth, formatDate=format_date,
                          keepUpToDate=False, chartOptions=[])

    while not app.data_end:
        time.sleep(1)
    result = app.df.to_dict('records')
    print(result)

    app.disconnect()

    return result


if __name__ == '__main__':
    # get_historical_data(symbol='EUR', sectype='CASH', exchange='IDEALPRO', currency='USD')
    get_historical_data(symbol='EUR', sectype='CASH', exchange='IDEALPRO', currency='US')  # wrong params


Solution

  • data_end only get set True in historicalDataEnd so if there's an error it will never get called and the program sleeps forever.

    Another reason to never use sleeps.