cryptodatapy 0.2.4__py3-none-any.whl → 0.2.6__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 (35) hide show
  1. cryptodatapy/conf/tickers.csv +0 -1
  2. cryptodatapy/extract/data_vendors/CoinMetrics.ipynb +747 -0
  3. cryptodatapy/extract/data_vendors/coinmetrics_api.py +279 -209
  4. cryptodatapy/extract/data_vendors/cryptocompare_api.py +3 -5
  5. cryptodatapy/extract/data_vendors/datavendor.py +32 -12
  6. cryptodatapy/extract/data_vendors/glassnode_api.py +3 -2
  7. cryptodatapy/extract/data_vendors/tiingo_api.py +3 -2
  8. cryptodatapy/extract/datarequest.py +55 -9
  9. cryptodatapy/extract/libraries/ccxt_api.py +51 -133
  10. cryptodatapy/transform/cc_onchain_data.csv +118423 -0
  11. cryptodatapy/transform/clean.py +17 -15
  12. cryptodatapy/transform/clean_onchain_data.ipynb +4750 -0
  13. cryptodatapy/transform/clean_perp_futures_ohlcv.ipynb +1874 -276
  14. cryptodatapy/transform/convertparams.py +28 -19
  15. cryptodatapy/transform/credit_data.ipynb +291 -0
  16. cryptodatapy/transform/eqty_data.ipynb +809 -0
  17. cryptodatapy/transform/filter.py +13 -11
  18. cryptodatapy/transform/global_credit_data_daily.parquet +0 -0
  19. cryptodatapy/transform/od.py +1 -0
  20. cryptodatapy/transform/rates_data.ipynb +465 -0
  21. cryptodatapy/transform/us_rates_daily.csv +227752 -0
  22. cryptodatapy/util/datacredentials.py +28 -7
  23. {cryptodatapy-0.2.4.dist-info → cryptodatapy-0.2.6.dist-info}/METADATA +2 -2
  24. {cryptodatapy-0.2.4.dist-info → cryptodatapy-0.2.6.dist-info}/RECORD +26 -27
  25. cryptodatapy/.DS_Store +0 -0
  26. cryptodatapy/.idea/.gitignore +0 -3
  27. cryptodatapy/.idea/cryptodatapy.iml +0 -12
  28. cryptodatapy/.idea/csv-plugin.xml +0 -16
  29. cryptodatapy/.idea/inspectionProfiles/Project_Default.xml +0 -6
  30. cryptodatapy/.idea/inspectionProfiles/profiles_settings.xml +0 -6
  31. cryptodatapy/.idea/misc.xml +0 -4
  32. cryptodatapy/.idea/modules.xml +0 -8
  33. cryptodatapy/.idea/vcs.xml +0 -6
  34. {cryptodatapy-0.2.4.dist-info → cryptodatapy-0.2.6.dist-info}/LICENSE +0 -0
  35. {cryptodatapy-0.2.4.dist-info → cryptodatapy-0.2.6.dist-info}/WHEEL +0 -0
@@ -84,8 +84,9 @@ class CryptoCompare(DataVendor):
84
84
  if categories is None:
85
85
  self.categories = ['crypto']
86
86
  if api_key is None:
87
- raise TypeError("Set your api key. We recommend setting your api key in environment variables as"
88
- "'CRYPTOCOMPARE_API_KEY', will allow DataCredentials to automatically load it.")
87
+ raise TypeError("Set your CryptoCompare api key in environment variables as 'CRYPTOCOMPARE_API_KEY' or "
88
+ "add it as an argument when instantiating the class. To get an api key, visit: "
89
+ "https://min-api.cryptocompare.com/")
89
90
  if exchanges is None:
90
91
  self.exchanges = self.get_exchanges_info(as_list=True)
91
92
  if indexes is None:
@@ -467,9 +468,6 @@ class CryptoCompare(DataVendor):
467
468
  urls_params = self.set_urls_params(data_req, data_type, ticker)
468
469
  url, params = urls_params['url'], urls_params['params']
469
470
 
470
- # # data req
471
- # data_resp = DataRequest().get_req(url=url, params=params)
472
-
473
471
  # data req
