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.

------------------------------------------------------------------------------
ThinkGeek and WIRED's GeekDad team up for the Ultimate 
GeekDad Father's Day Giveaway. ONE MASSIVE PRIZE to the 
lucky parental unit.  See the prize list and enter to win: 
http://p.sf.net/sfu/thinkgeek-promo
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to