1

I am currently preparing a window that should show mplfinance plots whenever I send a new dataset to my "MyDynamicMplCanvas". So far the solution works and also the Toolbar is linked to the current content of the canvas. My problem is that I want to print volume, too. Which is part of my pandas dataframe. But just setting volume = True brings the error: ValueError: volume must be of type matplotlib.axis.Axes. Here is my code:

import sys
import pandas as pd
import mplfinance as mpf
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QSizePolicy, QPushButton
from matplotlib.backends.backend_qt5agg import (
    FigureCanvasQTAgg as FigureCanvas,
    NavigationToolbar2QT as NavigationToolbar)
import matplotlib.pyplot as plt


class MyMplCanvas(FigureCanvas):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        self.fig, self.ax = plt.subplots(figsize=(width, height), dpi=dpi)
        super().__init__(self.fig)
        self.setParent(parent)


class MyDynamicMplCanvas(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.plot1_button = QPushButton("Plot 1")
        self.plot1_button.clicked.connect(self.plot1)
        self.plot2_button = QPushButton("Plot 2")
        self.plot2_button.clicked.connect(self.plot2)

        self.canvas = MyMplCanvas(self, width=5, height=4, dpi=100)
        self.toolbar = NavigationToolbar(self.canvas, self)

        vbox = QVBoxLayout()
        vbox.addWidget(self.toolbar)
        vbox.addWidget(self.canvas)
        vbox.addWidget(self.plot1_button)
        vbox.addWidget(self.plot2_button)

        self.setLayout(vbox)
        
        
    def getData(self, iisin):
        if iisin == 'A':
            pfad = '/path/to/file/Qt/DE000A0DJ6J9_4qs.csv'
        else:
            pfad = '/path/to/file/Qt/DE000A0DJ6J9_2qs.csv'
            
        data = pd.read_csv(pfad, header=0, sep='|', encoding='utf-8')
        data.index = pd.to_datetime(data['Datum'])
        data.rename(columns={'TradesSum': 'Volume'}, inplace=True)
        return data
    
        
    def plot1(self):
        # Start plot, after clearing the canvas        
        self.canvas.ax.clear()
    
        mpf.plot(data = self.getData('A'), ax = self.canvas.ax, 
                  type='candle', style='yahoo',
                  mav=(20, 38, 50), volume=False, returnfig=True)
        # When volume=True this error
        # ValueError: `volume` must be of type `matplotlib.axis.Axes`
        
        # Reconnect NavigationToolbar
        self.toolbar.update()
    
        # Show plot on canvas
        self.canvas.draw()
    
    
    def plot2(self):
        # Delete plots
        self.canvas.ax.clear()
    
        # Add plot from csv file
        data = self.getData('B')
        mpf.plot(data = data, ax = self.canvas.ax, 
                  type='candle', style='yahoo',
                  mav=(20, 38, 50), volume=False, returnfig=True)
        
        # Reconnect NavigationToolbar
        self.toolbar.update()
    
        # Show plot on canvas
        self.canvas.draw()



if __name__ == '__main__':
    app = QApplication(sys.argv)

    main_window = QWidget()
    main_layout = QVBoxLayout()
    main_window.setLayout(main_layout)

    mpl_widget = MyDynamicMplCanvas()
    main_layout.addWidget(mpl_widget)

    main_window.show()

    sys.exit(app.exec_())

Any ideas what chatgpt did wrong? ;-)

finny
  • 21
  • 7
  • The question https://stackoverflow.com/questions/65158801/matplotlib-axis-axes-error-in-mplfinance-for-volume deals with your problem. There is an example with the volume displayed. – Frédéric LOYER Mar 29 '23 at 12:49
  • Yes that's true. But the solution is in conjunction with a Jupyter notebook. My problem is to apply this solution for a pyqt environment. I don't understand how this should work. – finny Mar 29 '23 at 13:11
  • Btw, just curious, are you saying that chatgpt wrote *all* of the above code? – Daniel Goldfarb Mar 29 '23 at 17:16
  • @DanielGoldfarb: Yes it did apart from the getData method. But I had to ask three times to get a working example, Sometime it is necessary to rephrase the question. But I am happy with chatgpt. It already helped a lot to analyse my MIFID II tick data database I have for private use. – finny Mar 30 '23 at 04:22
  • @finnay Thanks. Would you be willing to share the final phrasing of the question that enabled it to write the code? I am very curious to learn more about chatgpt's ability to write code and I am very aware that it depends very much on how the question is phrased. Thank you. – Daniel Goldfarb Mar 30 '23 at 18:06
  • 1
    @DanielGoldfarb. Unfortunately I have cleared my conversations in chatgpt because it was a mess. Additionally the question was in german, as well. So, I don't know if it would have helped a lot. But yes. It is tricky to find the right questions. For me, chatgpt is just a big probability matrix and your question already must reduce the possible iteration paths as much as possible. Once you notice that it keeps repeating wrong or non functional code it is better to start a new chat. But it is definetly a game changer. – finny Mar 30 '23 at 19:59

1 Answers1

0

As noted in the mplfinance documentation for external axes mode when using external axes mode then you must provide all axes objects, including those needed for volume.

In your MyMplCanvas(FigureCanvas) class, when you create the Figure and Axes, you need to also create another axes for volume (unless you want to plot it on the same axes as the candlesticks).

Perhaps add a volume=[true|false] kwarg to your MyMplCanvas constructor:

If true, then instruct plt.subplots(figsize=(width, height), dpi=dpi) to make TWO Axes for you, perhaps something like:

self.fig, (self.ax, self.volax) = plt.subplots(
                                      nrows=2, 
                                      ncols=1, 
                                      figsize=(width, height),
                                      dpi=dpi)

Then pass volume=self.volax into mpf.plot()

Daniel Goldfarb
  • 6,937
  • 5
  • 29
  • 61
  • Hi Daniel, I have added a volume = True to my constructor but got an error that Figure object has no property 'volume'. Then I changed my constructor to: – finny Mar 30 '23 at 05:16
  • self.fig = plt.figure(figsize = (width, height) self.volax = self.fig.add_subplot(1, 1, 1) self.ax = self.fig.add_subplot(2, 1. 1) and it worked. I now just need to scale the volume axis because it is of same size as the chart itself. Thanks for your help on that. – finny Mar 30 '23 at 05:19