|
| 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:120,:] |
| 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.2*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 | +mpf.show() |
0 commit comments