Search code examples
pythonmatplotlibwebsocketreal-time-updatesbinance

Binance websocket realtime plot without blocking code?


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()

Solution

  • Matplotlib needs to run the GUI on the main thread.

    You can setup the websocket connection on a background thread.

    Imports

    import json
    import threading
    import websocket
    import matplotlib.pyplot as plt
    from matplotlib.animation import FuncAnimation
    import numpy as np
    

    Realtime updates to data in background

    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()
    

    Animated plot

    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()