I am using mplfinance
to create a OHLC chart (stock prices)
This is implemented as a custom class as I need to add additional elements to the final chart
class CandlestickChart:
def __init__(self, data):
self.data = data
self.fig, self.ax = mpf.plot(
self.data,
type="ohlc",
volume=False,
returnfig=True,
)
I then have another function that
def add_swap_zone(self, swap_zone):
zone_data = self.data.copy()
zone_data["in_swap_zone"] = (zone_data.index >= swap_zone.x_left) & (
zone_data.index <= swap_zone.x_right
)
zone_data["swap_zone_val"] = np.nan
zone_data.loc[zone_data["in_swap_zone"], "swap_zone_val"] = swap_zone.y_down
zone_data["swap_zone_val"].fillna(swap_zone.y_up, inplace=True)
ap_swap_zone = mpf.make_addplot(
zone_data["swap_zone_val"],
type="bar",
width=0.5,
color=swap_zone.style.FILL_COLOUR,
alpha=swap_zone.style.FILL_ALPHA,
panel=0,
secondary_y=False,
)
self.ax[0].add_artist(ap_swap_zone)
While the mechanics of the function may look complex, in essence I am using mpf.make_addplot
to create a new plot, which then gets added to self.ax[0]
The error I receive is
Traceback (most recent call last):
File "/Users/user/projects/charter/src/draw/demo/demo_plot.py", line 42, in <module>
main()
File "/Users/user/projects/charter/src/draw/demo/demo_plot.py", line 35, in main
cs_chart.add_swap_zone(
File "/Users/user/projects/charter/src/draw/plot.py", line 39, in add_swap_zone
self.ax[0].add_artist(ap_swap_zone)
File "/Users/user/projects/charter/venv/lib/python3.11/site-packages/matplotlib/axes/_base.py", line 2219, in add_artist
a.axes = self
^^^^^^
AttributeError: 'dict' object has no attribute 'axes'
I have tried the below alternative ways to add the plot and still failed
self.ax[list(self.ax.keys())[0]].add_artist(ap_swap_zone)
self.ax["0"].add_artist(ap_swap_zone)
EDIT: Revised code based on selected answer
class CandlestickChart:
def __init__(self, data):
self.data = data
self.addplot_specs = []
def add_swap_zone(self, swap_zone):
zone_data = self.data.copy()
zone_data["in_swap_zone"] = (zone_data.index >= swap_zone.x_left) & (
zone_data.index <= swap_zone.x_right
)
zone_data["swap_zone_val"] = np.nan
zone_data.loc[zone_data["in_swap_zone"], "swap_zone_val"] = swap_zone.y_down
zone_data["swap_zone_val"].fillna(swap_zone.y_up, inplace=True)
ap_swap_zone = mpf.make_addplot(
zone_data["swap_zone_val"],
type="bar",
width=0.5,
color=swap_zone.style.FILL_COLOUR,
alpha=swap_zone.style.FILL_ALPHA,
panel=0,
secondary_y=False,
)
self.addplot_specs.append(ap_swap_zone)
def plot(self):
self.fig, self.ax = mpf.plot(
self.data,
type="ohlc",
volume=False,
returnfig=True,
addplot=self.addplot_specs,
)
mpf.show()
Constructor initialises self.addplot_specs[]
which is then appended with addplot specification in add_swap_zone()
. The mpl.plot() passes addplot=self.addplot_specs
While the mechanics of the function may look complex, in essence I am using mpf.make_addplot to create a new plot, which then gets added to self.ax[0]
Your problem is that mpf.make_addplot()
does not return a matplotlib Artist
object.
Rather, mpf.make_addplot()
returns an "addplot
specification object" which can then be passed into the addplot=
kwarg of mpf.plot()
(alternatively one can also assign a list
of such "addplot
specification" objects to the addplot=
kwarg).
You should rearrange you code accordingly to first build the list of one or more make_addplot()
objects (inside your CandlestickChart
object), and then call mpf.plot()
.
Thus the CandlestickChart
constructor should not call mpf.plot()
but rather create a method on the class that will call mpf.plot()
when you are ready.
(Or alternatively the constructor can call mpf.plot()
but only if it has all of the addplot specification objects at the time of construction).
Let me know if that answers your question or if you need any additional information.
(P.S. Just fyi, the reason you are getting an error messsage about a dict
is because, for the time being, the addplot
specification object is a dict
, however this is not always guaranteed to be the case. It could be that some day in the future mplfinance will have a need to define its own class for the addplot
specification object, which is why mplfinance users should not manipulate this dict
on their own but rather only use mpf.make_addplot()
to build it.)