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 , end = end ,
47+ retry_count = retry_count , pause = pause , session = session , chunksize = chunksize )
48+
49+ @property
50+ def url (self ):
51+ return 'https://api.iextrading.com/1.0/stock/market/batch'
52+
53+ @property
54+ def endpoint (self ):
55+ return "chart"
56+
57+ def _get_params (self , symbol ):
58+ chart_range = self ._range_string_from_date ()
59+ print (chart_range )
60+ if isinstance (symbol , list ):
61+ symbolList = ',' .join (symbol )
62+ else :
63+ symbolList = symbol
64+ params = {
65+ "symbols" : symbolList ,
66+ "types" : self .endpoint ,
67+ "range" : chart_range ,
68+ }
69+ return params
70+
71+ def _range_string_from_date (self ):
72+ delta = relativedelta (self .start , datetime .datetime .now ())
73+ if 2 <= (delta .years * - 1 ) <= 5 :
74+ return "5y"
75+ elif 1 <= (delta .years * - 1 ) <= 2 :
76+ return "2y"
77+ elif 0 <= (delta .years * - 1 ) < 1 :
78+ return "1y"
79+ else :
80+ raise ValueError (
81+ "Invalid date specified. Must be within past 5 years." )
82+
83+ def read (self ):
84+ """read data"""
85+ try :
86+ return self ._read_one_data (self .url , self ._get_params (self .symbols ))
87+ finally :
88+ self .close ()
89+
90+ def _read_lines (self , out ):
91+ data = out .read ()
92+ json_data = json .loads (data )
93+ result = {}
94+ if type (self .symbols ) is str :
95+ syms = [self .symbols ]
96+ else :
97+ syms = self .symbols
98+ for symbol in syms :
99+ d = json_data .pop (symbol )["chart" ]
100+ df = pd .DataFrame (d )
101+ df .set_index ("date" , inplace = True )
102+ values = ["open" , "high" , "low" , "close" , "volume" ]
103+ df = df [values ]
104+ sstart = self .start .strftime ('%Y-%m-%d' )
105+ send = self .end .strftime ('%Y-%m-%d' )
106+ df = df .loc [sstart :send ]
107+ result .update ({symbol :df })
108+ if len (result ) > 1 :
109+ return result
110+ return result [self .symbols ]
0 commit comments