|
From: <jd...@us...> - 2009-03-18 15:22:44
|
Revision: 6988
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6988&view=rev
Author: jdh2358
Date: 2009-03-18 15:22:41 +0000 (Wed, 18 Mar 2009)
Log Message:
-----------
updated finance_work2.py demo
Modified Paths:
--------------
trunk/matplotlib/examples/pylab_examples/finance_work2.py
trunk/matplotlib/examples/pylab_examples/legend_demo3.py
Modified: trunk/matplotlib/examples/pylab_examples/finance_work2.py
===================================================================
--- trunk/matplotlib/examples/pylab_examples/finance_work2.py 2009-03-18 07:39:43 UTC (rev 6987)
+++ trunk/matplotlib/examples/pylab_examples/finance_work2.py 2009-03-18 15:22:41 UTC (rev 6988)
@@ -1,278 +1,221 @@
-"""
-You need a additional files to run this example. Save the following
-in the same dir as this file
+## Plot the stock price with some technical indicators
+## Example usage::
+## python stocks2.py --ticker=GE --startdate=2003
+##
+import datetime, os, urllib, optparse
+import numpy as np
+import dateutil.parser
+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
- http://matplotlib.sourceforge.net/screenshots/helpers.py
+today = datetime.date.today()
- http://matplotlib.sourceforge.net/screenshots/msft_nasdaq_d.csv
+optionparser = optparse.OptionParser()
- http://matplotlib.sourceforge.net/screenshots/__init__.py
+optionparser.add_option('-t', '--ticker',
+ dest='ticker',
+ help='a stock market ticker',
+ default='SPY')
-"""
+optionparser.add_option('-s', '--startdate',
+ dest='startdate',
+ help='the start date',
+ default=(today-datetime.timedelta(days=365*2)).strftime('%Y-%m-%d'))
-import time, os, sys, datetime
+optionparser.add_option('-e', '--enddate',
+ dest='enddate',
+ help='the end date',
+ default=today.strftime('%Y-%m-%d'))
-from matplotlib import rcParams
-from matplotlib.ticker import IndexLocator, FuncFormatter, NullFormatter, MultipleLocator
-from matplotlib.dates import IndexDateFormatter, date2num
-from matplotlib.finance import candlestick2, plot_day_summary2, \
- volume_overlay, index_bar
-from pylab import *
-rcParams['timezone'] = 'US/Eastern'
-rc('grid', color='0.75', linestyle='-', linewidth=0.5)
+(commandoptions, commandargs) = optionparser.parse_args()
-def ema(s, n):
- """
- returns an n period exponential moving average for
- the time series s
- s is a list ordered from oldest (index 0) to most recent (index
- -1) n is an integer
+startdate = dateutil.parser.parse(commandoptions.startdate)
+enddate = dateutil.parser.parse(commandoptions.enddate)
+ticker = commandoptions.ticker
- returns a numeric array of the exponential moving average
- """
- s = array(s)
- ema = []
- j = 1
- #get n sma first and calculate the next n period ema
- sma = sum(s[:n]) / n
- multiplier = 2 / float(1 + n)
- ema.append(sma)
- #EMA(current) = ( (Price(current) - EMA(prev) ) xMultiplier) + EMA(prev)
- ema.append(( (s[n] - sma) * multiplier) + sma)
- #now calculate the rest of the values
- for i in s[n+1:]:
- tmp = ( (i - ema[j]) * multiplier) + ema[j]
- j = j + 1
- ema.append(tmp)
- return ema
-def movavg(s, n):
- """
- returns an n period moving average for the time series s
+fh = finance.fetch_historical_yahoo(ticker, startdate, enddate)
+# a numpy record array with fields: date, open, high, low, close, volume, adj_close)
- s is a list ordered from oldest (index 0) to most recent (index -1)
- n is an integer
+r = mlab.csv2rec(fh); fh.close()
+r.sort()
- returns a numeric array of the moving average
- See also ema in this module for the exponential moving average.
+def moving_average(x, n, type='simple'):
"""
- s = array(s)
- c = cumsum(s)
- return (c[n-1:] - c[:-n+1]) / float(n-1)
+ compute an n period moving average.
-def fill_over(ax, x, y, val, color, over=True):
+ type is 'simple' | 'exponential'
+
"""
- Plot filled x,y for all y over val
- if over = False, fill all areas < val
- """
- ybase = asarray(y)-val
- crossings = nonzero(less(ybase[:-1] * ybase[1:],0))
+ x = np.asarray(x)
+ if type=='simple':
+ weights = np.ones(n)
+ else:
+ weights = np.exp(np.linspace(-1., 0., n))
- if ybase[0]>=0: fillon = over
- else: fillon = not over
+ weights /= weights.sum()
+ a = np.convolve(x, weights, mode='full')[:len(x)]
+ a[:n] = a[n]
+ return a
- indLast = 0
- for ind in crossings:
- if fillon:
- thisX = x[indLast:ind+1]
- thisY = y[indLast:ind+1]
- thisY[0] = val
- thisY[-1] = val
- ax.fill(thisX, thisY, facecolor=color)
- fillon = not fillon
- indLast = ind
+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(r.adj_close)
+ rsi[:n] = 100. - 100./(1.+rs)
-def random_signal(N, tau):
- 'generate a length N random signal with time constant tau'
- t = arange(float(N))
- filter = exp(-t/tau)
- return convolve( randn(N), filter, mode=2)[:len(t)]
+ 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
-# load a numpy record array from yahoo csv data with fields date,
-# open, close, volume, adj_close from the mpl-data/example directory.
-# The record array stores python datetime.date as an object array in
-# the date column
-datafile = matplotlib.get_example_data('goog.npy')
-r = np.load(datafile).view(np.recarray)
-r = r[-250:]
+ up = (up*(n-1) + upval)/n
+ down = (down*(n-1) + downval)/n
-N = len(r)
+ rs = up/down
+ rsi[i] = 100. - 100./(1.+rs)
-vind = np.arange(N)
+ return rsi
-figBG = 'w' # the figure background color
-axesBG = '#f6f6f6' # the axies background color
-textsize = 8 # size for axes text
-
-# the demo data are intc from (2003, 9, 1) to (2004, 4, 12 ) with
-# dates as epoch; I saved these to a file for ease of debugginh
-ticker = 'MSFT'
-
-
-figure(1, facecolor=figBG)
-
-def get_locator():
+def moving_average_convergence(x, nslow=26, nfast=12):
"""
- the axes cannot share the same locator, so this is a helper
- function to generate locators that have identical functionality
+ 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
- return IndexLocator(10, 1)
+plt.rc('axes', grid=True)
+plt.rc('grid', color='0.75', linestyle='-', linewidth=0.5)
-formatter = IndexDateFormatter(date2num(r.date), '%b %d %y')
-
-nullfmt = NullFormatter() # no labels
-
-def fmt_vol(x,pos):
- if pos>3: return '' # only label the first 3 ticks
- return '%dM' % int(x*1e-6)
-
-volumeFmt = FuncFormatter(fmt_vol)
-
+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]
-axUpper = axes(rect1, axisbg=axesBG) #left, bottom, width, height
-axMiddle = axes(rect2, axisbg=axesBG, sharex=axUpper)
-axMiddleVol = axMiddle.twinx()
-axLower = axes(rect3, axisbg=axesBG, sharex=axUpper)
-axUpper.xaxis.set_major_locator( get_locator() )
-axUpper.xaxis.set_major_formatter(nullfmt)
-axUpper.grid(True)
+fig = plt.figure(facecolor='white')
+axescolor = '#f6f6f6' # the axies background color
-# set up two scales on middle axes with left and right ticks
-axMiddle.yaxis.tick_left()
-axMiddle.xaxis.set_major_formatter(nullfmt)
+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)
-axMiddleVol.yaxis.set_major_formatter(volumeFmt)
-axMiddle.grid(True)
+### plot the relative strength indicator
+prices = r.adj_close
+rsi = relative_strength(prices)
+fillcolor = 'darkgoldenrod'
-axLower.xaxis.set_major_locator( get_locator() )
-axLower.xaxis.set_major_formatter( formatter )
-axLower.grid(True)
+ax1.plot(r.date, rsi, color=fillcolor)
+ax1.axhline(70, color=fillcolor)
+ax1.axhline(30, color=fillcolor)
+ax1.fill_between(r.date, rsi, 70, facecolor=fillcolor, where=(rsi>=70))
+ax1.fill_between(r.date, rsi, 30, facecolor=fillcolor, where=(rsi<=30))
+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)
-if 1: ############### Upper axes #################
+### plot the price and volume data
+deltas = np.zeros_like(prices)
+deltas[1:] = np.diff(prices)
+up = deltas>0
+ax2.vlines(r.date[up], r.low[up], r.high[up], color='black', label='_nolegend_')
+ax2.vlines(r.date[~up], r.low[~up], r.high[~up], color='black', label='_nolegend_')
+ma20 = moving_average(prices, 20, type='simple')
+ma200 = moving_average(prices, 200, type='simple')
- # make up a pseudo signal
- purple = '#660033'
- s = random_signal(N, tau=20)
- thresh = 4
- axUpper.plot(s, color=purple)
- # upper horiz line
+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)
- axUpper.plot( (0, N), [thresh, thresh], color=purple, linewidth=1)
- # lower horiz line
- axUpper.plot( (0, N), [-thresh, -thresh], color=purple, linewidth=1)
+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)
+vmax = r.volume.max()/1e6
+poly = ax2t.fill_between(r.date, r.volume/1e6, 0, facecolor=fillcolor, label='Volume')
+ax2t.set_ylim(0, 5*vmax)
+ymax = np.int(vmax)
+yticks = [vmax/2., vmax]
+ax2t.set_yticks(yticks)
+ax2t.set_yticklabels(['%d M'%val for val in yticks])
- # fill above threshold
- fill_over(axUpper, vind, s, thresh, purple, over=True)
- fill_over(axUpper, vind, s, -thresh, purple, over=False)
+### 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, facecolor=fillcolor, alpha=0.5)
- t = axUpper.set_title('Google (GOOG)', fontsize=12)
- t.set_y(1.05) # move it up a bit higher than the default
- t.set_x(0) # align the title left, axes coords
- t.set_horizontalalignment('left') # align the title left, axes coords
- axUpper.yaxis.set_major_locator( MultipleLocator(5) )
+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 tick labels, rorate them, 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')
- # now add some text
- left, height, top = 0.025, 0.06, 0.85
- t = axUpper.text(left, top, 'RSI(14) 51.0', fontsize=textsize,
- transform=axUpper.transAxes)
+ ax.fmt_xdata = mdates.DateFormatter('%Y-%m-%d')
+class PriceFormatter(mticker.FormatStrFormatter):
+ 'suppress the lowest tick label to prevent overlap'
+ def __call__(self, x, pos=None):
+ if pos==0:
+ return ''
+ else:
+ return mticker.FormatStrFormatter.__call__(self, x, pos=None)
-if 1: ############### Middle axes #################
+ax2.yaxis.set_major_formatter(PriceFormatter('%d'))
+plt.show()
- candlestick2(axMiddle, r.open, r.close, r.high, r.low, width=0.9)
-
- # specify the text in axes (0,1) coords. 0,0 is lower left and 1,1 is
- # upper right
-
- left, height, top = 0.025, 0.06, 0.9
- t1 = axMiddle.text(left, top, '%s daily'%ticker, fontsize=textsize,
- transform=axMiddle.transAxes)
- t2 = axMiddle.text(left, top-height, 'MA(5)', color='b', fontsize=textsize,
- transform=axMiddle.transAxes)
- t3 = axMiddle.text(left, top-2*height, 'MA(20)', color='r', fontsize=textsize,
- transform=axMiddle.transAxes)
-
- s = '%s O:%1.2f H:%1.2f L:%1.2f C:%1.2f, V:%1.1fM Chg:%+1.2f' %(
- time.strftime('%d-%b-%Y'),
- r.open[-1], r.high[-1],
- r.low[-1], r.close[-1],
- r.volume[-1]*1e-6,
- r.close[-1]-r.open[-1])
- t4 = axMiddle.text(0.4, top, s, fontsize=textsize,
- transform=axMiddle.transAxes)
-
-
- # now do the moviing average. I'll use a convolution to simulate a
- # real moving average
- ma5 = movavg(r.adj_close, 5)
- ma20 = movavg(r.adj_close, 20)
- axMiddle.plot(vind[5-1:], ma5, 'b', linewidth=1)
- axMiddle.plot(vind[20-1:], ma20, 'r', linewidth=1)
-
- axMiddle.set_ylim((300, 800))
- axMiddle.set_yticks(np.arange(800, 800, 100))
-
- # Now do the volume overlay
-
- # todo - this is broken
- bars = volume_overlay(axMiddleVol, r.open, r.close, r.volume, alpha=0.5)
- #axMiddleVol.set_ylim(0, 3*r.volume.max()) # use only a third of the viewlim
-
-
-if 1: ############### Lower axes #################
-
- # make up two signals; I don't know what the signals are in real life
- # so I'll just illustrate the plotting stuff
- s1 = random_signal(N, 10)
- s2 = random_signal(N, 20)
-
- axLower.plot(vind, s1, color=purple)
- axLower.plot(vind, s2, color='k', linewidth=1.0)
- s3 = s2-s1
- axLower.plot(vind, s3, color='#cccc99') # wheat
- bars = index_bar(axLower, s3, width=2, alpha=0.5,
- facecolor='#3087c7', edgecolor='#cccc99')
- axLower.yaxis.set_major_locator(MultipleLocator(5))
-
-
- # now add some text
- left, height, top = 0.025, 0.06, 0.85
-
- t = axLower.text(left, top, 'MACD(12,26,9) -0.26', fontsize=textsize,
- transform=axLower.transAxes)
-
- # make sure everyone has the same axes limits
-
- setp(axLower.get_xticklabels(), 'rotation', 45,
- 'horizontalalignment', 'right', fontsize=8)
-
-# force all the axes to have the same x data limits
-allAxes = (axUpper, axMiddle, axMiddleVol, axLower)
-xlim = 0, N
-for a in allAxes:
- a.set_xlim(xlim)
-
-for ax in axUpper, axMiddle, axMiddleVol:
- for ticklabel in ax.get_xticklabels():
- ticklabel.set_visible(False)
-
-show()
Modified: trunk/matplotlib/examples/pylab_examples/legend_demo3.py
===================================================================
--- trunk/matplotlib/examples/pylab_examples/legend_demo3.py 2009-03-18 07:39:43 UTC (rev 6987)
+++ trunk/matplotlib/examples/pylab_examples/legend_demo3.py 2009-03-18 15:22:41 UTC (rev 6988)
@@ -23,7 +23,7 @@
ax3 = plt.subplot(3,1,3)
myplot(ax3)
-ax3.legend(loc=1, ncol=4, mode="expand", shadow=True)
+ax3.legend(shadow=True, fancybox=True)
plt.draw()
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|