|
| 1 | +#!/usr/bin/env python |
| 2 | +# coding: utf-8 |
| 3 | +import os |
| 4 | +import os.path |
| 5 | +import glob |
| 6 | +import mplfinance as mpf |
| 7 | +import matplotlib.pyplot as plt |
| 8 | +from matplotlib.testing.compare import compare_images |
| 9 | + |
| 10 | +print('mpf.__version__ =',mpf.__version__) # for the record |
| 11 | +print("plt.rcParams['backend'] =",plt.rcParams['backend']) # for the record |
| 12 | + |
| 13 | +base='fill_between' |
| 14 | +tdir = os.path.join('tests','test_images') |
| 15 | +refd = os.path.join('tests','reference_images') |
| 16 | + |
| 17 | +globpattern = os.path.join(tdir,base+'*.png') |
| 18 | +oldtestfiles = glob.glob(globpattern) |
| 19 | + |
| 20 | +for fn in oldtestfiles: |
| 21 | + try: |
| 22 | + os.remove(fn) |
| 23 | + except: |
| 24 | + print('Error removing file "'+fn+'"') |
| 25 | + |
| 26 | +# IMGCOMP_TOLERANCE = 7.0 # this works fine for linux |
| 27 | +IMGCOMP_TOLERANCE = 11.0 # required for a windows pass. (really 10.25 may do it). |
| 28 | + |
| 29 | +def _get_scaled_down_dataset(bolldata): |
| 30 | + start = int(0.2*len(bolldata)) |
| 31 | + end = 2*start |
| 32 | + df = bolldata.iloc[start:end] |
| 33 | + scaled_down_data = df/df['Close'].mean() |
| 34 | + scaled_down_data -= 0.9*scaled_down_data['Low'].min() |
| 35 | + return scaled_down_data |
| 36 | + |
| 37 | + |
| 38 | +def test_fill_between01(bolldata): |
| 39 | + |
| 40 | + scaled_down_data = _get_scaled_down_dataset(bolldata) |
| 41 | + |
| 42 | + fname = base+'01.png' |
| 43 | + tname = os.path.join(tdir,fname) |
| 44 | + rname = os.path.join(refd,fname) |
| 45 | + |
| 46 | + mpf.plot(scaled_down_data,figscale=0.7, |
| 47 | + fill_between=scaled_down_data['Close'].values, |
| 48 | + savefig=tname) |
| 49 | + |
| 50 | + tsize = os.path.getsize(tname) |
| 51 | + print(glob.glob(tname),'[',tsize,'bytes',']') |
| 52 | + |
| 53 | + rsize = os.path.getsize(rname) |
| 54 | + print(glob.glob(rname),'[',rsize,'bytes',']') |
| 55 | + |
| 56 | + result = compare_images(rname,tname,tol=IMGCOMP_TOLERANCE) |
| 57 | + if result is not None: |
| 58 | + print('result=',result) |
| 59 | + assert result is None |
| 60 | + |
| 61 | +def test_fill_between02(bolldata): |
| 62 | + |
| 63 | + scaled_down_data = _get_scaled_down_dataset(bolldata) |
| 64 | + |
| 65 | + fname = base+'02.png' |
| 66 | + tname = os.path.join(tdir,fname) |
| 67 | + rname = os.path.join(refd,fname) |
| 68 | + |
| 69 | + mpf.plot(scaled_down_data,figscale=0.7, |
| 70 | + fill_between={'y1':scaled_down_data['Close'].values,'alpha':0.75}, |
| 71 | + savefig=tname) |
| 72 | + |
| 73 | + tsize = os.path.getsize(tname) |
| 74 | + print(glob.glob(tname),'[',tsize,'bytes',']') |
| 75 | + |
| 76 | + rsize = os.path.getsize(rname) |
| 77 | + print(glob.glob(rname),'[',rsize,'bytes',']') |
| 78 | + |
| 79 | + result = compare_images(rname,tname,tol=IMGCOMP_TOLERANCE) |
| 80 | + if result is not None: |
| 81 | + print('result=',result) |
| 82 | + assert result is None |
| 83 | + |
| 84 | + |
| 85 | +## import pandas as pd |
| 86 | +## |
| 87 | +## # ### Read in daily data for the S&P 500 from November of 2019 |
| 88 | +## |
| 89 | +## daily = pd.read_csv('data/SP500_NOV2019_Hist.csv',index_col=0,parse_dates=True) |
| 90 | +## daily.index.name = 'Date' |
| 91 | +## daily.shape |
| 92 | +## daily.head(3) |
| 93 | +## |
| 94 | +## import mplfinance as mpf |
| 95 | +## mpf.__version__ |
| 96 | +## |
| 97 | +## print(''' |
| 98 | +## |
| 99 | +## A single y-value or series **assumes** we fill between ZERO and that single y-value or series. |
| 100 | +## |
| 101 | +## Therefore, scale down the data, close to zero, so that we can demonstrate specifying |
| 102 | +## a single y-value, or single y-series for fill_between: |
| 103 | +## |
| 104 | +## ''') |
| 105 | +## scaled_down_data = (daily/3000)-1.0 |
| 106 | +## |
| 107 | +## mpf.plot(scaled_down_data,figscale=0.7) |
| 108 | +## mpf.plot(scaled_down_data,figscale=0.7,fill_between=0.03) |
| 109 | +## mpf.plot(scaled_down_data,figscale=0.7,fill_between=scaled_down_data['Close'].values) |
| 110 | +## |
| 111 | +## print(''' |
| 112 | +## |
| 113 | +## Use a dict to specify two y values, or two series, (y1 and y2) for `fill_between`: |
| 114 | +## |
| 115 | +## ''') |
| 116 | +## |
| 117 | +## mpf.plot(daily,figscale=0.7,fill_between=dict(y1=3090,y2=3120)) |
| 118 | +## mpf.plot(daily,figscale=0.7,fill_between=dict(y1=3100,y2=daily['Close'].values)) |
| 119 | +## mpf.plot(daily,figscale=0.7,fill_between=dict(y1=daily['Low'].values,y2=daily['High'].values)) |
| 120 | +## |
| 121 | +## print(''' |
| 122 | +## |
| 123 | +## Use a dict to specify other attributes (kwargs) for `fill_between`: |
| 124 | +## |
| 125 | +## To demonstrate use of the `where` kwarg to display a holding period, |
| 126 | +## we convert the datetime index into a dataframe, and use that to generate a boolean array: |
| 127 | +## |
| 128 | +## `where_values = pd.notnull(dates_df[ (dates_df>=buy_date) & (dates_df <= sell_date) ])['Date'].values` |
| 129 | +## |
| 130 | +## ''') |
| 131 | +## |
| 132 | +## dates_df = pd.DataFrame(daily.index) |
| 133 | +## buy_date = pd.Timestamp('2019-11-06') |
| 134 | +## sell_date = pd.Timestamp('2019-11-19') |
| 135 | +## |
| 136 | +## where_values = pd.notnull(dates_df[ (dates_df>=buy_date) & (dates_df <= sell_date) ])['Date'].values |
| 137 | +## |
| 138 | +## y1values = daily['Close'].values |
| 139 | +## y2value = daily['Low'].min() |
| 140 | +## |
| 141 | +## mpf.plot(daily,figscale=0.7, |
| 142 | +## fill_between=dict(y1=y1values,y2=y2value,where=where_values,alpha=0.5,color='g') |
| 143 | +## ) |
| 144 | +## |
| 145 | +## print(''' |
| 146 | +## |
| 147 | +## Use `panel=` in the `fill_between` dict to place the fill_between on a panel other than panel 0: |
| 148 | +## |
| 149 | +## In this example, we `fill_between` on the volume panel, |
| 150 | +## filling between the volume and the average volume. |
| 151 | +## |
| 152 | +## ''') |
| 153 | +## |
| 154 | +## mpf.plot(daily,volume=True,panel_ratios=(1.1,1), |
| 155 | +## type='candle',tight_layout=True,figratio=(1,1), |
| 156 | +## fill_between=dict(y1=daily['Volume'].values, |
| 157 | +## y2=daily['Volume'].mean(), |
| 158 | +## panel=1,alpha=0.5,color='lime')) |
| 159 | +## |
| 160 | +## print(''' |
| 161 | +## |
| 162 | +## There are two ways to do multiple `fill_betweens`: |
| 163 | +## (1) Specify a list of `fill_between` dicts. |
| 164 | +## (2) Specify a fill_between (or list of fill_betweens) for each `mpf.make_addplot()` |
| 165 | +## |
| 166 | +## Here, for example, we specify a `fill_between=` a list of fill between dicts: |
| 167 | +## |
| 168 | +## ''') |
| 169 | +## |
| 170 | +## fb1 = dict(y1=daily['Open'].values , y2=daily['Close'].values , panel=0, alpha=0.3, color='magenta') |
| 171 | +## fb2 = dict(y1=daily['Volume'].values, y2=daily['Volume'].mean(), panel=1, alpha=0.5, color='lime') |
| 172 | +## |
| 173 | +## mpf.plot(daily,volume=True,panel_ratios=(1.1,1), |
| 174 | +## type='candle',tight_layout=True,figratio=(1,1), |
| 175 | +## fill_between=[fb1,fb2]) |
| 176 | +## |
| 177 | +## print(''' |
| 178 | +## |
| 179 | +## We can accomplish the same thing by specifying one fill_between in `mpf.plot()` |
| 180 | +## and the other in `mpf.make_addplot()`. This is useful if we are already using |
| 181 | +## `make_addplot()` to plot some additional data. |
| 182 | +## |
| 183 | +## NOTE: Since make_addplot() accepts a panel argument, one should NOT specify |
| 184 | +## panel in the fill_between dict used by make_addplot. |
| 185 | +## |
| 186 | +## ''') |
| 187 | +## |
| 188 | +## fb1 = dict(y1=daily['Open'].values , y2=daily['Close'].values , alpha=0.4, color='magenta') |
| 189 | +## fb2 = dict(y1=daily['Volume'].values, y2=daily['Volume'].mean(), alpha=0.5, color='lime') |
| 190 | +## |
| 191 | +## avol = [daily['Volume'].mean()]*len(daily) |
| 192 | +## |
| 193 | +## ap = mpf.make_addplot(avol,panel=1,fill_between=fb2,color='k',linestyle='-.',width=0.25) |
| 194 | +## |
| 195 | +## mpf.plot(daily,volume=True,panel_ratios=(1.1,1), |
| 196 | +## type='candle',tight_layout=True,figratio=(1,1), |
| 197 | +## fill_between=fb1,addplot=ap) |
| 198 | +## |
| 199 | +## print(''' |
| 200 | +## |
| 201 | +## We can specify effectively a "multi-color" fill_between, by breaking it into |
| 202 | +## two separate fill_betweens, with two separate colors, each with a "where" clause to |
| 203 | +## indicate where the fill_between color should and should not appear along the datetime axis. |
| 204 | +## |
| 205 | +## This is useful, for example, if we want to highlight where a given value is |
| 206 | +## above or below the average value. |
| 207 | +## |
| 208 | +## Notice that when using multiple where clauses like this, it is helpful |
| 209 | +## to set `interpolate=True` in the `fill_between` dict, so that the space |
| 210 | +## between True values and False values also gets filled. |
| 211 | +## |
| 212 | +## ''') |
| 213 | +## |
| 214 | +## fb_above = dict(y1=daily['Volume'].values, |
| 215 | +## y2=daily['Volume'].mean(), |
| 216 | +## alpha=0.4, color='lime', |
| 217 | +## interpolate=True, |
| 218 | +## where=(daily['Volume'] > daily['Volume'].mean()).values) |
| 219 | +## |
| 220 | +## fb_below = fb_above.copy() |
| 221 | +## fb_below['color'] = 'magenta' |
| 222 | +## fb_below['where'] = (daily['Volume'] < daily['Volume'].mean()).values |
| 223 | +## |
| 224 | +## avol = [daily['Volume'].mean()]*len(daily) |
| 225 | +## ap = mpf.make_addplot(avol,panel=1,fill_between=[fb_above,fb_below],color='k',linestyle='-.',width=0.25) |
| 226 | +## |
| 227 | +## mpf.plot(daily,volume=True,panel_ratios=(0.8,1), |
| 228 | +## type='candle',tight_layout=True,figratio=(1,1),addplot=ap) |
| 229 | +## |
| 230 | +## print(''' |
| 231 | +## |
| 232 | +## Here, as an additional example, we create "multi-color" fill_between for both panels: |
| 233 | +## |
| 234 | +## ''') |
| 235 | +## |
| 236 | +## fbvolume_above = dict(y1=daily['Volume'].values, |
| 237 | +## y2=daily['Volume'].mean(), |
| 238 | +## alpha=0.4, color='lime', |
| 239 | +## interpolate=True, |
| 240 | +## where=(daily['Volume'] > daily['Volume'].mean()).values) |
| 241 | +## |
| 242 | +## fbvolume_below = fbvolume_above.copy() |
| 243 | +## fbvolume_below['color'] = 'magenta' |
| 244 | +## fbvolume_below['where'] = (daily['Volume'] < daily['Volume'].mean()).values |
| 245 | +## |
| 246 | +## avol = [daily['Volume'].mean()]*len(daily) |
| 247 | +## ap = mpf.make_addplot(avol,panel=1,fill_between=[fbvolume_above,fbvolume_below],color='k',linestyle='-.',width=0.25) |
| 248 | +## |
| 249 | +## fbclose_above = dict(y1=daily['Open'].values , y2=daily['Close'].values , alpha=0.4, |
| 250 | +## interpolate=True, |
| 251 | +## color='lime', |
| 252 | +## where=(daily['Close']>daily['Open']).values |
| 253 | +## ) |
| 254 | +## |
| 255 | +## fbclose_below = fbclose_above.copy() |
| 256 | +## fbclose_below['color'] = 'magenta' |
| 257 | +## fbclose_below['where'] = (daily['Close']<daily['Open']).values |
| 258 | +## |
| 259 | +## mpf.plot(daily,volume=True,panel_ratios=(1,1), |
| 260 | +## type='candle',tight_layout=True,figratio=(1,1), |
| 261 | +## fill_between=[fbclose_above,fbclose_below], |
| 262 | +## addplot=ap) |
| 263 | +## |
| 264 | +## print(''' |
| 265 | +## |
| 266 | +## Finally, as a more pratical example, we use `fill_between` to color a MACD plot: |
| 267 | +## |
| 268 | +## ''') |
| 269 | +## |
| 270 | +## df = pd.read_csv('data/SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True).loc['2011-07-01':'2011-12-30',:] |
| 271 | +## |
| 272 | +## # ======= |
| 273 | +## # MACD: |
| 274 | +## |
| 275 | +## exp12 = df['Close'].ewm(span=12, adjust=False).mean() |
| 276 | +## exp26 = df['Close'].ewm(span=26, adjust=False).mean() |
| 277 | +## macd = exp12 - exp26 |
| 278 | +## signal = macd.ewm(span=9, adjust=False).mean() |
| 279 | +## histogram = macd - signal |
| 280 | +## |
| 281 | +## fb_green = dict(y1=macd.values,y2=signal.values,where=signal<macd,color="#93c47d",alpha=0.6,interpolate=True) |
| 282 | +## fb_red = dict(y1=macd.values,y2=signal.values,where=signal>macd,color="#e06666",alpha=0.6,interpolate=True) |
| 283 | +## fb_green['panel'] = 1 |
| 284 | +## fb_red['panel'] = 1 |
| 285 | +## fb = [fb_green,fb_red] |
| 286 | +## |
| 287 | +## apds = [mpf.make_addplot(exp12,color='lime'), |
| 288 | +## mpf.make_addplot(exp26,color='c'), |
| 289 | +## mpf.make_addplot(histogram,type='bar',width=0.7,panel=1, |
| 290 | +## color='dimgray',alpha=1,secondary_y=True), |
| 291 | +## mpf.make_addplot(macd,panel=1,color='fuchsia',secondary_y=False), |
| 292 | +## mpf.make_addplot(signal,panel=1,color='b',secondary_y=False)#,fill_between=fb), |
| 293 | +## ] |
| 294 | +## |
| 295 | +## s = mpf.make_mpf_style(base_mpf_style='classic',rc={'figure.facecolor':'lightgray'}) |
| 296 | +## |
| 297 | +## mpf.plot(df,type='candle',addplot=apds,figscale=1.6,figratio=(6,5),title='\n\nMACD', |
| 298 | +## style=s,volume=True,volume_panel=2,panel_ratios=(3,4,1),fill_between=fb) |
| 299 | +## |
| 300 | +## def test_fill_between02(bolldata): |
0 commit comments