使用Interactive Brokers TWS API创建实时K线图遇504错误求调试
调试Interactive Brokers TWS API实时K线图连接问题
我尝试用Interactive Brokers TWS API创建实时蜡烛图,但运行Python代码时收到504 Not connected错误,且返回空DataFrame。
运行代码
from ibapi.client import EClient from ibapi.wrapper import EWrapper from ibapi.contract import Contract import matplotlib.pyplot as plt import pandas as pd import threading import time class IBapi(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) self.data = [] #Initialize variable to store candle def historicalData(self, reqId, bar): print(f'Time: {bar.date} Open: {bar.open} High: {bar.high} Low: {bar.low} Close: {bar.close}') self.data.append([bar.date, bar.open, bar.high, bar.low, bar.close]) def realtimeBar(self, reqId, bar): print(f'Time: {bar.date} Open: {bar.open} High: {bar.high} Low: {bar.low} Close: {bar.close}') self.data.append([bar.date, bar.open, bar.high, bar.low, bar.close]) def run_loop(): app.run() app = IBapi() app.connect('127.0.0.1', 7497, 123) #Start the socket in a thread api_thread = threading.Thread(target=run_loop, daemon=True) api_thread.start() time.sleep(1) #Sleep interval to allow time for connection to server #Create contract object contract = Contract() contract.symbol = 'AAPL' contract.secType = 'STK' contract.exchange = 'SMART' contract.currency = 'USD' #Request historical candles app.reqHistoricalData(1, contract, '', '2 D', '1 hour', 'BID', 0, 2, False, []) #Create a dataframe from the historical data df = pd.DataFrame(app.data,columns=["date","open","high","low","close"]) data = pd.DataFrame() data["date"] = df["date"] data["open"] = df["open"] data["high"] = df["high"] data["low"] = df["low"] data["close"] = df["close"] #Plot the candlestick chart plt.style.use('bmh') plt.figure(figsize=(15,8)) plt.title('AAPL Stock Price') plt.xlabel('date') plt.ylabel('price') plt.plot(df.index, df['open'], label='open') plt.plot(df.index, df['high'], label='high') plt.plot(df.index, df['low'], label='low') plt.plot(df.index, df['close'], label='close') plt.legend() plt.show() #Request Live Data Type app.reqMarketDataType(4) #Requesting the Live Market Data app.reqRealTimeBars(1, contract, 1, "MIDPOINT", True, []) #Create a dataframe from the real-time data df2 = pd.DataFrame(app.data,columns=["date","open","high","low","close"]) #Add the real-time data to the historical data if not df2.empty: df = df.append(df2) print(df) #Plot the live candlestick chart plt.style.use('bmh') plt.figure(figsize=(15,8)) plt.title('AAPL Stock Price') plt.xlabel('date') plt.ylabel('price') plt.plot(df.index, df['open'], label='open') plt.plot(df.index, df['high'], label='high') plt.plot(df.index, df['low'], label='low') plt.plot(df.index, df['close'], label='close') plt.legend() plt.show()
错误信息
Reloaded modules: ibapi, ibapi.message, ibapi.enum_implem, ibapi.object_implem, ibapi.common, ibapi.utils, ibapi.contract, ibapi.softdollartier, ibapi.order, ibapi.order_state, ibapi.execution, ibapi.ticktype, ibapi.commission_report, ibapi.wrapper, ibapi.server_versions, ibapi.tag_value, ibapi.scanner, ibapi.errors, ibapi.comm, ibapi.order_condition, ibapi.orderdecoder, ibapi.decoder, ibapi.reader, ibapi.connection, ibapi.client ERROR 1 504 Not connected ERROR -1 504 Not connected ERROR -1 504 Not connected Empty DataFrame Columns: [date, open, high, low, close] Index: []
调试方案
1. 修正连接流程顺序
原代码先调用app.connect再启动API循环线程,正确顺序是先启动线程,再执行连接,确保API循环在连接前运行:
app = IBapi() # 先启动API循环线程 api_thread = threading.Thread(target=run_loop, daemon=True) api_thread.start() # 再执行连接 app.connect('127.0.0.1', 7497, 123)
2. 替换固定sleep,等待连接确认
固定time.sleep(1)不可靠,在IBapi类中添加连接回调标记连接状态,主线程等待连接完成后再发起请求:
class IBapi(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) self.data = [] self.is_connected = False # 新增连接状态标记 def connectAck(self): self.is_connected = True print("已成功连接到TWS/IB Gateway") # 主线程中等待连接确认 while not app.is_connected: time.sleep(0.1)
3. 处理异步数据接收延迟
历史数据和实时数据是异步返回的,原代码请求后立刻创建DataFrame时数据尚未接收,需监听数据接收完成的回调:
class IBapi(EWrapper, EClient): # ... 原有代码 ... def historicalDataEnd(self, reqId, start, end): print(f"历史数据接收完成,请求ID: {reqId}") self.historical_data_ready = True # 新增标记 # 发起历史数据请求后等待接收完成 app.historical_data_ready = False app.reqHistoricalData(1, contract, '', '2 D', '1 hour', 'BID', 0, 2, False, []) while not app.historical_data_ready: time.sleep(0.1) # 此时再创建DataFrame df = pd.DataFrame(app.data,columns=["date","open","high","low","close"])
4. TWS/IB Gateway配置检查
- 确认TWS/IB Gateway已启动,端口匹配(模拟环境TWS默认7497,IB Gateway默认4002;实盘TWS默认7496,IB Gateway默认4001)
- 开启API权限:
Edit > Global Configuration > API > Settings,勾选"Enable Active X and Socket Clients" - 取消"Read-Only API"勾选(如需实时数据权限)
- 确认"Allow connections from localhost only"符合运行环境(本地运行则勾选)
5. 避免Request ID冲突
历史数据和实时数据请求使用了相同的Request ID=1,会导致冲突,改为不同ID:
# 历史数据用ID 1 app.reqHistoricalData(1, contract, '', '2 D', '1 hour', 'BID', 0, 2, False, []) # 实时数据用ID 2 app.reqRealTimeBars(2, contract, 1, "MIDPOINT", True, [])
6. 实现实时数据动态更新
原代码无法持续获取实时推送的数据,使用matplotlib的FuncAnimation实现图表动态更新:
import matplotlib.animation as animation # 初始化图表 fig, ax = plt.subplots(figsize=(15,8)) lines = [] lines.append(ax.plot(df.index, df['open'], label='open')[0]) lines.append(ax.plot(df.index, df['high'], label='high')[0]) lines.append(ax.plot(df.index, df['low'], label='low')[0]) lines.append(ax.plot(df.index, df['close'], label='close')[0]) ax.set_title('AAPL Stock Price') ax.set_xlabel('date') ax.set_ylabel('price') ax.legend() def update(frame): # 每次更新获取最新数据 df_live = pd.DataFrame(app.data,columns=["date","open","high","low","close"]) lines[0].set_data(df_live.index, df_live['open']) lines[1].set_data(df_live.index, df_live['high']) lines[2].set_data(df_live.index, df_live['low']) lines[3].set_data(df_live.index, df_live['close']) ax.relim() ax.autoscale_view() return lines # 每1秒更新一次图表 ani = animation.FuncAnimation(fig, update, interval=1000) plt.show()
内容的提问来源于stack exchange,提问作者Scieoner
相关产品推荐
相关产品推荐