474
472
  data_resp = DataRequest().get_req(url=url, params=params)
475
473
 
@@ -92,7 +92,7 @@ class DataVendor(ABC):
92
92
 
93
93
  @exchanges.setter
94
94
  def exchanges(
95
- self, exchanges: Optional[Union[str, List[str], Dict[str, List[str]]]]
95
+ self, exchanges: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]
96
96
  ):
97
97
  """
98
98
  Sets a list of available exchanges for the data vendor.
@@ -101,6 +101,7 @@ class DataVendor(ABC):
101
101
  exchanges is None
102
102
  or isinstance(exchanges, list)
103
103
  or isinstance(exchanges, dict)
104
+ or isinstance(exchanges, pd.DataFrame)
104
105
  ):
105
106
  self._exchanges = exchanges
106
107
  elif isinstance(exchanges, str):
@@ -126,11 +127,16 @@ class DataVendor(ABC):
126
127
  return self._indexes
127
128
 
128
129
  @indexes.setter
129
- def indexes(self, indexes: Optional[Union[str, List[str], Dict[str, List[str]]]]):
130
+ def indexes(self, indexes: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]):
130
131
  """
131
132
  Sets a list of available indexes for the data vendor.
132
133
  """
133
- if indexes is None or isinstance(indexes, list) or isinstance(indexes, dict):
134
+ if (
135
+ indexes is None
136
+ or isinstance(indexes, list)
137
+ or isinstance(indexes, dict)
138
+ or isinstance(indexes, pd.DataFrame)
139
+ ):
134
140
  self._indexes = indexes
135
141
  elif isinstance(indexes, str):
136
142
  self._indexes = [indexes]
@@ -155,11 +161,16 @@ class DataVendor(ABC):
155
161
  return self._assets
156
162
 
157
163
  @assets.setter
158
- def assets(self, assets: Optional[Union[str, List[str], Dict[str, List[str]]]]):
164
+ def assets(self, assets: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]):
159
165
  """
160
166
  Sets a list of available assets for the data vendor.
161
167
  """
162
- if assets is None or isinstance(assets, list) or isinstance(assets, dict):
168
+ if (
169
+ assets is None
170
+ or isinstance(assets, list)
171
+ or isinstance(assets, dict)
172
+ or isinstance(assets, pd.DataFrame)
173
+ ):
163
174
  self._assets = assets
164
175
  elif isinstance(assets, str):
165
176
  self._assets = [assets]
@@ -184,11 +195,16 @@ class DataVendor(ABC):
184
195
  return self._markets
185
196
 
186
197
  @markets.setter
187
- def markets(self, markets: Optional[Union[str, List[str], Dict[str, List[str]]]]):
198
+ def markets(self, markets: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]):
188
199
  """
189
200
  Sets a list of available markets for the data vendor.
190
201
  """
191
- if markets is None or isinstance(markets, list) or isinstance(markets, dict):
202
+ if (
203
+ markets is None
204
+ or isinstance(markets, list)
205
+ or isinstance(markets, dict)
206
+ or isinstance(markets, pd.DataFrame)
207
+ ):
192
208
  self._markets = markets
193
209
  elif isinstance(markets, str):
194
210
  self._markets = [markets]
@@ -251,11 +267,16 @@ class DataVendor(ABC):
251
267
  return self._fields
252
268
 
253
269
  @fields.setter
254
- def fields(self, fields: Optional[Union[str, List[str], Dict[str, List[str]]]]):
270
+ def fields(self, fields: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]):
255
271
  """
256
272
  Sets a list of available fields for the data vendor.
257
273
  """
258
- if fields is None or isinstance(fields, list) or isinstance(fields, dict):
274
+ if (
275
+ fields is None
276
+ or isinstance(fields, list)
277
+ or isinstance(fields, dict)
278
+ or isinstance(fields, pd.DataFrame)
279
+ ):
259
280
  self._fields = fields
260
281
  elif isinstance(fields, str):
261
282
  self._fields = [fields]
@@ -280,9 +301,7 @@ class DataVendor(ABC):
280
301
  return self._frequencies
281
302
 
282
303
  @frequencies.setter
283
- def frequencies(
284
- self, frequencies: Optional[Union[str, List[str], Dict[str, List[str]]]]
285
- ):
304
+ def frequencies(self, frequencies: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]):
286
305
  """
