I am trying to obtain and plot real-time data from Binance (ETHUSDT) using a WebSocket. Getting the data is no problem, but I cannot get a realtime plot to work when using matplotlib.
In the code, I update the close prices each time a message is received and try to plot this data in realtime. The plot is shown on the screen but blocks further code execution. I have to manually close the plot for the next message to be received.
My question is: how can I plot the data in realtime without blocking the code?
import websocket, json
import matplotlib.pyplot as plt
import numpy as np
TRADE_SYMBOL = "ETHUSDT"
SOCKET = "wss://stream.binance.com:9443/ws/ethusdt@kline_1m"
closes = np.array([])
# CREATING AXIS
plt.axis([0, 1000, 0, 1])
def on_message(ws, message):
global closes
message = json.loads(message)
candle = message['k']
close = candle['c']
closes = np.append(closes, float(close))
# PLOTTING HERE
plt.plot(closes)
plt.show()
ws = websocket.WebSocketApp(SOCKET, on_message=on_message)
ws.run_forever()
Matplotlib needs to run the GUI on the main thread.
You can setup the websocket connection on a background thread.
import json
import threading
import websocket
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
Run the websocket data changes in a thread.
TRADE_SYMBOL = "ETHUSDT"
SOCKET = "wss://stream.binance.com:9443/ws/ethusdt@kline_1m"
closes = np.array([])
def on_message(ws, message):
global closes
message = json.loads(message)
candle = message['k']
close = candle['c']
closes = np.append(closes, float(close))
def wsthread(closes):
ws = websocket.WebSocketApp(SOCKET, on_message=on_message)
ws.run_forever()
t = threading.Thread(target=wsthread, args=(closes,))
t.start()
Now, animate changes to the closes
array.
Refer to documentation for matplotlib.animation.FuncAnimation.
fig, ax = plt.subplots()
plt.axis([0, 100, 0, 3000])
x= np.arange(100)
y=[np.nan] * 100
line, = ax.plot(x, y)
def animate(i):
global y
# shift left to most recent 100 closing prices
y[:len(closes)] = closes[-100:]
line.set_ydata(y)
return line,
def init():
line.set_ydata([np.nan] * 100)
return line,
anim = FuncAnimation(
fig, animate, interval=20,
init_func=init,
blit=True
)
plt.show()