Skip to content

Commit cfb787c

Browse files
first working version of ohlc/candle in addplot
1 parent 51de01a commit cfb787c

File tree

3 files changed

+1009
-27
lines changed

3 files changed

+1009
-27
lines changed

examples/scratch_pad/addplot_ohlc_rawtest.ipynb

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

src/mplfinance/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
version_info = (0, 12, 5, 'alpha', 3)
2+
version_info = (0, 12, 6, 'alpha', 0)
33

44
_specifier_ = {'alpha': 'a','beta': 'b','candidate': 'rc','final': ''}
55

src/mplfinance/plotting.py

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,50 @@ def plot( data, **kwargs ):
474474
raise TypeError('addplot must be `dict`, or `list of dict`, NOT '+str(type(addplot)))
475475

476476
for apdict in addplot:
477+
478+
panid = apdict['panel']
479+
if panid == 'main' : panid = 0 # for backwards compatibility
480+
elif panid == 'lower': panid = 1 # for backwards compatibility
481+
482+
#--------------------------------------------------------------#
483+
# Note: _auto_secondary_y() sets the 'magnitude' column in the
484+
# `panels` dataframe, which is needed for automatically
485+
# determining if secondary_y is needed. Therefore we call
486+
# _auto_secondary_y() for *all* addplots, even those that
487+
# are set to True or False (not 'auto') for secondary_y
488+
# because their magnitudes may be needed if *any* apdicts
489+
# contain secondary_y='auto'.
490+
# In theory we could first loop through all apdicts to see
491+
# if any have secondary_y='auto', but since that is the
492+
# default value, we will just assume we have at least one.
493+
477494
apdata = apdict['data']
495+
aptype = apdict['type']
496+
497+
if aptype == 'ohlc' or aptype == 'candle':
498+
#import pdb; pdb.set_trace()
499+
if not isinstance(apdata,pd.DataFrame):
500+
raise TypeError('addplot type "'+aptype+'" MUST be accompanied by addplot data of type `pd.DataFrame`')
501+
d,o,h,l,c,v = _check_and_prepare_data(apdata,config)
502+
collections = _construct_mpf_collections(aptype,d,xdates,o,h,l,c,v,config,style)
503+
lo = math.log(max(math.fabs(np.nanmin(l)),1e-7),10) - 0.5
504+
hi = math.log(max(math.fabs(np.nanmax(h)),1e-7),10) + 0.5
505+
secondary_y = _auto_secondary_y( panels, panid, lo, hi )
506+
if 'auto' != apdict['secondary_y']:
507+
secondary_y = apdict['secondary_y']
508+
if secondary_y:
509+
ax = panels.at[panid,'axes'][1]
510+
panels.at[panid,'used2nd'] = True
511+
else:
512+
ax = panels.at[panid,'axes'][0]
513+
for coll in collections:
514+
ax.add_collection(coll)
515+
datalim = (minx, min(l)), (maxx, max(h))
516+
#ax.update_datalim(datalim)
517+
ax.autoscale_view() # Is this really necessary??
518+
#ax.set_ylim(min(l),max(h))
519+
continue
520+
478521
if isinstance(apdata,list) and not isinstance(apdata[0],(float,int)):
479522
raise TypeError('apdata is list but NOT of float or int')
480523
if isinstance(apdata,pd.DataFrame):
@@ -488,26 +531,12 @@ def plot( data, **kwargs ):
488531
ydata = apdata.loc[:,column]
489532
else:
490533
ydata = column
491-
yd = [y for y in ydata if not math.isnan(y)]
492-
ymhi = math.log(max(math.fabs(np.nanmax(yd)),1e-7),10)
493-
ymlo = math.log(max(math.fabs(np.nanmin(yd)),1e-7),10)
494534
secondary_y = False
495-
panid = apdict['panel']
496-
if panid == 'main' : panid = 0 # for backwards compatibility
497-
elif panid == 'lower': panid = 1 # for backwards compatibility
498535
if apdict['secondary_y'] == 'auto':
499-
# If mag(nitude) for this panel is not yet set, then set it
500-
# here, as this is the first ydata to be plotted on this panel:
501-
# i.e. consider this to be the 'primary' axis for this panel.
502-
p = panid,'mag'
503-
if panels.at[p] is None:
504-
panels.at[p] = {'lo':ymlo,'hi':ymhi}
505-
elif ymlo < panels.at[p]['lo'] or ymhi > panels.at[p]['hi']:
506-
secondary_y = True
507-
#if secondary_y:
508-
# print('auto says USE secondary_y ... for panel',panid)
509-
#else:
510-
# print('auto says do NOT use secondary_y ... for panel',panid)
536+
yd = [y for y in ydata if not math.isnan(y)]
537+
ymhi = math.log(max(math.fabs(np.nanmax(yd)),1e-7),10)
538+
ymlo = math.log(max(math.fabs(np.nanmin(yd)),1e-7),10)
539+
secondary_y = _auto_secondary_y( panels, panid, ymlo, ymhi )
511540
else:
512541
secondary_y = apdict['secondary_y']
513542
#print("apdict['secondary_y'] says secondary_y is",secondary_y)
@@ -540,13 +569,6 @@ def plot( data, **kwargs ):
540569
ls = apdict['linestyle']
541570
color = apdict['color']
542571
ax.plot(xdates, ydata, linestyle=ls, color=color)
543-
#elif aptype == 'ohlc' or aptype == 'candle':
544-
# This won't work as is, because here we are looping through one column at a time
545-
# and mpf_collections needs ohlc columns:
546-
# collections =_construct_mpf_collections(aptype,dates,xdates,opens,highs,lows,closes,volumes,config,style)
547-
# if len(collections) == 1: collections = [collections]
548-
# for collection in collections:
549-
# ax.add_collection(collection)
550572
else:
551573
raise ValueError('addplot type "'+str(aptype)+'" NOT yet supported.')
552574