287
306
  Sets a list of available data frequencies for the data vendor.
288
307
  """
@@ -290,6 +309,7 @@ class DataVendor(ABC):
290
309
  frequencies is None
291
310
  or isinstance(frequencies, list)
292
311
  or isinstance(frequencies, dict)
312
+ or isinstance(frequencies, pd.DataFrame)
293
313
  ):
294
314
  self._frequencies = frequencies
295
315
  elif isinstance(frequencies, str):
@@ -79,8 +79,9 @@ class Glassnode(DataVendor):
79
79
  if categories is None:
80
80
  self.categories = ['crypto']
81
81
  if api_key is None:
82
- raise TypeError("Set your api key. We recommend setting your api key in environment variables as"
83
- "'GLASSNODE_API_KEY', will allow DataCredentials to automatically load it.")
82
+ raise TypeError("Set your Glassnode api key in environment variables as 'GLASSNODE_API_KEY' or "
83
+ "add it as an argument when instantiating the class. To get an api key, visit: "
84
+ "https://docs.glassnode.com/basic-api/api-key")
84
85
  if assets is None:
85
86
  self.assets = self.get_assets_info(as_list=True)
86
87
  if fields is None:
@@ -109,8 +109,9 @@ class Tiingo(DataVendor):
109
109
  if categories is None:
110
110
  self.categories = ["crypto", "fx", "eqty"]
111
111
  if api_key is None:
112
- raise TypeError("Set your api key. We recommend setting your api key in environment variables as"
113
- "'TIINGO_API_KEY', will allow DataCredentials to automatically load it.")
112
+ raise TypeError("Set your Tiingo api key in environment variables as 'TIINGO_API_KEY' or "
113
+ "add it as an argument when instantiating the class. To get an api key, visit: "
114
+ "https://www.tiingo.com/")
114
115
  if exchanges is None:
115
116
  self.exchanges = self.get_exchanges_info()
116
117
  if assets is None:
@@ -197,7 +197,9 @@ class DataRequest:
197
197
  "y": "yearly",
198
198
  }
199
199
 
200
- if frequency not in list(freq_dict.keys()):
200
+ if frequency is None:
201
+ self._frequency = frequency
202
+ elif frequency not in list(freq_dict.keys()):
201
203
  raise ValueError(
202
204
  f"{frequency} is an invalid data frequency. Valid frequencies are: {freq_dict}"
203
205
  )
@@ -555,15 +557,59 @@ class DataRequest:
555
557
  # get request
556
558
  try:
557
559
  resp = requests.get(url, params=params, headers=headers)
558
- assert resp.status_code == 200
559
- # exception
560
- except AssertionError as e:
561
- logging.warning(e)
560
+ # check for status code
561
+ resp.raise_for_status()
562
+
563
+ return resp.json()
564
+
565
+ # handle HTTP errors
566
+ except requests.exceptions.HTTPError as http_err:
567
+ status_code = resp.status_code
568
+
569
+ # Tailored handling for different status codes
570
+ if status_code == 400:
571
+ logging.warning(f"Bad Request (400): {resp.text}")
572
+ elif status_code == 401:
573
+ logging.warning("Unauthorized (401): Check the authentication credentials.")
574
+ elif status_code == 403:
575
+ logging.warning("Forbidden (403): You do not have permission to access this resource.")
576
+ elif status_code == 404:
577
+ logging.warning("Not Found (404): The requested resource could not be found.")
578
+ elif status_code == 500:
579
+ logging.error("Internal Server Error (500): The server encountered an error.")
580
+ elif status_code == 503:
581
+ logging.error("Service Unavailable (503): The server is temporarily unavailable.")
582
+ else:
583
+ logging.error(f"HTTP error occurred: {http_err} (Status Code: {status_code})")
584
+ logging.error(f"Response Content: {resp.text}")
585
+
586
+ # Increment attempts and log warning
587
+ attempts += 1
588
+ logging.warning(f"Attempt #{attempts}: Failed to get data due to: {http_err}")
589
+ sleep(self.pause) # Pause before retrying
590
+ if attempts == self.trials:
591
+ logging.error("Max attempts reached. Unable to fetch data.")
592
+ break
593
+
594
+ # handle non-HTTP exceptions (e.g., network issues)
595
+ except requests.exceptions.RequestException as req_err:
562
596
  attempts += 1
563
- logging.warning(f"Failed to get data on attempt #{attempts}.")
597
+ logging.warning(f"Request error on attempt #{attempts}: {req_err}. "
598
+ f"Retrying after {self.pause} seconds...")
564
599
  sleep(self.pause)
565
- if attempts == 3:
600
+ if attempts == self.trials:
601
+ logging.error("Max attempts reached. Unable to fetch data due to request errors.")
566
602
  break
567
603
 
568
- else:
569
- return resp.json()
604
+ # handle other exceptions
605
+ except Exception as e:
606
+ attempts += 1
607
+ logging.warning(f"An unexpected error occurred: {e}. "
608
+ f"Retrying after {self.pause} seconds...")
609
+ sleep(self.pause)
610
+ if attempts == self.trials:
611
+ logging.error("Max attempts reached. Unable to fetch data due to request errors.")
612
+ break
613
+
614
+ # return None if the API call fails
615
+ return None
@@ -3,7 +3,6 @@ from time import sleep
3
3
  from typing import Any, Dict, List, Optional, Union
4
4
 
5
5
  import ccxt
6
- import numpy as np
7
6
  import pandas as pd
8
7
 
9
8
  from cryptodatapy.extract.datarequest import DataRequest
@@ -20,7 +19,6 @@ class CCXT(Library):
20
19
  """
