TWS API请求参数错误时脚本冻结问题求助
TWS API历史数据请求参数错误时脚本挂起的解决方法
问题描述
使用Python的IB API调用reqHistoricalData()获取历史数据时,参数正确情况下脚本可正常执行结束;但传入错误参数(例如contract.currency = 'US')时,控制台会输出错误:
ERROR 123 321 Request validation failed. -'bS' : cause - US currency is not allowed
但脚本会无限挂起,必须手动停止才能终止。
问题原因
代码中通过while not app.data_end:循环等待数据获取完成,而data_end仅在historicalDataEnd回调中被设置为True。当参数验证失败时,TWS不会触发historicalDataEnd回调,导致data_end始终为False,循环无限执行。
解决方案
1. 实现error回调捕获请求错误
重写EWrapper的error方法,捕获TWS返回的错误信息,并触发终止循环的条件。
2. 添加超时机制
为等待循环设置超时时间,避免因异常情况(如网络问题导致错误回调未触发)导致脚本永久挂起。
修改后的代码示例
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 self.error_occurred = False # 标记是否发生错误 self.error_msg = "" # 存储错误信息 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): 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 def error(self, reqId, errorCode, errorString): print(f"ERROR {reqId} {errorCode} {errorString}") # 针对历史数据请求的错误(reqId=123),触发终止条件 if reqId == 123: self.error_occurred = True self.data_end = True self.error_msg = errorString class Connect: app = None def __init__(self): self.app = IBapi() self.app.connect('127.0.0.1', 7497, 123) api_thread = threading.Thread(target=self.run_loop, daemon=True) api_thread.start() time.sleep(1) 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.symbol = symbol contract.secType = sectype contract.exchange = exchange contract.currency = currency format_date = format_date or 1 enddatetime = enddatetime or '' barsize = barsize or '1 hour' duration = duration or '1 D' whattoshow = whattoshow or 'BID' userth = userth or 1 app.reqHistoricalData(reqId=123, contract=contract, endDateTime=enddatetime, durationStr=duration, barSizeSetting=barsize, whatToShow=whattoshow, useRTH=userth, formatDate=format_date, keepUpToDate=False, chartOptions=[]) # 带超时的等待循环 start_time = time.time() timeout = 10 # 自定义超时时间(秒) while not app.data_end: time.sleep(1) if time.time() - start_time > timeout: print("请求超时,终止循环") app.data_end = True app.error_occurred = True app.error_msg = "请求超时" # 根据状态返回结果或错误提示 if app.error_occurred: print(f"请求失败: {app.error_msg}") result = [] else: 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') # 错误参数测试
关键修改说明
- 新增错误处理回调:通过
error方法捕获请求错误,当对应reqId的历史数据请求出错时,设置data_end=True终止等待循环,并记录错误信息。 - 超时机制:设置固定超时时间,避免因异常情况导致脚本永久挂起。
- 错误状态判断:在返回结果前判断是否发生错误,返回对应空列表或错误提示,保证脚本流程正常结束。
内容的提问来源于stack exchange,提问作者Eldellano
相关产品推荐
相关产品推荐

