From: <ef...@us...> - 2010-06-20 16:46:40
|
Revision: 8443 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8443&view=rev Author: efiring Date: 2010-06-20 16:46:34 +0000 (Sun, 20 Jun 2010) Log Message: ----------- finance: restore original adjustment algorithm, but use ndarray. A numpy recarray replaces the Bunch when asobject is True. Additional fields are provided. Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/date_demo1.py trunk/matplotlib/examples/pylab_examples/date_demo2.py trunk/matplotlib/examples/pylab_examples/finance_demo.py trunk/matplotlib/lib/matplotlib/finance.py Modified: trunk/matplotlib/examples/pylab_examples/date_demo1.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/date_demo1.py 2010-06-20 01:34:30 UTC (rev 8442) +++ trunk/matplotlib/examples/pylab_examples/date_demo1.py 2010-06-20 16:46:34 UTC (rev 8443) @@ -27,7 +27,7 @@ quotes = quotes_historical_yahoo( 'INTC', date1, date2) -if not quotes: +if len(quotes) == 0: raise SystemExit dates = [q[0] for q in quotes] Modified: trunk/matplotlib/examples/pylab_examples/date_demo2.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/date_demo2.py 2010-06-20 01:34:30 UTC (rev 8442) +++ trunk/matplotlib/examples/pylab_examples/date_demo2.py 2010-06-20 16:46:34 UTC (rev 8443) @@ -23,7 +23,7 @@ quotes = quotes_historical_yahoo('INTC', date1, date2) -if not quotes: +if len(quotes) == 0: print 'Found no quotes' raise SystemExit Modified: trunk/matplotlib/examples/pylab_examples/finance_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/finance_demo.py 2010-06-20 01:34:30 UTC (rev 8442) +++ trunk/matplotlib/examples/pylab_examples/finance_demo.py 2010-06-20 16:46:34 UTC (rev 8443) @@ -5,20 +5,18 @@ from matplotlib.finance import quotes_historical_yahoo, candlestick,\ plot_day_summary, candlestick2 -import datetime +# (Year, month, day) tuples suffice as args for quotes_historical_yahoo +date1 = ( 2004, 2, 1) +date2 = ( 2004, 4, 12 ) -date1 = datetime.date( 2004, 2, 1) -date2 = datetime.date( 2004, 4, 12 ) - mondays = WeekdayLocator(MONDAY) # major ticks on the mondays alldays = DayLocator() # minor ticks on the days weekFormatter = DateFormatter('%b %d') # Eg, Jan 12 dayFormatter = DateFormatter('%d') # Eg, 12 -quotes = quotes_historical_yahoo( - 'INTC', date1, date2) -if not quotes: +quotes = quotes_historical_yahoo('INTC', date1, date2) +if len(quotes) == 0: raise SystemExit fig = figure() Modified: trunk/matplotlib/lib/matplotlib/finance.py =================================================================== --- trunk/matplotlib/lib/matplotlib/finance.py 2010-06-20 01:34:30 UTC (rev 8442) +++ trunk/matplotlib/lib/matplotlib/finance.py 2010-06-20 16:46:34 UTC (rev 8443) @@ -11,90 +11,130 @@ from hashlib import md5 except ImportError: from md5 import md5 #Deprecated in 2.5 +import datetime -try: import datetime -except ImportError: - raise ImportError('The finance module requires datetime support (python2.3)') - import numpy as np from matplotlib import verbose, get_configdir -from dates import date2num -from matplotlib.cbook import Bunch +from matplotlib.dates import date2num +from matplotlib.cbook import iterable, is_string_like from matplotlib.collections import LineCollection, PolyCollection from matplotlib.colors import colorConverter -from lines import Line2D, TICKLEFT, TICKRIGHT -from patches import Rectangle +from matplotlib.lines import Line2D, TICKLEFT, TICKRIGHT +from matplotlib.patches import Rectangle from matplotlib.transforms import Affine2D - configdir = get_configdir() cachedir = os.path.join(configdir, 'finance.cache') -def parse_yahoo_historical(fh, asobject=False, adjusted=True): +stock_dt = np.dtype([('date', object), + ('year', np.int16), + ('month', np.int8), + ('day', np.int8), + ('d', np.float), # mpl datenum + ('open', np.float), + ('close', np.float), + ('high', np.float), + ('low', np.float), + ('volume', np.int), + ('aclose', np.float)]) + + +def parse_yahoo_historical(fh, adjusted=True, asobject=False): """ - Parse the historical data in file handle fh from yahoo finance and return - results as a list of + Parse the historical data in file handle fh from yahoo finance. - d, open, close, high, low, volume + *adjusted* + If True (default) replace open, close, high, low, and volume with + their adjusted values. + The adjustment is by a scale factor, S = adjusted_close/close. + Adjusted volume is actual volume divided by S; + Adjusted prices are actual prices multiplied by S. Hence, + the product of price and volume is unchanged by the adjustment. - where d is a floating poing representation of date, as returned by date2num + *asobject* + If False (default for compatibility with earlier versions) + return a list of tuples containing - if adjusted=True, use adjusted prices. Note that volume is not - adjusted and we are not able to handle volume adjustments properly - because the Yahoo CSV does not distinguish between split and - dividend adjustments. + d, open, close, high, low, volume + + If None (preferred alternative to False), return + a 2-D ndarray corresponding to the list of tuples. + + Otherwise return a numpy recarray with + + date, year, month, day, d, open, close, high, low, + volume, adjusted_close + + where d is a floating poing representation of date, + as returned by date2num, and date is a python standard + library datetime.date instance. + + The name of this kwarg is a historical artifact. Formerly, + True returned a cbook Bunch + holding 1-D ndarrays. The behavior of a numpy recarray is + very similar to the Bunch. + """ - results = [] lines = fh.readlines() - datefmt = None + results = [] + datefmt = '%Y-%m-%d' + for line in lines[1:]: vals = line.split(',') - - if len(vals)!=7: continue + if len(vals)!=7: + continue # add warning? datestr = vals[0] - if datefmt is None: - try: - datefmt = '%Y-%m-%d' - dt = datetime.date(*time.strptime(datestr, datefmt)[:3]) - except ValueError: - datefmt = '%d-%b-%y' # Old Yahoo--cached file? - dt = datetime.date(*time.strptime(datestr, datefmt)[:3]) - d = date2num(dt) + #dt = datetime.date(*time.strptime(datestr, datefmt)[:3]) + # Using strptime doubles the runtime. With the present + # format, we don't need it. + dt = datetime.date(*[int(val) for val in datestr.split('-')]) + dnum = date2num(dt) open, high, low, close = [float(val) for val in vals[1:5]] volume = int(vals[5]) - if adjusted: - aclose = float(vals[6]) - delta = aclose-close - open += delta - high += delta - low += delta - close = aclose + aclose = float(vals[6]) - results.append((d, open, close, high, low, volume)) + results.append((dt, dt.year, dt.month, dt.day, + dnum, open, close, high, low, volume, aclose)) results.reverse() - if asobject: - if len(results)==0: return None - else: - date, open, close, high, low, volume = map(np.asarray, zip(*results)) - return Bunch(date=date, open=open, close=close, high=high, low=low, volume=volume) - else: + d = np.array(results, dtype=stock_dt) + if adjusted: + scale = d['aclose'] / d['close'] + scale[np.isinf(scale)] = np.nan + d['open'] *= scale + d['close'] *= scale + d['high'] *= scale + d['low'] *= scale - return results + if not asobject: + # 2-D sequence; formerly list of tuples, now ndarray + ret = np.zeros((len(d), 6), dtype=np.float) + ret[:,0] = d['d'] + ret[:,1] = d['open'] + ret[:,2] = d['close'] + ret[:,3] = d['high'] + ret[:,4] = d['low'] + ret[:,5] = d['volume'] + if asobject is None: + return ret + return [tuple(row) for row in ret] + return d.view(np.recarray) # Close enough to former Bunch return + + def fetch_historical_yahoo(ticker, date1, date2, cachename=None): """ Fetch historical data for ticker between date1 and date2. date1 and - date2 are datetime instances + date2 are date or datetime instances, or (year, month, day) sequences. Ex: - fh = fetch_historical_yahoo('^GSPC', d1, d2) + fh = fetch_historical_yahoo('^GSPC', (2000, 1, 1), (2001, 12, 31)) cachename is the name of the local file cache. If None, will default to the md5 hash or the url (which incorporates the ticker @@ -106,8 +146,14 @@ ticker = ticker.upper() - d1 = (date1.month-1, date1.day, date1.year) - d2 = (date2.month-1, date2.day, date2.year) + if iterable(date1): + d1 = (date1[1]-1, date1[2], date1[0]) + else: + d1 = (date1.month-1, date1.day, date1.year) + if iterable(date2): + d2 = (date2[1]-1, date2[2], date2[0]) + else: + d2 = (date2.month-1, date2.day, date2.year) urlFmt = 'http://table.finance.yahoo.com/table.csv?a=%d&b=%d&c=%d&d=%d&e=%d&f=%d&s=%s&y=0&g=d&ignore=.csv' @@ -123,7 +169,8 @@ fh = file(cachename) verbose.report('Using cachefile %s for %s'%(cachename, ticker)) else: - if not os.path.isdir(cachedir): os.mkdir(cachedir) + if not os.path.isdir(cachedir): + os.mkdir(cachedir) urlfh = urlopen(url) fh = file(cachename, 'w') @@ -135,27 +182,18 @@ return fh -def quotes_historical_yahoo(ticker, date1, date2, asobject=False, adjusted=True, cachename=None): +def quotes_historical_yahoo(ticker, date1, date2, asobject=False, + adjusted=True, cachename=None): """ Get historical data for ticker between date1 and date2. date1 and - date2 are datetime instances + date2 are datetime instances or (year, month, day) sequences. - results are a list of tuples + See :func:`parse_yahoo_historical` for explanation of output formats + and the *asobject* and *adjusted* kwargs. - (d, open, close, high, low, volume) - - where d is a floating poing representation of date, as returned by date2num - - if asobject is True, the return val is an object with attrs date, - open, close, high, low, volume, which are equal length arrays - - if adjusted=True, use adjusted prices. Note that volume is not - adjusted and we are not able to handle volume adjustments properly - because the Yahoo CSV does not distinguish between split and - dividend adjustments. - Ex: - sp = f.quotes_historical_yahoo('^GSPC', d1, d2, asobject=True, adjusted=True) + sp = f.quotes_historical_yahoo('^GSPC', d1, d2, + asobject=True, adjusted=True) returns = (sp.open[1:] - sp.open[:-1])/sp.open[1:] [n,bins,patches] = hist(returns, 100) mu = mean(returns) @@ -167,10 +205,18 @@ default to the md5 hash or the url (which incorporates the ticker and date range) """ + # Maybe enable a warning later as part of a slow transition + # to using None instead of False. + #if asobject is False: + # warnings.warn("Recommend changing to asobject=None") fh = fetch_historical_yahoo(ticker, date1, date2, cachename) - try: ret = parse_yahoo_historical(fh, asobject, adjusted) + try: + ret = parse_yahoo_historical(fh, asobject=asobject, + adjusted=adjusted) + if len(ret) == 0: + return None except IOError, exc: warnings.warn('urlopen() failure\n' + url + '\n' + exc.strerror[1]) return None @@ -181,7 +227,7 @@ colorup='k', colordown='r', ): """ - quotes is a list of (time, open, close, high, low, ...) tuples + quotes is a sequence of (time, open, close, high, low, ...) sequences Represent the time, open, close, high, low as a vertical line ranging from low to high. The left tick is the open and the right @@ -196,9 +242,6 @@ return value is a list of lines added """ - - - lines = [] for q in quotes: @@ -244,9 +287,9 @@ """ - quotes is a list of (time, open, close, high, low, ...) tuples. - As long as the first 5 elements of the tuples are these values, - the tuple can be as long as you want (eg it may store volume). + quotes is a sequence of (time, open, close, high, low, ...) sequences. + As long as the first 5 elements are these values, + the record can be as long as you want (eg it may store volume). time must be in float days format - see date2num @@ -263,12 +306,11 @@ return value is lines, patches where lines is a list of lines added and patches is a list of the rectangle patches added + """ - OFFSET = width/2.0 - lines = [] patches = [] for q in quotes: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |