Skip to content

Commit 51b2482

Browse files
Merge pull request #297 from DanielGoldfarb/master
New handling of volume exponent; and assorted scratch_pad additions: This PR is primarily to implement a new way to handle the volume exponent; the new way avoids calling `.draw()` which in turn eliminates the bug mentioned in issue #296, and also mentioned/described in a previous email from S.G. ([commit 2c88663](2c88663)) This PR also includes a lot of scratch_pad work for investigations of various issues (#282, #236, #241), and for an mplfinance presentation: including comparison of old and new api's, intraday animation with volumes, generation of animation gifs, etc.
2 parents 5d31680 + 80c9478 commit 51b2482

38 files changed

+5962
-12
lines changed

Diff for: examples/data/SP500_NOV2019_IDayRVol.csv

+1,564
Large diffs are not rendered by default.

Diff for: examples/original_flavor/finance_demo_newapi.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import pandas as pd
2+
from pandas.plotting import register_matplotlib_converters
3+
register_matplotlib_converters()
4+
import os.path
5+
6+
import mplfinance as mpf
7+
8+
date1 = "2004-2-1"
9+
date2 = "2004-4-12"
10+
11+
infile = os.path.join('data','yahoofinance-INTC-19950101-20040412.csv')
12+
quotes = pd.read_csv(infile, index_col=0, parse_dates=True, infer_datetime_format=True)
13+
14+
# select desired range of dates
15+
quotes = quotes[(quotes.index >= date1) & (quotes.index <= date2)]
16+
17+
mpf.plot(quotes,type='candle',style='checkers')

Diff for: examples/scratch_pad/Axes.scatter.ipynb

+521
Large diffs are not rendered by default.

Diff for: examples/scratch_pad/animation/gc.gif

2.75 MB
Loading

Diff for: examples/scratch_pad/animation/gcmacd.gif

5.51 MB
Loading

Diff for: examples/scratch_pad/animation/gcmacd2.gif

4.46 MB
Loading

Diff for: examples/scratch_pad/animation/genvol.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
import random
3+
4+
def gen_vol( numpoints, totalvol ):
5+
rlist = []
6+
for jj in range(0,num):
7+
r = 0
8+
for kk in range(0,3):
9+
r += random.random()
10+
rlist.append(r/3.0)
11+
rlist
12+
s = sum(rlist)
13+
vol = []
14+
for r in rlist:
15+
vol.append((r/s)*tot)
16+
17+
18+
19+
11/5/2019,3080.8,3083.95,3072.15,3074.62,585634570
20+
5 11/6/2019,3075.1,3078.34,3065.89,3076.78,544288522
21+
6 11/7/2019,3087.02,3097.77,3080.23,3085.18,566117910
22+
7 11/8/2019,3081.25,3093.09,3073.58,3093.08,460757054

Diff for: examples/scratch_pad/animation/idaygcmacd.gif

5.86 MB
Loading

