cryptodatapy 0.2.7__py3-none-any.whl → 0.2.9__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.
Files changed (28) hide show
  1. cryptodatapy/extract/datarequest.py +27 -2
  2. cryptodatapy/extract/exchanges/__init__.py +2 -0
  3. cryptodatapy/extract/exchanges/dydx.py +137 -0
  4. cryptodatapy/extract/exchanges/exchange.py +439 -0
  5. cryptodatapy/extract/libraries/ccxt_api.py +684 -189
  6. cryptodatapy/extract/libraries/library.py +1 -3
  7. cryptodatapy/extract/libraries/pandasdr_api.py +151 -137
  8. cryptodatapy/extract/web/web.py +62 -0
  9. cryptodatapy/transform/convertparams.py +73 -164
  10. cryptodatapy/transform/wrangle.py +43 -23
  11. {cryptodatapy-0.2.7.dist-info → cryptodatapy-0.2.9.dist-info}/METADATA +1 -1
  12. {cryptodatapy-0.2.7.dist-info → cryptodatapy-0.2.9.dist-info}/RECORD +14 -25
  13. cryptodatapy/conf/fx_tickers.csv +0 -31
  14. cryptodatapy/extract/data_vendors/CoinMetrics.ipynb +0 -747
  15. cryptodatapy/extract/libraries/Untitled.ipynb +0 -33
  16. cryptodatapy/extract/libraries/ccxt.ipynb +0 -747
  17. cryptodatapy/extract/libraries/yfinance_api.py +0 -511
  18. cryptodatapy/transform/cc_onchain_data.csv +0 -118423
  19. cryptodatapy/transform/clean_onchain_data.ipynb +0 -4750
  20. cryptodatapy/transform/clean_perp_futures_ohlcv.ipynb +0 -2819
  21. cryptodatapy/transform/cmdty_data.ipynb +0 -402
  22. cryptodatapy/transform/credit_data.ipynb +0 -291
  23. cryptodatapy/transform/eqty_data.ipynb +0 -836
  24. cryptodatapy/transform/global_credit_data_daily.parquet +0 -0
  25. cryptodatapy/transform/rates_data.ipynb +0 -465
  26. cryptodatapy/transform/us_rates_daily.csv +0 -227752
  27. {cryptodatapy-0.2.7.dist-info → cryptodatapy-0.2.9.dist-info}/LICENSE +0 -0
  28. {cryptodatapy-0.2.7.dist-info → cryptodatapy-0.2.9.dist-info}/WHEEL +0 -0
@@ -11,7 +11,6 @@ class Library(ABC):
11
11
  Library is an abstract base class which provides a blueprint for properties and methods for the
12
12
  library subclass.
13
13
  """
14
-
15
14
  def __init__(
16
15
  self,
17
16
  categories,
@@ -400,8 +399,7 @@ class Library(ABC):
400
399
 
401
400
  @staticmethod
402
401
  @abstractmethod
403
- def wrangle_data_resp(data_req: DataRequest, data_resp: Union[Dict[str, Any], pd.DataFrame]) \
404
- -> pd.DataFrame:
402
+ def wrangle_data_resp(data_req: DataRequest, data_resp: Union[Dict[str, Any], pd.DataFrame]) -> pd.DataFrame:
405
403
  """
406
404
  Wrangles data response from data vendor API into tidy format.
