cryptodatapy 0.2.7__py3-none-any.whl → 0.2.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cryptodatapy/extract/datarequest.py +27 -1
- cryptodatapy/extract/libraries/Untitled.ipynb +168 -2
- cryptodatapy/extract/libraries/ccxt_api.py +17 -17
- cryptodatapy/extract/libraries/pandasdr_api.py +151 -137
- cryptodatapy/transform/convertparams.py +73 -164
- cryptodatapy/transform/wrangle.py +43 -23
- {cryptodatapy-0.2.7.dist-info → cryptodatapy-0.2.8.dist-info}/METADATA +1 -1
- {cryptodatapy-0.2.7.dist-info → cryptodatapy-0.2.8.dist-info}/RECORD +10 -10
- {cryptodatapy-0.2.7.dist-info → cryptodatapy-0.2.8.dist-info}/LICENSE +0 -0
- {cryptodatapy-0.2.7.dist-info → cryptodatapy-0.2.8.dist-info}/WHEEL +0 -0
@@ -735,7 +735,7 @@ class CCXT(Library):
|
|
735
735
|
|
736
736
|
return data
|
737
737
|
|
738
|
-
def convert_params(self, data_req: DataRequest) ->
|
738
|
+
def convert_params(self, data_req: DataRequest) -> DataRequest:
|
739
739
|
"""
|
740
740
|
Converts data request parameters to CCXT format.
|
741
741
|
|
@@ -746,8 +746,8 @@ class CCXT(Library):
|
|
746
746
|
|
747
747
|
Returns
|
748
748
|
-------
|
749
|
-
|
750
|
-
|
749
|
+
data_req: DataRequest
|
750
|
+
Parameters of data request in CCXT format.
|
751
751
|
"""
|
752
752
|
self.data_req = ConvertParams(data_req).to_ccxt()
|
753
753
|
|
@@ -775,19 +775,19 @@ class CCXT(Library):
|
|
775
775
|
f"Use the '.assets' attribute to check supported currencies."
|
776
776
|
)
|
777
777
|
|
778
|
-
# mkt type
|
778
|
+
# check mkt type
|
779
779
|
if self.data_req.mkt_type not in self.market_types:
|
780
780
|
raise ValueError(
|
781
781
|
f"{self.data_req.mkt_type} is not available for {self.data_req.exch}."
|
782
782
|
)
|
783
783
|
|
784
|
-
# start date
|
784
|
+
# check start date
|
785
785
|
if not isinstance(self.data_req.source_start_date, int):
|
786
786
|
raise ValueError(
|
787
787
|
f"Start date must be in integers in milliseconds since Unix epoch."
|
788
788
|
)
|
789
789
|
|
790
|
-
# end date
|
790
|
+
# check end date
|
791
791
|
if not isinstance(self.data_req.source_end_date, int):
|
792
792
|
raise ValueError(
|
793
793
|
f"End date must be in integers in milliseconds since Unix epoch."
|
@@ -834,15 +834,12 @@ class CCXT(Library):
|
|
834
834
|
|
835
835
|
return self.data_req
|
836
836
|
|
837
|
-
|
838
|
-
def wrangle_data_resp(data_req: DataRequest, data_resp: pd.DataFrame, data_type: str) -> pd.DataFrame:
|
837
|
+
def wrangle_data_resp(self, data_resp: pd.DataFrame, data_type: str) -> pd.DataFrame:
|
839
838
|
"""
|
840
839
|
Wrangle data response.
|
841
840
|
|
842
841
|
Parameters
|
843
842
|
----------
|
844
|
-
data_req: DataRequest
|
845
|
-
Parameters of data request in CryptoDataPy format.
|
846
843
|
data_resp: pd.DataFrame
|
847
844
|
Data response from GET request.
|
848
845
|
data_type: str
|
@@ -854,7 +851,7 @@ class CCXT(Library):
|
|
854
851
|
Wrangled dataframe with DatetimeIndex and values in tidy format.
|
855
852
|
"""
|
856
853
|
|
857
|
-
return WrangleData(data_req, data_resp).ccxt(data_type=data_type)
|
854
|
+
return WrangleData(self.data_req, data_resp).ccxt(data_type=data_type)
|
858
855
|
|
859
856
|
async def fetch_tidy_ohlcv(self, data_req: DataRequest) -> pd.DataFrame:
|
860
857
|
"""
|
@@ -871,7 +868,8 @@ class CCXT(Library):
|
|
871
868
|
Dataframe with entire OHLCV data history retrieved and wrangled into tidy data format.
|
872
869
|
"""
|
873
870
|
# convert data request parameters to CCXT format
|
874
|
-
self.
|
871
|
+
if self.data_req is None:
|
872
|
+
self.convert_params(data_req)
|
875
873
|
|
876
874
|
# get entire data history
|
877
875
|
data_resp = await self.fetch_all_ohlcv(self.data_req.source_markets,
|
@@ -884,7 +882,7 @@ class CCXT(Library):
|
|
884
882
|
|
885
883
|
# wrangle df
|
886
884
|
if any(data_resp):
|
887
|
-
df = self.wrangle_data_resp(
|
885
|
+
df = self.wrangle_data_resp(data_resp, data_type='ohlcv')
|
888
886
|
return df
|
889
887
|
else:
|
890
888
|
logging.warning("Failed to get requested OHLCV data.")
|
@@ -904,7 +902,8 @@ class CCXT(Library):
|
|
904
902
|
Dataframe with entire data history retrieved and wrangled into tidy data format.
|
905
903
|
"""
|
906
904
|
# convert data request parameters to CCXT format
|
907
|
-
self.
|
905
|
+
if self.data_req is None:
|
906
|
+
self.convert_params(data_req)
|
908
907
|
|
909
908
|
# get entire data history
|
910
909
|
data_resp = await self.fetch_all_funding_rates(self.data_req.source_markets,
|
@@ -916,7 +915,7 @@ class CCXT(Library):
|
|
916
915
|
|
917
916
|
# wrangle df
|
918
917
|
if any(data_resp):
|
919
|
-
df = self.wrangle_data_resp(
|
918
|
+
df = self.wrangle_data_resp(data_resp, data_type='funding_rates')
|
920
919
|
return df
|
921
920
|
else:
|
922
921
|
logging.warning("Failed to get requested funding rates.")
|
@@ -936,7 +935,8 @@ class CCXT(Library):
|
|
936
935
|
Dataframe with entire data history retrieved and wrangled into tidy data format.
|
937
936
|
"""
|
938
937
|
# convert data request parameters to CCXT format
|
939
|
-
self.
|
938
|
+
if self.data_req is None:
|
939
|
+
self.convert_params(data_req)
|
940
940
|
|
941
941
|
# get entire data history
|
942
942
|
data_resp = await self.fetch_all_open_interest(self.data_req.source_markets,
|
@@ -949,7 +949,7 @@ class CCXT(Library):
|
|
949
949
|
|
950
950
|
# wrangle df
|
951
951
|
if any(data_resp):
|
952
|
-
df = self.wrangle_data_resp(
|
952
|
+
df = self.wrangle_data_resp(data_resp, data_type='open_interest')
|
953
953
|
return df
|
954
954
|
else:
|
955
955
|
logging.warning("Failed to get requested open interest.")
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import logging
|
2
|
-
from typing import Any, Dict, List, Optional
|
2
|
+
from typing import Any, Dict, List, Optional, Union
|
3
3
|
|
4
4
|
import pandas as pd
|
5
5
|
import yfinance as yf
|
6
|
-
|
6
|
+
import pandas_datareader.data as web
|
7
7
|
from pandas_datareader import wb
|
8
8
|
|
9
9
|
from cryptodatapy.extract.datarequest import DataRequest
|
@@ -22,16 +22,16 @@ class PandasDataReader(Library):
|
|
22
22
|
"""
|
23
23
|
def __init__(
|
24
24
|
self,
|
25
|
-
categories=
|
25
|
+
categories: Union[str, List[str]] = ["fx", "rates", "eqty", "cmdty", "credit", "macro"],
|
26
26
|
exchanges: Optional[List[str]] = None,
|
27
27
|
indexes: Optional[Dict[str, List[str]]] = None,
|
28
28
|
assets: Optional[Dict[str, List[str]]] = None,
|
29
29
|
markets: Optional[Dict[str, List[str]]] = None,
|
30
|
-
market_types=
|
30
|
+
market_types: List[str] = ["spot", "future"],
|
31
31
|
fields: Optional[Dict[str, List[str]]] = None,
|
32
|
-
frequencies=
|
32
|
+
frequencies: Optional[Dict[str, List[str]]] = ["d", "w", "m", "q", "y"],
|
33
33
|
base_url: Optional[str] = None,
|
34
|
-
api_key=None,
|
34
|
+
api_key: Optional[str] = None,
|
35
35
|
max_obs_per_call: Optional[int] = None,
|
36
36
|
rate_limit: Optional[Any] = None,
|
37
37
|
):
|
@@ -88,29 +88,8 @@ class PandasDataReader(Library):
|
|
88
88
|
max_obs_per_call,
|
89
89
|
rate_limit,
|
90
90
|
)
|
91
|
-
|
92
|
-
|
93
|
-
self.api_key = {
|
94
|
-
"fred": None,
|
95
|
-
"yahoo": None,
|
96
|
-
"fama_french": None
|
97
|
-
}
|
98
|
-
if frequencies is None:
|
99
|
-
self.frequencies = {
|
100
|
-
"crypto": ["d", "w", "m", "q", "y"],
|
101
|
-
"fx": ["d", "w", "m", "q", "y"],
|
102
|
-
"rates": ["d", "w", "m", "q", "y"],
|
103
|
-
"cmdty": ["d", "w", "m", "q", "y"],
|
104
|
-
"eqty": ["d", "w", "m", "q", "y"],
|
105
|
-
"credit": ["d", "w", "m", "q", "y"],
|
106
|
-
"macro": ["d", "w", "m", "q", "y"],
|
107
|
-
}
|
108
|
-
if market_types is None:
|
109
|
-
self.market_types = ["spot"]
|
110
|
-
if categories is None:
|
111
|
-
self.categories = ["fx", "rates", "eqty", "cmdty", "credit", "macro"]
|
112
|
-
if fields is None:
|
113
|
-
self.fields = self.get_fields_info()
|
91
|
+
self.data_req = None
|
92
|
+
self.data = pd.DataFrame()
|
114
93
|
|
115
94
|
@staticmethod
|
116
95
|
def get_vendors_info():
|
@@ -121,7 +100,8 @@ class PandasDataReader(Library):
|
|
121
100
|
f"See providers page to find available vendors: {data_cred.pdr_vendors_url} "
|
122
101
|
)
|
123
102
|
|
124
|
-
|
103
|
+
@staticmethod
|
104
|
+
def get_exchanges_info() -> None:
|
125
105
|
"""
|
126
106
|
Get exchanges info.
|
127
107
|
"""
|
@@ -129,7 +109,8 @@ class PandasDataReader(Library):
|
|
129
109
|
f"See specific data vendor for available exchanges: {data_cred.pdr_vendors_url}"
|
130
110
|
)
|
131
111
|
|
132
|
-
|
112
|
+
@staticmethod
|
113
|
+
def get_indexes_info() -> None:
|
133
114
|
"""
|
134
115
|
Get indexes info.
|
135
116
|
"""
|
@@ -137,7 +118,8 @@ class PandasDataReader(Library):
|
|
137
118
|
f"See specific data vendor for available indexes: {data_cred.pdr_vendors_url}"
|
138
119
|
)
|
139
120
|
|
140
|
-
|
121
|
+
@staticmethod
|
122
|
+
def get_assets_info() -> None:
|
141
123
|
"""
|
142
124
|
Get assets info.
|
143
125
|
"""
|
@@ -145,7 +127,8 @@ class PandasDataReader(Library):
|
|
145
127
|
f"See specific data vendor for available assets: {data_cred.pdr_vendors_url} "
|
146
128
|
)
|
147
129
|
|
148
|
-
|
130
|
+
@staticmethod
|
131
|
+
def get_markets_info() -> None:
|
149
132
|
"""
|
150
133
|
Get markets info.
|
151
134
|
"""
|
@@ -153,56 +136,111 @@ class PandasDataReader(Library):
|
|
153
136
|
f"See specific data vendor for available markets: {data_cred.pdr_vendors_url}"
|
154
137
|
)
|
155
138
|
|
156
|
-
|
157
|
-
def get_fields_info(
|
158
|
-
data_type: Optional[str] = "market", cat: Optional[str] = None
|
159
|
-
) -> Dict[str, List[str]]:
|
139
|
+
def get_fields_info(self) -> Dict[str, List[str]]:
|
160
140
|
"""
|
161
141
|
Get fields info.
|
162
142
|
|
163
|
-
Parameters
|
164
|
-
----------
|
165
|
-
data_type: str, {'market', 'on-chain', 'off-chain'}, default 'market'
|
166
|
-
Type of data.
|
167
|
-
cat: str, {'crypto', 'eqty', 'fx', 'rates', 'cmdty', 'macro'}, optional, default None
|
168
|
-
Asset class or time series category.
|
169
|
-
|
170
143
|
Returns
|
171
144
|
-------
|
172
145
|
fields: dictionary
|
173
146
|
Dictionary with info on available fields, by category.
|
174
147
|
"""
|
175
|
-
if
|
176
|
-
|
177
|
-
"
|
178
|
-
|
148
|
+
if self.fields is None:
|
149
|
+
self.fields = {
|
150
|
+
"fx": ["open", "high", "low", "close", "volume", "close_adj", "er"],
|
151
|
+
"rates": ["open", "high", "low", "close", "volume", "close_adj", "er"],
|
152
|
+
"eqty": ["open", "high", "low", "close", "volume", "close_adj", "er"],
|
153
|
+
"cmdty": ["open", "high", "low", "close", "volume", "close_adj", "er"],
|
154
|
+
"credit": ["open", "high", "low", "close", "volume", "close_adj", "er"],
|
155
|
+
"macro": ["actual"],
|
156
|
+
}
|
157
|
+
|
158
|
+
# fields cat
|
159
|
+
if self.data_req is not None:
|
160
|
+
self.fields = self.fields[self.data_req.cat]
|
179
161
|
|
180
|
-
|
181
|
-
market_fields_list = ["open", "high", "low", "close", "volume", "close_adj", "er"]
|
182
|
-
macro_fields_list = ["actual"]
|
162
|
+
return self.fields
|
183
163
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
"rates": market_fields_list,
|
188
|
-
"eqty": market_fields_list,
|
189
|
-
"cmdty": market_fields_list,
|
190
|
-
"credit": market_fields_list,
|
191
|
-
"macro": macro_fields_list,
|
192
|
-
}
|
164
|
+
def get_frequencies_info(self) -> Dict[str, Union[str, int]]:
|
165
|
+
"""
|
166
|
+
Get frequencies info.
|
193
167
|
|
194
|
-
|
195
|
-
|
196
|
-
|
168
|
+
Returns
|
169
|
+
-------
|
170
|
+
freq: dictionary
|
171
|
+
Dictionary with info on available frequencies.
|
172
|
+
"""
|
173
|
+
if self.frequencies is None:
|
174
|
+
self.frequencies = {
|
175
|
+
"crypto": ["d", "w", "m", "q", "y"],
|
176
|
+
"fx": ["d", "w", "m", "q", "y"],
|
177
|
+
"rates": ["d", "w", "m", "q", "y"],
|
178
|
+
"cmdty": ["d", "w", "m", "q", "y"],
|
179
|
+
"eqty": ["d", "w", "m", "q", "y"],
|
180
|
+
"credit": ["d", "w", "m", "q", "y"],
|
181
|
+
"macro": ["d", "w", "m", "q", "y"],
|
182
|
+
}
|
197
183
|
|
198
|
-
return
|
184
|
+
return self.frequencies
|
199
185
|
|
200
|
-
|
186
|
+
@staticmethod
|
187
|
+
def get_rate_limit_info() -> None:
|
201
188
|
"""
|
202
189
|
Get rate limit info.
|
203
190
|
"""
|
204
191
|
print(f"See specific data vendor for rate limits: {data_cred.pdr_vendors_url}")
|
205
192
|
|
193
|
+
def convert_params(self, data_req: DataRequest) -> DataRequest:
|
194
|
+
"""
|
195
|
+
Converts data request parameters to source format.
|
196
|
+
|
197
|
+
Parameters
|
198
|
+
----------
|
199
|
+
data_req: DataRequest
|
200
|
+
Parameters of data request in CryptoDataPy format.
|
201
|
+
|
202
|
+
Returns
|
203
|
+
-------
|
204
|
+
data_req: DataRequest
|
205
|
+
Parameters of data request in source format.
|
206
|
+
"""
|
207
|
+
# convert params to source format
|
208
|
+
if self.data_req is None:
|
209
|
+
self.data_req = getattr(ConvertParams(data_req), f"to_{data_req.source}")()
|
210
|
+
|
211
|
+
# check cat
|
212
|
+
if self.data_req.cat not in self.categories:
|
213
|
+
raise ValueError(
|
214
|
+
f"Select a valid category. Valid categories are: {self.categories}."
|
215
|
+
)
|
216
|
+
|
217
|
+
# check tickers
|
218
|
+
if not self.data_req.source_tickers:
|
219
|
+
raise ValueError("No tickers provided for data request.")
|
220
|
+
|
221
|
+
# check freq
|
222
|
+
if self.data_req.source_freq not in self.frequencies:
|
223
|
+
raise ValueError(
|
224
|
+
f"{self.data_req.source_freq} frequency is not available. "
|
225
|
+
f"Use the '.frequencies' attribute to check available frequencies."
|
226
|
+
)
|
227
|
+
|
228
|
+
# mkt type
|
229
|
+
if self.data_req.mkt_type not in self.market_types:
|
230
|
+
raise ValueError(
|
231
|
+
f"{self.data_req.mkt_type} is not available for {self.data_req.exch}."
|
232
|
+
)
|
233
|
+
|
234
|
+
# check fields
|
235
|
+
if self.fields is None:
|
236
|
+
self.get_fields_info()
|
237
|
+
if not any(field in self.fields for field in self.data_req.fields):
|
238
|
+
raise ValueError(
|
239
|
+
f"{self.data_req.fields} fields are not available for {self.data_req.cat}."
|
240
|
+
)
|
241
|
+
|
242
|
+
return self.data_req
|
243
|
+
|
206
244
|
def get_series(self, data_req: DataRequest) -> pd.DataFrame:
|
207
245
|
"""
|
208
246
|
Gets series from python client.
|
@@ -219,57 +257,50 @@ class PandasDataReader(Library):
|
|
219
257
|
|
220
258
|
"""
|
221
259
|
# convert params to source format
|
222
|
-
self.data_req
|
223
|
-
|
260
|
+
if self.data_req is None:
|
261
|
+
self.convert_params(data_req)
|
224
262
|
|
225
263
|
try:
|
226
|
-
#
|
227
|
-
if data_req.source == "yahoo":
|
264
|
+
# yahoo
|
265
|
+
if self.data_req.source == "yahoo":
|
228
266
|
# fetch yf data
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
# conv_data_req["end_date"])
|
235
|
-
|
236
|
-
# fetch fama-french data
|
267
|
+
self.data = yf.download(self.data_req.source_tickers,
|
268
|
+
self.data_req.source_start_date,
|
269
|
+
self.data_req.source_end_date)
|
270
|
+
|
271
|
+
# fama-french
|
237
272
|
elif data_req.source == "famafrench":
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
# featch wb data
|
273
|
+
for ticker in self.data_req.source_tickers:
|
274
|
+
df1 = web.DataReader(ticker,
|
275
|
+
self.data_req.source,
|
276
|
+
self.data_req.source_start_date,
|
277
|
+
self.data_req.source_end_date)
|
278
|
+
self.data = pd.concat([self.data, df1[0]], axis=1)
|
279
|
+
|
280
|
+
# world bank
|
247
281
|
elif data_req.source == "wb":
|
248
|
-
|
249
|
-
for ticker in conv_data_req["tickers"]:
|
282
|
+
for ticker in self.data_req.source_tickers:
|
250
283
|
df1 = wb.download(indicator=ticker,
|
251
|
-
country=
|
252
|
-
start=
|
253
|
-
end=
|
254
|
-
|
284
|
+
country=self.data_req.countries,
|
285
|
+
start=self.data_req.source_start_date,
|
286
|
+
end=self.data_req.source_end_date)
|
287
|
+
self.data = pd.concat([self.data, df1], axis=1)
|
255
288
|
|
256
|
-
#
|
289
|
+
# other pdr data
|
257
290
|
else:
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
291
|
+
self.data = web.DataReader(self.data_req.source_tickers,
|
292
|
+
self.data_req.source,
|
293
|
+
self.data_req.source_start_date,
|
294
|
+
self.data_req.source_end_date)
|
262
295
|
|
263
296
|
except Exception as e:
|
264
297
|
logging.warning(e)
|
265
|
-
logging.warning(f"Failed to get data for: {
|
298
|
+
logging.warning(f"Failed to get data for source tickers: {self.data_req.source_tickers}.")
|
266
299
|
|
267
300
|
else:
|
301
|
+
return self.data
|
268
302
|
|
269
|
-
|
270
|
-
|
271
|
-
@staticmethod
|
272
|
-
def wrangle_data_resp(data_req: DataRequest, data_resp: pd.DataFrame) -> pd.DataFrame:
|
303
|
+
def wrangle_data_resp(self, data_req: DataRequest, data_resp: pd.DataFrame) -> pd.DataFrame:
|
273
304
|
"""
|
274
305
|
Wrangle data response.
|
275
306
|
|
@@ -286,8 +317,11 @@ class PandasDataReader(Library):
|
|
286
317
|
Wrangled dataframe with DatetimeIndex (level 0), ticker (level 1), and values for market or macro series
|
287
318
|
for selected fields (cols), in tidy format.
|
288
319
|
"""
|
320
|
+
if self.data_req is None:
|
321
|
+
self.convert_params(data_req)
|
322
|
+
|
289
323
|
# wrangle data resp
|
290
|
-
df = getattr(WrangleData(data_req, data_resp), data_req.source)()
|
324
|
+
df = getattr(WrangleData(self.data_req, data_resp), self.data_req.source)()
|
291
325
|
|
292
326
|
return df
|
293
327
|
|
@@ -306,38 +340,17 @@ class PandasDataReader(Library):
|
|
306
340
|
Dataframe with DatetimeIndex (level 0), tickers (level 1) and actual values (cols),
|
307
341
|
in tidy data format.
|
308
342
|
"""
|
309
|
-
#
|
310
|
-
|
311
|
-
|
312
|
-
df = self.wrangle_data_resp(data_req, df)
|
313
|
-
|
314
|
-
return df
|
343
|
+
# convert params to source format
|
344
|
+
if self.data_req is None:
|
345
|
+
self.convert_params(data_req)
|
315
346
|
|
316
|
-
|
317
|
-
|
318
|
-
Checks the data request parameters before requesting data to reduce API calls
|
319
|
-
and improve efficiency.
|
347
|
+
# get series
|
348
|
+
data_resp = self.get_series(self.data_req)
|
320
349
|
|
321
|
-
|
322
|
-
|
323
|
-
if data_req.source not in ['fred', 'yahoo', 'famafrench', 'wb']:
|
324
|
-
raise ValueError(
|
325
|
-
"Select a Pandas-datareader supported data source for the data request."
|
326
|
-
)
|
350
|
+
# wrangle data resp
|
351
|
+
df = self.wrangle_data_resp(self.data_req, data_resp)
|
327
352
|
|
328
|
-
|
329
|
-
if data_req.cat not in self.categories:
|
330
|
-
raise ValueError(
|
331
|
-
f"Select a valid category. Valid categories are: {self.categories}."
|
332
|
-
)
|
333
|
-
# check freq
|
334
|
-
if data_req.freq not in self.frequencies[data_req.cat]:
|
335
|
-
raise ValueError(
|
336
|
-
f"Invalid data frequency. Valid data frequencies are: {self.frequencies}."
|
337
|
-
)
|
338
|
-
# check fields
|
339
|
-
if not any(field in self.fields[data_req.cat] for field in data_req.fields):
|
340
|
-
raise ValueError(f"Invalid fields. Valid data fields are: {self.fields}.")
|
353
|
+
return df
|
341
354
|
|
342
355
|
def get_data(self, data_req: DataRequest) -> pd.DataFrame:
|
343
356
|
"""
|
@@ -353,16 +366,17 @@ class PandasDataReader(Library):
|
|
353
366
|
DataFrame with DatetimeIndex (level 0), ticker (level 1), and values for selected fields (cols),
|
354
367
|
in tidy format.
|
355
368
|
"""
|
356
|
-
#
|
357
|
-
self.
|
369
|
+
# convert params to source format
|
370
|
+
if self.data_req is None:
|
371
|
+
self.convert_params(data_req)
|
358
372
|
|
359
373
|
# get tidy data
|
360
|
-
|
374
|
+
self.data = self.get_tidy_data(self.data_req)
|
361
375
|
|
362
376
|
# check if df empty
|
363
|
-
if
|
377
|
+
if self.data.empty:
|
364
378
|
raise Exception(
|
365
379
|
"No data returned. Check data request parameters and try again."
|
366
380
|
)
|
367
381
|
|
368
|
-
return
|
382
|
+
return self.data.sort_index()
|