21
20
  Retrieves data from CCXT API.
22
21
  """
23
-
24
22
  def __init__(
25
23
  self,
26
24
  categories: Union[str, List[str]] = "crypto",
@@ -86,97 +84,16 @@ class CCXT(Library):
86
84
  rate_limit,
87
85
  )
88
86
 
89
- def get_exchanges_info(
90
- self,
91
- exch: Optional[str] = None,
92
- as_list: bool = True
93
- ) -> Union[List[str], pd.DataFrame]:
87
+ def get_exchanges_info(self) -> List[str]:
94
88
  """
95
89
  Get exchanges info.
96
90
 
97
- Parameters
98
- ----------
99
- exch: str, default None
100
- Name of exchange.
101
- as_list: bool, default False
102
- Returns exchanges info as list.
103
-
104
91
  Returns
105
92
  -------
106
93
  exch: list or pd.DataFrame
107
94
  List or dataframe with info on supported exchanges.
108
95
  """
109
- # list
110
- if as_list:
111
- self.exchanges = ccxt.exchanges
112
-
113
- # df
114
- else:
115
- if exch is not None:
116
- self.exchanges = [exch]
117
- else:
118
- self.exchanges = ccxt.exchanges
119
- print(
120
- "Getting metadata for all supported exchanges can take a few minutes."
121
- " For quick info on a specific exchange, provide the name of the exchange in the exch parameter."
122
- )
123
-
124
- # exch df
125
- exch_df = pd.DataFrame(
126
- index=self.exchanges,
127
- columns=[
128
- "id",
129
- "name",
130
- "countries",
131
- "urls",
132
- "version",
133
- "api",
134
- "has",
135
- "timeframes",
136
- "timeout",
137
- "rateLimit",
138
- "userAgent",
139
- "verbose",
140
- "markets",
141
- "symbols",
142
- "currencies",
143
- "markets_by_id",
144
- "currencies_by_id",
145
- "api_key",
146
- "secret",
147
- "uid",
148
- "options",
149
- ],
150
- )
151
-
152
- # Extract exchange info
153
- for index, row in exch_df.iterrows():
154
- try:
155
- exchange = getattr(ccxt, index)()
156
- exchange.load_markets()
157
- except AttributeError as e:
158
- print(f"AttributeError: {e} for exchange {index}")
159
- exch_df.loc[index, :] = np.nan
160
- except ccxt.BaseError as e: # Catch specific ccxt exceptions
161
- print(f"CCXT Error: {e} for exchange {index}")
162
- exch_df.loc[index, :] = np.nan
163
- except Exception as e: # Fallback for any other exceptions
164
- print(f"Unexpected error: {e} for exchange {index}")
165
- exch_df.loc[index, :] = np.nan
166
- else:
167
- for col in exch_df.columns:
168
- try:
169
- exch_df.loc[index, col] = str(getattr(exchange, col))
170
- except AttributeError as e:
171
- print(f"AttributeError: {e} for attribute {col} in exchange {index}")
172
- exch_df.loc[index, col] = np.nan
173
- except Exception as e: # Fallback for any other exceptions
174
- print(f"Unexpected error: {e} for attribute {col} in exchange {index}")
175
- exch_df.loc[index, col] = np.nan
176
-
177
- # set index name
178
- exch_df.index.name = "exchange"
179
- self.exchanges = exch_df
96
+ self.exchanges = ccxt.exchanges
180
97
 
181
98
  return self.exchanges
182
99
 
@@ -337,7 +254,7 @@ class CCXT(Library):
337
254
  Get CCXT metadata.
338
255
  """
