0
votes

This is a follow up to the question posted at How do I use a button to switch between two different graphs in matplotlib
Many thanks to endyd for that solution.

The code at that url works perfectly if the graphs being switched between are typical matplotlib graphs but I am attempting to use a button to switch between two graphs where one is a candlestick chart without a volume overlay and the second is the same candlestick chart but with a volume overlay. Unfortunately I have been unable to get this code to display the candlestick data or the volume data using the code from the shown url.

The matplotlib code for the candlestick code is from mpl_finance and is located at https://github.com/matplotlib/mpl_finance/blob/master/mpl_finance.py

Here is my complete code that embeds the two candlestick charts (without and with the volume overlay) into a tkinter widget and which allows the use of a button to switch between the two graphs per the code from my initial question:

import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from tkinter import *
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from SQL_code import connectToSQL
from matplotlib.dates import date2num, num2date
from matplotlib.dates import DateFormatter, WeekdayLocator, 
DayLocator, MONDAY
from mpl_finance import candlestick_ohlc
from datetime import datetime

# Separated out config of plot to just do it once
def config_plot():
    fig, ax = plt.subplots(figsize=(18, 5))
    #ax.set(xlabel='time (s)', ylabel='voltage (mV)', title='Graph 
One')
    return (fig, ax)

class matplotlibStock:
    def __init__(self, master):
        self.master = master
        self.frame = Frame(self.master)
        self.fig, self.ax = config_plot()
        self.graphIndex = 0
        self.canvas = FigureCanvasTkAgg(self.fig, self.master)
        self.config_window()
        self.ohlc_daily_date_axis()
        self.frame.pack(expand=YES, fill=BOTH)

    def quotes_info(self):
        quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
                  (737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
                  (737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
                  (737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
                  (737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
                  (737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
                  (737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
                  (737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
                  (737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
                  (737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]

        return quotes

    def config_window(self):
        self.canvas.mpl_connect("key_press_event", self.on_key_press)
        toolbar = NavigationToolbar2Tk(self.canvas, self.master)
        toolbar.update()
        self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, 
expand=1)
        self.button = Button(self.master, text="Quit", 
command=self._quit)
        self.button.pack(side=BOTTOM)
        self.button_switch = Button(self.master, text="Switch Graphs", 
command=self.switch_graphs)
        self.button_switch.pack(side=BOTTOM)

    def ohlc_daily_date_axis(self):
        quotes = self.quotes_info()
        mondays = WeekdayLocator(MONDAY)  # major ticks on the mondays
        alldays = DayLocator()  # minor ticks on the days
        weekFormatter = DateFormatter('%b %d %Y')  # e.g., Jan 12 2018
        dayFormatter = DateFormatter('%d')  # e.g., 12



        #self.fig, self.ax = plt.subplots(figsize=(18, 5))
        self.ax.clear()  # clear current axes
        plt.subplots_adjust(bottom=0.2)
        self.ax.xaxis.set_major_locator(mondays)
        self.ax.xaxis.set_minor_locator(alldays)
        self.ax.xaxis.set_major_formatter(weekFormatter)
        # ax.xaxis.set_minor_formatter(dayFormatter)
        plt.title('Graph One')
        self.ax.set_ylabel('Share Price ($)', size=10)

        # plot_day_summary(ax, quotes, ticksize=3)
        candlestick_ohlc(self.ax, quotes, width=0.6)

        self.ax.xaxis_date()
        self.ax.autoscale_view()
        plt.setp(plt.gca().get_xticklabels(), rotation=45, 
horizontalalignment='right')

        #plt.show()
        self.canvas.draw()

    def ohlc_daily_date_axis_w_vol(self):
        mondays = WeekdayLocator(MONDAY)  # major ticks on the mondays
        alldays = DayLocator()  # minor ticks on the days
        weekFormatter = DateFormatter('%b %d %Y')  # e.g., Jan 12 2018
        dayFormatter = DateFormatter('%d')  # e.g., 12

        quotes = self.quotes_info()
        dates_pos = []
        dates_neg = []
        vols_pos = []
        vols_neg = []
        for i in range(len(quotes)):
            if quotes[i][1] - quotes[i][4] <= 0:
                dates_pos.append(quotes[i][0])
                vols_pos.append(quotes[i][5])
            if quotes[i][1] - quotes[i][4] > 0:
                dates_neg.append(quotes[i][0])
                vols_neg.append(quotes[i][5])


        self.ax.clear()  # clear current axes
        self.fig = plt.figure(figsize=(18, 5))
        self.ax = plt.subplot2grid((5, 4), (0, 0), rowspan=4, 
colspan=4)
        plt.subplots_adjust(bottom=0.2)
        self.ax.xaxis.set_major_locator(mondays)
        self.ax.xaxis.set_minor_locator(alldays)
        self.ax.xaxis.set_major_formatter(weekFormatter)
        # ax.xaxis.set_minor_formatter(dayFormatter)
        self.ax.set_ylabel('Share Price ($)', size=10)
        plt.title("Graph Two")

        # begin volume code
        self.ax2 = plt.subplot2grid((5, 4), (4, 0), sharex=self.ax, 
rowspan=1, colspan=4)

        self.ax2.bar(dates_pos, vols_pos, color='green', width=1, 
align='center')
        self.ax2.bar(dates_neg, vols_neg, color='red', width=1, 
align='center')
        labels = [int(l) for l in self.ax2.get_yticks()]
        self.ax2.axes.yaxis.set_ticklabels(labels)

        yticks = self.ax2.get_yticks()
        self.ax2.yaxis.tick_right()
        self.ax2.set_yticks(yticks)

        self.ax2.yaxis.set_label_position("right")
        self.ax2.set_ylabel('Volume', size=10)

        candlestick_ohlc(self.ax, quotes, width=0.6)

        self.ax.xaxis_date()
        self.ax.autoscale_view()
        plt.setp(plt.gca().get_xticklabels(), rotation=45, 
horizontalalignment='right')

        # these next 2 lines are vol lines
        plt.setp(self.ax.get_xticklabels(), visible=False)
        plt.subplots_adjust(hspace=0)

        #plt.show()
        self.canvas.draw()


    def on_key_press(event):
        print("you pressed {}".format(event.key))
        key_press_handler(event, canvas, toolbar)

    def _quit(self):
        self.master.quit()  # stops mainloop

    def switch_graphs(self):
        # Need to call the correct draw, whether we're on graph one or 
two
        self.graphIndex = (self.graphIndex + 1 ) % 2
        if self.graphIndex == 0:
            self.ohlc_daily_date_axis()
        else:
            self.ohlc_daily_date_axis_w_vol()



def main():
    root = Tk()
    matplotlibStock(root)
    root.mainloop()

if __name__ == '__main__':
    main()

I am encountering two problems in using this code with my candlestick charts. The first is that the data does not display and I believe this is somehow related to the

self.canvas.draw()

command. The second problem is that my candlestick chart with the volume overlay has two sets of axis, one for the candlestick OHLC data and the second for the volume data, which does not seem workable using the config_plot def. Any advice on resolving these issues is greatly appreciated.

The following is code that shows just how the candlestick chart with the volume overlay looks. This code uses two axes, one for the candlestick chart and the second for the volume overlay and then combines them into one figure.

For general information purposes an OHLC candlestick chart shows stock prices where for every date the open (O), high (H), low (L) and close (C) are all plotted. The volume overlay is a separate graph below the candlestick chart that plots the number of shares (volume) traded for that date. It is typical when viewing stock charts that a candlestick chart is used to view the changes in a stocks price on a day to day basis where the stocks volume can be made visible or not visible.

import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, WeekdayLocator, 
DayLocator, MONDAY
from mpl_finance import candlestick_ohlc




def quotes_info():
    return [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
            (737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
            (737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
            (737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
            (737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
            (737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
            (737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
            (737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
            (737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
            (737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]





def ohlc_daily_date_axis_w_vol():
    mondays = WeekdayLocator(MONDAY)        
    alldays = DayLocator()              
    weekFormatter = DateFormatter('%b %d %Y')  
    dayFormatter = DateFormatter('%d')      

    quotes = quotes_info()
    dates_pos = []
    dates_neg = []
    vols_pos = []
    vols_neg = []
    for i in range(len(quotes)):
        if quotes[i][1] - quotes[i][4] <= 0:
            dates_pos.append(quotes[i][0])
            vols_pos.append(quotes[i][5])
        if quotes[i][1] - quotes[i][4] > 0:
            dates_neg.append(quotes[i][0])
            vols_neg.append(quotes[i][5])



    fig = plt.figure(figsize=(18,5))
    ax = plt.subplot2grid((5, 4), (0, 0), rowspan=4, colspan=4)
    plt.subplots_adjust(bottom=0.2)
    ax.xaxis.set_major_locator(mondays)
    ax.xaxis.set_minor_locator(alldays)
    ax.xaxis.set_major_formatter(weekFormatter)
    ax.set_ylabel('Share Price ($)', size=10)


    # begin volume code
    ax2 = plt.subplot2grid((5, 4), (4, 0), sharex=ax, rowspan=1, 
colspan=4)

    #ax2.bar(dates, vols)
    ax2.bar(dates_pos, vols_pos, color='green', width=1, 
align='center')
    ax2.bar(dates_neg, vols_neg, color='red', width=1, align='center')
    labels = [int(l) for l in ax2.get_yticks()]
    ax2.axes.yaxis.set_ticklabels(labels)

    yticks = ax2.get_yticks()
    ax2.yaxis.tick_right()
    ax2.set_yticks(yticks)

    ax2.yaxis.set_label_position("right")
    ax2.set_ylabel('Volume', size=10)

    candlestick_ohlc(ax, quotes, width=0.6)

    ax.xaxis_date()
    ax.autoscale_view()
    plt.setp(plt.gca().get_xticklabels(), rotation=45, 
horizontalalignment='right')

    # these next 2 lines are vol lines
    plt.setp(ax.get_xticklabels(), visible=False)
    plt.subplots_adjust(hspace=0)



    plt.show()




if __name__ == '__main__':
    ohlc_daily_date_axis_w_vol()

Image of graph two - candlestick chart with volume overlay

2

2 Answers

0
votes

Alright, I followed you here. I'm not sure if this is your desired effect, so I'm attaching the picture to see what you say. Don't know what any of the data means (or what volume overlay means) so please check the image below and let me know... Basically I moved more things into the config_window() method and tried to see if we can do things without creating another axes in self.ax2. Maybe that messes up your graph with volume overlay totally...

import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from tkinter import *
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
# from SQL_code import connectToSQL
from matplotlib.dates import date2num, num2date
from matplotlib.dates import DateFormatter, WeekdayLocator, DayLocator, MONDAY
from mpl_finance import candlestick_ohlc
from datetime import datetime

# Separated out config of plot to just do it once
def config_plot():
    fig, ax = plt.subplots(figsize=(18, 5))
    #ax.set(xlabel='time (s)', ylabel='voltage (mV)', title='Graph One')
    return (fig, ax)

class matplotlibStock:
    def __init__(self, master):
        self.master = master
        self.frame = Frame(self.master)
        self.fig, self.ax = config_plot()
        self.graphIndex = 0
        self.canvas = FigureCanvasTkAgg(self.fig, self.master)
        self.config_window()
        self.ohlc_daily_date_axis()
        self.frame.pack(expand=YES, fill=BOTH)

    def quotes_info(self):
        return [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
                  (737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
                  (737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
                  (737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
                  (737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
                  (737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
                  (737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
                  (737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
                  (737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
                  (737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]

    def config_window(self):
        self.canvas.mpl_connect("key_press_event", self.on_key_press)
        toolbar = NavigationToolbar2Tk(self.canvas, self.master)
        toolbar.update()
        self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
        self.button = Button(self.master, text="Quit", command=self._quit)
        self.button.pack(side=BOTTOM)
        self.button_switch = Button(self.master, text="Switch Graphs", command=self.switch_graphs)
        self.button_switch.pack(side=BOTTOM)
        plt.subplots_adjust(bottom=0.2)

        # Moved to config
        mondays = WeekdayLocator(MONDAY)  # major ticks on the mondays
        alldays = DayLocator()  # minor ticks on the days
        weekFormatter = DateFormatter('%b %d %Y')  # e.g., Jan 12 2018
        dayFormatter = DateFormatter('%d')  # e.g., 12
        self.ax.xaxis.set_major_locator(mondays)
        self.ax.xaxis.set_minor_locator(alldays)
        self.ax.xaxis.set_major_formatter(weekFormatter)
        # ax.xaxis.set_minor_formatter(dayFormatter)
        self.ax.set_ylabel('Share Price ($)', size=10)


    def ohlc_daily_date_axis(self):
        plt.title('Without Volume Overlay')
        quotes = self.quotes_info()

        #self.fig, self.ax = plt.subplots(figsize=(18, 5))
        self.ax.clear()  # clear current axes

        # plot_day_summary(ax, quotes, ticksize=3)
        candlestick_ohlc(self.ax, quotes, width=0.6)
        self.ax.xaxis_date()
        self.ax.autoscale_view()
        plt.setp(plt.gca().get_xticklabels(), rotation=45, horizontalalignment='right')

        #plt.show()
        self.canvas.draw()

    def ohlc_daily_date_axis_w_vol(self):
        plt.title('With Volume Overlay')
        quotes = self.quotes_info()

        dates_pos = []
        dates_neg = []
        vols_pos = []
        vols_neg = []
        for i in range(len(quotes)):
            if quotes[i][1] - quotes[i][4] <= 0:
                dates_pos.append(quotes[i][0])
                vols_pos.append(quotes[i][5])
            if quotes[i][1] - quotes[i][4] > 0:
                dates_neg.append(quotes[i][0])
                vols_neg.append(quotes[i][5])


        self.ax.clear()  # clear current axes
        # self.fig = plt.figure(figsize=(18, 5))
        # self.ax = plt.subplot2grid((5, 4), (0, 0), rowspan=4, colspan=4)

        # begin volume code
        # self.ax2 = plt.subplot2grid((5, 4), (4, 0), sharex=self.ax, rowspan=1, colspan=4)

        self.ax.bar(dates_pos, vols_pos, color='green', width=1, align='center')
        self.ax.bar(dates_neg, vols_neg, color='red', width=1, align='center')
        labels = [int(l) for l in self.ax.get_yticks()]
        self.ax.axes.yaxis.set_ticklabels(labels)

        yticks = self.ax.get_yticks()
        self.ax.yaxis.tick_right()
        self.ax.set_yticks(yticks)

        self.ax.yaxis.set_label_position("right")
        self.ax.set_ylabel('Volume', size=10)

        candlestick_ohlc(self.ax, quotes, width=0.6)

        self.ax.xaxis_date()
        self.ax.autoscale_view()
        plt.setp(plt.gca().get_xticklabels(), rotation=45, horizontalalignment='right')

        # these next 2 lines are vol lines
        plt.setp(self.ax.get_xticklabels(), visible=False)
        plt.subplots_adjust(hspace=0)

        #plt.show()
        self.canvas.draw()


    def on_key_press(event):
        print("you pressed {}".format(event.key))
        key_press_handler(event, canvas, toolbar)

    def _quit(self):
        self.master.quit()  # stops mainloop

    def switch_graphs(self):
        # Need to call the correct draw, whether we're on graph one or two
        self.graphIndex = (self.graphIndex + 1 ) % 2
        if self.graphIndex == 0:
            self.ohlc_daily_date_axis()
        else:
            self.ohlc_daily_date_axis_w_vol()



def main():
    root = Tk()
    matplotlibStock(root)
    root.mainloop()

if __name__ == '__main__':
    main()

enter image description here

0
votes

The following code now works correctly

I cleaned up this code and resolved part of the problem. I changed config_plot so that only the fig was being configured and created the axes inside their respective graphs. I changed it so that each axis on the two graphs were class instances and had a unique name and moved the axis.clear() statements to the correct locations. It turns out that axis.clear() should be axis.remove(). With all these changes in place the code works correctly

import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from tkinter import *
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, 
NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from matplotlib.dates import date2num, num2date
from matplotlib.dates import DateFormatter, WeekdayLocator, DayLocator, 
MONDAY
from mpl_finance import candlestick_ohlc
from datetime import datetime

# Separated out config of plot to just do it once
def config_plot():
    fig = plt.figure(figsize=(18, 5))
    return (fig)

class matplotlibStock:
    def __init__(self, master):
        self.master = master
        self.frame = Frame(self.master)
        self.fig = config_plot()
        self.graphIndex = 0
        self.canvas = FigureCanvasTkAgg(self.fig, self.master)
        self.config_window()
        self.ohlc_daily_date_axis(self.fig)
        self.frame.pack(expand=YES, fill=BOTH)

    def quotes_info(self):
        return [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
                  (737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
                  (737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
                  (737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
                  (737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
                  (737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
                  (737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
                  (737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
                  (737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
                  (737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]

    def config_window(self):
        self.canvas.mpl_connect("key_press_event", self.on_key_press)
        toolbar = NavigationToolbar2Tk(self.canvas, self.master)
        toolbar.update()
        self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
        self.button = Button(self.master, text="Quit", command=self._quit)
        self.button.pack(side=BOTTOM)
        self.button_switch = Button(self.master, text="Switch Graphs", 
command=self.switch_graphs)
        self.button_switch.pack(side=BOTTOM)
        plt.subplots_adjust(bottom=0.2)


    def ohlc_daily_date_axis(self, fig):
        quotes = self.quotes_info()

        self.ax = fig.subplots()
        try:
            self.ax1.remove()  # clear current axes
            self.ax2.remove()
        except AttributeError:
            pass


        mondays = WeekdayLocator(MONDAY)  # major ticks on the mondays
        alldays = DayLocator()  # minor ticks on the days
        weekFormatter = DateFormatter('%b %d %Y')  # e.g., Jan 12 2018
        dayFormatter = DateFormatter('%d')  # e.g., 12
        self.ax.xaxis.set_major_locator(mondays)
        self.ax.xaxis.set_minor_locator(alldays)
        self.ax.xaxis.set_major_formatter(weekFormatter)
        # ax.xaxis.set_minor_formatter(dayFormatter)
        self.ax.set_ylabel('Share Price ($)', size=10)
        plt.title('Without Volume Overlay')

        candlestick_ohlc(self.ax, quotes, width=0.6)
        plt.setp(plt.gca().get_xticklabels(), rotation=45, 
horizontalalignment='right')

        self.canvas.draw()


    def ohlc_daily_date_axis_w_vol(self, fig):
        quotes = self.quotes_info()
        dates_pos = []
        dates_neg = []
        vols_pos = []
        vols_neg = []
        for i in range(len(quotes)):
            if quotes[i][1] - quotes[i][4] <= 0:
                dates_pos.append(quotes[i][0])
                vols_pos.append(quotes[i][5])
            if quotes[i][1] - quotes[i][4] > 0:
                dates_neg.append(quotes[i][0])
                vols_neg.append(quotes[i][5])

        self.ax.remove()


        self.ax1 = plt.subplot2grid((5, 4), (0, 0), rowspan=4, 
colspan=4)
        plt.subplots_adjust(bottom=0.2)
        self.ax1.set_ylabel('Share Price ($)', size=10)
        plt.title('With Volume Overlay')


        # begin volume code
        self.ax2 = plt.subplot2grid((5, 4), (4, 0), sharex=self.ax1, 
rowspan=1, colspan=4)

        self.ax2.bar(dates_pos, vols_pos, color='green', width=1, 
align='center')
        self.ax2.bar(dates_neg, vols_neg, color='red', width=1, 
align='center')
        labels = [int(l) for l in self.ax2.get_yticks()]
        self.ax2.axes.yaxis.set_ticklabels(labels)

        yticks = self.ax2.get_yticks()
        self.ax2.yaxis.tick_right()
        self.ax2.set_yticks(yticks)

        self.ax2.yaxis.set_label_position("right")
        self.ax2.set_ylabel('Volume', size=10)

        candlestick_ohlc(self.ax1, quotes, width=0.6)

        self.ax1.xaxis_date()
        self.ax1.autoscale_view()
        plt.setp(plt.gca().get_xticklabels(), rotation=45, 
horizontalalignment='right')

        # these next 2 lines are vol lines
        plt.setp(self.ax1.get_xticklabels(), visible=False)
        plt.subplots_adjust(hspace=0)

        self.canvas.draw()

    def on_key_press(event):
        key_press_handler(event, canvas, toolbar)

    def _quit(self):
        self.master.quit()  # stops mainloop

    def switch_graphs(self):
        # Need to call the correct draw, whether we're on graph one or two
        self.graphIndex = (self.graphIndex + 1 ) % 2
        if self.graphIndex == 0:
            self.ohlc_daily_date_axis(self.fig)
        else:
            self.ohlc_daily_date_axis_w_vol(self.fig)



def main():
    root = Tk()
    matplotlibStock(root)
    root.mainloop()

if __name__ == '__main__':
    main()