Skip to content

Commit

Permalink
Merge pull request #529 from DanielGoldfarb/fill_between
Browse files Browse the repository at this point in the history
fill_between support for addplot, and for list of fill_between.
  • Loading branch information
DanielGoldfarb authored May 10, 2022
2 parents f52adac + 3f063b4 commit 5114322
Show file tree
Hide file tree
Showing 15 changed files with 1,156 additions and 40 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pip install --upgrade mplfinance
- **[Customizing the Appearance of Plots](https://github.com/matplotlib/mplfinance/blob/master/markdown/customization_and_styles.md)**
- **[Adding Your Own Technical Studies to Plots](https://github.com/matplotlib/mplfinance/blob/master/examples/addplot.ipynb)**
- **[Subplots: Multiple Plots on a Single Figure](https://github.com/matplotlib/mplfinance/blob/master/markdown/subplots.md)**
- **[Fill Between: Filling Plots with Color](https://github.com/matplotlib/mplfinance/blob/master/examples/fill_between.ipynb)**
- **[Price-Movement Plots (Renko, P&F, etc)](https://github.com/matplotlib/mplfinance/blob/master/examples/price-movement_plots.ipynb)**
- **[Trends, Support, Resistance, and Trading Lines](https://github.com/matplotlib/mplfinance/blob/master/examples/using_lines.ipynb)**
- **[Coloring Individual Candlesticks](https://github.com/matplotlib/mplfinance/blob/master/examples/marketcolor_overrides.ipynb)** (New: December 2021)
Expand Down
807 changes: 807 additions & 0 deletions examples/fill_between.ipynb

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions examples/macd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import pandas as pd
import mplfinance as mpf

import matplotlib.dates as mdates

idf = pd.read_csv('data/SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True)
df = idf.loc['2011-07-01':'2011-12-30',:]


# =======
# MACD:

# df = df.iloc[0:30]

exp12 = df['Close'].ewm(span=12, adjust=False).mean()
exp26 = df['Close'].ewm(span=26, adjust=False).mean()
macd = exp12 - exp26
signal = macd.ewm(span=9, adjust=False).mean()
histogram = macd - signal

fb_green = dict(y1=macd.values,y2=signal.values,where=signal<macd,color="#93c47d",alpha=0.6,interpolate=True)
fb_red = dict(y1=macd.values,y2=signal.values,where=signal>macd,color="#e06666",alpha=0.6,interpolate=True)
fb_green['panel'] = 1
fb_red['panel'] = 1
fb = [fb_green,fb_red]

apds = [mpf.make_addplot(exp12,color='lime'),
mpf.make_addplot(exp26,color='c'),
mpf.make_addplot(histogram,type='bar',width=0.7,panel=1,
color='dimgray',alpha=1,secondary_y=True),
mpf.make_addplot(macd,panel=1,color='fuchsia',secondary_y=False),
mpf.make_addplot(signal,panel=1,color='b',secondary_y=False)#,fill_between=fb),
]

s = mpf.make_mpf_style(base_mpf_style='classic',rc={'figure.facecolor':'lightgray'})

mpf.plot(df,type='candle',addplot=apds,figscale=1.6,figratio=(6,5),title='\n\nMACD',
style=s,volume=True,volume_panel=2,panel_ratios=(3,4,1),fill_between=fb)#,show_nontrading=True)

44 changes: 22 additions & 22 deletions examples/plot_customizations.ipynb

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions src/mplfinance/_arg_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import numpy as np
import datetime
from mplfinance._helpers import _list_of_dict, _mpf_is_color_like
from mplfinance._helpers import _num_or_seq_of_num
import matplotlib as mpl
import warnings

Expand Down Expand Up @@ -430,3 +431,15 @@ def _check_for_external_axes(config):

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

def _valid_fb_dict(value):
return (isinstance(value,dict) and
'y1' in value and
_num_or_seq_of_num(value['y1']))

def _fill_between_validator(value):
if _num_or_seq_of_num(value): return True
if _valid_fb_dict(value): return True
if _list_of_dict(value):
return all([_valid_fb_dict(v) for v in value])
return False
4 changes: 2 additions & 2 deletions src/mplfinance/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ def _list_of_dict(x):
return isinstance(x,list) and all([isinstance(item,dict) for item in x])

def _num_or_seq_of_num(value):
return ( isinstance(value,(int,float)) or
return ( isinstance(value,(int,float,np.integer,np.floating)) or
(isinstance(value,(list,tuple,np.ndarray)) and
all([isinstance(v,(int,float)) for v in value]))
all([isinstance(v,(int,float,np.integer,np.floating)) for v in value]))
)

def roundTime(dt=None, roundTo=60):
Expand Down
2 changes: 1 addition & 1 deletion src/mplfinance/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version_info = (0, 12, 8, 'beta', 11)
version_info = (0, 12, 9, 'beta', 0)

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

Expand Down
50 changes: 35 additions & 15 deletions src/mplfinance/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from mplfinance import _styles

from mplfinance._arg_validators import _check_and_prepare_data, _mav_validator
from mplfinance._arg_validators import _get_valid_plot_types
from mplfinance._arg_validators import _get_valid_plot_types, _fill_between_validator
from mplfinance._arg_validators import _process_kwargs, _validate_vkwargs_dict
from mplfinance._arg_validators import _kwarg_not_implemented, _bypass_kwarg_validation
from mplfinance._arg_validators import _hlines_validator, _vlines_validator
Expand Down Expand Up @@ -304,9 +304,7 @@ def _valid_plot_kwargs():
'Description' : 'fill between specification as y-value, or sequence of'+
' y-values, or dict containing key "y1" plus any additional'+
' kwargs for `fill_between()`',
'Validator' : lambda value: _num_or_seq_of_num(value) or
(isinstance(value,dict) and 'y1' in value and
_num_or_seq_of_num(value['y1'])) },
'Validator' : _fill_between_validator },

'tight_layout' : { 'Default' : False,
'Description' : 'True|False implement tight layout (minimal padding around Figure)'+
Expand Down Expand Up @@ -687,7 +685,7 @@ def plot( data, **kwargs ):
aptype = apdict['type']
if aptype == 'ohlc' or aptype == 'candle':
ax = _addplot_collections(panid,panels,apdict,xdates,config)
_addplot_apply_supplements(ax,apdict)
_addplot_apply_supplements(ax,apdict,xdates)
else:
apdata = apdict['data']
if isinstance(apdata,list) and not isinstance(apdata[0],(float,int)):
Expand All @@ -700,24 +698,28 @@ def plot( data, **kwargs ):
for column in apdata:
ydata = apdata.loc[:,column] if havedf else column
ax = _addplot_columns(panid,panels,ydata,apdict,xdates,config)
_addplot_apply_supplements(ax,apdict)
_addplot_apply_supplements(ax,apdict,xdates)

# fill_between is NOT supported for external_axes_mode
# (caller can easily call ax.fill_between() themselves).
if config['fill_between'] is not None and not external_axes_mode:
fb = config['fill_between']
panid = config['main_panel']
if isinstance(fb,dict):
fblist = copy.deepcopy(config['fill_between'])
if _num_or_seq_of_num(fblist):
fblist = [dict(y1=fblist),]
elif isinstance(fblist,dict):
fblist = [fblist,]
if not _list_of_dict(fblist):
raise TypeError('Bad type for `fill_between` specifier.')
for fb in fblist:
if 'x' in fb:
raise ValueError('fill_between dict may not contain `x`')
panid = config['main_panel']
if 'panel' in fb:
panid = fb['panel']
del fb['panel']
else:
fb = dict(y1=fb)
fb['x'] = xdates
ax = panels.at[panid,'axes'][0]
ax.fill_between(**fb)
fb['x'] = xdates # add 'x' to caller's fb dict
ax = panels.at[panid,'axes'][0]
ax.fill_between(**fb)

# put the primary axis on one side,
# and the twinx() on the "other" side:
Expand Down Expand Up @@ -1045,7 +1047,7 @@ def _addplot_columns(panid,panels,ydata,apdict,xdates,config):

return ax

def _addplot_apply_supplements(ax,apdict):
def _addplot_apply_supplements(ax,apdict,xdates):
if (apdict['ylabel'] is not None):
ax.set_ylabel(apdict['ylabel'])
if apdict['ylim'] is not None:
Expand All @@ -1059,6 +1061,20 @@ def _addplot_apply_supplements(ax,apdict):
ax.set_yscale(yscale,**ysd)
elif isinstance(ysd,str):
ax.set_yscale(ysd)
# added by Wen
if "fill_between" in apdict and apdict['fill_between'] is not None:
# deep copy because mplfinance code sometimes modifies the fill_between dict
fblist = copy.deepcopy(apdict['fill_between'])
if isinstance(fblist,dict):
fblist = [fblist,]
if _list_of_dict(fblist):
for fb in fblist:
if 'x' in fb:
raise ValueError('fill_between dict may not contain `x`')
fb['x'] = xdates # add 'x' to caller's fb dict
ax.fill_between(**fb)
else:
raise ValueError('Invalid addplot fill_between: must be `dict` or `list of dict`')

def _set_ylabels_side(ax_pri,ax_sec,primary_on_right):
# put the primary axis on one side,
Expand Down Expand Up @@ -1234,6 +1250,10 @@ def _valid_addplot_kwargs():
" style\'s marketcolors). For addplot `type='ohlc'`"+
" and type='candle'",
'Validator' : lambda value: _is_marketcolor_object(value) },
'fill_between': { 'Default' : None, # added by Wen
'Description' : " fill region",
'Validator' : _fill_between_validator },

}

_validate_vkwargs_dict(vkwargs)
Expand Down
Binary file added tests/reference_images/fill_between01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/reference_images/fill_between02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/reference_images/fill_between03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/reference_images/fill_between04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/reference_images/fill_between05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/reference_images/fill_between06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 5114322

Please sign in to comment.