cryptodatapy 0.2.32__tar.gz → 0.2.33__tar.gz
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-0.2.32 → cryptodatapy-0.2.33}/PKG-INFO +1 -1
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/pyproject.toml +1 -1
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/libraries/ccxt_api.py +211 -77
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/LICENSE +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/README.md +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/__init__.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/conf/__init__.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/conf/fields.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/conf/tickers.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/__init__.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/br_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/ca_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/cn_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/de_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/ez_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/fr_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/gb_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/get_econ_calendars.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/id_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/in_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/it_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/jp_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/kr_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/mx_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/ru_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/tr_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/datasets/us_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/__init__.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/.ipynb_checkpoints/CCXT-checkpoint.ipynb +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/.ipynb_checkpoints/DBNomics-checkpoint.ipynb +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/.ipynb_checkpoints/InvestPy-checkpoint.ipynb +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/.ipynb_checkpoints/NasdaqDataLink-checkpoint.ipynb +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/.ipynb_checkpoints/PandasDataReader-checkpoint.ipynb +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/__init__.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/coinmetrics_api.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/cryptocompare_api.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/datavendor.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/glassnode_api.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/polygon_api.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/tiingo_api.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/datarequest.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/exchanges/__init__.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/exchanges/dydx.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/exchanges/exchange.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/getdata.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/libraries/__init__.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/libraries/dbnomics_api.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/libraries/investpy_api.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/libraries/library.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/libraries/pandasdr_api.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/web/__init__.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/web/aqr.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/web/web.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/transform/__init__.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/transform/clean.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/transform/convertparams.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/transform/filter.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/transform/impute.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/transform/od.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/transform/wrangle.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/util/__init__.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/util/datacatalog.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/util/datacredentials.py +0 -0
- {cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/util/utils.py +0 -0
|
@@ -37,6 +37,9 @@ class CCXT(Library):
|
|
|
37
37
|
api_key: Optional[str] = None,
|
|
38
38
|
max_obs_per_call: Optional[int] = 1000,
|
|
39
39
|
rate_limit: Optional[Any] = None,
|
|
40
|
+
ip_ban_wait_time_s: float = 320.0, # 5.3 minutes for IP ban
|
|
41
|
+
recovery_base_delay_s: float = 2.0, # base delay for exponential backoff
|
|
42
|
+
max_recovery_delay_s: float = 60.0 # max delay for exponential backoff
|
|
40
43
|
):
|
|
41
44
|
"""
|
|
42
45
|
Constructor
|
|
@@ -71,6 +74,12 @@ class CCXT(Library):
|
|
|
71
74
|
api_limit stored in DataCredentials.
|
|
72
75
|
rate_limit: Any, optional, Default None
|
|
73
76
|
Number of API calls made and left, by time frequency.
|
|
77
|
+
ip_ban_wait_time_s: float, default 320.0
|
|
78
|
+
Time in seconds to wait if IP ban is detected (HTTP 403).
|
|
79
|
+
recovery_base_delay_s: float, default 2.0
|
|
80
|
+
Base delay in seconds for exponential backoff strategy.
|
|
81
|
+
max_recovery_delay_s: float, default 60.0
|
|
82
|
+
Maximum delay in seconds for exponential backoff strategy.
|
|
74
83
|
"""
|
|
75
84
|
super().__init__(
|
|
76
85
|
categories, exchanges, indexes, assets, markets, market_types,
|
|
@@ -79,6 +88,11 @@ class CCXT(Library):
|
|
|
79
88
|
self.exchange = None
|
|
80
89
|
self.exchange_async = None
|
|
81
90
|
self.data_req = None
|
|
91
|
+
|
|
92
|
+
self.ip_ban_wait_time_s = ip_ban_wait_time_s
|
|
93
|
+
self.recovery_base_delay_s = recovery_base_delay_s
|
|
94
|
+
self.max_recovery_delay_s = max_recovery_delay_s
|
|
95
|
+
|
|
82
96
|
self.data_resp = []
|
|
83
97
|
self.data = pd.DataFrame()
|
|
84
98
|
|
|
@@ -313,17 +327,170 @@ class CCXT(Library):
|
|
|
313
327
|
if self.rate_limit is None:
|
|
314
328
|
self.rate_limit = self.exchange.rateLimit
|
|
315
329
|
|
|
316
|
-
@staticmethod
|
|
317
|
-
def exponential_backoff_with_jitter(base_delay: float, max_delay: int, attempts: int) -> None:
|
|
330
|
+
# @staticmethod
|
|
331
|
+
# def exponential_backoff_with_jitter(base_delay: float, max_delay: int, attempts: int) -> None:
|
|
332
|
+
# delay = min(max_delay, base_delay * (2 ** attempts))
|
|
333
|
+
# delay_with_jitter = delay + random.uniform(0, delay * 0.5)
|
|
334
|
+
# sleep(delay_with_jitter)
|
|
335
|
+
#
|
|
336
|
+
# @staticmethod
|
|
337
|
+
# async def exponential_backoff_with_jitter_async(base_delay: float, max_delay: int, attempts: int) -> None:
|
|
338
|
+
# delay = min(max_delay, base_delay * (2 ** attempts))
|
|
339
|
+
# delay_with_jitter = delay + random.uniform(0, delay * 0.5)
|
|
340
|
+
# await asyncio.sleep(delay_with_jitter)
|
|
341
|
+
#
|
|
342
|
+
|
|
343
|
+
def exponential_backoff_with_jitter(
|
|
344
|
+
self,
|
|
345
|
+
attempts: int,
|
|
346
|
+
status_code: Optional[int] = None
|
|
347
|
+
) -> float:
|
|
348
|
+
"""
|
|
349
|
+
Calculates and applies exponential backoff with full jitter, honoring
|
|
350
|
+
specific error codes (403/429) using configurable instance properties.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
attempts: The current retry number (starting at 1 after the first failure).
|
|
354
|
+
status_code: The HTTP status code received (e.g., 403, 429).
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
The actual time slept in seconds.
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
# 1. CRITICAL: Hard IP ban handling (403)
|
|
361
|
+
if status_code == 403:
|
|
362
|
+
sleep_time = self.ip_ban_wait_time_s
|
|
363
|
+
logging.error(
|
|
364
|
+
f"🚨 CRITICAL: HTTP 403 Forbidden (IP Ban) detected. Pausing for mandatory {sleep_time} seconds.")
|
|
365
|
+
sleep(sleep_time)
|
|
366
|
+
return sleep_time
|
|
367
|
+
|
|
368
|
+
# 2. Standard Exponential Backoff for 429 or generic exceptions
|
|
369
|
+
base_delay = self.recovery_base_delay_s
|
|
370
|
+
max_delay = self.max_recovery_delay_s
|
|
371
|
+
|
|
372
|
+
# Calculate exponential growth
|
|
318
373
|
delay = min(max_delay, base_delay * (2 ** attempts))
|
|
319
|
-
delay_with_jitter = delay + random.uniform(0, delay * 0.5)
|
|
320
|
-
sleep(delay_with_jitter)
|
|
321
374
|
|
|
322
|
-
|
|
323
|
-
|
|
375
|
+
# Add full jitter (random delay between 0 and the calculated delay)
|
|
376
|
+
sleep_time = random.uniform(0, delay)
|
|
377
|
+
|
|
378
|
+
logging.warning(
|
|
379
|
+
f"Applying recovery backoff (Attempt {attempts}, Code {status_code if status_code else 'N/A'}): "
|
|
380
|
+
f"{sleep_time:.2f} seconds."
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
sleep(sleep_time)
|
|
384
|
+
return sleep_time
|
|
385
|
+
|
|
386
|
+
async def exponential_backoff_with_jitter_async(
|
|
387
|
+
self,
|
|
388
|
+
attempts: int,
|
|
389
|
+
status_code: Optional[int] = None
|
|
390
|
+
) -> float:
|
|
391
|
+
"""
|
|
392
|
+
Async version of exponential_backoff_with_jitter, using configurable instance properties.
|
|
393
|
+
"""
|
|
394
|
+
|
|
395
|
+
# 1. CRITICAL: Hard IP ban handling (403)
|
|
396
|
+
if status_code == 403:
|
|
397
|
+
sleep_time = self.ip_ban_wait_time_s
|
|
398
|
+
logging.error(
|
|
399
|
+
f"🚨 CRITICAL: HTTP 403 Forbidden (IP Ban) detected. Pausing for mandatory {sleep_time} seconds.")
|
|
400
|
+
await asyncio.sleep(sleep_time)
|
|
401
|
+
return sleep_time
|
|
402
|
+
|
|
403
|
+
# 2. Standard Exponential Backoff for 429 or generic exceptions
|
|
404
|
+
base_delay = self.recovery_base_delay_s
|
|
405
|
+
max_delay = self.max_recovery_delay_s
|
|
406
|
+
|
|
407
|
+
# Calculate exponential growth
|
|
324
408
|
delay = min(max_delay, base_delay * (2 ** attempts))
|
|
325
|
-
|
|
326
|
-
|
|
409
|
+
|
|
410
|
+
# Add full jitter (random delay between 0 and the calculated delay)
|
|
411
|
+
sleep_time = random.uniform(0, delay)
|
|
412
|
+
|
|
413
|
+
logging.warning(
|
|
414
|
+
f"Applying recovery backoff (Attempt {attempts}, Code {status_code if status_code else 'N/A'}): "
|
|
415
|
+
f"{sleep_time:.2f} seconds."
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
await asyncio.sleep(sleep_time)
|
|
419
|
+
return sleep_time
|
|
420
|
+
|
|
421
|
+
# --- New Exception Helper Methods for Modularity ---
|
|
422
|
+
|
|
423
|
+
def _handle_exception_and_backoff(self, e: Exception, attempts: int) -> bool:
|
|
424
|
+
"""
|
|
425
|
+
Analyzes a synchronous exception, applies backoff if recoverable, and logs the result.
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
True if the error was recoverable (retries should continue).
|
|
429
|
+
False if the error is terminal (retries should stop).
|
|
430
|
+
"""
|
|
431
|
+
# 1. CRITICAL: 403 IP Ban Check (Must parse string due to CCXT wrapping)
|
|
432
|
+
error_message = str(e)
|
|
433
|
+
if '403' in error_message or 'Forbidden' in error_message:
|
|
434
|
+
logging.error(f"CCXT Exception: Critical 403 IP Ban detected via parsing.")
|
|
435
|
+
self.exponential_backoff_with_jitter(attempts, status_code=403)
|
|
436
|
+
return True # Recoverable with mandatory long pause
|
|
437
|
+
|
|
438
|
+
# 2. Recoverable CCXT Exceptions (Standard Backoff: proxy 429)
|
|
439
|
+
if isinstance(e, (
|
|
440
|
+
ccxt.RequestTimeout,
|
|
441
|
+
ccxt.RateLimitExceeded,
|
|
442
|
+
ccxt.DDoSProtection,
|
|
443
|
+
ccxt.ExchangeNotAvailable,
|
|
444
|
+
ccxt.OperationFailed,
|
|
445
|
+
)):
|
|
446
|
+
logging.warning(f"CCXT Exception: Recoverable error ({e.__class__.__name__}) on attempt {attempts}.")
|
|
447
|
+
self.exponential_backoff_with_jitter(attempts, status_code=429)
|
|
448
|
+
return True # Recoverable, continue retries
|
|
449
|
+
|
|
450
|
+
# 3. Unrecoverable CCXT Errors (Terminal: e.g., bad symbol)
|
|
451
|
+
if isinstance(e, ccxt.ExchangeError):
|
|
452
|
+
logging.error(f"CCXT Exception: Terminal ExchangeError ({e.__class__.__name__}). Halting retries.")
|
|
453
|
+
return False # Unrecoverable, stop retries
|
|
454
|
+
|
|
455
|
+
# 4. Other/Unknown Exceptions (Terminal)
|
|
456
|
+
logging.error(f"CCXT Exception: Unhandled error ({e.__class__.__name__}). Halting retries: {e}")
|
|
457
|
+
return False
|
|
458
|
+
|
|
459
|
+
async def _handle_exception_and_backoff_async(self, e: Exception, attempts: int) -> bool:
|
|
460
|
+
"""
|
|
461
|
+
Analyzes an asynchronous exception, applies backoff if recoverable, and logs the result.
|
|
462
|
+
|
|
463
|
+
Returns:
|
|
464
|
+
True if the error was recoverable (retries should continue).
|
|
465
|
+
False if the error is terminal (retries should stop).
|
|
466
|
+
"""
|
|
467
|
+
# 1. CRITICAL: 403 IP Ban Check (Must parse string due to CCXT wrapping)
|
|
468
|
+
error_message = str(e)
|
|
469
|
+
if '403' in error_message or 'Forbidden' in error_message:
|
|
470
|
+
logging.error(f"CCXT Exception: Critical 403 IP Ban detected via parsing.")
|
|
471
|
+
await self.exponential_backoff_with_jitter_async(attempts, status_code=403)
|
|
472
|
+
return True # Recoverable with mandatory long pause
|
|
473
|
+
|
|
474
|
+
# 2. Recoverable CCXT Exceptions (Standard Backoff: proxy 429)
|
|
475
|
+
if isinstance(e, (
|
|
476
|
+
ccxt.RequestTimeout,
|
|
477
|
+
ccxt.RateLimitExceeded,
|
|
478
|
+
ccxt.DDoSProtection,
|
|
479
|
+
ccxt.ExchangeNotAvailable,
|
|
480
|
+
ccxt.OperationFailed,
|
|
481
|
+
)):
|
|
482
|
+
logging.warning(f"CCXT Exception: Recoverable error ({e.__class__.__name__}) on attempt {attempts}.")
|
|
483
|
+
await self.exponential_backoff_with_jitter_async(attempts, status_code=429)
|
|
484
|
+
return True # Recoverable, continue retries
|
|
485
|
+
|
|
486
|
+
# 3. Unrecoverable CCXT Errors (Terminal: e.g., bad symbol)
|
|
487
|
+
if isinstance(e, ccxt.ExchangeError):
|
|
488
|
+
logging.error(f"CCXT Exception: Terminal ExchangeError ({e.__class__.__name__}). Halting retries.")
|
|
489
|
+
return False # Unrecoverable, stop retries
|
|
490
|
+
|
|
491
|
+
# 4. Other/Unknown Exceptions (Terminal)
|
|
492
|
+
logging.error(f"CCXT Exception: Unhandled error ({e.__class__.__name__}). Halting retries: {e}")
|
|
493
|
+
return False
|
|
327
494
|
|
|
328
495
|
async def _fetch_ohlcv_async(self,
|
|
329
496
|
ticker: str,
|
|
@@ -332,7 +499,6 @@ class CCXT(Library):
|
|
|
332
499
|
end_date: int,
|
|
333
500
|
exch: str,
|
|
334
501
|
trials: int = 3,
|
|
335
|
-
pause: int = 1
|
|
336
502
|
) -> Union[List, None]:
|
|
337
503
|
"""
|
|
338
504
|
Fetches OHLCV data for a specific ticker.
|
|
@@ -351,8 +517,6 @@ class CCXT(Library):
|
|
|
351
517
|
Name of exchange.
|
|
352
518
|
trials: int, default 3
|
|
353
519
|
Number of attempts to fetch data.
|
|
354
|
-
pause: int, default 60
|
|
355
|
-
Pause in seconds to respect the rate limit.
|
|
356
520
|
|
|
357
521
|
Returns
|
|
358
522
|
-------
|
|
@@ -383,17 +547,15 @@ class CCXT(Library):
|
|
|
383
547
|
|
|
384
548
|
# add data to list
|
|
385
549
|
if data:
|
|
550
|
+
# noinspection PyUnusedLocal
|
|
386
551
|
start_date = data[-1][0] + 1
|
|
387
552
|
data_resp.extend(data)
|
|
388
553
|
else:
|
|
389
554
|
break
|
|
390
555
|
|
|
391
556
|
except Exception as e:
|
|
392
|
-
logging.warning(
|
|
393
|
-
f"Failed to get OHLCV data from {self.exchange_async.id} for {ticker} "
|
|
394
|
-
f"on attempt #{attempts + 1}: {e}."
|
|
395
|
-
)
|
|
396
557
|
attempts += 1
|
|
558
|
+
|
|
397
559
|
if attempts >= trials:
|
|
398
560
|
logging.warning(
|
|
399
561
|
f"Failed to get OHLCV data from {self.exchange_async.id} "
|
|
@@ -401,17 +563,17 @@ class CCXT(Library):
|
|
|
401
563
|
)
|
|
402
564
|
break
|
|
403
565
|
|
|
404
|
-
|
|
405
|
-
await self.
|
|
406
|
-
|
|
407
|
-
|
|
566
|
+
# exception handling
|
|
567
|
+
if not await self._handle_exception_and_backoff_async(e, attempts):
|
|
568
|
+
# If the helper returns False, the error is terminal (ExchangeError, etc.)
|
|
569
|
+
break
|
|
408
570
|
|
|
409
|
-
|
|
410
|
-
|
|
571
|
+
await self.exchange_async.close()
|
|
572
|
+
return data_resp
|
|
411
573
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
574
|
+
else:
|
|
575
|
+
logging.warning(f"OHLCV data is not available for {self.exchange_async.id}.")
|
|
576
|
+
return None
|
|
415
577
|
|
|
416
578
|
def _fetch_ohlcv(self,
|
|
417
579
|
ticker: str,
|
|
@@ -420,7 +582,6 @@ class CCXT(Library):
|
|
|
420
582
|
end_date: str,
|
|
421
583
|
exch: str,
|
|
422
584
|
trials: int = 3,
|
|
423
|
-
pause: int = 1
|
|
424
585
|
) -> Union[List, None]:
|
|
425
586
|
"""
|
|
426
587
|
Fetches OHLCV data for a specific ticker.
|
|
@@ -439,8 +600,6 @@ class CCXT(Library):
|
|
|
439
600
|
Name of exchange.
|
|
440
601
|
trials: int, default 3
|
|
441
602
|
Number of attempts to fetch data.
|
|
442
|
-
pause: int, default 60
|
|
443
|
-
Pause in seconds to respect the rate limit.
|
|
444
603
|
|
|
445
604
|
Returns
|
|
446
605
|
-------
|
|
@@ -480,11 +639,8 @@ class CCXT(Library):
|
|
|
480
639
|
break
|
|
481
640
|
|
|
482
641
|
except Exception as e:
|
|
483
|
-
logging.warning(
|
|
484
|
-
f"Failed to get OHLCV data from {self.exchange.id} for {ticker} "
|
|
485
|
-
f"on attempt #{attempts + 1}: {e}."
|
|
486
|
-
)
|
|
487
642
|
attempts += 1
|
|
643
|
+
|
|
488
644
|
if attempts >= trials:
|
|
489
645
|
logging.warning(
|
|
490
646
|
f"Failed to get OHLCV data from {self.exchange.id} "
|
|
@@ -492,8 +648,10 @@ class CCXT(Library):
|
|
|
492
648
|
)
|
|
493
649
|
break
|
|
494
650
|
|
|
495
|
-
|
|
496
|
-
self.
|
|
651
|
+
# exception handling
|
|
652
|
+
if not self._handle_exception_and_backoff(e, attempts):
|
|
653
|
+
# If the helper returns False, the error is terminal (ExchangeError, etc.)
|
|
654
|
+
break
|
|
497
655
|
|
|
498
656
|
return data_resp
|
|
499
657
|
|
|
@@ -609,7 +767,6 @@ class CCXT(Library):
|
|
|
609
767
|
end_date: int,
|
|
610
768
|
exch: str,
|
|
611
769
|
trials: int = 3,
|
|
612
|
-
pause: int = 1
|
|
613
770
|
) -> Union[List, None]:
|
|
614
771
|
"""
|
|
615
772
|
Fetches funding rates data for a specific ticker.
|
|
@@ -624,8 +781,6 @@ class CCXT(Library):
|
|
|
624
781
|
End date in integers in milliseconds since Unix epoch.
|
|
625
782
|
trials: int, default 3
|
|
626
783
|
Number of attempts to fetch data.
|
|
627
|
-
pause: int, default 1
|
|
628
|
-
Pause in seconds to respect the rate limit.
|
|
629
784
|
|
|
630
785
|
Returns
|
|
631
786
|
-------
|
|
@@ -661,11 +816,8 @@ class CCXT(Library):
|
|
|
661
816
|
break
|
|
662
817
|
|
|
663
818
|
except Exception as e:
|
|
664
|
-
logging.warning(
|
|
665
|
-
f"Failed to get funding rates from {self.exchange_async.id} for {ticker} "
|
|
666
|
-
f"on attempt #{attempts + 1}: {e}."
|
|
667
|
-
)
|
|
668
819
|
attempts += 1
|
|
820
|
+
|
|
669
821
|
if attempts >= trials:
|
|
670
822
|
logging.warning(
|
|
671
823
|
f"Failed to get funding rates from {self.exchange_async.id} "
|
|
@@ -673,10 +825,9 @@ class CCXT(Library):
|
|
|
673
825
|
)
|
|
674
826
|
break
|
|
675
827
|
|
|
676
|
-
|
|
677
|
-
await self.
|
|
678
|
-
|
|
679
|
-
attempts)
|
|
828
|
+
# exception handling
|
|
829
|
+
if not await self._handle_exception_and_backoff_async(e, attempts):
|
|
830
|
+
break
|
|
680
831
|
|
|
681
832
|
await self.exchange_async.close()
|
|
682
833
|
return data_resp
|
|
@@ -691,7 +842,6 @@ class CCXT(Library):
|
|
|
691
842
|
end_date: int,
|
|
692
843
|
exch: str,
|
|
693
844
|
trials: int = 3,
|
|
694
|
-
pause: int = 1
|
|
695
845
|
) -> Union[List, None]:
|
|
696
846
|
"""
|
|
697
847
|
Fetches funding rates data for a specific ticker.
|
|
@@ -706,8 +856,6 @@ class CCXT(Library):
|
|
|
706
856
|
End date in integers in milliseconds since Unix epoch.
|
|
707
857
|
trials: int, default 3
|
|
708
858
|
Number of attempts to fetch data.
|
|
709
|
-
pause: int, default 1
|
|
710
|
-
Pause in seconds to respect the rate limit.
|
|
711
859
|
|
|
712
860
|
Returns
|
|
713
861
|
-------
|
|
@@ -743,11 +891,8 @@ class CCXT(Library):
|
|
|
743
891
|
break
|
|
744
892
|
|
|
745
893
|
except Exception as e:
|
|
746
|
-
logging.warning(
|
|
747
|
-
f"Failed to get funding rates from {self.exchange.id} for {ticker} "
|
|
748
|
-
f"on attempt #{attempts + 1}: {e}."
|
|
749
|
-
)
|
|
750
894
|
attempts += 1
|
|
895
|
+
|
|
751
896
|
if attempts >= trials:
|
|
752
897
|
logging.warning(
|
|
753
898
|
f"Failed to get funding rates from {self.exchange.id} "
|
|
@@ -755,8 +900,9 @@ class CCXT(Library):
|
|
|
755
900
|
)
|
|
756
901
|
break
|
|
757
902
|
|
|
758
|
-
|
|
759
|
-
self.
|
|
903
|
+
# exception handling
|
|
904
|
+
if not self._handle_exception_and_backoff(e, attempts):
|
|
905
|
+
break
|
|
760
906
|
|
|
761
907
|
return data_resp
|
|
762
908
|
|
|
@@ -868,7 +1014,6 @@ class CCXT(Library):
|
|
|
868
1014
|
end_date: int,
|
|
869
1015
|
exch: str,
|
|
870
1016
|
trials: int = 3,
|
|
871
|
-
pause: int = 1
|
|
872
1017
|
) -> Union[List, None]:
|
|
873
1018
|
"""
|
|
874
1019
|
Fetches open interest data for a specific ticker.
|
|
@@ -887,8 +1032,6 @@ class CCXT(Library):
|
|
|
887
1032
|
Name of exchange.
|
|
888
1033
|
trials: int, default 3
|
|
889
1034
|
Number of attempts to fetch data.
|
|
890
|
-
pause: int, default 1
|
|
891
|
-
Pause in seconds to respect the rate limit.
|
|
892
1035
|
|
|
893
1036
|
Returns
|
|
894
1037
|
-------
|
|
@@ -926,28 +1069,24 @@ class CCXT(Library):
|
|
|
926
1069
|
break
|
|
927
1070
|
|
|
928
1071
|
except Exception as e:
|
|
929
|
-
logging.warning(
|
|
930
|
-
f"Failed to get open interest from {self.exchange_async.id} for {ticker} "
|
|
931
|
-
f"on attempt #{attempts + 1}: {e}."
|
|
932
|
-
)
|
|
933
1072
|
attempts += 1
|
|
1073
|
+
|
|
934
1074
|
if attempts >= trials:
|
|
935
1075
|
logging.warning(
|
|
936
|
-
f"Failed to get
|
|
1076
|
+
f"Failed to get funding rates from {self.exchange_async.id} "
|
|
937
1077
|
f"for {ticker} after {trials} attempts."
|
|
938
1078
|
)
|
|
939
1079
|
break
|
|
940
1080
|
|
|
941
|
-
|
|
942
|
-
await self.
|
|
943
|
-
|
|
944
|
-
attempts)
|
|
1081
|
+
# exception handling
|
|
1082
|
+
if not await self._handle_exception_and_backoff_async(e, attempts):
|
|
1083
|
+
break
|
|
945
1084
|
|
|
946
1085
|
await self.exchange_async.close()
|
|
947
1086
|
return data_resp
|
|
948
1087
|
|
|
949
1088
|
else:
|
|
950
|
-
logging.warning(f"
|
|
1089
|
+
logging.warning(f"Funding rates are not available for {self.exchange_async.id}.")
|
|
951
1090
|
return None
|
|
952
1091
|
|
|
953
1092
|
def _fetch_open_interest(self,
|
|
@@ -957,7 +1096,6 @@ class CCXT(Library):
|
|
|
957
1096
|
end_date: int,
|
|
958
1097
|
exch: str,
|
|
959
1098
|
trials: int = 3,
|
|
960
|
-
pause: int = 1
|
|
961
1099
|
) -> Union[List, None]:
|
|
962
1100
|
"""
|
|
963
1101
|
Fetches open interest data for a specific ticker.
|
|
@@ -976,8 +1114,6 @@ class CCXT(Library):
|
|
|
976
1114
|
Name of exchange.
|
|
977
1115
|
trials: int, default 3
|
|
978
1116
|
Number of attempts to fetch data.
|
|
979
|
-
pause: int, default 1
|
|
980
|
-
Pause in seconds to respect the rate limit.
|
|
981
1117
|
|
|
982
1118
|
Returns
|
|
983
1119
|
-------
|
|
@@ -1014,25 +1150,23 @@ class CCXT(Library):
|
|
|
1014
1150
|
break
|
|
1015
1151
|
|
|
1016
1152
|
except Exception as e:
|
|
1017
|
-
logging.warning(
|
|
1018
|
-
f"Failed to get open interest from {self.exchange.id} for {ticker} "
|
|
1019
|
-
f"on attempt #{attempts + 1}: {e}."
|
|
1020
|
-
)
|
|
1021
1153
|
attempts += 1
|
|
1154
|
+
|
|
1022
1155
|
if attempts >= trials:
|
|
1023
1156
|
logging.warning(
|
|
1024
|
-
f"Failed to get
|
|
1157
|
+
f"Failed to get funding rates from {self.exchange.id} "
|
|
1025
1158
|
f"for {ticker} after {trials} attempts."
|
|
1026
1159
|
)
|
|
1027
1160
|
break
|
|
1028
1161
|
|
|
1029
|
-
|
|
1030
|
-
self.
|
|
1162
|
+
# exception handling
|
|
1163
|
+
if not self._handle_exception_and_backoff(e, attempts):
|
|
1164
|
+
break
|
|
1031
1165
|
|
|
1032
1166
|
return data_resp
|
|
1033
1167
|
|
|
1034
1168
|
else:
|
|
1035
|
-
logging.warning(f"
|
|
1169
|
+
logging.warning(f"Funding rates are not available for {self.exchange.id}.")
|
|
1036
1170
|
return None
|
|
1037
1171
|
|
|
1038
1172
|
async def _fetch_all_open_interest_async(self,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/__init__.py
RENAMED
|
File without changes
|
{cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/coinmetrics_api.py
RENAMED
|
File without changes
|
|
File without changes
|
{cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/datavendor.py
RENAMED
|
File without changes
|
{cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/glassnode_api.py
RENAMED
|
File without changes
|
{cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/polygon_api.py
RENAMED
|
File without changes
|
{cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/data_vendors/tiingo_api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/libraries/dbnomics_api.py
RENAMED
|
File without changes
|
{cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/libraries/investpy_api.py
RENAMED
|
File without changes
|
|
File without changes
|
{cryptodatapy-0.2.32 → cryptodatapy-0.2.33}/src/cryptodatapy/extract/libraries/pandasdr_api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|