407
405
  """
@@ -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
- from pandas_datareader.data import DataReader as pdr_fetch
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=None,
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=None,
30
+ market_types: List[str] = ["spot", "future"],
31
31
  fields: Optional[Dict[str, List[str]]] = None,
32
- frequencies=None,
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
- if api_key is None:
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
- def get_exchanges_info(self) -> None:
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
- def get_indexes_info(self) -> None:
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
- def get_assets_info(self) -> None:
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
- def get_markets_info(self) -> None:
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
- @staticmethod
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 data_type == "on-chain":
176
- raise ValueError(
177
- "Pandas Data Reader is a market data aggregator of market and off-chain data."
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
- # list of fields
181
- market_fields_list = ["open", "high", "low", "close", "volume", "close_adj", "er"]
182
- macro_fields_list = ["actual"]
162
+ return self.fields
183
163
 
184
- # fields dict
185
- fields = {
186
- "fx": market_fields_list,
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
- # fields obj
195
- if cat is not None:
196
- fields = fields[cat]
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 fields
184
+ return self.frequencies
199
185
 
200
- def get_rate_limit_info(self) -> None:
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 = getattr(ConvertParams(data_req), f"to_{data_req.source}")()
223
- # conv_data_req = getattr(ConvertParams(data_req), f"to_{data_req.source}")()
260
+ if self.data_req is None:
261
+ self.convert_params(data_req)
224
262
 
225
263
  try:
226
- # fetch yahoo data
227
- if data_req.source == "yahoo":
264
+ # yahoo
265
+ if self.data_req.source == "yahoo":
228
266
  # fetch yf data
229
- df = yf.download(self.data_req.source_tickers,
230
- self.data_req.source_start_date,
231
- self.data_req.source_end_date)
232
- # df = yf.download(conv_data_req["tickers"],
233
- # conv_data_req["start_date"],
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
- df = pd.DataFrame()
239
- for ticker in conv_data_req["tickers"]:
240
- df1 = pdr_fetch(ticker,
241
- data_req.source,
242
- conv_data_req["start_date"],
243
- conv_data_req["end_date"])
244
- df = pd.concat([df, df1[0]], axis=1)
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
- df = pd.DataFrame()
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=conv_data_req['ctys'],
252
- start=conv_data_req["start_date"],
253
- end=conv_data_req["end_date"])
254
- df = pd.concat([df, df1], axis=1)
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
- # fetch pdr data
289
+ # other pdr data
257
290
  else:
258
- df = pdr_fetch(conv_data_req["tickers"],
259
- data_req.source,
260
- conv_data_req["start_date"],
261
- conv_data_req["end_date"])
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: {conv_data_req['tickers']}.")
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
- return df
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
- # change to get series
310
- df = self.get_series(data_req)
311
- # wrangle data resp
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
- def check_params(self, data_req: DataRequest) -> None:
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
- # check data source
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
- # check cat
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
- # check params
357
- self.check_params(data_req)
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
- df = self.get_tidy_data(data_req)
374
+ self.data = self.get_tidy_data(self.data_req)
361
375
 
362
376
  # check if df empty
363
- if df.empty:
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 df.sort_index()
382
+ return self.data.sort_index()
@@ -46,6 +46,11 @@ class Web(ABC):
46
46
  def categories(self, categories: Union[str, List[str]]):
47
47
  """
48
48
  Sets a list of available categories for the data vendor.
49
+
50
+ Parameters
51
+ ----------
52
+ categories : Union[str, List[str]]
53
+ A string or list of strings containing the categories to filter the data vendor's data.
49
54
  """
50
55
  valid_categories = [
51
56
  None,
@@ -89,6 +94,11 @@ class Web(ABC):
89
94
  def indexes(self, indexes: Optional[Union[str, List[str], Dict[str, List[str]]]]):
90
95
  """
91
96
  Sets a list of available indexes for the web page.
