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.pywill download SPY daily market data from Yahoo! finance and then will "publish" using a socket
client.pywill 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: