Skip to content

Commit 5a00fc5

Browse files
rgkimballbashtage
authored andcommitted
Implements HTTP requests for IEX DEEP API, see: https://www.iextrading.com/developer/docs/#deep
1 parent bae2597 commit 5a00fc5

File tree

4 files changed

+146
-1
lines changed

4 files changed

+146
-1
lines changed

pandas_datareader/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
from ._version import get_versions
2+
23
from .data import (get_components_yahoo, get_data_famafrench, get_data_google,
34
get_data_yahoo, get_data_enigma, get_data_yahoo_actions,
4-
get_quote_google, get_quote_yahoo, DataReader, Options)
5+
get_quote_google, get_quote_yahoo, get_tops_iex,
6+
get_last_iex, get_markets_iex, get_data_iex,
7+
get_summary_iex, get_records_iex, get_recent_iex,
8+
get_iex_symbols, get_iex_book,
9+
DataReader, Options)
510

611
__version__ = get_versions()['version']
712
del get_versions
813

914
__all__ = ['__version__', 'get_components_yahoo', 'get_data_enigma',
1015
'get_data_famafrench', 'get_data_google', 'get_data_yahoo',
1116
'get_data_yahoo_actions', 'get_quote_google', 'get_quote_yahoo',
17+
'get_data_iex', 'get_iex_book', 'get_iex_symbols', 'get_last_iex',
18+
'get_markets_iex', 'get_recent_iex', 'get_records_iex',
19+
'get_summary_iex', 'get_tops_iex',
1220
'DataReader', 'Options']

pandas_datareader/data.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from pandas_datareader.iex.ref import SymbolsReader as IEXSymbols
1818
from pandas_datareader.iex.stats import DailySummaryReader as IEXHistorical
1919
from pandas_datareader.iex.stats import MonthlySummaryReader as IEXMonthSummary
20+
from pandas_datareader.iex.deep import Deep as IEXDeep
2021
from pandas_datareader.iex.stats import RecentReader as IEXRecents
2122
from pandas_datareader.iex.stats import RecordsReader as IEXRecords
2223
from pandas_datareader.iex.tops import LastReader as IEXLasts
@@ -114,6 +115,10 @@ def get_iex_symbols(*args, **kwargs):
114115
return IEXSymbols(*args, **kwargs).read()
115116

116117

