Skip to content

Commit f3a6fb2

Browse files
rgkimballbashtage
authored andcommitted
Implements all available webhooks in IEX API 1.0
1 parent 5edbbca commit f3a6fb2

File tree

6 files changed

+329
-1
lines changed

6 files changed

+329
-1
lines changed

pandas_datareader/data.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@
1313
from pandas_datareader.google.daily import GoogleDailyReader
1414
from pandas_datareader.google.options import Options as GoogleOptions
1515
from pandas_datareader.google.quotes import GoogleQuotesReader
16+
from pandas_datareader.iex.market import MarketReader as IEXMarkets
17+
from pandas_datareader.iex.ref import SymbolsReader as IEXSymbols
18+
from pandas_datareader.iex.stats import DailySummaryReader as IEXHistorical
19+
from pandas_datareader.iex.stats import MonthlySummaryReader as IEXMonthlySummary
20+
from pandas_datareader.iex.stats import RecentReader as IEXRecents
21+
from pandas_datareader.iex.stats import RecordsReader as IEXRecords
22+
from pandas_datareader.iex.tops import LastReader as IEXLasts
23+
from pandas_datareader.iex.tops import TopsReader as IEXTops
1624
from pandas_datareader.moex import MoexReader
1725
from pandas_datareader.nasdaq_trader import get_nasdaq_symbols
1826
from pandas_datareader.oecd import OECDReader
@@ -71,10 +79,40 @@ def get_data_quandl(*args, **kwargs):
7179
def get_data_moex(*args, **kwargs):
7280
return MoexReader(*args, **kwargs).read()
7381

74-
7582
def get_data_stooq(*args, **kwargs):
7683
return StooqDailyReader(*args, **kwargs).read()
7784

85+
def get_tops_iex(*args, **kwargs):
86+
return IEXTops(*args, **kwargs).read()
87+
88+
89+
def get_last_iex(*args, **kwargs):
90+
return IEXLasts(*args, **kwargs).read()
91+
92+
93+
def get_markets_iex(*args, **kwargs):
94+
return IEXMarkets(*args, **kwargs).read()
95+
96+
97+
def get_data_iex(*args, **kwargs):
98+
return IEXHistorical(*args, **kwargs).read()
99+
100+
101+
def get_summary_iex(*args, **kwargs):
102+
return IEXMonthlySummary(*args, **kwargs).read()
103+
104+
105+
def get_records_iex(*args, **kwargs):
106+
return IEXRecords(*args, **kwargs).read()
107+
108+
109+
def get_recent_iex(*args, **kwargs):
110+
return IEXRecents(*args, **kwargs).read()
111+
112+
113+
def get_iex_symbols(*args, **kwargs):
114+
return IEXSymbols(*args, **kwargs).read()
115+
78116

79117
def DataReader(name, data_source=None, start=None, end=None,
80118
retry_count=3, pause=0.001, session=None, access_key=None):
@@ -151,6 +189,15 @@ def DataReader(name, data_source=None, start=None, end=None,
151189
chunksize=25,
152190
retry_count=retry_count, pause=pause,
153191
session=session).read()
192+
elif data_source == "iex-tops":
193+
return IEXTops(symbols=name, start=start, end=end,
194+
retry_count=retry_count, pause=pause,
195+
session=session).read()
196+
197+
elif data_source == "iex-last":
198+
return IEXLasts(symbols=name, start=start, end=end,
199+
retry_count=retry_count, pause=pause,
200+
session=session).read()
154201