339
256
  if self.exchanges is None:
340
- self.exchanges = self.get_exchanges_info(as_list=True)
257
+ self.exchanges = self.get_exchanges_info()
341
258
  if self.market_types is None:
342
259
  self.market_types = ["spot", "future", "perpetual_future", "option"]
343
260
  if self.assets is None:
@@ -356,6 +273,7 @@ class CCXT(Library):
356
273
  data_type: str,
357
274
  ticker: str,
358
275
  start_date: str = None,
276
+ end_date: str = None,
359
277
  ) -> pd.DataFrame:
360
278
  """
361
279
  Sends data request to Python client.
@@ -370,9 +288,8 @@ class CCXT(Library):
370
288
  Ticker symbol to request data for.
371
289
  start_date: str
372
290
  Start date in 'YYYY-MM-DD' format.
373
-
374
- Other Parameters
375
- ----------------
291
+ end_date: str
292
+ End date in 'YYYY-MM-DD' format.
376
293
 
377
294
 
378
295
  Returns
@@ -384,6 +301,8 @@ class CCXT(Library):
384
301
  cx_data_req = ConvertParams(data_req).to_ccxt()
385
302
  if start_date is None:
386
303
  start_date = cx_data_req['start_date']
304
+ if end_date is None:
305
+ end_date = cx_data_req['end_date']
387
306
 
388
307
  # data types
389
308
  data_types = {'ohlcv': 'fetchOHLCV', 'funding_rates': 'fetchFundingRateHistory'}
@@ -399,24 +318,25 @@ class CCXT(Library):
399
318
  cx_data_req["freq"],
400
319
  since=start_date,
401
320
  limit=self.max_obs_per_call,
321
+ params={'until': end_date}
402
322
  )
403
323
  elif data_type == 'funding_rates':
404
324
  data_resp = getattr(exch, data_types[data_type])(
405
325
  ticker,
406
326
  since=start_date,
407
327
  limit=1000,
328
+ params={'until': end_date}
408
329
  )
409
330
 
410
- assert data_resp != []
331
+ return data_resp
411
332
 
412
333
  except Exception as e:
413
334
  logging.warning(f"Failed to get {data_type} data for {ticker}.")
414
335
  logging.warning(e)
415
336
 
416
- else:
417
- return data_resp
337
+ return None
418
338
 
419
- def get_all_ohlcv_hist(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
339
+ def fetch_all_ohlcv_hist(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
420
340
  """
421
341
  Submits get requests to API until entire OHLCV history has been collected. Only necessary when
422
342
  number of observations is larger than the maximum number of observations per call.
@@ -436,24 +356,24 @@ class CCXT(Library):
436
356
  # convert data request parameters to CCXT format and set start date
437
357
  cx_data_req = ConvertParams(data_req).to_ccxt()
438
358
  start_date = cx_data_req['start_date']
359
+ end_date = cx_data_req['end_date']
439
360
 
440
361
  # create empty df
441
362
  df = pd.DataFrame()