118+
def get_iex_book(*args, **kwargs):
119+
return IEXDeep(*args, **kwargs).read()
120+
121+
117122
def DataReader(name, data_source=None, start=None, end=None,
118123
retry_count=3, pause=0.001, session=None, access_key=None):
119124
"""
@@ -209,6 +214,11 @@ def DataReader(name, data_source=None, start=None, end=None,
209214
retry_count=retry_count, pause=pause,
210215
session=session).read()
211216

217+
elif data_source == "iex-book":
218+
return IEXDeep(symbols=name, service="book", start=start, end=end,
219+
retry_count=retry_count, pause=pause,
220+
session=session).read()
221+
212222
elif data_source == "enigma":
213223
return EnigmaReader(dataset_id=name, api_key=access_key).read()
214224

pandas_datareader/iex/deep.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
from pandas_datareader.iex import IEX
2+
from datetime import datetime
3+
4+
# Data provided for free by IEX
5+
# Data is furnished in compliance with the guidelines promulgated in the IEX
6+
# API terms of service and manual
7+
# See https://iextrading.com/api-exhibit-a/ for additional information
8+
# and conditions of use
9+
10+
11+
class Deep(IEX):
12+
def __init__(self, symbols=None, service=None, start=None, end=None,
13+
retry_count=3, pause=0.001, session=None):
14+
super(Deep, self).__init__(symbols=symbols,
15+
start=start, end=end,
16+
retry_count=retry_count,
17+
pause=pause, session=session)
18+
self.sub = service
19+
20+
@property
21+
def service(self):
22+
ss = "/" + self.sub if self.sub is not None else ""
23+
return "deep{}".format(ss)
24+
25+
def _read_lines(self, out):
26+
"""
27+
IEX depth of book data varies and shouldn't always be returned in a DF
28+
29+
:param out: Raw HTTP Output
30+
:return: DataFrame
31+
"""
32+
33+
# Runs appropriate output functions per the service being accessed.
34+
fmap = {
35+
'book': '_pass',
36+
'op-halt-status': '_convert_tstamp',
37+
'security-event': '_convert_tstamp',
38+
'ssr-status': '_convert_tstamp',
39+
'system-event': '_read_system_event',
40+
'trades': '_pass',
41+
'trade-breaks': '_convert_tstamp',
42+
'trading-status': '_read_trading_status',
43+
None: '_pass',
44+
}
45+
46+
if self.sub in fmap:
47+
return getattr(self, fmap[self.sub])(out)
48+
else:
49+
raise "Invalid service specified: {}.".format(self.sub)
50+
51+
def _read_system_event(self, out):
52+
# Map the response code to a string output per the API docs.
53+
# Per: https://www.iextrading.com/developer/docs/#system-event-message
54+
smap = {
55+
'O': 'Start of messages',
56+
'S': 'Start of system hours',
57+
'R': 'Start of regular market hours',
58+
'M': 'End of regular market hours',
59+
'E': 'End of system hours',
60+
'C': 'End of messages'
61+
}
62+
tid = out["systemEvent"]
63+
out["eventResponse"] = smap[tid]
64+
65+
return self._convert_tstamp(out)
66+
67+
@staticmethod
68+
def _pass(out):
69+
return out
70+
71+
def _read_trading_status(self, out):
72+
# Reference: https://www.iextrading.com/developer/docs/#trading-status
73+
smap = {
74+
'H': 'Trading halted across all US equity markets',
75+
'O': 'Trading halt released into an Order Acceptance Period '
76+
'(IEX-listed securities only)',
77+
'P': 'Trading paused and Order Acceptance Period on IEX '
78+
'(IEX-listed securities only)',
79+
'T': 'Trading on IEX'
80+
}
81+
rmap = {
82+
# Trading Halt Reasons
83+
'T1': 'Halt News Pending',
84+
'IPO1': 'IPO/New Issue Not Yet Trading',
85+
'IPOD': 'IPO/New Issue Deferred',
86+
'MCB3': 'Market-Wide Circuit Breaker Level 3 - Breached',
87+
'NA': 'Reason Not Available',
88+
89+
# Order Acceptance Period Reasons
90+
'T2': 'Halt News Dissemination',
91+
'IPO2': 'IPO/New Issue Order Acceptance Period',
92+
'IPO3': 'IPO Pre-Launch Period',
93+
'MCB1': 'Market-Wide Circuit Breaker Level 1 - Breached',
94+
'MCB2': 'Market-Wide Circuit Breaker Level 2 - Breached'
95+
}
96+
for ticker, data in out.items():
97+
if data['status'] in smap:
98+
data['statusText'] = smap[data['status']]
99+
100+
if data['reason'] in rmap:
101+
data['reasonText'] = rmap[data['reason']]
102+
103+
out[ticker] = data
104+
105+
return self._convert_tstamp(out)
106+
107+
@staticmethod
108+
def _convert_tstamp(out):
109+
# Searches for top-level timestamp attributes or within dictionaries
110+
if 'timestamp' in out:
111+
# Convert UNIX to datetime object
112+
f = float(out["timestamp"])
113+
out["timestamp"] = datetime.fromtimestamp(f/1000)
114+
else:
115+
for ticker, data in out.items():
116+
if 'timestamp' in data:
117+
f = float(data["timestamp"])
118+
data["timestamp"] = datetime.fromtimestamp(f/1000)
119+
out[ticker] = data
120+
121+
return out

pandas_datareader/tests/test_iex.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,9 @@ def test_live_prices(self):
3939
tickers = dftickers[:5].symbol.values
4040
df = get_last_iex(tickers[:5])
4141
assert df["price"].mean() > 0
42+
43+
def test_deep(self):
44+
dob = DataReader('GS', 'iex-book')
45+
assert 'GS' in dob
46+
assert 'asks' in dob['GS']
47+
assert dob['GS']['bids'][0]['price'] > 0

0 commit comments

Comments
 (0)