155202
elif data_source == "bankofcanada":
156203
return BankOfCanadaReader(symbols=name, start=start, end=end,

pandas_datareader/iex/__init__.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import json
2+
import pandas as pd
3+
from pandas.io.common import urlencode
4+
from pandas_datareader.base import _BaseReader
5+
6+
# Data provided for free by IEX
7+
# Data is furnished in compliance with the guidelines promulgated in the IEX API terms of service and manual
8+
# See https://iextrading.com/api-exhibit-a/ for additional information and conditions of use
9+
10+
11+
class IEX(_BaseReader):
12+
13+
"""
14+
Serves as the base class for all IEX API services.
15+
"""
16+
17+
_format = 'json'
18+
19+
def __init__(self, symbols=None, start=None, end=None, retry_count=3,
20+
pause=0.001, session=None):
21+
super(IEX, self).__init__(symbols=symbols,
22+
start=start, end=end,
23+
retry_count=retry_count,
24+
pause=pause, session=session)
25+
26+
@property
27+
def service(self):
28+
# This property will be overridden by the subclass
29+
raise NotImplementedError("IEX API service not specified.")
30+
31+
@property
32+
def url(self):
33+
qstring = urlencode(self._get_params(self.symbols))
34+
return "https://api.iextrading.com/1.0/{}?{}".format(self.service, qstring)
35+
36+
def read(self):
37+
df = super(IEX, self).read()
38+
return df
39+
40+
def _get_params(self, symbols):
41+
p = {}
42+
if isinstance(symbols, list):
43+
p['symbols'] = ','.join(symbols)
44+
elif isinstance(symbols, str):
45+
p['symbols'] = symbols
46+
return p
47+
48+
def _output_error(self, out):
49+
"""If IEX returns a non-200 status code, we need to notify the user of the error returned.
50+
51+
:param out: Raw HTTP Output
52+
"""
53+
try:
54+
content = json.loads(out.text)
55+
except:
56+
raise TypeError("Failed to interpret response as JSON.")
57+
58+
for key, string in content.items():
59+
e = "IEX Output error encountered: {}".format(string)
60+
if key == 'error':
61+
raise Exception(e)
62+
63+
def _read_lines(self, out):
64+
"""IEX's output does not need anything complex, so we're overriding to use Pandas' default interpreter
65+
66+
:param out: Raw HTTP Output
67+
:return: DataFrame
68+
"""
69+
70+
# IEX will return a blank line for invalid tickers:
71+
if isinstance(out, list):
72+
out = [x for x in out if x is not None]
73+
return pd.DataFrame(out) if len(out) > 0 else pd.DataFrame()

pandas_datareader/iex/market.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from pandas_datareader.iex import IEX
2+
3+
# Data provided for free by IEX
4+
# Data is furnished in compliance with the guidelines promulgated in the IEX API terms of service and manual
5+
# See https://iextrading.com/api-exhibit-a/ for additional information and conditions of use
6+
7+
8+
class MarketReader(IEX):
9+
def __init__(self, symbols=None, start=None, end=None, retry_count=3,
10+
pause=0.001, session=None):
11+
super(MarketReader, self).__init__(symbols=symbols,
12+
start=start, end=end,
13+
retry_count=retry_count,
14+
pause=pause, session=session)
15+
16+
@property
17+
def service(self):
18+
return "market"
19+
20+
def _get_params(self, symbols):
21+
# Market API does not take any parameters, returning empty dict
22+
return {}

pandas_datareader/iex/ref.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from pandas_datareader.iex import IEX
2+
3+
# Data provided for free by IEX
4+
# Data is furnished in compliance with the guidelines promulgated in the IEX API terms of service and manual
5+
# See https://iextrading.com/api-exhibit-a/ for additional information and conditions of use
6+
7+
8+
class SymbolsReader(IEX):
9+
def __init__(self, symbols=None, start=None, end=None, retry_count=3,
10+
pause=0.001, session=None):
11+
super(SymbolsReader, self).__init__(symbols=symbols,
12+
start=start, end=end,
13+
retry_count=retry_count,
14+
pause=pause, session=session)
15+
16+
@property
17+
def service(self):
18+
return "ref-data/symbols"
19+
20+
def _get_params(self, symbols):
21+
# Ref Data API does not take any parameters, returning empty dict
22+
return {}

pandas_datareader/iex/stats.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import pandas as pd
2+
from datetime import datetime, timedelta
3+
from pandas_datareader.iex import IEX
4+
5+
# Data provided for free by IEX
6+
# Data is furnished in compliance with the guidelines promulgated in the IEX API terms of service and manual
7+
# See https://iextrading.com/api-exhibit-a/ for additional information and conditions of use
8+
9+
10+
class DailySummaryReader(IEX):
11+
def __init__(self, symbols=None, start=None, end=None, retry_count=3,
12+
pause=0.001, session=None):
13+
self.curr_date = start
14+
super(DailySummaryReader, self).__init__(symbols=symbols,
15+
start=start, end=end,
16+
retry_count=retry_count,
17+
pause=pause, session=session)
18+
19+
@property
20+
def service(self):
21+
return "stats/historical/daily"
22+
23+
def _get_params(self, symbols):
24+
p = {}
25+
26+
if self.curr_date is not None:
27+
p['date'] = self.curr_date.strftime('%Y%m%d')
28+
29+
return p
30+
31+
def read(self):
32+
"""Unfortunately, IEX's API can only retrieve data one day or one month at a time. Rather than specifying a date
33+
range, we will have to run the read function for each date provided.
34+
35+
:return: DataFrame
36+
"""
37+
tlen = self.end - self.start
38+
dfs = []
39+
for date in (self.start + timedelta(n) for n in range(tlen.days)):
40+
self.curr_date = date
41+
tdf = super(IEX, self).read()
42+
dfs.append(tdf)
43+
return pd.concat(dfs)
44+
45+
46+
class MonthlySummaryReader(IEX):
47+
def __init__(self, symbols=None, start=None, end=None, retry_count=3,
48+
pause=0.001, session=None):
49+
self.curr_date = start
50+
self.date_format = '%Y%m'
51+
52+
super(MonthlySummaryReader, self).__init__(symbols=symbols,
53+
start=start, end=end,
54+
retry_count=retry_count,
55+
pause=pause, session=session)
56+
57+
@property
58+
def service(self):
59+
return "stats/historical"
60+
61+
def _get_params(self, symbols):
62+
p = {}
63+
64+
if self.curr_date is not None:
65+
p['date'] = self.curr_date.strftime(self.date_format)
66+
67+
return p
68+
69+
def read(self):
70+
"""Unfortunately, IEX's API can only retrieve data one day or one month at a time. Rather than specifying a date
71+
range, we will have to run the read function for each date provided.
72+
73+
:return: DataFrame
74+
"""
75+
tlen = self.end - self.start
76+
dfs = []
77+
78+
# Build list of all dates within the given range
79+
lrange = [x for x in (self.start + timedelta(n) for n in range(tlen.days))]
80+
81+
mrange = []
82+
for dt in lrange:
83+
if datetime(dt.year, dt.month, 1) not in mrange:
84+
mrange.append(datetime(dt.year, dt.month, 1))
85+
lrange = mrange
86+
87+
for date in lrange:
88+
self.curr_date = date
89+
tdf = super(IEX, self).read()
90+
91+
# We may not return data if this was a weekend/holiday:
92+
if not tdf.empty:
93+
tdf['date'] = date.strftime(self.date_format)
94+
dfs.append(tdf)
95+
96+
# We may not return any data if we failed to specify useful parameters:
97+
return pd.concat(dfs) if len(dfs) > 0 else pd.DataFrame()
98+
99+
100+
class RecordsReader(IEX):
101+
def __init__(self, symbols=None, start=None, end=None, retry_count=3,
102+
pause=0.001, session=None):
103+
super(RecordsReader, self).__init__(symbols=symbols,
104+
start=start, end=end,
105+
retry_count=retry_count,
106+
pause=pause, session=session)
107+
108+
@property
109+
def service(self):
110+
return "stats/records"
111+
112+
def _get_params(self, symbols):
113+
# Record Stats API does not take any parameters, returning empty dict
114+
return {}
115+
116+
117+
class RecentReader(IEX):
118+
def __init__(self, symbols=None, start=None, end=None, retry_count=3,
119+
pause=0.001, session=None):
120+
super(RecentReader, self).__init__(symbols=symbols,
121+
start=start, end=end,
122+
retry_count=retry_count,
123+
pause=pause, session=session)
124+
125+
@property
126+
def service(self):
127+
return "stats/recent"
128+
129+
def _get_params(self, symbols):
130+
# Record Stats API does not take any parameters, returning empty dict
131+
return {}

pandas_datareader/iex/tops.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from pandas_datareader.iex import IEX
2+
3+
# Data provided for free by IEX
4+
# Data is furnished in compliance with the guidelines promulgated in the IEX API terms of service and manual
5+
# See https://iextrading.com/api-exhibit-a/ for additional information and conditions of use
6+
7+
8+
class TopsReader(IEX):
9+
10+
def __init__(self, symbols=None, start=None, end=None, retry_count=3,
11+
pause=0.001, session=None):
12+
super(TopsReader, self).__init__(symbols=symbols,
13+
start=start, end=end,
14+
retry_count=retry_count,
15+
pause=pause, session=session)
16+
17+
@property
18+
def service(self):
19+
return "tops"
20+
21+
22+
class LastReader(IEX):
23+
# todo: Eventually we'll want to implement the WebSockets version as an option.
24+
def __init__(self, symbols=None, start=None, end=None, retry_count=3,
25+
pause=0.001, session=None):
26+
super(LastReader, self).__init__(symbols=symbols,
27+
start=start, end=end,
28+
retry_count=retry_count,
29+
pause=pause, session=session)
30+
31+
@property
32+
def service(self):
33+
return "tops/last"

0 commit comments

Comments
 (0)