@@ -604,6 +626,12 @@ def plot( data, **kwargs ):
604626
#fig.autofmt_xdate()
605627

606628
axA1.autoscale_view() # Is this really necessary??
629+
# It appears to me, based on experience coding types 'ohlc' and 'candle'
630+
# for `addplot`, that this IS necessary when the only thing done to the
631+
# the axes is .add_collection(). (However, if ax.plot() .scatter() or
632+
# .bar() was called, then possibly this is not necessary; not entirely
633+
# sure, but it definitely was necessary to get 'ohlc' and 'candle'
634+
# working in `addplot`).
607635

608636
axA1.set_ylabel(config['ylabel'])
609637

@@ -674,12 +702,27 @@ def plot( data, **kwargs ):
674702
# print('rcpdfhead(3)=',rcpdf.head(3))
675703
# return # rcpdf
676704

705+
def _auto_secondary_y( panels, panid, ylo, yhi ):
706+
# If mag(nitude) for this panel is not yet set, then set it
707+
# here, as this is the first ydata to be plotted on this panel:
708+
# i.e. consider this to be the 'primary' axis for this panel.
709+
secondary_y = False
710+
p = panid,'mag'
711+
if panels.at[p] is None:
712+
panels.at[p] = {'lo':ylo,'hi':yhi}
713+
elif ylo < panels.at[p]['lo'] or yhi > panels.at[p]['hi']:
714+
secondary_y = True
715+
#if secondary_y:
716+
# print('auto says USE secondary_y ... for panel',panid)
717+
#else:
718+
# print('auto says do NOT use secondary_y ... for panel',panid)
719+
return secondary_y
677720

678721
def _valid_addplot_kwargs():
679722

680723
valid_linestyles = ('-','solid','--','dashed','-.','dashdot','.','dotted',None,' ','')
681724
#valid_types = ('line','scatter','bar','ohlc','candle')
682-
valid_types = ('line','scatter','bar')
725+
valid_types = ('line','scatter','bar', 'ohlc', 'candle')
683726

684727
vkwargs = {
685728
'scatter' : { 'Default' : False,

0 commit comments

Comments
 (0)