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
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.