Diff for: examples/scratch_pad/animation/mpf_anim_iday_gif.py

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
'''
2+
This file contains a animation demo using mplfinance demonstating resampling
3+
of the data and re-displaying the most recent, partially resampled, candle.
4+
5+
The idea for this example came from Issue #256
6+
(https://github.com/matplotlib/mplfinance/issues/256)
7+
8+
The typical use-case is where the user has real-time data from an API,
9+
perhaps to the second, or minute, but wants to aggregate that data to
10+
15 minutes per canlde, or maybe 30 minutes per candle. At the same time,
11+
during those 15 or 30 minutes, the user wants to see the most recent
12+
candle changing and developing as real-time data continues to come in.
13+
14+
In the example presented in this file, the data is once per minute,
15+
with an aggregation of 15 minutes per candle. But, for this *simulation*
16+
we set the animation rate to 250ms, which means we are getting 1 minute's
17+
worth of data from the API every 1/4 second. Thus, this simulation is
18+
running 240 times faster than real-time.
19+
20+
In a real-life case, if we have data once per second, and want to aggregate
21+
15 minutes per candle, we would set the animation interval to something
22+
like 5000ms (once every 5 seconds) because a more frequent visualization
23+
might be impractical to watch or to use for decision making.
24+
25+
PLEASE NOTE: In this example, we resample the *entire* data set with each
26+
animation cycle. This is inefficient, but works fine for less than 5000
27+
data points or so. For larger data sets it may be practical to cache
28+
the resampled data up to the last "full" candle, and only resample the
29+
data that contributes to the final candle (and append it to the cached
30+
resampled data). If I have time, I will work up and example doing that.
31+
32+
NOTE: Presently mplfinance does not support "blitting" (blitting makes animation
33+
more efficient). Nonetheless, the animation is efficient enough to update at least
34+
once per second, and typically more frequently depending on the size of the plot.
35+
'''
36+
import pandas as pd
37+
import mplfinance as mpf
38+
import matplotlib.animation as animation
39+
40+
## Class to simulate getting more data from API:
41+
42+
class RealTimeAPI():
43+
def __init__(self):
44+
self.data_pointer = 0
45+
self.data_frame = pd.read_csv('data/SP500_NOV2019_IDay.csv',index_col=0,parse_dates=True)
46+
self.data_frame = self.data_frame.iloc[0:480,:]
47+
self.df_len = len(self.data_frame)
48+
49+
def fetch_next(self):
50+
r1 = self.data_pointer
51+
self.data_pointer += 1
52+
if self.data_pointer >= self.df_len:
53+
return None
54+
return self.data_frame.iloc[r1:self.data_pointer,:]
55+
56+
def initial_fetch(self):
57+
if self.data_pointer > 0:
58+
return
59+
r1 = self.data_pointer
60+
self.data_pointer += int(0.3*self.df_len)
61+
return self.data_frame.iloc[r1:self.data_pointer,:]
62+
63+
rtapi = RealTimeAPI()
64+
65+
resample_map ={'Open' :'first',
66+
'High' :'max' ,
67+
'Low' :'min' ,
68+
'Close':'last' }
69+
resample_period = '15T'
70+
71+
df = rtapi.initial_fetch()
72+
rs = df.resample(resample_period).agg(resample_map).dropna()
73+
74+
fig, axes = mpf.plot(rs,returnfig=True,figsize=(11,8),type='candle',title='\n\nGrowing Candle')
75+
ax = axes[0]
76+
77+
def animate(ival):
78+
global df
79+
global rs
80+
nxt = rtapi.fetch_next()
81+
if nxt is None:
82+
#print('no more data to plot')
83+
#ani.event_source.interval *= 3
84+
#if ani.event_source.interval > 12000:
85+
exit()
86+
#return
87+
df = df.append(nxt)
88+
rs = df.resample(resample_period).agg(resample_map).dropna()
89+
ax.clear()
90+
mpf.plot(rs,ax=ax,type='candle')
91+
92+
ani = animation.FuncAnimation(fig, animate, interval=250)
93+
94+
print('about to save')
95+
ani.save('gc.gif', writer='imagemagick',fps=4)
96+
print('DONE saving')
97+
98+
mpf.show()
+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
'''
2+
This file contains a animation demo using mplfinance "external axes mode",
3+
in which animate both the display of candlesticks as well as the display
4+
of MACD (Moving Average Convergence Divergence) visual analysis.
5+
6+
In this example, instead of creating the Figure and Axes external to mplfiance,
7+
we allow mplfinance to create the Figure and Axes using its "panel method", and
8+
set kwarg `returnfig=True` so that mplfinance will return the Figure and Axes.
9+
10+
We then take those Axes and pass them back into mplfinance ("external axes mode")
11+
as part of the animation.
12+
13+
Note that presently mplfinance does not support "blitting" (blitting makes animation
14+
more efficient). Nonetheless, the animation is efficient enough to update at least
15+
once per second, and typically more frequently depending on the size of the plot.
16+
'''
17+
import pandas as pd
18+
import mplfinance as mpf
19+
import matplotlib.animation as animation
20+
21+
mpf.__version__
22+
23+
## Class to simulate getting more data from API:
24+
25+
class RealTimeAPI():
26+
def __init__(self):
27+
self.data_pointer = 0
28+
self.data_frame = pd.read_csv('../../data/SP500_NOV2019_IDayRVol.csv',index_col=0,parse_dates=True)
29+
#self.data_frame = self.data_frame.iloc[0:120,:]
30+
self.df_len = len(self.data_frame)
31+
32+
def fetch_next(self):
33+
r1 = self.data_pointer
34+
self.data_pointer += 1
35+
if self.data_pointer >= self.df_len:
36+
return None
37+
return self.data_frame.iloc[r1:self.data_pointer,:]
38+
39+
def initial_fetch(self,num):
40+
if self.data_pointer > 0:
41+
return
42+
r1 = self.data_pointer
43+
self.data_pointer += num
44+
return self.data_frame.iloc[r1:self.data_pointer,:]
45+
46+
rtapi = RealTimeAPI()
47+
48+
# =======
49+
# MACD:
50+
51+
df = rtapi.initial_fetch(450)
52+
53+
resample_map ={'Open' :'first',
54+
'High' :'max' ,
55+
'Low' :'min' ,
56+
'Close' :'last' ,
57+
'Volume':'sum' }
58+
59+
resample_period = '5T'
60+
61+
rs = df.resample(resample_period).agg(resample_map).dropna()
62+
63+
exp12 = rs['Close'].ewm(span=12, adjust=False).mean()
64+
exp26 = rs['Close'].ewm(span=26, adjust=False).mean()
65+
macd = exp12 - exp26
66+
signal = macd.ewm(span=9, adjust=False).mean()
67+
histogram = macd - signal
68+
69+
apds = [mpf.make_addplot(exp12,color='lime'),
70+
mpf.make_addplot(exp26,color='c'),
71+
mpf.make_addplot(histogram,type='bar',width=0.7,panel=1,
72+
color='dimgray',alpha=1,secondary_y=False),
73+
mpf.make_addplot(macd,panel=1,color='fuchsia',secondary_y=True),
74+
mpf.make_addplot(signal,panel=1,color='b',secondary_y=True),
75+
]
76+
77+
s = mpf.make_mpf_style(base_mpf_style='classic',rc={'figure.facecolor':'lightgray'})
78+
79+
fig, axes = mpf.plot(rs,type='candle',addplot=apds,figscale=1.5,figratio=(7,5),title='\n\nMACD',
80+
style=s,volume=True,volume_panel=2,panel_ratios=(6,3,2),returnfig=True)
81+
82+
ax_main = axes[0]
83+
ax_emav = ax_main
84+
ax_hisg = axes[2]
85+
ax_macd = axes[3]
86+
ax_sign = ax_macd
87+
ax_volu = axes[4]
88+
89+
90+
def animate(ival):
91+
global df, rs
92+
nxt = rtapi.fetch_next()
93+
if nxt is None:
94+
print('no more data to plot')
95+
exit()
96+
df = df.append(nxt)
97+
rs = df.resample(resample_period).agg(resample_map).dropna()
98+
if len(rs) > 90:
99+
rs = rs[-90:]
100+
data = rs
101+
exp12 = data['Close'].ewm(span=12, adjust=False).mean()
102+
exp26 = data['Close'].ewm(span=26, adjust=False).mean()
103+
macd = exp12 - exp26
104+
signal = macd.ewm(span=9, adjust=False).mean()
105+
histogram = macd - signal
106+
apds = [mpf.make_addplot(exp12,color='lime',ax=ax_emav),
107+
mpf.make_addplot(exp26,color='c',ax=ax_emav),
108+
mpf.make_addplot(histogram,type='bar',width=0.7,
109+
color='dimgray',alpha=1,ax=ax_hisg),
110+
mpf.make_addplot(macd,color='fuchsia',ax=ax_macd),
111+
mpf.make_addplot(signal,color='b',ax=ax_sign),
112+
]
113+
114+
for ax in axes:
115+
ax.clear()
116+
mpf.plot(data,type='candle',addplot=apds,ax=ax_main,volume=ax_volu)
117+
118+
ani = animation.FuncAnimation(fig,animate,interval=50)
119+
120+
## print('about to save')
121+
## ani.save('idaygcmacd.gif', writer='imagemagick',fps=5)
122+
## print('DONE saving')
123+
124+
import matplotlib.pyplot as plt
125+
print('Backend: ',plt.get_backend())
126+
127+
mpf.show()
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'''
2+
This file contains a simple animation demo using mplfinance "external axes mode".
3+
4+
Note that presently mplfinance does not support "blitting" (blitting makes animation
5+
more efficient). Nonetheless, the animation is efficient enough to update at least
6+
once per second, and typically more frequently depending on the size of the plot.
7+
'''
8+
import pandas as pd
9+
import mplfinance as mpf
10+
import matplotlib.animation as animation
11+
12+
idf = pd.read_csv('data/SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True)
13+
idf.shape
14+
idf.head(3)
15+
idf.tail(3)
16+
df = idf.loc['2011-07-01':'2011-12-30',:]
17+
18+
fig = mpf.figure(style='charles',figsize=(7,8))
19+
ax1 = fig.add_subplot(2,1,1)
20+
ax2 = fig.add_subplot(3,1,3)
21+
22+
def animate(ival):
23+
if (22+ival) > len(df):
24+
print('no more data to plot')
25+
ani.event_source.interval *= 3
26+
if ani.event_source.interval > 12000:
27+
exit()
28+
return
29+
data = df.iloc[0+ival:(22+ival)]
30+
ax1.clear()
31+
ax2.clear()
32+
mpf.plot(data,ax=ax1,volume=ax2,type='ohlc')
33+
34+
ani = animation.FuncAnimation(fig, animate, interval=250)
35+
36+
mpf.show()
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'''
2+
This file contains a simple animation demo using mplfinance "external axes mode".
3+
4+
In this example, instead of creating the Figure and Axes external to mplfiance,
5+
we allow mplfinance to create the Figure and Axes using its "panel method", and
6+
set kwarg `returnfig=True` so that mplfinance will return the Figure and Axes.
7+
8+
We then take those Axes and pass them back into mplfinance ("external axes mode")
9+
as part of the animation.
10+
11+
Note that presently mplfinance does not support "blitting" (blitting makes animation
12+
more efficient). Nonetheless, the animation is efficient enough to update at least
13+
once per second, and typically more frequently depending on the size of the plot.
14+
'''
15+
import pandas as pd
16+
import mplfinance as mpf
17+
import matplotlib.animation as animation
18+
19+
idf = pd.read_csv('data/SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True)
20+
21+
df = idf.loc['2011-07-01':'2011-12-30',:]
22+
23+
pkwargs=dict(type='candle',mav=(7,12))
24+
25+
fig, axes = mpf.plot(df.iloc[0:22],returnfig=True,volume=True,
26+
figsize=(11,8),panel_ratios=(2,1),
27+
title='\n\nS&P 500 ETF',**pkwargs)
28+
ax1 = axes[0]
29+
ax2 = axes[2]
30+
31+
def animate(ival):
32+
if (20+ival) > len(df):
33+
print('no more data to plot')
34+
ani.event_source.interval *= 3
35+
if ani.event_source.interval > 12000:
36+
exit()
37+
return
38+
data = df.iloc[0+ival:(22+ival)]
39+
ax1.clear()
40+
ax2.clear()
41+
mpf.plot(data,ax=ax1,volume=ax2,**pkwargs)
42+
43+
ani = animation.FuncAnimation(fig, animate, interval=200)
44+
45+
mpf.show()

0 commit comments

Comments
 (0)