Skip to content

Commit caa315a

Browse files
continue external axes development
1 parent 7b7b0cd commit caa315a

File tree

7 files changed

+1251
-91
lines changed

7 files changed

+1251
-91
lines changed

examples/external_axes.ipynb

Lines changed: 770 additions & 0 deletions
Large diffs are not rendered by default.

examples/scratch_pad/dev_external_axes.ipynb

Lines changed: 239 additions & 32 deletions
Large diffs are not rendered by default.

src/mplfinance/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
from mplfinance.plotting import plot, make_addplot
33
from mplfinance._styles import make_mpf_style, make_marketcolors, available_styles
44
from mplfinance._version import __version__
5+
from mplfinance._mplwraps import figure

src/mplfinance/_arg_validators.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -303,18 +303,18 @@ def _check_for_external_axes(config):
303303

304304
# At this point, if we have not raised an exception, then plot(ax=) and make_addplot(ax=)
305305
# are in sync: either they are all None, or they are all of type `matplotlib.axes.Axes`.
306-
# Therefore we only need plot(ax=), i.e. config['ax'], as we check `volume` and `fig`:
306+
# Therefore we only need plot(ax=), i.e. config['ax'], as we check `volume`: ### and `fig`:
307307

308308
if config['ax'] is None:
309309
if isinstance(config['volume'],mpl.axes.Axes):
310310
raise ValueError('`volume` set to external Axes requires all other Axes be external.')
311-
if config['fig'] is not None:
312-
raise ValueError('`fig` kwarg must be None if `ax` kwarg is None.')
311+
#if config['fig'] is not None:
312+
# raise ValueError('`fig` kwarg must be None if `ax` kwarg is None.')
313313
else:
314314
if not isinstance(config['volume'],mpl.axes.Axes) and config['volume'] != False:
315315
raise ValueError('`volume` must be of type `matplotlib.axis.Axes`')
316-
if not isinstance(config['fig'],mpl.figure.Figure):
317-
raise ValueError('`fig` kwarg must be of type `matplotlib.figure.Figure`')
316+
#if not isinstance(config['fig'],mpl.figure.Figure):
317+
# raise ValueError('`fig` kwarg must be of type `matplotlib.figure.Figure`')
318318

319319
external_axes_mode = True if isinstance(config['ax'],mpl.axes.Axes) else False
320320
return external_axes_mode

