Skip to content

fill_between support for addplot, and for list of fill_between. #529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
May 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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