如何在Plotly中实时更新图表而不重复打开新标签页?
解决Plotly图表重复打开新标签页并实现实时更新的方案
问题根源
你每次在while循环里重新创建Figure对象并调用fig.show(),Plotly默认的show()方法每次都会启动独立的浏览器标签页展示新图表,自然无法更新原有页面。要实现实时刷新,需要复用同一个图表容器并动态更新数据,推荐使用Dash(Plotly官方的交互式Web框架)来搭建本地实时更新的图表应用。
解决方案步骤
- 安装Dash依赖:
pip install dash
- 重构代码,将IB API数据获取与Dash的实时回调结合,核心是用
dcc.Interval定时触发数据更新,在回调中修改已有图表的数据而非新建图表。
修改后的完整代码
import ibapi import pandas as pd import plotly.graph_objects as go from ibapi.client import EClient from ibapi.wrapper import EWrapper from ibapi.contract import Contract import threading import time from dash import Dash, dcc, html, Input, Output class IBapi(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) self.data = [] # 存储K线数据 self.latest_timestamp = 0 # 记录最后一条数据的时间戳,避免重复添加 def historicalData(self, reqId, bar): # 仅添加新的K线数据,避免重复 if bar.date > self.latest_timestamp: print(f'Time: {bar.date} High: {bar.high} Low: {bar.low} Open: {bar.open} Close: {bar.close}') self.data.append([bar.date, bar.high, bar.low, bar.open, bar.close]) self.latest_timestamp = bar.date def run_loop(): app_ib.run() # 初始化IB API连接 app_ib = IBapi() app_ib.connect('127.0.0.1', 7496, 123) api_thread = threading.Thread(target=run_loop, daemon=True) api_thread.start() time.sleep(1) # 等待连接建立 # 定义EURUSD合约 eurusd_contract = Contract() eurusd_contract.symbol = 'EUR' eurusd_contract.secType = 'CASH' eurusd_contract.exchange = 'IDEALPRO' eurusd_contract.currency = 'USD' # 初始化Dash应用 app_dash = Dash(__name__) app_dash.layout = html.Div([ html.H1('EURUSD INTERACTIVE CHART'), dcc.Graph(id='live-candlestick'), # 每15秒触发一次数据更新(可根据需求调整) dcc.Interval( id='interval-component', interval=15*1000, # 毫秒 n_intervals=0 ) ]) # Dash回调函数:定时更新图表 @app_dash.callback( Output('live-candlestick', 'figure'), Input('interval-component', 'n_intervals') ) def update_graph(n): # 请求最新历史数据 app_ib.reqHistoricalData(1, eurusd_contract, '', '2 D', '1 hour', 'BID', 1, 2, False, []) time.sleep(2) # 等待数据返回 # 转换为DataFrame并处理时间格式 if app_ib.data: df = pd.DataFrame(app_ib.data, columns=['DateTime','High', 'Low', 'Open', 'Close']) df['DateTime'] = pd.to_datetime(df['DateTime'], unit='s') df.to_csv('EURUSD_Hourly.csv') # 生成蜡烛图 fig = go.Figure(data=[go.Candlestick( x=df['DateTime'], open=df['Open'], high=df['High'], low=df['Low'], close=df['Close'] )]) fig.update_layout(yaxis_title=eurusd_contract.symbol) return fig # 数据为空时返回空图表 return go.Figure() if __name__ == '__main__': app_dash.run_server(debug=True)
关键优化点
- 数据去重:新增
latest_timestamp变量,只添加比最后一条数据时间戳更新的K线,避免重复存储旧数据。 - 复用图表容器:通过Dash的
dcc.Graph组件作为固定容器,在回调函数中动态更新其figure属性,无需每次新建图表。 - 定时触发:用
dcc.Interval组件定时触发数据更新和图表刷新,替代原有的while循环,更符合Web应用的逻辑。 - 线程安全:IB API的运行线程与Dash的主线程分离,避免阻塞。
运行说明
执行代码后,会自动在浏览器打开本地地址(默认是http://127.0.0.1:8050/),图表会按照设定的时间间隔自动刷新,无需手动操作或打开新标签页。
内容的提问来源于stack exchange,提问作者Ofir Cohen
相关产品推荐
相关产品推荐

