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.
@@ -735,7 +735,7 @@ class CCXT(Library):
735
735
 
736
736
  return data
737
737
 
738
- def convert_params(self, data_req: DataRequest) -> Dict[str, Any]:
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
- cx_data_req: dict
750
- Data request parameters in CCXT format.
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
- @staticmethod
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.convert_params(data_req)
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(data_req, data_resp, data_type='ohlcv')
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.convert_params(data_req)
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(data_req, data_resp, data_type='funding_rates')
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.convert_params(data_req)
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(data_req, data_resp, data_type='open_interest')
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
- 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()