如何用Matplotlib实现Binance WebSocket ETHUSDT数据非阻塞实时绘图
阻塞问题核心成因
原代码的阻塞问题由两个逻辑缺陷直接导致:
- 每收到一条WebSocket消息就调用
plt.show(),该函数默认运行在阻塞模式下,会暂停整个进程的执行,直到手动关闭弹出的图表窗口,完全中断了WebSocket的持续消息接收流程。 - 每次收到数据就重新执行
plt.plot()创建新的绘图元素,没有复用已有的画布、坐标轴资源,既会生成冗余的图形对象,也无法实现“单张图表持续更新”的实时绘图效果。
无阻塞实时绘图调整方案
- 开启Matplotlib交互模式,替代默认的阻塞绘图逻辑
调用plt.ion()切换为交互绘图模式,该模式下创建的绘图窗口不会卡住后续代码执行,可通过手动触发短暂停实现界面刷新。 - 提前初始化可复用的绘图对象
程序启动阶段就创建好画布、坐标轴、折线图形的空对象,后续收到新数据仅需更新折线的数据源,无需重复创建绘图元素。 - 拆分线程规避跨线程UI操作
Matplotlib的绝大多数绘图后端都不支持跨线程安全操作,将WebSocket的长连接接收逻辑放到独立子线程运行,主线程专门负责绘图刷新,可彻底避免绘图卡顿、闪退问题。 - 限制数据缓存长度
为收盘价数组设置最大缓存长度(比如匹配原代码x轴的1000个点上限),避免长期运行后数据量过大导致绘图性能下降。
调整后的可运行代码
import websocket, json import matplotlib.pyplot as plt import numpy as np import threading TRADE_SYMBOL = "ETHUSDT" SOCKET = "wss://stream.binance.com:9443/ws/ethusdt@kline_1m" # 匹配原x轴设置的最大数据缓存长度 MAX_DATA_LENGTH = 1000 closes = [] # 初始化绘图配置 plt.ion() fig, ax = plt.subplots(figsize=(10, 6)) price_line, = ax.plot([], [], linewidth=2) ax.set_xlim(0, MAX_DATA_LENGTH) # 根据ETH常规价格区间设置初始y轴范围 ax.set_ylim(3000, 5000) def on_message(ws, message): global closes parsed_msg = json.loads(message) kline_data = parsed_msg['k'] close_price = float(kline_data['c']) # 更新收盘价缓存,超出长度则丢弃最早的数据 closes.append(close_price) if len(closes) > MAX_DATA_LENGTH: closes.pop(0) def run_websocket(): ws = websocket.WebSocketApp(SOCKET, on_message=on_message) ws.run_forever() if __name__ == "__main__": # 启动WebSocket接收子线程,设置为守护线程,主程序退出时自动回收 ws_thread = threading.Thread(target=run_websocket, daemon=True) ws_thread.start() # 主线程循环刷新绘图,窗口关闭时自动退出 while plt.fignum_exists(fig.number): # 更新折线图的数据源 data_len = len(closes) price_line.set_xdata(np.arange(data_len)) price_line.set_ydata(closes) # 自适应调整y轴范围,给数据留少量展示边距 if data_len > 0: min_price = min(closes) max_price = max(closes) ax.set_ylim(min_price * 0.999, max_price * 1.001) # 触发界面重绘 fig.canvas.draw_idle() # 20ms短暂停驱动界面刷新,不会造成长阻塞 plt.pause(0.02)
长期运行注意事项
- 不要在WebSocket的消息回调函数中直接调用Matplotlib绘图接口,回调函数运行在独立子线程,直接操作UI会触发未知的卡顿或崩溃。
- 无需在交互模式下手动调用
plt.show(),通过循环内的plt.pause()即可持续驱动界面刷新。 - 可根据实际需求调整
MAX_DATA_LENGTH和y轴自适应的边距比例,兼顾展示效果和性能。
内容的提问来源于stack exchange,提问作者roadrunner38
相关产品推荐
相关产品推荐