src/mplfinance/_mplwraps.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import matplotlib.pyplot as plt
2+
import matplotlib.figure as mplfigure
3+
from mplfinance import _styles
4+
5+
"""
6+
This file contains:
7+
8+
(1) A wrapper of method `matplotlib.pyplot.figure()` that creates a
9+
`mplfinance.Mpf_Figure` which is derived from `matplotlib.figure.Figure`
10+
The wrapper function is the same as `matplotlib.pyplot.figure()` except
11+
that it additionally accepts kwarg `style=` to set the mplfinance style.
12+
13+
(2) Class `mplfinance.Mpf_Figure` derived from `matplotlib.figure.Figure`
14+
which has the following overrides:
15+
- Attribute `mpfstyle` indicating the mplfinance style used at Figure creation.
16+
- Methods (listed below) which are identical to the same method in class
17+
`matplotlib.figure.Figure` except that the `mplfinance.Mpf_Figure` versions:
18+
- accept kwarg `style=` to set the mplfinance style of Subplot Axes, or
19+
- if `style=` is not specified, then the attribute
20+
`mplfinance.Mpf_Figure.mpfstyle` is used for the Subplot Axes style.
21+
- Figure.add_subplot()
22+
- Figure.add_axes()
23+
- Figure.subplot() (this is analogous to pyplot.subplot() which calls Figure.add_subplot())
24+
- Figure.subplots()
25+
"""
26+
27+
28+
def _check_for_and_apply_style(kwargs):
29+
30+
if 'style' in kwargs:
31+
style = kwargs['style']
32+
del kwargs['style']
33+
else:
34+
style = 'default'
35+
36+
if not _styles._valid_mpf_style(style):
37+
raise TypeError('Invalid mplfinance style')
38+
39+
if isinstance(style,str):
40+
style = _styles._get_mpfstyle(style)
41+
42+
if isinstance(style,dict):
43+
_styles._apply_mpfstyle(style)
44+
else:
45+
raise TypeError('style should be a `dict`; why is it not?')
46+
47+
return style
48+
49+
50+
def figure(*args,**kwargs):
51+
52+
style = _check_for_and_apply_style(kwargs)
53+
54+
f = plt.figure(FigureClass=Mpf_Figure,*args,**kwargs)
55+
f.mpfstyle = style
56+
return f
57+
58+
59+
class Mpf_Figure(mplfigure.Figure):
60+
61+
def add_subplot(self,*args,**kwargs):
62+
63+
if 'style' in kwargs or not hasattr(self,'mpfstyle'):
64+
style = _check_for_and_apply_style(kwargs)
65+
else:
66+
style = _check_for_and_apply_style(dict(style=self.mpfstyle))
67+
68+
ax = mplfigure.Figure.add_subplot(self,*args,**kwargs)
69+
ax.mpfstyle = style
70+
return ax
71+
72+
def add_axes(self,*args,**kwargs):
73+
74+
if 'style' in kwargs or not hasattr(self,'mpfstyle'):
75+
style = _check_for_and_apply_style(kwargs)
76+
else:
77+
style = _check_for_and_apply_style(dict(style=self.mpfstyle))
78+
79+
ax = mplfigure.Figure.add_axes(self,*args,**kwargs)
80+
ax.mpfstyle = style
81+
return ax
82+
83+
def subplot(self,*args,**kwargs):
84+
85+
plt.figure(self.number) # make it the current Figure
86+
87+
if 'style' in kwargs or not hasattr(self,'mpfstyle'):
88+
style = _check_for_and_apply_style(kwargs)
89+
else:
90+
style = _check_for_and_apply_style(dict(style=self.mpfstyle))
91+
92+
ax = plt.subplot(*args,**kwargs)
93+
ax.mpfstyle = style
94+
return ax
95+
96+
97+
def subplots(self,*args,**kwargs):
98+
99+
if 'style' in kwargs or not hasattr(self,'mpfstyle'):
100+
style = _check_for_and_apply_style(kwargs)
101+
else:
102+
style = _check_for_and_apply_style(dict(style=self.mpfstyle))
103+
104+
axlist = mplfigure.Figure.subplots(self,*args,**kwargs)
105+
106+
self.mpfstyle = style
107+
if ax in axlist:
108+
ax.mpfstyle = style
109+
return fig, axlist

src/mplfinance/_styles.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,33 @@ def _valid_mpf_color_spec(value):
125125
)
126126
)
127127

128+
def _valid_mpf_style(value):
129+
if value in available_styles():
130+
return True
131+
if not isinstance(value,dict):
132+
return False
133+
if 'marketcolors' not in value:
134+
return False
135+
if not isinstance(value['marketcolors'],dict):
136+
return False
137+
# {'candle': {'up': 'b', 'down': 'g'},
138+
# 'edge': {'up': 'k', 'down': 'k'},
139+
# 'wick': {'up': 'k', 'down': 'k'},
140+
# 'ohlc': {'up': 'k', 'down': 'k'},
141+
# 'volume': {'up': '#1f77b4', 'down': '#1f77b4'},
142+
# 'vcedge': {'up': '#1f77b4', 'down': '#1f77b4'},
143+
# 'vcdopcod': False,
144+
# 'alpha': 0.9}
145+
for item in ('candle','edge','wick','ohlc','volume'):
146+
if item not in value['marketcolors']:
147+
return False
148+
itemcolors = value['marketcolors'][item]
149+
if not isinstance(itemcolors,dict):
150+
return False
151+
if 'up' not in itemcolors or 'down' not in itemcolors:
152+
return False
153+
return True
154+
128155
def _valid_make_marketcolors_kwargs():
129156
vkwargs = {
130157
'up' : { 'Default' : None,

0 commit comments

Comments
 (0)