From 5fb5e335a140cd0cedbb67ae9a845bde1e354389 Mon Sep 17 00:00:00 2001 From: addisonlynch Date: Wed, 22 May 2019 13:57:16 -0400 Subject: [PATCH 1/5] Refactor _ex_Quandl.py docstrings to numpydoc format --- fecon236/host/_ex_Quandl.py | 109 +++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/fecon236/host/_ex_Quandl.py b/fecon236/host/_ex_Quandl.py index d856eb4..8f55bf7 100755 --- a/fecon236/host/_ex_Quandl.py +++ b/fecon236/host/_ex_Quandl.py @@ -1,7 +1,6 @@ # Python Module for import Date : 2018-05-23 # vim: set fileencoding=utf-8 ff=unix tw=78 ai syn=python : per PEP 0263 -''' -_______________| _ex_Quandl.py :: fecon236 fork of Quandl API 2.8.9. +"""fecon236 fork of Quandl API 2.8.9. Currently supports getting and searching datasets. @@ -12,34 +11,38 @@ Their version 2.8.9, however, had the virtue of being just a *single* module, and has been battle-tested in fecon235 notebooks since 2015-08-03. -That module has been forked at fecon236 and renamed _ex_Quandl.py. -The fecon236 qdl module is a wrapper around _ex_Quandl.py. +That module has been forked at fecon236 and renamed `_ex_Quandl.py`. +The fecon236 qdl module is a wrapper around `_ex_Quandl.py`. -REFERENCES: +References +---------- -- Using Quandl's Python module: https://www.quandl.com/tools/python +* Using Quandl's Python module: https://www.quandl.com/tools/python GitHub repo: https://github.com/quandl/quandl-python - -- Source code for Quandl.py version 2.8.9: +* Source code for Quandl.py version 2.8.9: https://github.com/quandl/quandl-python/blob/v2.8.9/Quandl/Quandl.py - -- Complete Quandl API documentation: https://www.quandl.com/docs/api +* Complete Quandl API documentation: https://www.quandl.com/docs/api including error codes. - -- RESTful interface introduction: https://www.quandl.com/help/api +* RESTful interface introduction: https://www.quandl.com/help/api Not needed here, but it's available for their API version 3. +Notes +----- +For LATEST version, see https://git.io/fecon236 -CHANGE LOG For LATEST version, see https://git.io/fecon236 -2018-05-23 _ex_Quandl.py, fecon236 fork of Quandl.py version 2.8.9. +Change Log +---------- + +* 2018-05-23 _ex_Quandl.py, fecon236 fork of Quandl.py version 2.8.9. Fix over 80 flake8 violations. Pass test_qdl.py. -2016-04-22 [Ignore newly introduced complex Quandl package version 3.] -2016-02-23 [Quandl.py API version 2.8.9 removes push() to upload data.] -2015-08-03 Quandl.py API version 2.8.7 adopted as yi_quandl_api.py +* 2016-04-22 [Ignore newly introduced complex Quandl package version 3.] +* 2016-02-23 [Quandl.py API version 2.8.9 removes push() to upload data.] +* 2015-08-03 Quandl.py API version 2.8.7 adopted as yi_quandl_api.py in fecon235, unchanged and operative through 2018-03-12, v5.18.0312, https://git.io/fecon235 -''' + +""" from __future__ import (print_function, division, absolute_import, unicode_literals) @@ -58,7 +61,7 @@ except ImportError: from urllib import urlencode # Python 2 from urllib2 import HTTPError, Request, urlopen - strings = unicode + strings = unicode # noqa: F821 # Base API call URL @@ -70,27 +73,43 @@ def get(dataset, **kwargs): """Return dataframe of requested dataset from Quandl. - :param dataset: str or list, depending on single or multiset dataset usage - Dataset codes are available on the Quandl website - :param str authtoken: Downloads are limited to 10 unless token is specified - :param str trim_start, trim_end: Optional datefilers, otherwise entire - dataset is returned - :param str collapse: Options are daily, weekly, monthly, quarterly, annual - :param str transformation: options are diff, rdiff, cumul, and normalize - :param int rows: Number of rows which will be returned - :param str sort_order: options are asc, desc. Default: `asc` - :param str returns: specify what format you wish your dataset returned as, - either `numpy` for a numpy ndarray or `pandas`. Default: `pandas` - :param bool verbose: print output text to stdout? Default is False. - :param str text: Deprecated. Use `verbose` instead. - :returns: :class:`pandas.DataFrame` or :class:`numpy.ndarray` - - Note that Pandas expects timeseries data to be sorted ascending for most + Parameters + ---------- + dataset: str or list + Single or multiset dataset usage. Dataset codes are available on the + Quandl website + authtoken: str, optional + Downloads are limited to 10 unless token is specified + trim_start: datefiler, optional + Start date + trim_end: datefiler, optional + End date + collapse: str, optional + Daily, weekly, monthly, quarterly, annual + transformation: str, optional + Options are diff, rdiff, cumul, normalize + rows: int, optional + Number of rows which will be returned + sort_order: str, optional + Sorting order, `asc` or `desc`. Defaults to `asc` + returns: str, optional + Dataset output format. Options are `pandas` (default) or `numpy` for + an `ndarray` + verbose: bool, optional + Toggle printing of output text to stdout. Defaults to False. + + Notes + ----- + + Pandas expects timeseries data to be sorted ascending for most timeseries functionality to work. Any other `kwargs` passed to `get` are sent as field/value params to Quandl with no interference. + Returns + ------- + `pandas.DataFrame` or `numpy.ndarray` """ # Check whether dataset is given as a string (for a single dataset) # or an array (for a multiset call) @@ -184,11 +203,21 @@ def get(dataset, **kwargs): def search(query, source=None, page=1, authtoken=None, verbose=True, prints=None): """Return array of dictionaries of search results. - :param str query: (required), query to search with - :param str source: (optional), source to search - :param +'ve int: (optional), page number of search - :param str authotoken: (optional) Quandl auth token for extended API access - :returns: :array: search results + + Parameters + ---------- + query: str + Query to search with + source: str, optional + Source to search with + page: int, optional + Page number to search + authtoken: str, optional + Quandl auth token for extended API access + + Returns + ------- + `list` search results """ if prints is not None: print('Deprecated: "prints", use "verbose" instead.') From fecda57b37f803f93164b555432056d763f54736 Mon Sep 17 00:00:00 2001 From: addisonlynch Date: Wed, 22 May 2019 13:59:02 -0400 Subject: [PATCH 2/5] Refactor fred.py docstrings to numpydoc format --- fecon236/host/fred.py | 357 +++++++++++++++++++++++++++++------------- 1 file changed, 250 insertions(+), 107 deletions(-) diff --git a/fecon236/host/fred.py b/fecon236/host/fred.py index 1c67acd..65b11d6 100644 --- a/fecon236/host/fred.py +++ b/fecon236/host/fred.py @@ -1,43 +1,58 @@ -# Python Module for import Date : 2018-12-10 +# Python Module for import Date : 2018-05-14 # vim: set fileencoding=utf-8 ff=unix tw=78 ai syn=python : per PEP 0263 -''' -_______________| fred.py :: FRED database into pandas. +"""FRED database into pandas. Functions access data from the Federal Reserve Bank, St. Louis. Each economic time series and its frequency has its own "fredcode" which is freely available from https://fred.stlouisfed.org - Usage: df = getfred(fredcode) - Favorite fredcodes are variables named d4*, m4*, q4* which indicate their frequency: daily, monthly, or quarterly. -Principal functions: getfred(), daily(), monthly(), quarterly(). - Some series are synthetically created using raw data from FRED. Also we may extend their past history, but your working directory must contain our supplemental CSV files. -REFERENCES: -- pandas, http://pandas.pydata.org/pandas-docs/stable/computation.html +Principal Functions +------------------- + +* `getfred()` +* `daily()` +* `monthly()` +* `quarterly()` + +Usage +----- + +.. code-block:: python + :flake8-add-ignore: F821 -- Wes McKinney, 2013, Python for Data Analysis. + df = getfred(fredcode) -- Mico Loretan, Federal Reserve Bulletin, Winter 2005, - "Indexes of the Foreign Exchange Value of the Dollar", - http://www.federalreserve.gov/pubs/bulletin/2005/winter05_index.pdf +References +---------- +* Mico Loretan, Federal Reserve Bulletin, Winter 2005, "Indexes of the Foreign + Exchange Value of the Dollar", + http://www.federalreserve.gov/pubs/bulletin/2005/winter05_index.pdf +* pandas, http://pandas.pydata.org/pandas-docs/stable/computation.html +* Wes McKinney, 2013, Python for Data Analysis. -CHANGE LOG For LATEST version, see https://git.io/fecon236 -2018-12-10 Include more fredcodes for Treasury bonds. -2018-05-14 Gracefully deprecate plotfred(). -2018-05-13 Eliminate lazy abbreviations, clarify comments. -2018-05-12 Given new division, eliminate float(integer). -2018-05-11 Fix imports. Deprecate plotfred(). -2018-05-09 fred.py, fecon236 fork. Pass flake8. -2018-03-11 yi_fred.py, fecon235 v5.18.0312, https://git.io/fecon235 -''' +Notes +----- +For LATEST version, see https://git.io/fecon236 + +Change Log +---------- + +* 2018-05-14 Gracefully deprecate plotfred(). +* 2018-05-13 Eliminate lazy abbreviations, clarify comments. +* 2018-05-12 Given new division, eliminate float(integer). +* 2018-05-11 Fix imports. Deprecate plotfred(). +* 2018-05-09 fred.py, fecon236 fork. Pass flake8. +* 2018-03-11 yi_fred.py, fecon235 v5.18.0312, https://git.io/fecon235 +""" from __future__ import absolute_import, print_function, division @@ -68,18 +83,9 @@ d4libusd = 'USD3MTD156N' # 3-m LIBOR USD, daily d4ff = 'DFF' # Fed Funds, daily since 1954 d4ff30 = 'd4ff30' # Fed Funds synthetic, "30-day" exp.mov.avg. -d4bills = 'DTB3' # Treasury bills, daily, 1954 (not DGS3MO, 1982) - -d4bond1 = 'DGS1' # Treasury 1-y constant maturity, daily, 1962 -d4bond2 = 'DGS2' # Treasury 2-y constant maturity, daily, 1976 -d4bond3 = 'DGS3' # Treasury 3-y constant maturity, daily, 1962 -d4bond5 = 'DGS5' # Treasury 5-y constant maturity, daily, 1962 -d4bond7 = 'DGS7' # Treasury 7-y constant maturity, daily, 1969 -d4bond10 = 'DGS10' # Treasury 10-y constant maturity, daily, 1962 -d4bond20 = 'DGS20' # Treasury 20-y constant maturity, daily, 1993* -d4bond30 = 'DGS30' # Treasury 30-y constant maturity, daily, 1977 - +d4bills = 'DTB3' # Treasury bills, daily d4zero10 = 'd4zero10' # Zero-coupon price of Treasury 10-y, daily +d4bond10 = 'DGS10' # Treasury 10-y constant, daily d4tips10 = 'DFII10' # TIPS 10-y constant, daily d4curve = 'd4curve' # Treasury 10_y-bills, getfred synthetic d4bei = 'd4bei' # 10_y Break-even inflation, getfred synthetic @@ -203,10 +209,23 @@ # make conversions for numerical work later. def readfile(filename, separator=',', compress=None): - '''Read file (CSV default) as pandas dataframe.''' - # If separator is space, use '\s+' since regex will work. - # compress will take 'gzip' or 'bzip' as value. - # + """ + Read file (CSV default) as pandas dataframe. + + Parameters + ---------- + filename: str + Name of file to be read + separator: str, optional + Delimiter to use. Defaults to comma (',') + compress: str, optional + File format for compressed files. 'gzip' and 'bzip' are supported. + Defaults to none. + + Notes + ----- + If separator is space, use '\s+' since regex will work. + """ # noqa: W605 dataframe = pd.read_csv(filename, sep=separator, compression=compress, index_col=0, parse_dates=True, @@ -236,7 +255,14 @@ def readfile(filename, separator=',', compress=None): def makeURL(fredcode): - '''Create http address to access FRED's CSV files.''' + """ + Create http address to access FRED's CSV files. + + Parameters + ---------- + fredcode: str + FRED series code + """ # Validated July 2014. return 'http://research.stlouisfed.org/fred2/series/' \ + fredcode + '/downloaddata/' + fredcode + '.csv' @@ -246,7 +272,14 @@ def makeURL(fredcode): # It's the best primitive to get raw FRED data. def getdata_fred(fredcode): - '''Download CSV file from FRED and read it as pandas DATAFRAME.''' + """ + Download CSV file from FRED and read it as pandas DATAFRAME. + + Parameters + ---------- + fredcode: str + FRED series code + """ # 2014-08-11 former name "getdataframe". # 2015-12-05 fredcsv = urllib2.urlopen(makeURL(fredcode)) # Change import style for python3 compatibility. @@ -255,7 +288,49 @@ def getdata_fred(fredcode): def index_delta_secs(dataframe): - '''Find minimum in seconds between index values.''' + """ + Find minimum in seconds between index values. + + Parameters + ---------- + dataframe: pandas.DataFrame + + Notes + ----- + There are OTHER METHODS to get the FREQUENCY of a dataframe: e.g. + `df.index.freq` OR `df.index.freqstr`, however, these work only + if the frequency was attributed: e.g. '1 Hour' OR 'H' respectively. + The fecon235 derived dataframes will usually return None. + + Two `timedelta64` units, 'Y' years and 'M' months, are + specially treated because the time they represent depends upon + their context. While a `timedelta64` day unit is equivalent to + 24 hours, there is difficulty converting a month unit into days + because months have varying number of days. + + Other `numpy` `timedelta64` units can be found here: + http://docs.scipy.org/doc/numpy/reference/arrays.datetime.html + + For pandas we could do: `pd.infer_freq(df.index)` which, for example, + might output 'B' for business daily series. + + But the STRING representation of index frequency is IMPRACTICAL + since we may want to compare two unevenly timed indexes. + That comparison is BEST DONE NUMERICALLY in some common unit + (we use seconds since that is the Unix epoch convention). + + Such comparison will be crucial for the machine + to chose whether downsampling or upsampling is appropriate. + The casual user should not be expected to know the functions + within `index_delta_secs` to smoothly work with a notebook. + + See Also + -------- + For details on frequency conversion, see McKinney 2013, + Chp. 10 RESAMPLING, esp. Table 10-5 on downsampling. + pandas defaults are: how='mean', closed='right', label='right' + + """ nanosecs_timedelta64 = np.diff(dataframe.index.values).min() # Picked min() over median() to conserve memory; ^^^^^! # also avoids missing values issue, @@ -269,38 +344,7 @@ def index_delta_secs(dataframe): else: return secs - # There are OTHER METHODS to get the FREQUENCY of a dataframe: - # e.g. df.index.freq OR df.index.freqstr , - # however, these work only if the frequency was attributed: - # e.g. '1 Hour' OR 'H' respectively. - # The fecon235 derived dataframes will usually return None. - # - # Two timedelta64 units, 'Y' years and 'M' months, are - # specially treated because the time they represent depends upon - # their context. While a timedelta64 day unit is equivalent to - # 24 hours, there is difficulty converting a month unit into days - # because months have varying number of days. - # Other numpy timedelta64 units can be found here: - # http://docs.scipy.org/doc/numpy/reference/arrays.datetime.html - # - # For pandas we could do: pd.infer_freq(df.index) - # which, for example, might output 'B' for business daily series. - # - # But the STRING representation of index frequency is IMPRACTICAL - # since we may want to compare two unevenly timed indexes. - # That comparison is BEST DONE NUMERICALLY in some common unit - # (we use seconds since that is the Unix epoch convention). - # - # Such comparison will be crucial for the machine - # to chose whether downsampling or upsampling is appropriate. - # The casual user should not be expected to know the functions - # within index_delta_secs() to smoothly work with a notebook. - -# For details on frequency conversion, see McKinney 2013, -# Chp. 10 RESAMPLING, esp. Table 10-5 on downsampling. -# pandas defaults are: how='mean', closed='right', label='right' -# # 2014-08-10 closed and label to the 'left' conform to FRED practices. # how='median' since it is more robust than 'mean'. # 2014-08-14 If upsampling, interpolate() does linear evenly, @@ -309,10 +353,17 @@ def index_delta_secs(dataframe): def resample_main(dataframe, rule, secs): - '''Generalized resample routine for downsampling or upsampling.''' - # rule is the offset string or object representing target conversion, - # e.g. 'B', 'MS', or 'QS-OCT' to be compatible with FRED. - # secs should be the maximum seconds expected for rule frequency. + """ + Generalized resample routine for downsampling or upsampling. + + Parameters + ---------- + rule: str + Offset string or object representing target conversion (e.g. 'B', 'MS', + or 'QS-OCT' to be compatible with FRED.) + secs: int + Maximum seconds expected for rule frequency. + """ if index_delta_secs(dataframe) < secs: df = dataframe.resample(rule, closed='left', label='left').median() # how='median' for DOWNSAMPLING deprecated as of pandas 0.18 @@ -327,23 +378,53 @@ def resample_main(dataframe, rule, secs): def daily(dataframe): - '''Resample data to daily using only business days.''' - # 'D' is used calendar daily - # 'B' for business daily + """ + Resample data to daily using only business days. + + Parameters + ---------- + dataframe: pandas.DataFrame + Dataframe to resample + """ secs1day2hours = 93600.0 return resample_main(dataframe, 'B', secs1day2hours) def monthly(dataframe): - '''Resample data to FRED's month start frequency.''' - # FRED uses the start of the month to index its monthly data. - # 'M' is used for end of month. - # 'MS' for start of month. + """ + Resample data to FRED's month start frequency. FRED uses the start of the + month to index its monthly data. + + Parameters + ---------- + dataframe: pandas.DataFrame + Dataframe to resample + """ secs31days = 2678400.0 return resample_main(dataframe, 'MS', secs31days) def quarterly(dataframe): + """ + Resample data to FRED's quarterly start frequency. + + Parameters + ---------- + dataframe: pandas.DataFrame + Dataframe to resample + + Notes + ----- + The quarterly data is + indexed as follows: + + +----------+----------+-----------+-----------+ + | Q1 | Q2 | Q3 | Q4 | + +----------+----------+-----------+-----------+ + | 01-01 | 04-01 | 07-01 | 10-01 | + +----------+----------+-----------+-----------+ + + """ '''Resample data to FRED's quarterly start frequency.''' # FRED uses the start of the month to index its monthly data. # Then for quarterly data: 1-01, 4-01, 7-01, 10-01. @@ -356,10 +437,19 @@ def quarterly(dataframe): def getm4eurusd(fredcode=d4eurusd): - '''Make monthly EURUSD, and try to prepend 1971-2002 archive.''' - # Synthetic euro is the average between - # DEM fixed at 1.95583 and - # FRF fixed at 6.55957. + """ + Make monthly EURUSD, and try to prepend 1971-2002 archive. + + Parameters + ---------- + fredcode: str, optional + FRED series code, defaults to "DEXUSEU" + + Notes + ----- + Synthetic euro is the average between DEM fixed at 1.95583 and FRF fixed at + 6.55957. + """ eurnow = monthly(getdata_fred(fredcode)) try: eurold = readfile('FRED-EURUSD_1971-2002-ARC.csv.gz', compress='gzip') @@ -373,9 +463,19 @@ def getm4eurusd(fredcode=d4eurusd): def getspx(fredcode=d4spx): - '''Make daily S&P 500 series, and try to prepend 1957-archive.''' - # Fred is currently licensed for only 10 years worth, - # however, we have a local copy of 1957-2014 daily data. + """ + Make daily S&P 500 series, and try to prepend 1957-archive. + + Parameters + ---------- + fredcode: str, optional + FRED series code, defaults to "SP500" + + Notes + ----- + Fred is currently licensed for only 10 years worth, however, we have a + local copy of 1957-2014 daily data. + """ spnow = getdata_fred(fredcode) try: spold = readfile('FRED-SP500_1957-2014-ARC.csv.gz', compress='gzip') @@ -389,9 +489,23 @@ def getspx(fredcode=d4spx): def gethomepx(fredcode=m4homepx): - '''Make Case-Shiller 20-city, and try to prepend 1987-2000 10-city.''' - # Fred's licensing may change since source is S&P, - # however, we have a local copy of 1987-2013 monthly SA data. + """ + Make Case-Shiller 20-city, and try to prepend 1987-2000 10-city. + + Parameters + ---------- + fredcode: str, optional + FRED series code, defaults to helper m4homepx + + Notes + ----- + Fred's licensing may change since source is S&P, however, we have a local + copy of 1987-2013 monthly SA data. + + See Also + -------- + m4homepx: Helper function + """ hpnow = getdata_fred('SPCS20RSA') # 20-city home price index back to 2000-01-01. try: @@ -415,9 +529,15 @@ def gethomepx(fredcode=m4homepx): def getinflations(inflations=ml_infl): - '''Normalize and average all inflation measures.''' - # We will take the average of indexes after their - # current value is set to 1 for equal weighting. + """ + Normalize and average all inflation measures. Take the average of indexes + after their current value is set to 1 for equal weighting. + + Parameters + ---------- + inflations: list + List of inflation measures to normalize + """ inflsum = getdata_fred(inflations[0]) inflsum = inflsum / float(tool.tailvalue(inflsum)) for i in inflations[1:]: @@ -428,11 +548,21 @@ def getinflations(inflations=ml_infl): def getdeflator(inflation=m4infl): - '''Construct a de-inflation dataframe suitable as multiplier.''' - # Usually we encounter numbers which have been deflated to dollars - # of some arbitrary year (where the value is probably 100). - # Here we set the present to 1, while past values have increasing - # multiplicative "returns" which will yield current dollars. + """ + Construct a de-inflation dataframe suitable as multiplier. + + Parameters + ---------- + inflation: str, optional + Inflation data to use, detaults to synthetic inflation + + Notes + ----- + Usually we encounter numbers which have been deflated to dollars + of some arbitrary year (where the value is probably 100). + Here we set the present to 1, while past values have increasing + multiplicative "returns" which will yield current dollars. + """ infl = getfred(inflation) lastin = tool.tailvalue(infl) return float(lastin) / infl @@ -440,9 +570,14 @@ def getdeflator(inflation=m4infl): def getm4infleu(): - '''Normalize and average Eurozone Consumer Prices.''' - # FRED carries only NSA data from Eurostat, - # so we shall use Holt-Winters levels. + """ + Normalize and average Eurozone Consumer Prices. + + Notes + ----- + FRED carries only NSA data from Eurostat, + so we shall use Holt-Winters levels. + """ cpiall = getdata_fred('CP0000EZ17M086NEST') # ^for 17 countries. holtall = hw.holtlevel(cpiall) @@ -458,8 +593,13 @@ def getm4infleu(): def getfred(fredcode): - '''Retrieve from FRED in dataframe format, INCL. SPECIAL CASES.''' - # We can SYNTHESIZE a FREDCODE by use of string equivalent arg: + """ + Retrieve from FRED in dataframe format, INCL. SPECIAL CASES. + + Notes + ----- + We can SYNTHESIZE a FREDCODE by use of string equivalent arg + """ if fredcode == m4gdpus: df = monthly(getdata_fred(q4gdpus)) elif fredcode == m4gdpusr: @@ -570,8 +710,11 @@ def getfred(fredcode): def plotfred(data, title='tmp', maxi=87654321): - '''DEPRECATED: Plot data should be given as dataframe or fredcode.''' - # ^2018-05-11. Removal OK after 2020-01-01. + """ + .. deprecated:: 0.18.0 + Plot data should be given as dataframe or fredcode. `plotfred` will + be removed on or after 2020-01-01 + """ msg = "plotfred() DEPRECATED. Instead use get() and plot()." raise DeprecationWarning(msg) From 0cbbd9e9f3ff8c00bfdf1bd7e9f9861bca45235d Mon Sep 17 00:00:00 2001 From: addisonlynch Date: Wed, 22 May 2019 14:00:17 -0400 Subject: [PATCH 3/5] Refactor hostess.py docstrings to numpydoc format --- fecon236/host/hostess.py | 163 +++++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 75 deletions(-) diff --git a/fecon236/host/hostess.py b/fecon236/host/hostess.py index 46d0c27..1153b7a 100644 --- a/fecon236/host/hostess.py +++ b/fecon236/host/hostess.py @@ -1,16 +1,72 @@ # Python Module for import Date : 2018-06-18 # vim: set fileencoding=utf-8 ff=unix tw=78 ai syn=python : per PEP 0263 -''' -_______________| hostess.py :: Brings together fecon236 host modules - -CHANGE LOG For LATEST version, see https://git.io/fecon236 -2018-06-18 Circular dependency HACK [Endnotes]: put imports inside get() - and avoid "from" import syntax there. Fix #3 -2018-06-15 Faster get() by exploiting startswith('s4'). Pass flake8. - Raised error messaging improved for getstock(). -2018-06-14 Spin-off get() from top.py. -2018-03-12 fecon235.py, fecon235 v5.18.0312, https://git.io/fecon235 -''' +"""fecon236 host modules + +Notes +----- +* For LATEST version, see https://git.io/fecon236 + +Circular Dependencies +~~~~~~~~~~~~~~~~~~~~~ + +By putting imports within a function we used a technique called +"DEFERRED IMPORTING." This does not violate Python syntax, as the +official documentation states: + + "It is customary but not required to place all import statements + at the beginning of a module (or script, for that matter)." + https://docs.python.org/2/tutorial/modules.html + +The downside to deferred imports generally is readability: one +cannot look at the very top of a module and see all its dependencies. + +The official documentation also says that it is advisable to use +"import X.Y", instead of other statements such as +"from X import Y", or in the worst case: "from X.Y import *". + +As for circular imports, the candidate solutions are explained at +https://stackoverflow.com/a/37126790 by brendan-abel +which can be BEST summarized as: use absolute (not relative) imports, +and definitely AVOID the "from" import style. + +The downside to absolute import style is that the names can +get super long. But one can create aliases using "=" with Python objects, +especially functions, at the cost of readability. Generally this solution +across the tops of many modules becomes very tiresome to implement. + +"Deferred importing" can speed up startup time, so it is not bad practice, +but some claim that it is a sign of bad design. The notion that circular +dependencies are somehow an indication of poor design seems to be more a +reflection on Python as a language ("glaring bug in the import machinery") +rather than a legitimate design point. + +.. seealso:: http://stackabuse.com/python-circular-imports by Scott Robinson. + + +Example of fecon236 circular dependency +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In the fred module, `holtwinters.ema` is called to smooth data. +In the holtwinters module, `hostess.get` is called to retrieve data. +But the hostess module relies on `fred.getfred` in the fred module. + +Our deferred import hack in this module, allows us to freely use the +`from` syntax everywhere else: e.g. `from fecon236.host.hostess import get` +at the top of modules without `ImportError` or `AttributeError` in both +python27 and python3. + + + +Change Log +---------- + +* 2018-06-18 Circular dependency HACK [Endnotes]: put imports inside get() + and avoid "from" import syntax there. Fix #3 +* 2018-06-15 Faster get() by exploiting startswith('s4'). Pass flake8. + Raised error messaging improved for getstock(). +* 2018-06-14 Spin-off get() from top.py. +* 2018-03-12 fecon235.py, fecon235 v5.18.0312, https://git.io/fecon235 + +""" from __future__ import absolute_import, print_function, division @@ -18,26 +74,34 @@ def get(code, maxi=0): - '''Unifies getfred, getqdl, and getstock for data retrieval. - code is fredcode, quandlcode, futures slang, or stock slang. - maxi should be an integer to set maximum number of data points, - where 0 implies the default value. + """Unifies getfred, getqdl, and getstock for data retrieval. - get() will accept the vendor code directly as string, e.g. - from FRED and Quandl, or use one of our abbreviated variables - documented in the appropriate module listed in the import section. - The notebooks provide good code examples in action. Futures slang is of the form 'f4spotyym' where - spot is the spot symbol in lower case, - yy is the last two digits of the year - m is the delivery month code, - so for December 2015 COMEX Gold: 'f4xau15z' + + * spot is the spot symbol in lower case, + * yy is the last two digits of the year + * m is the delivery month code, + + so for December 2015 COMEX Gold: 'f4xau15z' Stock slang can be also used for ETFs and mutual funds. The general form is 's4symbol' where the symbol must be in lower case, so for SPY, use 's4spy' as an argument. - ''' + + `get` will accept the vendor code directly as string, e.g. + from FRED and Quandl, or use one of our abbreviated variables + documented in the appropriate module listed in the import section. + The notebooks provide good code examples in action. + + Parameters + ---------- + code: str + fredcode, quandlcode, futures slang, or stock slang + maxi: int + Set maximum number of data points, where 0 implies the default value. + + """ # Just to avoid long names... # and a verbose way of avoiding "from" import syntax: import fecon236.host.fred @@ -71,54 +135,3 @@ def get(code, maxi=0): if __name__ == "__main__": system.endmodule() - - -# ======================================================== ENDNOTES =========== -''' -_______________ On CIRCULAR DEPENDENCIES - -By putting imports within a function we used a technique called -"DEFERRED IMPORTING." This does not violate Python syntax, as the -official documentation states: - - "It is customary but not required to place all import statements - at the beginning of a module (or script, for that matter)." - https://docs.python.org/2/tutorial/modules.html - -The downside to deferred imports generally is readability: one -cannot look at the very top of a module and see all its dependencies. - -The official documentation also says that it is advisable to use -"import X.Y", instead of other statements such as -"from X import Y", or in the worst case: "from X.Y import *". - -As for circular imports, the candidate solutions are explained at -https://stackoverflow.com/a/37126790 by brendan-abel -which can be BEST summarized as: use absolute (not relative) imports, -and definitely AVOID the "from" import style. - -The downside to absolute import style is that the names can -get super long. But one can create aliases using "=" with Python objects, -especially functions, at the cost of readability. Generally this solution -across the tops of many modules becomes very tiresome to implement. - -"Deferred importing" can speed up startup time, so it is not bad practice, -but some claim that it is a sign of bad design. The notion that circular -dependencies are somehow an indication of poor design seems to be more a -reflection on Python as a language ("glaring bug in the import machinery") -rather than a legitimate design point. - -See also: http://stackabuse.com/python-circular-imports by Scott Robinson. - - - __________ Example of fecon236 circular dependency - -In the fred module, holtwinters.ema() is called to smooth data. -In the holtwinters module, hostess.get() is called to retrieve data. -But the hostess module relies on fred.getfred() in the fred module. - -Our deferred import hack in this module, allows us to freely use the -"from" syntax everywhere else: e.g. "from fecon236.host.hostess import get" -at the top of modules without ImportError or AttributeError in both -python27 and python3. -''' From 701d11b74055fa58724f3e3456665ecec3e2217c Mon Sep 17 00:00:00 2001 From: addisonlynch Date: Wed, 22 May 2019 14:01:18 -0400 Subject: [PATCH 4/5] Refactor qdl.py docstrings to numpydoc format --- fecon236/host/qdl.py | 293 +++++++++++++++++++++++++++---------------- 1 file changed, 185 insertions(+), 108 deletions(-) diff --git a/fecon236/host/qdl.py b/fecon236/host/qdl.py index 5800b60..ddb9e5d 100644 --- a/fecon236/host/qdl.py +++ b/fecon236/host/qdl.py @@ -1,7 +1,7 @@ # Python Module for import Date : 2018-05-23 # vim: set fileencoding=utf-8 ff=unix tw=78 ai syn=python : per PEP 0263 -''' -_______________| qdl.py :: Access Quandl data vendors using fecon236. + +"""Access Quandl data vendors using fecon236. We define functions to access data from Quandl. Each time-series and its frequency has its own "quandlcode" available at: https://www.quandl.com @@ -12,11 +12,6 @@ Favorite quandlcodes are variables named d4*, m4*, q4* which indicate their frequency: daily, monthly, or quarterly. -USAGE: df = getqdl(quandlcode) - - -_______________ SYNOPSIS - On 2016-04-22 Quandl released version 3 of their API which was a "drop-in" replacement of version 2, but a very complex *package* which has not seen any substantive improvement since that date -- see @@ -24,17 +19,23 @@ Their version 2.8.7, however, had the virtue of being just a *single* module, and has been battle-tested in fecon235 notebooks since 2015-08-03. -That module has been forked at fecon236 and renamed _ex_Quandl.py. -This qdl module is a wrapper around _ex_Quandl.py, and -as of 2018-05-22 has passed tests/test_qdl.py. +That module has been forked at fecon236 and renamed `_ex_Quandl.py`. +This qdl module is a wrapper around `_ex_Quandl.py`, and +as of 2018-05-22 has passed `tests/test_qdl.py`. - _____ API Key +API Key +------- You will need an API Key UNLESS you are doing fewer than 50 calls per day. After creating an account at quandl.com, set your authentication token by executing this fecon236 function (described in this module): +.. code-block:: python + :flake8-group: Ignore + + import fecon236 as fe + fe.qdl.setQuandlToken(API_key) The token will then be stored in your working directory for continued use. @@ -43,7 +44,8 @@ working directory. - _____ Usage Rules +Usage Rules +----------- API usage is free for registered users. Registered users have a limit of 2,000 calls per 10 minutes, and a limit of 50,000 calls per day. Premium data @@ -53,7 +55,8 @@ All API requests must be made using HTTPS. Requests made over HTTP will fail. - _____ Quandl Codes +Quandl Codes +------------ To use the API to download a dataset, you will need to know the dataset's "quandlcode". Each dataset on Quandl has a unique Quandl code, comprising a @@ -66,7 +69,8 @@ dataset_code to fully identify a dataset. - _____ Pre-calculations +Pre-calculations +---------------- In general, we suggest downloading the data in raw format in the highest frequency possible and preforming any data manipulation in pandas itself. @@ -75,58 +79,94 @@ prior to downloading. The transformations currently availabe are row-on-row change, percentage change, cumulative sum, and normalize (set starting value at 100). If a datapoint for time t is denoted as y[t] and the transformed -data as y'[t], the available transformations are defined as below: - -Transformation Parameter Effect -Row-on-row change diff y'[t] = y[t] - y[t-1] -Row-on-row % change rdiff y'[t] = (y[t] - y[t-1])/y[t-1] -Cumulative sum cumul y'[t] = y[t] +y[t-1] + ... + y[0] -Start at 100 normalize y'[t] = (y[t]/y[0]) * 100 +data as y'[t], the available transformations are defined as below. + ++---------------------+---------------+-----------------------------------+ +| Transformation | Parameter | Effect | ++=====================+===============+===================================+ +| Row-on-row change | `diff` | y'[t] = y[t] - y[t-1] | ++---------------------+---------------+-----------------------------------+ +| Row-on-row % change | `rdiff` | y'[t] = (y[t] - y[t-1])/y[t-1] | ++---------------------+---------------+-----------------------------------+ +| Cumulative sum | `cumul` | y'[t] = y[t] +y[t-1] + ... + y[0] | ++---------------------+---------------+-----------------------------------+ +| Start at 100 | `normalize` | y'[t] = (y[t]/y[0]) * 100 | ++---------------------+---------------+-----------------------------------+ Note that y[0] in the above table refers to the starting date for the API call, i.e., the date specified by trim_start= or rows=, NOT the starting date of the entire dataset. - _____ Specific API Guides +API Guides +---------- + ++---------------+---------------------------------------------------------+ +| Economics | https://www.quandl.com/resources/api-for-economic-data | ++---------------+---------------------------------------------------------+ +| Stocks | https://www.quandl.com/resources/api-for-stock-data | ++---------------+---------------------------------------------------------+ +| Earnings | https://www.quandl.com/resources/api-for-earnings-data | ++---------------+---------------------------------------------------------+ +| Futures | https://www.quandl.com/resources/api-for-futures-data | ++---------------+---------------------------------------------------------+ +| Currencies | https://www.quandl.com/resources/api-for-currency-data | ++---------------+---------------------------------------------------------+ +| Bitcoin | https://www.quandl.com/resources/api-for-bitcoin-data | ++---------------+---------------------------------------------------------+ +| Commodities | https://www.quandl.com/resources/api-for-commodity-data | ++---------------+---------------------------------------------------------+ +| Housing | https://www.quandl.com/resources/api-for-housing-data | ++---------------+---------------------------------------------------------+ +| International | https://www.quandl.com/resources/api-for-country-data | ++---------------+---------------------------------------------------------+ + +Contact +------- -Economics: https://www.quandl.com/resources/api-for-economic-data -Stocks: https://www.quandl.com/resources/api-for-stock-data -Earnings: https://www.quandl.com/resources/api-for-earnings-data -Futures: https://www.quandl.com/resources/api-for-futures-data -Currencies: https://www.quandl.com/resources/api-for-currency-data -Bitcoin: https://www.quandl.com/resources/api-for-bitcoin-data -Commodities: https://www.quandl.com/resources/api-for-commodity-data -Housing: https://www.quandl.com/resources/api-for-housing-data -International: https://www.quandl.com/resources/api-for-country-data +Reach out to Quandl for direct support at connect@quandl.com +Inquires about the Python API package to Chris@quandl.com - _____ Contact -Reach out to Quandl for direct support at connect@quandl.com -Inquires about the Python API package to Chris@quandl.com +Usage +----- +.. code-block:: python + :flake8-add-ignore: F821 - __________ Using low-level API + df = getqdl(quandlcode) -A fecon236 user would normally just employ high-level wrapper getqdl(), +Low-level API +~~~~~~~~~~~~~ + +A `fecon236` user would normally just employ high-level wrapper getqdl(), not _qget(), but for the developer the following may be interesting: The module named as quandlapi is able to return data in 2 formats: a pandas data series ("pandas") and a numpy array ("numpy"). "pandas" is the default. One can specify the format explicitly: +.. code-block:: python + :flake8-add-ignore: F821 + mydata = fe.qdl._qget("WIKI/AAPL", returns="numpy") You can get multiple datasets in one call by passing an array of Quandl codes: - mydata = fe.qdl._qget(["NSE/OIL.4","WIKI/AAPL.1"]) +.. code-block:: python + :flake8-add-ignore: F821 + + mydata = fe.qdl._qget(["NSE/OIL.4", "WIKI/AAPL.1"]) This grabs the 4th column of dataset NSE/OIL and the 1st column of dataset WIKI/AAPL, and returns them in a single call. We can manipulate or transform the data prior to download [not advised]: +.. code-block:: python + :flake8-add-ignore: F821 + # Specific Date Range: mydata = fe.qdl._qget("NSE/OIL", trim_start="yyyy-mm-dd", trim_end="yyyy-mm-dd") @@ -144,45 +184,49 @@ A request with a full list of options would look like the following: -data = fe.qdl._qget('PRAGUESE/PX', authtoken='xxxxxx', trim_start='2001-01-01', - trim_end='2010-01-01', collapse='annual', - transformation='rdiff', rows=4, returns='numpy') - - - _____ Low-level push() [deprecated in Quandl API 2.8.9] +.. code-block:: python + :flake8-add-ignore: F821 -You can no longer upload your own data to Quandl using version 2. + data = fe.qdl._qget('PRAGUESE/PX', authtoken='xxxxxx', + trim_start='2001-01-01', + trim_end='2010-01-01', collapse='annual', + transformation='rdiff', rows=4, returns='numpy') -REFERENCES: +References +---------- -- Using Quandl's Python module: https://www.quandl.com/help/python +* Using Quandl's Python module: https://www.quandl.com/help/python GitHub repo: https://github.com/quandl/quandl-python - -- Complete Quandl API documentation: https://www.quandl.com/docs/api +* Complete Quandl API documentation: https://www.quandl.com/docs/api including error codes. - -- [RESTful interface introduction: https://www.quandl.com/help/api - Not needed here, but it's available for their API version 3.] - -- CFTC Commitments of Traders Report, explanatory notes: +* RESTful interface introduction: https://www.quandl.com/help/api + Not needed here, but it's available for their API version 3. +* CFTC Commitments of Traders Report, explanatory notes: http://www.cftc.gov/MarketReports/CommitmentsofTraders/ExplanatoryNotes - - Traders' option positions are computed on a futures-equivalent basis using delta factors supplied by the exchanges. - -- Computational tools for pandas +* Computational tools for pandas http://pandas.pydata.org/pandas-docs/stable/computation.html +* Wes McKinney, 2013, *Python for Data Analysis*. -- Wes McKinney, 2013, _Python for Data Analysis_. +Notes +----- +* For LATEST version, see https://git.io/fecon236 +* Low-level push() [deprecated in Quandl API 2.8.9] +* You can no longer upload your own data to Quandl using version 2. -CHANGE LOG For LATEST version, see https://git.io/fecon236 -2018-05-23 qdl.py, fecon236 fork. Edit intro. Pass flake8 and test_qdl. + +Change Log +---------- + +* 2018-05-23 qdl.py, fecon236 fork. Edit intro. Pass flake8 and test_qdl. Fix imports. Deprecate plotqdl() and holtqdl. Rename quandl() as _qget() for future clarity. -2017-02-07 yi_quandl.py, fecon235 v5.18.0312, https://git.io/fecon235 -''' +* 2017-02-07 yi_quandl.py, fecon235 v5.18.0312, https://git.io/fecon235 + +""" from __future__ import absolute_import, print_function, division @@ -238,9 +282,13 @@ def setQuandlToken(API_key): - '''Generate authtoken.p in the local directory for API access.''' - # Must have API key which is free by creating a Quandl account, - # however, this is not necessary for limited usage. + """Generate authtoken.p in the local directory for API access. + + Notes + ----- + Must have API key which is free by creating a Quandl account, + however, this is not necessary for limited usage. + """ _ = _qget("NSE/OIL", authtoken=API_key, rows=1) # noqa # This dummy request creates your token file "authtoken.p" # in your current working directory. @@ -253,22 +301,30 @@ def setQuandlToken(API_key): def cotr_get(futures='GC', type='FO'): - '''Get CFTC Commitment of Traders Report COTR.''' - # Report for futures only requested by type "F". - # Report for both futures and options requested by type "FO". - # e.g. 'CFTC/GC_FO_ALL' for CFTC COTR: Gold futures and options. - # - # Traders' option positions are computed on a futures-equivalent basis - # using delta factors supplied by the exchanges. + """Get CFTC Commitment of Traders Report COTR. + + Notes + ----- + + Report for futures only requested by type "F". + Report for both futures and options requested by type "FO". + e.g. 'CFTC/GC_FO_ALL' for CFTC COTR: Gold futures and options. + + Traders' option positions are computed on a futures-equivalent basis + using delta factors supplied by the exchanges. + """ quandlcode = 'CFTC/' + futures + '_' + type + '_ALL' return _qget(quandlcode) def cotr_position(futures='GC'): - '''Extract market position from CFTC Commitment of Traders Report.''' + """Extract market position from CFTC Commitment of Traders Report. + + Notes + ----- + Report for both futures and options requested by implicit "FO". + """ cotr = cotr_get(futures) - # Report for both futures and options requested by implicit "FO". - # # For directionality we use these categories: try: longs = cotr['Asset Manager Longs'] @@ -286,7 +342,7 @@ def cotr_position(futures='GC'): def cotr_position_usd(): - '''Market position for USD from COTR of JY and EC.''' + """Market position for USD from COTR of JY and EC.""" # We ignore USD index DX from ICE. pos1 = cotr_position('JY') # JPY futures. @@ -299,7 +355,7 @@ def cotr_position_usd(): def cotr_position_metals(): - '''Market position for precious metals from COTR of GC and SI.''' + """Market position for precious metals from COTR of GC and SI.""" pos1 = cotr_position('GC') # Gold Comex pos2 = cotr_position('SI') @@ -310,7 +366,7 @@ def cotr_position_metals(): def cotr_position_bonds(): - '''Market position for bonds from COTR of TY and ED.''' + """Market position for bonds from COTR of TY and ED.""" pos1 = cotr_position('TY') # TY is 10-years. pos2 = cotr_position('ED') @@ -321,7 +377,7 @@ def cotr_position_bonds(): def cotr_position_equities(): - '''Market position for equities from COTR of both SP and ES.''' + """Market position for equities from COTR of both SP and ES.""" pos1 = cotr_position('SP') # SP better for options reading. pos2 = cotr_position('ES') @@ -359,20 +415,27 @@ def cotr_position_equities(): def fut_decode(slang): - '''Validate and translate slang string into vendor futures code. + """Validate and translate slang string into vendor futures code. - Quandl uses format: {EXCHANGE}/{CODE}{MONTH}{YEAR} - {EXCHANGE} is the acronym for the futures exchange - {CODE} is the futures ticker code - {MONTH} is the single-letter month code - {YEAR} is a 4-digit year + Quandl uses format: `{EXCHANGE}/{CODE}{MONTH}{YEAR}` + `{EXCHANGE}` is the acronym for the futures exchange + `{CODE}` is the futures ticker code + `{MONTH}` is the single-letter month code + `{YEAR}` is a 4-digit year So for COMEX Gold Dec 2015: 'CME/GCZ2015' [note: CAPITALIZATION] - !! Our short slang must be in all lower case, e.g. + Notes + ----- + + Our short slang must be in all lower case, e.g. - >>> print(fut_decode('f4xau15z')) - CME/GCZ2015 - ''' + .. code-block:: python + :flake8-add-ignore: F821 + + print(fut_decode('f4xau15z')) + # CME/GCZ2015 + + """ if slang.isupper(): # So if given argument is in all CAPS... raise ValueError('Futures slang argument is invalid.') @@ -393,7 +456,7 @@ def fut_decode(slang): def getfut(slang, maxi=512, col='Settle'): - '''slang string retrieves single column for one futures contract. + """slang string retrieves single column for one futures contract. The string consists of a key from fut_dict concatenated with 'yym' where yy is shorthand for year and m is the month symbol @@ -401,10 +464,16 @@ def getfut(slang, maxi=512, col='Settle'): Available col are: Open, High, Low, Last, Change, Settle, Volume, 'Open Interest' - ''' - # Other than Eurodollars, we should not need more than 512 days - # of data due to finite life of a futures contract. - # 2015-09-11 quandl default seems to be maxi around 380. + + Notes + ----- + + * Other than Eurodollars, we should not need more than 512 days of data due + to finite life of a futures contract. + * 2015-09-11 quandl default seems to be maxi around 380. + + """ + # fut = _qget(fut_decode(slang), rows=maxi) # return just a single column dataframe: @@ -433,9 +502,13 @@ def getfut(slang, maxi=512, col='Settle'): def freqM2MS(dataframe): - '''Change Monthly dates to (FRED-compatible) Month Start frequency.''' - # FRED uses first day of month 'MS' to index that month's data, - # whereas Quandl data *may* use varying end of month dates. + """Change Monthly dates to (FRED-compatible) Month Start frequency. + + Notes + ----- + FRED uses first day of month 'MS' to index that month's data, whereas + Quandl data *may* use varying end of month dates. + """ df = dataframe.set_index(pd.DatetimeIndex( [i.replace(day=1) for i in dataframe.index])) df.index = df.index.normalize() @@ -453,7 +526,7 @@ def freqM2MS(dataframe): def getm4spx_1871_p(): - '''Retrieve nominal monthly Shiller S&P500 price, starting 1871.''' + """Retrieve nominal monthly Shiller S&P500 price, starting 1871.""" price = freqM2MS(_qget('MULTPL/SP500_REAL_PRICE_MONTH')) # ^But they meant NOMINAL! # Their inflation-adjusted monthly series is called @@ -463,7 +536,7 @@ def getm4spx_1871_p(): def getm4spx_1871_e(): - '''Retrieve nominal monthly Shiller S&P500 earnings, starting 1871.''' + """Retrieve nominal monthly Shiller S&P500 earnings, starting 1871.""" ratio = freqM2MS(_qget('MULTPL/SP500_PE_RATIO_MONTH')) # Gets price/earnings ratio, so solve for 12-month earnings. # Alternative: official YALE/SPCOMP, but 9 months latency! @@ -473,7 +546,7 @@ def getm4spx_1871_e(): def getm4spx_1871_d(): - '''Retrieve nominal monthly Shiller S&P500 dividends, starting 1871.''' + """Retrieve nominal monthly Shiller S&P500 dividends, starting 1871.""" dyield = freqM2MS(_qget('MULTPL/SP500_DIV_YIELD_MONTH')) # Gets dividend yield in percentage form, # but we want just plain dividends over previous 12 months. @@ -484,13 +557,17 @@ def getm4spx_1871_d(): def getqdl(quandlcode, maxi=87654321): - '''Retrieve from Quandl in dataframe format, INCL. SPECIAL CASES. - maxi is just arbitrarily large as default, - useful to limit data to last maxi rows, - e.g. maxi=1 for most recent row only, - but NOT used in all cases below. - We can SYNTHESIZE a quandlcode by use of string equivalent arg. - ''' + """Retrieve from Quandl in dataframe format, INCL. SPECIAL CASES. + + Notes + ----- + + * `maxi` is just arbitrarily large as default, useful to limit data to + last maxi rows, e.g. maxi=1 for most recent row only, but NOT used in all + cases below. + * We can SYNTHESIZE a quandlcode by use of string equivalent arg. + + """ if quandlcode == w4cotr_xau: df = cotr_position(f4xau) elif quandlcode == w4cotr_metals: @@ -527,14 +604,14 @@ def getqdl(quandlcode, maxi=87654321): def plotqdl(data, title='tmp', maxi=87654321): - '''DEPRECATED: Plot data should be it given as dataframe or quandlcode.''' + """DEPRECATED: Plot data should be it given as dataframe or quandlcode.""" # ^2018-05-22. Removal OK after 2020-01-01. msg = "plotqdl() DEPRECATED. Instead use get() and plot()." raise DeprecationWarning(msg) def holtqdl(data, h=24, alpha=0.26, beta=0.19): - '''DEPRECATED: Holt-Winters forecast h-periods ahead (quandlcode aware).''' + """DEPRECATED: Holt-Winters forecast h-periods ahead (quandlcode aware).""" # ^2018-05-22. Removal OK after 2020-01-01. msg = "holtqdl() DEPRECATED. Instead use get(), holt(), holtforecast()." raise DeprecationWarning(msg) From 610e190aabcc57ae9f097809837b61401b6cfa88 Mon Sep 17 00:00:00 2001 From: addisonlynch Date: Wed, 22 May 2019 14:02:04 -0400 Subject: [PATCH 5/5] Refactor stock.py docstrings to numpydoc format --- fecon236/host/stock.py | 89 +++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/fecon236/host/stock.py b/fecon236/host/stock.py index ea844bf..b8c9b9a 100644 --- a/fecon236/host/stock.py +++ b/fecon236/host/stock.py @@ -1,34 +1,54 @@ # Python Module for import Date : 2018-06-04 # vim: set fileencoding=utf-8 ff=unix tw=78 ai syn=python : per PEP 0263 -''' -_______________| stock.py :: Access stock quotes for fecon236 +"""Access stock quotes for fecon236 Functions here are designed to access stock quotes FREELY. Vendors of choice have been Yahoo Finance or Google Finance, but given disruptions since late 2017, we consider alternates; see https://github.com/rsvp/fecon235/issues/7 for details. - Usage: df = getstock('s4code', 7) - # ^one week. - # ^begin with s4, then - # append stock SYMBOL in lower case. +Usage +----- - Dependencies: pandas-datareader (for pandas>=0.17) +.. code-block:: python -REFERENCES -- pandas, http://pandas.pydata.org/pandas-docs/stable/computation.html -- pandas-datareader, + from fecon236.host.stock import getstock + + df = getstock('s4code', 7) + # ^one week. + # ^begin with s4, then + # append stock SYMBOL in lower case. + +Dependencies +------------ +`pandas-datareader` (for `pandas` >=0.17) + +References +---------- + +* `pandas`, http://pandas.pydata.org/pandas-docs/stable/computation.html +* `pandas-datareader`: https://pydata.github.io/pandas-datareader/stable/index.html - ATTN: Package name is "pandas-datareader", but it is - imported under the "pandas_datareader" module name. -- Wes McKinney, 2013, Python for Data Analysis. +.. warning:: Package name is `pandas-datareader`, but it is imported under + the `pandas_datareader` module name. + +* Wes McKinney, 2013, Python for Data Analysis. + +Notes +----- + +For LATEST version, see https://git.io/fecon236 -CHANGE LOG For LATEST version, see https://git.io/fecon236 -2018-05-23 Rename to stock.py, fecon236 fork. Fix imports, pass flake8. -2017-02-06 yi_stocks.py, fecon235 v5.18.0312, https://git.io/fecon235 - pandas<=0.17 supported in fecon235. -''' +Change Log +---------- + +* 2018-05-23 Rename to `stock.py`, `fecon236` fork. Fix imports, pass + flake8. +* 2017-02-06 `yi_stocks.py`, fecon235 v5.18.0312, https://git.io/fecon235 + `pandas` <=0.17 supported in `fecon235`. + +""" from __future__ import absolute_import, print_function, division @@ -43,12 +63,13 @@ def stock_decode(slang): - '''Validate and translate slang string into vendor stock code. - Our short slang must be in all lower case starting with s4, - e.g. 's4spy' with SYMBOL in lower case. + """Validate and translate slang string into vendor stock code. - Using slang helps to avoid collision in our larger namespace. - ''' + Our short slang must be in all lower case starting with s4, e.g. 's4spy' + with SYMBOL in lower case. + + Using slang helps to avoid collision in our larger namespace. + """ if slang.isupper() or slang[:2] != 's4': # So if given argument is in all CAPS, # or does not begin with 's4' @@ -62,13 +83,13 @@ def stock_decode(slang): def stock_all(slang, maxi=3650): - '''slang string retrieves ALL columns for single stock. + """slang string retrieves ALL columns for single stock. The slang string consists of 's4' + symbol, all in lower case, e.g. 's4spy' for SPY. - maxi is set to default of ten years past data. - ''' + `maxi` is set to default of ten years past data. + """ # Typical: start = datetime.datetime(2013, 1, 20) # but we just want the most current window of data. now = datetime.datetime.now() @@ -90,19 +111,23 @@ def stock_all(slang, maxi=3650): def stock_one(slang, maxi=3650, col='Close'): - '''slang string retrieves SINGLE column for said stock. - Available col include: Open, High, Low, Close, Volume - ''' + """slang string retrieves SINGLE column for said stock. + + Available col include: Open, High, Low, Close, Volume + + """ df = stock_all(slang, maxi) # return just a single column dataframe: return tool.todf(df[[col]]) def getstock(slang, maxi=3650): - '''Retrieve stock data from Yahoo Finance or Google Finance. - maxi is the number of chronological, not trading, days. + """Retrieve stock data from Yahoo Finance or Google Finance. + + `maxi` is the number of chronological, not trading, days. + We can SYNTHESIZE a s4 slang by use of string equivalent arg. - ''' + """ if False: pass elif False: