1from datetime import datetime, timedelta
2
3import pandas as pd
4
5from pandas_datareader.exceptions import UnstableAPIWarning
6from pandas_datareader.iex import IEX
7
8# Data provided for free by IEX
9# Data is furnished in compliance with the guidelines promulgated in the IEX
10# API terms of service and manual
11# See https://iextrading.com/api-exhibit-a/ for additional information
12# and conditions of use
13
14
15class DailySummaryReader(IEX):
16    """
17    Daily statistics from IEX for a day or month
18    """
19
20    def __init__(
21        self, symbols=None, start=None, end=None, retry_count=3, pause=0.1, session=None
22    ):
23        import warnings
24
25        warnings.warn(
26            "Daily statistics is not working due to issues with the " "IEX API",
27            UnstableAPIWarning,
28        )
29        self.curr_date = start
30        super(DailySummaryReader, self).__init__(
31            symbols=symbols,
32            start=start,
33            end=end,
34            retry_count=retry_count,
35            pause=pause,
36            session=session,
37        )
38
39    @property
40    def service(self):
41        """Service endpoint"""
42        return "stats/historical/daily"
43
44    def _get_params(self, symbols):
45        p = {}
46
47        if self.curr_date is not None:
48            p["date"] = self.curr_date.strftime("%Y%m%d")
49
50        return p
51
52    def read(self):
53        """Unfortunately, IEX's API can only retrieve data one day or one month
54        at a time. Rather than specifying a date range, we will have to run
55        the read function for each date provided.
56
57        :return: DataFrame
58        """
59        tlen = self.end - self.start
60        dfs = []
61        for date in (self.start + timedelta(n) for n in range(tlen.days)):
62            self.curr_date = date
63            tdf = super(DailySummaryReader, self).read()
64            dfs.append(tdf)
65        return pd.concat(dfs)
66
67
68class MonthlySummaryReader(IEX):
69    """Monthly statistics from IEX"""
70
71    def __init__(
72        self, symbols=None, start=None, end=None, retry_count=3, pause=0.1, session=None
73    ):
74        self.curr_date = start
75        self.date_format = "%Y%m"
76
77        super(MonthlySummaryReader, self).__init__(
78            symbols=symbols,
79            start=start,
80            end=end,
81            retry_count=retry_count,
82            pause=pause,
83            session=session,
84        )
85
86    @property
87    def service(self):
88        """Service endpoint"""
89        return "stats/historical"
90
91    def _get_params(self, symbols):
92        p = {}
93
94        if self.curr_date is not None:
95            p["date"] = self.curr_date.strftime(self.date_format)
96
97        return p
98
99    def read(self):
100        """Unfortunately, IEX's API can only retrieve data one day or one month
101         at a time. Rather than specifying a date range, we will have to run
102         the read function for each date provided.
103
104        :return: DataFrame
105        """
106        tlen = self.end - self.start
107        dfs = []
108
109        # Build list of all dates within the given range
110        lrange = [x for x in (self.start + timedelta(n) for n in range(tlen.days))]
111
112        mrange = []
113        for dt in lrange:
114            if datetime(dt.year, dt.month, 1) not in mrange:
115                mrange.append(datetime(dt.year, dt.month, 1))
116        lrange = mrange
117
118        for date in lrange:
119            self.curr_date = date
120            tdf = super(MonthlySummaryReader, self).read()
121
122            # We may not return data if this was a weekend/holiday:
123            if not tdf.empty:
124                tdf["date"] = date.strftime(self.date_format)
125                dfs.append(tdf)
126
127        # We may not return any data if we failed to specify useful parameters:
128        return pd.concat(dfs) if len(dfs) > 0 else pd.DataFrame()
129
130
131class RecordsReader(IEX):
132    """
133    Total matched volume information from IEX
134    """
135
136    def __init__(
137        self, symbols=None, start=None, end=None, retry_count=3, pause=0.1, session=None
138    ):
139        super(RecordsReader, self).__init__(
140            symbols=symbols,
141            start=start,
142            end=end,
143            retry_count=retry_count,
144            pause=pause,
145            session=session,
146        )
147
148    @property
149    def service(self):
150        """Service endpoint"""
151        return "stats/records"
152
153    def _get_params(self, symbols):
154        # Record Stats API does not take any parameters, returning empty dict
155        return {}
156
157
158class RecentReader(IEX):
159    """Recent trading volume from IEX
160
161    Notes
162    -----
163    Returns 6 fields for each day:
164
165      * date: refers to the trading day.
166      * volume: refers to executions received from order routed to away
167        trading centers.
168      * routedVolume: refers to single counted shares matched from executions
169        on IEX.
170      * marketShare: refers to IEX's percentage of total US Equity market
171        volume.
172      * isHalfday: will be true if the trading day is a half day.
173      * litVolume: refers to the number of lit shares traded on IEX
174        (single-counted).
175    """
176
177    def __init__(
178        self, symbols=None, start=None, end=None, retry_count=3, pause=0.1, session=None
179    ):
180        super(RecentReader, self).__init__(
181            symbols=symbols,
182            start=start,
183            end=end,
184            retry_count=retry_count,
185            pause=pause,
186            session=session,
187        )
188
189    @property
190    def service(self):
191        """Service endpoint"""
192        return "stats/recent"
193
194    def _get_params(self, symbols):
195        # Record Stats API does not take any parameters, returning empty dict
196        return {}
197