Live Plotting in Python using Matplotlib and ZeroMQ
While working on a new dashboard for QTPyLib I needed to get a Matplotlib plot to refresh and update based on data coming from a ZeroMQ stream.
After going over the documentation and Stack Overflow I was finally able to get this working. To save others from the headache, I decided to post a working demo here as a public service.
First, here's a quick example:
Peaked your interest? Good! Let's get started...
We'll start with a simpler example than shown above.
In our example, we'll use ZeroMQ's pub-sub architecture using 2 files:
server.py
will download SPY daily market data from Yahoo! finance and then will "publish" using a socketclient.py
will listen on our server's port and will plot every new data point
Here's the code:
The server (streamer)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
server.py
download SPY data from Yahoo finance and stream it using ZeroMQ
"""
# import libraries
import zmq
import time
from pandas_datareader import data
# create the zmq context and socket and bind the socket to port 1234
socket = zmq.Context(zmq.REP).socket(zmq.PUB)
socket.bind("tcp://*:1234")
# get market data from yahoo
data = data.get_data_yahoo("SPY", start="2017-01-01")
# iterate over rows
for ix, _ in data.iterrows():
# get row as dataframe
row = data[data.index==ix]['Adj Close']
# stream row as python object
socket.send_pyobj(row)
# wait 1 second
time.sleep(1)
The Client (plotter)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
client.py
listens on ZeroMQ's port and plot every new data point
"""
# import libraries
import zmq
import time
import matplotlib.pyplot as plt
import pandas as pd
# create the zmq client and listen on port 1234
socket = zmq.Context(zmq.REP).socket(zmq.SUB)
socket.setsockopt_string(zmq.SUBSCRIBE, '')
socket.connect('tcp://127.0.0.1:1234')
# create an empty dataframe that will store streaming data
df = pd.DataFrame()
# create plot
plt.ion() # <-- work in "interactive mode"
fig, ax = plt.subplots()
fig.canvas.set_window_title('Live Chart')
ax.set_title("SPY, 2017")
# act on new data coming from streamer
while True:
# receive python object
row = socket.recv_pyobj()
# append new row to dataframe
df = pd.concat([df, row])
# plot all data
ax.plot(df, color='r')
# show the plot
plt.show()
plt.pause(0.0001) # <-- sets the current plot until refreshed
# be nice to the cpu :)
time.sleep(.1)
Once you run both scripts you should see somehting like this:
* I recommend you run client.py
first, so you won't miss plotting any data points :)
You can, of course, use any of the formatting options available in Matplotlib, so let your imagination run wild.
Updated on 4 April 2017.
For the latest version and comments, please see:
https://aroussi.com/post/live-plotting-with-matplotlib-and-python