Search code examples
pythonpython-3.xmatplotlibmatplotlib-animation

How to display Live Data using Python


I would like to display time series data of stock prices and averages in a Python 3 project.

The data is currently stored in a CSV file which is updated periodically and my interpreter is Anaconda.

I've tried using

Matplotlib.animation.FuncAnimation()

However the Figure window pops up without any axes and then fails to respond and crashes.

Here is my Charting Class in the Project:

class Chart:

    import matplotlib.pyplot
    import matplotlib.animation as animation
    plt = matplotlib.pyplot

    @classmethod
    def __init__(cls):
        fig = cls.plt.figure(figsize=(5, 5))
        global ax1
        ax1 = fig.add_subplot(1, 1, 1)
        ani = cls.animation.FuncAnimation(fig, cls.animate, interval=1000, blit=True)
        cls.plt.show()

    @classmethod
    def animate(cls, i):
        xs = []
        ys = []
        ax1.clear()
        data = pd.read_csv('chart_values.csv')
        date = data['timestamp']
        last = data['last']
        short_ma = data['short_ma']
        long_ma = data['long_ma']

        xs.append(date)
        ys.append(last)
        ax1.clear()
        ax1.plot(xs, ys)
        ax1.set_title('Trade data')
Note *

Another issue I encountered was that I needed to set an environment PATH variable that pointed to /library/plugins in my anaconda dir which solves the problem of getting this error message:

this application failed to start because it could not find or load the qt platform plugin "windows" in "".

However it needs to be deleted as soon as id like to use any other program which requires PyQt.

* EDIT *

I've changed the code as per the responses but have yet to get the plot to load and display data continuously.

class Chart:

    import matplotlib.pyplot
    import matplotlib.animation as animation
    plt = matplotlib.pyplot

    def __init__(self):
        fig = self.plt.figure(figsize=(5, 5))
        self.ax1 = fig.add_subplot(1, 1, 1)
        self.line, = self.ax1.plot([], [])
        ani = self.animation.FuncAnimation(fig, self.update, interval=1000, blit=True)
        self.plt.show()

    def update(self, i):
# I don't think its essential to pass the loc as a param just yet.
        data = pd.read_csv('chart_values.csv', header=0) 
        self.line.set_data(data['timestamp'], data['last'])
        self.ax1.set_title('Trade data')

I get the following errors after I KeyboardInterrupt the program :

ValueError: could not convert string to float: '2020-05-21T17:04:13.645Z'

As well as:

RuntimeError: The animation function must return a sequence of Artist objects.


Solution

  • I have a solution!

    A few things to note:

    • Matlotlib's Funcanimation lib doesn't work in Classes.
    • Having the main project and Chart functionality in separate files allows you to run or instantiate them each on separate threads whenever you choose.
    • Matplotlib has 2 ways of interfacing with their lib. Using the OOP API is recommended.
    • A axis date formatter is needed to use the timestamp on the x axis.

    The Updated Code

    chart.py:

    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    import pandas as pd
    import dateutil.parser
    import matplotlib.dates as mdates
    import numpy as np
    
    
    def init():  # only required for blitting to give a clean slate.
        plt.ion()
        line = ax1.set_ydata([np.nan] * 1000)
        return line,
    
    
    def update(i):
        ax1.clear()  # necessary to stop plots from being redrawn on canvas
        ax1.set_title('Trade data')
        ax1.set_xlabel('Date & Time')
        my_fmt = mdates.DateFormatter('%H:%M')  
        ax1.xaxis.set_major_formatter(my_fmt) # Display Hour and Min on X axis 
        data = pd.read_csv('D:/Development applications/Trading Bot/Foxbot_v0.1/main/sources/ticker_history.csv',
                           header=0, index_col=0)
        line = []  # Different MA Lines to be placed onto the figure
        dates = data['timestamp'].iloc[-1000:]  # selecting last 1000 timestamp points to be displayed at any given time
        x = []  # empty list to store dates
        types = {  # dict: keys = features I wish to plot & values = color of the line
    
            'last': 'cyan',
            'short_ma': 'r',
            'long_ma': 'k',
            'base_ma': 'green', }
    
        for date in dates:
            x.append(dateutil.parser.parse(str(date)))
    
        for t in types.keys():  # iterate through dict keys
            y = data[t].iloc[-1000:]  # select last 1000 points where column = dict key
            line.append(ax1.plot(x, y, lw=1.5, ls='-', color=types[t], label=t))  # append the col pts to the line
            ax1.legend(types.keys(), loc='upper right')
    
        return line[0], line[1], line[2], line[3],  # return last and short, base, long moving avg
    
    
    fig = plt.figure(figsize=(5, 5))
    ax1 = fig.add_subplot(1, 1, 1)
    ani = animation.FuncAnimation(fig, update, interval=3000)
    plt.show()
    
    
    

    That worked for me!

    Error:

    I think the error might be related to the i variable in the update() method. I think the self or cls params get in the way of the interval being passed into i.