97
+
98
+ Parameters
99
+ ----------
100
+ indexes : Optional[Union[str, List[str], Dict[str, List[str]]]
101
+ A string, list of strings, or dict containing the indexes to filter the web page's data.
92
102
  """
93
103
  if indexes is None or isinstance(indexes, list) or isinstance(indexes, dict):
94
104
  self._indexes = indexes
@@ -118,6 +128,11 @@ class Web(ABC):
118
128
  def assets(self, assets: Optional[Union[str, List[str], Dict[str, List[str]]]]):
119
129
  """
120
130
  Sets a list of available assets for the data vendor.
131
+
132
+ Parameters
133
+ ----------
134
+ assets : Optional[Union[str, List[str], Dict[str, List[str]]]
135
+ A string, list of strings, or dict containing the assets to filter the data vendor's data.
121
136
  """
122
137
  if assets is None or isinstance(assets, list) or isinstance(assets, dict):
123
138
  self._assets = assets
@@ -147,6 +162,11 @@ class Web(ABC):
147
162
  def markets(self, markets: Optional[Union[str, List[str], Dict[str, List[str]]]]):
148
163
  """
149
164
  Sets a list of available markets for the data vendor.
165
+
166
+ Parameters
167
+ ----------
168
+ markets : Optional[Union[str, List[str], Dict[str, List[str]]]
169
+ A string, list of strings, or dict containing the markets to filter the data vendor's data.
150
170
  """
151
171
  if markets is None or isinstance(markets, list) or isinstance(markets, dict):
152
172
  self._markets = markets
@@ -176,6 +196,11 @@ class Web(ABC):
176
196
  def market_types(self, market_types: Optional[Union[str, List[str]]]):
177
197
  """
178
198
  Sets a list of available market types for the data vendor.
199
+
200
+ Parameters
201
+ ----------
202
+ market_types : Optional[Union[str, List[str]]
203
+ A string or list of strings containing the market types to filter the data vendor's data.
179
204
  """
180
205
  valid_mkt_types, mkt_types_list = [
181
206
  None,
@@ -214,6 +239,11 @@ class Web(ABC):
214
239
  def fields(self, fields: Optional[Union[str, List[str], Dict[str, List[str]]]]):
215
240
  """
216
241
  Sets a list of available fields for the data vendor.
242
+
243
+ Parameters
244
+ ----------
245
+ fields : Optional[Union[str, List[str], Dict[str, List[str]]]
246
+ A string, list of strings, or dict containing the fields to filter the data vendor's data.
217
247
  """
218
248
  if fields is None or isinstance(fields, list) or isinstance(fields, dict):
219
249
  self._fields = fields
@@ -229,6 +259,11 @@ class Web(ABC):
229
259
  def get_fields_info(self, data_type: Optional[str]):
230
260
  """
231
261
  Gets info for available fields from the data vendor.
262
+
263
+ Parameters
264
+ ----------
265
+ data_type : Optional[str]
266
+ A string containing the data type to filter the data vendor's fields
232
267
  """
233
268
  # to be implemented by subclasses
234
269
 
@@ -245,6 +280,11 @@ class Web(ABC):
245
280
  ):
246
281
  """
247
282
  Sets a list of available data frequencies for the data vendor.
283
+
284
+ Parameters
285
+ ----------
286
+ frequencies : Optional[Union[str, List[str], Dict[str, List[str]]]
287
+ A string, list of strings, or dict containing the frequencies to filter the data vendor's data.
248
288
  """
249
289
  if (
250
290
  frequencies is None
@@ -271,6 +311,11 @@ class Web(ABC):
271
311
  def base_url(self, url: Optional[str]):
272
312
  """
273
313
  Sets the base url for the data vendor.
314
+
315
+ Parameters
316
+ ----------
317
+ url : Optional[str]
318
+ A string containing the data vendor's base URL to which endpoint paths are appended.
274
319
  """
275
320
  if url is None or isinstance(url, str):
276
321
  self._base_url = url
@@ -291,6 +336,11 @@ class Web(ABC):
291
336
  def file_formats(self, formats: Optional[Union[str, List[str]]]):
292
337
  """
293
338
  Sets the file formats for the files on the web page.
339
+
340
+ Parameters
341
+ ----------
342
+ formats : Optional[Union[str, List[str]]
343
+ A string or list of strings containing the file formats for the web page's data files.
294
344
  """
295
345
  if formats is None or isinstance(formats, list):
296
346
  self._file_formats = formats
@@ -305,6 +355,11 @@ class Web(ABC):
305
355
  def get_data(self, data_req: DataRequest) -> pd.DataFrame:
306
356
  """
307
357
  Submits get data request to API.
358
+
359
+ Parameters
360
+ ----------
361
+ data_req : DataRequest
362
+ A DataRequest object containing the request parameters.
308
363
  """
309
364
  # to be implemented by subclasses
310
365
 
@@ -313,5 +368,12 @@ class Web(ABC):
313
368
  def wrangle_data_resp(data_req: DataRequest, data_resp: Union[Dict[str, Any], pd.DataFrame]) -> pd.DataFrame:
314
369
  """
315
370
  Wrangles data response from data vendor API into tidy format.
371
+
372
+ Parameters
373
+ ----------
374
+ data_req : DataRequest
375
+ A DataRequest object containing the request parameters.
376
+ data_resp : Union[Dict[str, Any], pd.DataFrame]
377
+ A dictionary or DataFrame containing the data response from the data vendor API.
316
378
  """
317
379
  # to be implemented by subclasses