1
1
import matplotlib .dates as mdates
2
2
import matplotlib .pyplot as plt
3
3
import matplotlib .colors as mcolors
4
+ import matplotlib .axes as mpl_axes
5
+ import matplotlib .figure as mpl_fig
4
6
import pandas as pd
5
7
import numpy as np
6
8
import copy
33
35
from mplfinance ._arg_validators import _hlines_validator , _vlines_validator
34
36
from mplfinance ._arg_validators import _alines_validator , _tlines_validator
35
37
from mplfinance ._arg_validators import _scale_padding_validator
36
- from mplfinance ._arg_validators import _valid_panel_id
38
+ from mplfinance ._arg_validators import _valid_panel_id , _check_for_external_axes
37
39
38
40
from mplfinance ._panels import _build_panels
39
41
from mplfinance ._panels import _set_ticks_on_bottom_panel_only
@@ -104,7 +106,7 @@ def _valid_plot_kwargs():
104
106
'Validator' : lambda value : value in _styles .available_styles () or isinstance (value ,dict ) },
105
107
106
108
'volume' : { 'Default' : False ,
107
- 'Validator' : lambda value : isinstance (value ,bool ) },
109
+ 'Validator' : lambda value : isinstance (value ,bool ) or isinstance ( value , mpl_axes . Axes ) },
108
110
109
111
'mav' : { 'Default' : None ,
110
112
'Validator' : _mav_validator },
@@ -241,6 +243,12 @@ def _valid_plot_kwargs():
241
243
242
244
'scale_padding' : { 'Default' : 1.0 , # Issue#193
243
245
'Validator' : lambda value : _scale_padding_validator (value ) },
246
+
247
+ 'ax' : { 'Default' : None ,
248
+ 'Validator' : lambda value : isinstance (value ,mpl_axes .Axes ) },
249
+
250
+ 'fig' : { 'Default' : None ,
251
+ 'Validator' : lambda value : isinstance (value ,mpl_fig .Figure ) },
244
252
}
245
253
246
254
_validate_vkwargs_dict (vkwargs )
@@ -266,14 +274,17 @@ def plot( data, **kwargs ):
266
274
err = "`addplot` is not supported for `type='" + config ['type' ] + "'`"
267
275
raise ValueError (err )
268
276
277
+ external_axes_mode = _check_for_external_axes (config )
278
+ print ('external_axes_mode =' ,external_axes_mode )
279
+
269
280
style = config ['style' ]
270
281
if isinstance (style ,str ):
271
282
style = config ['style' ] = _styles ._get_mpfstyle (style )
272
283
273
284
if isinstance (style ,dict ):
274
- _styles ._apply_mpfstyle (style )
285
+ if not external_axes_mode : _styles ._apply_mpfstyle (style )
275
286
else :
276
- raise TypeError ('style should be a `dict`; why is it not?' )
287
+ raise TypeError ('style should be a `dict`; why is it not?' )
277
288
278
289
if config ['figsize' ] is None :
279
290
w ,h = config ['figratio' ]
@@ -289,15 +300,22 @@ def plot( data, **kwargs ):
289
300
else :
290
301
fsize = config ['figsize' ]
291
302
292
- fig = plt .figure ()
303
+ if external_axes_mode :
304
+ fig = config ['fig' ]
305
+ else :
306
+ fig = plt .figure ()
307
+
293
308
fig .set_size_inches (fsize )
294
309
295
310
if config ['volume' ] and volumes is None :
296
311
raise ValueError ('Request for volume, but NO volume data.' )
297
312
298
- panels = _build_panels (fig , config )
299
-
300
- volumeAxes = panels .at [config ['volume_panel' ],'axes' ][0 ] if config ['volume' ] is True else None
313
+ if external_axes_mode :
314
+ panels = None
315
+ volumeAxes = config ['volume' ]
316
+ else :
317
+ panels = _build_panels (fig , config )
318
+ volumeAxes = panels .at [config ['volume_panel' ],'axes' ][0 ] if config ['volume' ] is True else None
301
319
302
320
fmtstring = _determine_format_string ( dates , config ['datetime_format' ] )
303
321
@@ -310,7 +328,10 @@ def plot( data, **kwargs ):
310
328
formatter = IntegerIndexDateTimeFormatter (dates , fmtstring )
311
329
xdates = np .arange (len (dates ))
312
330
313
- axA1 = panels .at [config ['main_panel' ],'axes' ][0 ]
331
+ if external_axes_mode :
332
+ axA1 = config ['ax' ]
333
+ else :
334
+ axA1 = panels .at [config ['main_panel' ],'axes' ][0 ]
314
335
315
336
# Will have to handle widths config separately for PMOVE types ??
316
337
config ['_width_config' ] = _determine_width_config (xdates , config )
@@ -437,7 +458,11 @@ def plot( data, **kwargs ):
437
458
volumeAxes .set_ylim ( miny , maxy )
438
459
439
460
xrotation = config ['xrotation' ]
440
- _set_ticks_on_bottom_panel_only (panels ,formatter ,rotation = xrotation )
461
+ if not external_axes_mode :
462
+ _set_ticks_on_bottom_panel_only (panels ,formatter ,rotation = xrotation )
463
+ else :
464
+ axA1 .tick_params (axis = 'x' ,rotation = xrotation )
465
+ axA1 .xaxis .set_major_formatter (formatter )
441
466
442
467
addplot = config ['addplot' ]
443
468
if addplot is not None and ptype not in VALID_PMOVE_TYPES :
@@ -511,7 +536,7 @@ def plot( data, **kwargs ):
511
536
# corners = (minx, miny), (maxx, maxy)
512
537
# ax.update_datalim(corners)
513
538
514
- if config ['fill_between' ] is not None :
539
+ if config ['fill_between' ] is not None and not external_axes_mode :
515
540
fb = config ['fill_between' ]
516
541
panid = config ['main_panel' ]
517
542
if isinstance (fb ,dict ):
@@ -528,10 +553,14 @@ def plot( data, **kwargs ):
528
553
529
554
# put the primary axis on one side,
530
555
# and the twinx() on the "other" side:
531
- for panid ,row in panels .iterrows ():
532
- ax = row ['axes' ]
533
- y_on_right = style ['y_on_right' ] if row ['y_on_right' ] is None else row ['y_on_right' ]
534
- _set_ylabels_side (ax [0 ],ax [1 ],y_on_right )
556
+ if not external_axes_mode :
557
+ for panid ,row in panels .iterrows ():
558
+ ax = row ['axes' ]
559
+ y_on_right = style ['y_on_right' ] if row ['y_on_right' ] is None else row ['y_on_right' ]
560
+ _set_ylabels_side (ax [0 ],ax [1 ],y_on_right )
561
+ else :
562
+ y_on_right = style ['y_on_right' ]
563
+ _set_ylabels_side (axA1 ,None ,y_on_right )
535
564
536
565
# TODO: ================================================================
537
566
# TODO: Investigate:
@@ -584,9 +613,13 @@ def plot( data, **kwargs ):
584
613
else :
585
614
fig .suptitle (config ['title' ],size = 'x-large' ,weight = 'semibold' , va = 'center' )
586
615
587
- for panid ,row in panels .iterrows ():
588
- if not row ['used2nd' ]:
589
- row ['axes' ][1 ].set_visible (False )
616
+ if not external_axes_mode :
617
+ for panid ,row in panels .iterrows ():
618
+ if not row ['used2nd' ]:
619
+ row ['axes' ][1 ].set_visible (False )
620
+
621
+ if external_axes_mode :
622
+ return None
590
623
591
624
# Should we create a new kwarg to return a flattened axes list
592
625
# versus a list of tuples of primary and secondary axes?
@@ -721,13 +754,15 @@ def _set_ylabels_side(ax_pri,ax_sec,primary_on_right):
721
754
if primary_on_right == True :
722
755
ax_pri .yaxis .set_label_position ('right' )
723
756
ax_pri .yaxis .tick_right ()
724
- ax_sec .yaxis .set_label_position ('left' )
725
- ax_sec .yaxis .tick_left ()
757
+ if ax_sec is not None :
758
+ ax_sec .yaxis .set_label_position ('left' )
759
+ ax_sec .yaxis .tick_left ()
726
760
else : # treat non-True as False, whether False, None, or anything else.
727
761
ax_pri .yaxis .set_label_position ('left' )
728
762
ax_pri .yaxis .tick_left ()
729
- ax_sec .yaxis .set_label_position ('right' )
730
- ax_sec .yaxis .tick_right ()
763
+ if ax_sec is not None :
764
+ ax_sec .yaxis .set_label_position ('right' )
765
+ ax_sec .yaxis .tick_right ()
731
766
732
767
def _plot_mav (ax ,config ,xdates ,prices ,apmav = None ,apwidth = None ):
733
768
style = config ['style' ]
@@ -827,6 +862,9 @@ def _valid_addplot_kwargs():
827
862
'ylim' : {'Default' : None ,
828
863
'Validator' : lambda value : isinstance (value , (list ,tuple )) and len (value ) == 2
829
864
and all ([isinstance (v ,(int ,float )) for v in value ])},
865
+
866
+ 'ax' : {'Default' : None ,
867
+ 'Validator' : lambda value : isinstance (value ,mpl_axes .Axes ) },
830
868
}
831
869
832
870
_validate_vkwargs_dict (vkwargs )
0 commit comments