準備工作-用python繪製金融資料曲線的進階例項
阿新 • • 發佈:2019-01-02
import datetime import numpy as np import matplotlib.colors as colors import matplotlib.finance as finance import matplotlib.dates as mdates import matplotlib.ticker as mticker import matplotlib.mlab as mlab import matplotlib.pyplot as plt import matplotlib.font_manager as font_manager startdate =datetime.date(2006,1,1) today = enddate = datetime.date.today() ticker = 'SPY' fh = finance.fetch_historical_yahoo(ticker, startdate, enddate) # a numpy record array with fields: date, open, high, low, close, volume, adj_close) r = mlab.csv2rec(fh); fh.close() r.sort() def moving_average(x, n, type='simple'): """ compute an n period moving average. type is 'simple' | 'exponential' """ x = np.asarray(x) if type=='simple': weights = np.ones(n) else: weights = np.exp(np.linspace(-1., 0., n)) weights /= weights.sum() a = np.convolve(x, weights, mode='full')[:len(x)] a[:n] = a[n] return a def relative_strength(prices, n=14): """ compute the n period relative strength indicator http://stockcharts.com/school/doku.php?id=chart_school:glossary_r#relativestrengthindex http://www.investopedia.com/terms/r/rsi.asp """ deltas = np.diff(prices) seed = deltas[:n+1] up = seed[seed>=0].sum()/n down = -seed[seed<0].sum()/n rs = up/down rsi = np.zeros_like(prices) rsi[:n] = 100. - 100./(1.+rs) for i in range(n, len(prices)): delta = deltas[i-1] # cause the diff is 1 shorter if delta>0: upval = delta downval = 0. else: upval = 0. downval = -delta up = (up*(n-1) + upval)/n down = (down*(n-1) + downval)/n rs = up/down rsi[i] = 100. - 100./(1.+rs) return rsi def moving_average_convergence(x, nslow=26, nfast=12): """ compute the MACD (Moving Average Convergence/Divergence) using a fast and slow exponential moving avg' return value is emaslow, emafast, macd which are len(x) arrays """ emaslow = moving_average(x, nslow, type='exponential') emafast = moving_average(x, nfast, type='exponential') return emaslow, emafast, emafast - emaslow plt.rc('axes', grid=True) plt.rc('grid', color='0.75', linestyle='-', linewidth=0.5) textsize = 9 left, width = 0.1, 0.8 rect1 = [left, 0.7, width, 0.2] rect2 = [left, 0.3, width, 0.4] rect3 = [left, 0.1, width, 0.2] fig = plt.figure(facecolor='white') axescolor = '#f6f6f6' # the axes background color ax1 = fig.add_axes(rect1, axisbg=axescolor) #left, bottom, width, height ax2 = fig.add_axes(rect2, axisbg=axescolor, sharex=ax1) ax2t = ax2.twinx() ax3 = fig.add_axes(rect3, axisbg=axescolor, sharex=ax1) ### plot the relative strength indicator prices = r.adj_close rsi = relative_strength(prices) fillcolor = 'darkgoldenrod' ax1.plot(r.date, rsi, color=fillcolor) ax1.axhline(70, color=fillcolor) ax1.axhline(30, color=fillcolor) ax1.fill_between(r.date, rsi, 70, where=(rsi>=70), facecolor=fillcolor, edgecolor=fillcolor) ax1.fill_between(r.date, rsi, 30, where=(rsi<=30), facecolor=fillcolor, edgecolor=fillcolor) ax1.text(0.6, 0.9, '>70 = overbought', va='top', transform=ax1.transAxes, fontsize=textsize) ax1.text(0.6, 0.1, '<30 = oversold', transform=ax1.transAxes, fontsize=textsize) ax1.set_ylim(0, 100) ax1.set_yticks([30,70]) ax1.text(0.025, 0.95, 'RSI (14)', va='top', transform=ax1.transAxes, fontsize=textsize) ax1.set_title('%s daily'%ticker) ### plot the price and volume data dx = r.adj_close - r.close low = r.low + dx high = r.high + dx deltas = np.zeros_like(prices) deltas[1:] = np.diff(prices) up = deltas>0 ax2.vlines(r.date[up], low[up], high[up], color='black', label='_nolegend_') ax2.vlines(r.date[~up], low[~up], high[~up], color='black', label='_nolegend_') ma20 = moving_average(prices, 20, type='simple') ma200 = moving_average(prices, 200, type='simple') linema20, = ax2.plot(r.date, ma20, color='blue', lw=2, label='MA (20)') linema200, = ax2.plot(r.date, ma200, color='red', lw=2, label='MA (200)') last = r[-1] s = '%s O:%1.2f H:%1.2f L:%1.2f C:%1.2f, V:%1.1fM Chg:%+1.2f' % ( today.strftime('%d-%b-%Y'), last.open, last.high, last.low, last.close, last.volume*1e-6, last.close-last.open ) t4 = ax2.text(0.3, 0.9, s, transform=ax2.transAxes, fontsize=textsize) props = font_manager.FontProperties(size=10) leg = ax2.legend(loc='center left', shadow=True, fancybox=True, prop=props) leg.get_frame().set_alpha(0.5) volume = (r.close*r.volume)/1e6 # dollar volume in millions vmax = volume.max() poly = ax2t.fill_between(r.date, volume, 0, label='Volume', facecolor=fillcolor, edgecolor=fillcolor) ax2t.set_ylim(0, 5*vmax) ax2t.set_yticks([]) ### compute the MACD indicator fillcolor = 'darkslategrey' nslow = 26 nfast = 12 nema = 9 emaslow, emafast, macd = moving_average_convergence(prices, nslow=nslow, nfast=nfast) ema9 = moving_average(macd, nema, type='exponential') ax3.plot(r.date, macd, color='black', lw=2) ax3.plot(r.date, ema9, color='blue', lw=1) ax3.fill_between(r.date, macd-ema9, 0, alpha=0.5, facecolor=fillcolor, edgecolor=fillcolor) ax3.text(0.025, 0.95, 'MACD (%d, %d, %d)'%(nfast, nslow, nema), va='top', transform=ax3.transAxes, fontsize=textsize) #ax3.set_yticks([]) # turn off upper axis tick labels, rotate the lower ones, etc for ax in ax1, ax2, ax2t, ax3: if ax!=ax3: for label in ax.get_xticklabels(): label.set_visible(False) else: for label in ax.get_xticklabels(): label.set_rotation(30) label.set_horizontalalignment('right') ax.fmt_xdata = mdates.DateFormatter('%Y-%m-%d') class MyLocator(mticker.MaxNLocator): def __init__(self, *args, **kwargs): mticker.MaxNLocator.__init__(self, *args, **kwargs) def __call__(self, *args, **kwargs): return mticker.MaxNLocator.__call__(self, *args, **kwargs) # at most 5 ticks, pruning the upper and lower so they don't overlap # with other ticks #ax2.yaxis.set_major_locator(mticker.MaxNLocator(5, prune='both')) #ax3.yaxis.set_major_locator(mticker.MaxNLocator(5, prune='both')) ax2.yaxis.set_major_locator(MyLocator(5, prune='both')) ax3.yaxis.set_major_locator(MyLocator(5, prune='both')) plt.show()