363
+
442
364
  # while loop condition
443
365
  missing_vals, attempts = True, 0
444
366
 
445
367
  # run a while loop until all data collected
446
368
  while missing_vals and attempts < cx_data_req['trials']:
447
369
 
448
- try:
449
- # data req
450
- data_resp = self.req_data(data_req=data_req,
451
- data_type='ohlcv',
452
- ticker=ticker,
453
- start_date=start_date)
370
+ data_resp = self.req_data(data_req=data_req,
371
+ data_type='ohlcv',
372
+ ticker=ticker,
373
+ start_date=start_date,
374
+ end_date=end_date)
454
375
 
455
- except AssertionError as e:
456
- logging.warning(e)
376
+ if data_resp is None:
457
377
  attempts += 1
458
378
  sleep(self.get_rate_limit_info(exch=cx_data_req['exch'])[cx_data_req['exch']] / 1000)
459
379
  logging.warning(
@@ -463,6 +383,7 @@ class CCXT(Library):
463
383
  logging.warning(
464
384
  f"Failed to get OHLCV data from {cx_data_req['exch']} for {ticker} after many attempts."
465
385
  )
386
+ return None
466
387
 
467
388
  else:
468
389
  # name cols and create df
@@ -488,7 +409,7 @@ class CCXT(Library):
488
409
 
489
410
  return df
490
411
 
491
- def get_all_funding_hist(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
412
+ def fetch_all_funding_hist(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
492
413
  """
493
414
  Submits get requests to API until entire funding rate history has been collected. Only necessary when
494
415
  number of observations is larger than the maximum number of observations per call.
@@ -508,6 +429,7 @@ class CCXT(Library):
508
429
  # convert data request parameters to CCXT format and set start date
509
430
  cx_data_req = ConvertParams(data_req).to_ccxt()
510
431
  start_date = cx_data_req['start_date']
432
+ end_date = cx_data_req['end_date']
511
433
 
512
434
  # create empty df
513
435
  df = pd.DataFrame()
@@ -517,15 +439,14 @@ class CCXT(Library):
517
439
  # run a while loop until all data collected
518
440
  while missing_vals and attempts < cx_data_req['trials']:
519
441
 
520
- try:
521
- # data req
522
- data_resp = self.req_data(data_req=data_req,
523
- data_type='funding_rates',
524
- ticker=ticker,
525
- start_date=start_date)
442
+ # data req
443
+ data_resp = self.req_data(data_req=data_req,
444
+ data_type='funding_rates',
445
+ ticker=ticker,
446
+ start_date=start_date,
447
+ end_date=end_date)
526
448
 
527
- except AssertionError as e:
528
- logging.warning(e)
449
+ if data_resp is None:
529
450
  attempts += 1
530
451
  sleep(self.get_rate_limit_info(exch=cx_data_req['exch'])[cx_data_req['exch']] / 1000)
531
452
  logging.warning(
@@ -533,8 +454,9 @@ class CCXT(Library):
533
454
  )
534
455
  if attempts == cx_data_req["trials"]:
535
456
  logging.warning(
536
- f"Failed to get funding rates from {cx_data_req['exch']} for {ticker} after many attempts."
457
+ f"Failed to get funding_rates from {cx_data_req['exch']} for {ticker} after many attempts."
537
458
  )
459
+ return None
538
460
 
539
461
  else:
540
462
  # add to df
@@ -580,7 +502,7 @@ class CCXT(Library):
580
502
 
581
503
  return WrangleData(data_req, data_resp).ccxt()
582
504
 
583
- def get_tidy_ohlcv(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
505
+ def fetch_tidy_ohlcv(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
584
506
  """
585
507
  Gets entire OHLCV history and wrangles the data response into tidy data format.
586
508
 
@@ -597,13 +519,15 @@ class CCXT(Library):
597
519
  Dataframe with entire data history retrieved and wrangled into tidy data format.
598
520
  """
599
521
  # get entire data history
600
- df = self.get_all_ohlcv_hist(data_req, ticker)
522
+ df = self.fetch_all_ohlcv_hist(data_req, ticker)
523
+
601
524
  # wrangle df
602
- df = self.wrangle_data_resp(data_req, df)
525
+ if df is not None:
526
+ df = self.wrangle_data_resp(data_req, df)
603
527
 
604
528
  return df
605
529
 
606
- def get_tidy_funding_rates(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
530
+ def fetch_tidy_funding_rates(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
607
531
  """
608
532
  Gets entire funding rates history and wrangles the data response into tidy data format.
609
533
 
@@ -620,9 +544,11 @@ class CCXT(Library):
620
544
  Dataframe with entire data history retrieved and wrangled into tidy data format.
621
545
  """
622
546
  # get entire data history
623
- df = self.get_all_funding_hist(data_req, ticker)
547
+ df = self.fetch_all_funding_hist(data_req, ticker)
548
+
624
549
  # wrangle df
625
- df = self.wrangle_data_resp(data_req, df)
550
+ if df is not None:
551
+ df = self.wrangle_data_resp(data_req, df)
626
552
 
627
553
  return df
628
554
 
@@ -680,7 +606,7 @@ class CCXT(Library):
680
606
  f" Market type must be perpetual futures."
681
607
  )
682
608
 
683
- def get_ohlcv(self, data_req: DataRequest) -> pd.DataFrame:
609
+ def fetch_ohlcv(self, data_req: DataRequest) -> pd.DataFrame:
684
610
  """
685
611
  Loops list of tickers, retrieves OHLCV data for each ticker in tidy format and stores it in a
686
612
  multiindex dataframe.
@@ -707,13 +633,9 @@ class CCXT(Library):
707
633
  # loop through tickers
708
634
  for mkt, ticker in zip(cx_data_req['mkts'], data_req.tickers):
709
635
 
710
- try:
711
- df0 = self.get_tidy_ohlcv(data_req, mkt)
712
-
713
- except AssertionError:
714
- logging.info(f"Failed to get OHLCV data for {ticker} after many attempts.")
636
+ df0 = self.fetch_tidy_ohlcv(data_req, mkt)
715
637
 
716
- else:
638
+ if df0 is not None:
717
639
  # add ticker to index
718
640
  df0['ticker'] = ticker.upper()
719
641
  df0.set_index(['ticker'], append=True, inplace=True)
@@ -722,7 +644,7 @@ class CCXT(Library):
722
644
 
723
645
  return df
724
646
 
725
- def get_funding_rates(self, data_req: DataRequest) -> pd.DataFrame:
647
+ def fetch_funding_rates(self, data_req: DataRequest) -> pd.DataFrame:
726
648
  """
727
649
  Loops list of tickers, retrieves funding rates data for each ticker in tidy format and stores it in a
728
650
  multiindex dataframe.
@@ -749,13 +671,9 @@ class CCXT(Library):
749
671
  # loop through tickers
750
672
  for mkt, ticker in zip(cx_data_req['mkts'], data_req.tickers):
751
673
 
752
- try:
753
- df0 = self.get_tidy_funding_rates(data_req, mkt)
754
-
755
- except AssertionError:
756
- logging.info(f"Failed to get funding rates for {ticker} after many attempts.")
674
+ df0 = self.fetch_tidy_funding_rates(data_req, mkt)
757
675
 
758
- else:
676
+ if df0 is not None:
759
677
  # add ticker to index
760
678
  df0['ticker'] = ticker.upper()
761
679
  df0.set_index(['ticker'], append=True, inplace=True)
@@ -783,12 +701,12 @@ class CCXT(Library):
783
701
  # get OHLCV data
784
702
  ohlcv_list = ["open", "high", "low", "close", "volume"]
785
703
  if any([field in ohlcv_list for field in data_req.fields]):
786
- df0 = self.get_ohlcv(data_req)
704
+ df0 = self.fetch_ohlcv(data_req)
787
705
  df = pd.concat([df, df0])
788
706
 
789
707
  # get funding rate data
790
708
  if any([field == "funding_rate" for field in data_req.fields]):
791
- df1 = self.get_funding_rates(data_req)
709
+ df1 = self.fetch_funding_rates(data_req)
792
710
  df = pd.concat([df, df1], axis=1)
793
711
 
794
712
  # check if df empty