|
| 1 | +import datetime |
| 2 | +import json |
| 3 | + |
| 4 | +import pandas as pd |
| 5 | + |
| 6 | +from dateutil.relativedelta import relativedelta |
| 7 | +from pandas_datareader.base import _DailyBaseReader |
| 8 | + |
| 9 | +# Data provided for free by IEX |
| 10 | +# Data is furnished in compliance with the guidelines promulgated in the IEX |
| 11 | +# API terms of service and manual |
| 12 | +# See https://iextrading.com/api-exhibit-a/ for additional information |
| 13 | +# and conditions of use |
| 14 | + |
| 15 | + |
| 16 | +class IEXDailyReader(_DailyBaseReader): |
| 17 | + |
| 18 | + """ |
| 19 | + Returns DataFrame/Panel of historical stock prices from symbols, over date |
| 20 | + range, start to end. To avoid being penalized by Google Finance servers, |
| 21 | + pauses between downloading 'chunks' of symbols can be specified. |
| 22 | +
|
| 23 | + Parameters |
| 24 | + ---------- |
| 25 | + symbols : string, array-like object (list, tuple, Series), or DataFrame |
| 26 | + Single stock symbol (ticker), array-like object of symbols or |
| 27 | + DataFrame with index containing stock symbols. |
| 28 | + start : string, (defaults to '1/1/2010') |
| 29 | + Starting date, timestamp. Parses many different kind of date |
| 30 | + representations (e.g., 'JAN-01-2010', '1/1/10', 'Jan, 1, 1980') |
| 31 | + end : string, (defaults to today) |
| 32 | + Ending date, timestamp. Same format as starting date. |
| 33 | + retry_count : int, default 3 |
| 34 | + Number of times to retry query request. |
| 35 | + pause : int, default 0 |
| 36 | + Time, in seconds, to pause between consecutive queries of chunks. If |
| 37 | + single value given for symbol, represents the pause between retries. |
| 38 | + chunksize : int, default 25 |
| 39 | + Number of symbols to download consecutively before intiating pause. |
| 40 | + session : Session, default None |
| 41 | + requests.sessions.Session instance to be used |
| 42 | + """ |
| 43 | + |
| 44 | + def __init__(self, symbols=None, start=None, end=None, retry_count=3, |
| 45 | + pause=0.35, session=None, chunksize=25): |
| 46 | + super(IEXDailyReader, self).__init__(symbols=symbols, start=start, |
| 47 | + end=end, retry_count=retry_count, |
| 48 | + pause=pause, session=session, |
| 49 | + chunksize=chunksize) |
| 50 | + |
| 51 | + @property |
| 52 | + def url(self): |
| 53 | + return 'https://api.iextrading.com/1.0/stock/market/batch' |
| 54 | + |
| 55 | + @property |
| 56 | + def endpoint(self): |
| 57 | + return "chart" |
| 58 | + |
| 59 | + def _get_params(self, symbol): |
| 60 | + chart_range = self._range_string_from_date() |
| 61 | + print(chart_range) |
| 62 | + if isinstance(symbol, list): |
| 63 | + symbolList = ','.join(symbol) |
| 64 | + else: |
| 65 | + symbolList = symbol |
| 66 | + params = { |
| 67 | + "symbols": symbolList, |
| 68 | + "types": self.endpoint, |
| 69 | + "range": chart_range, |
| 70 | + } |
| 71 | + return params |
| 72 | + |
| 73 | + def _range_string_from_date(self): |
| 74 | + delta = relativedelta(self.start, datetime.datetime.now()) |
| 75 | + if 2 <= (delta.years * -1) <= 5: |
| 76 | + return "5y" |
| 77 | + elif 1 <= (delta.years * -1) <= 2: |
| 78 | + return "2y" |
| 79 | + elif 0 <= (delta.years * -1) < 1: |
| 80 | + return "1y" |
| 81 | + else: |
| 82 | + raise ValueError( |
| 83 | + "Invalid date specified. Must be within past 5 years.") |
| 84 | + |
| 85 | + def read(self): |
| 86 | + """read data""" |
| 87 | + try: |
| 88 | + return self._read_one_data(self.url, |
| 89 | + self._get_params(self.symbols)) |
| 90 | + finally: |
| 91 | + self.close() |
| 92 | + |
| 93 | + def _read_lines(self, out): |
| 94 | + data = out.read() |
| 95 | + json_data = json.loads(data) |
| 96 | + result = {} |
| 97 | + if type(self.symbols) is str: |
| 98 | + syms = [self.symbols] |
| 99 | + else: |
| 100 | + syms = self.symbols |
| 101 | + for symbol in syms: |
| 102 | + d = json_data.pop(symbol)["chart"] |
| 103 | + df = pd.DataFrame(d) |
| 104 | + df.set_index("date", inplace=True) |
| 105 | + values = ["open", "high", "low", "close", "volume"] |
| 106 | + df = df[values] |
| 107 | + sstart = self.start.strftime('%Y-%m-%d') |
| 108 | + send = self.end.strftime('%Y-%m-%d') |
| 109 | + df = df.loc[sstart:send] |
| 110 | + result.update({symbol: df}) |
| 111 | + if len(result) > 1: |
| 112 | + return result |
| 113 | + return result[self.symbols] |
0 commit comments