This commit is contained in:
Jakub W. 2024-09-15 21:34:58 +02:00 committed by GitHub
commit bf088f3d4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 75 additions and 0 deletions

View File

@ -6,6 +6,8 @@ from pathlib import Path
from typing import Dict, List, Optional, Tuple
import ccxt
from cachetools import TTLCache, cached
from threading import Lock
from freqtrade.enums import CandleType, MarginMode, PriceType, TradingMode
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
@ -53,6 +55,11 @@ class Binance(Exchange):
(TradingMode.FUTURES, MarginMode.ISOLATED)
]
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self._spot_delist_schedule_cache: TTLCache = TTLCache(maxsize=100, ttl=300)
self._spot_delist_schedule_cache_lock = Lock()
def get_tickers(self, symbols: Optional[List[str]] = None, cached: bool = False) -> Tickers:
tickers = super().get_tickers(symbols=symbols, cached=cached)
if self.trading_mode == TradingMode.FUTURES:
@ -214,3 +221,58 @@ class Binance(Exchange):
return self.get_leverage_tiers()
else:
return {}
@retrier
@cached(cache=TTLCache(maxsize=100, ttl=10), lock=Lock())
def get_deslist_schedule(self):
try:
delist_schedule = self._api.sapi_get_spot_delist_schedule()
return delist_schedule
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not get delist schedule {e.__class__.__name__}. Message: {e}') from e
except ccxt.BaseError as e:
raise OperationalException(e) from e
def get_spot_pair_delist_time(self, pair, refresh: bool = True) -> int | None:
"""
Get the delisting time for a pair if it will be delisted
:param pair: Pair to get the delisting time for
:param refresh: true if you need fresh data
:return: int: delisting time None if not delisting
"""
if not pair:
return
cache = self._spot_delist_schedule_cache
lock = self._spot_delist_schedule_cache_lock
schedule_pair = pair.replace('/', '')
if not refresh:
with lock:
delist_time = cache[f"{schedule_pair}"]
if delist_time:
return delist_time
delist_schedule = self.get_deslist_schedule()
if delist_schedule is None:
return None
with lock:
for schedule in delist_schedule:
for symbol in schedule['symbols']:
cache[f"{symbol}"] = int(schedule['delistTime'])
with lock:
return cache.get(f"{schedule_pair}")

View File

@ -723,3 +723,16 @@ def test_get_maintenance_ratio_and_amt_binance(
exchange._leverage_tiers = leverage_tiers
(result_ratio, result_amt) = exchange.get_maintenance_ratio_and_amt(pair, notional_value)
assert (round(result_ratio, 8), round(result_amt, 8)) == (mm_ratio, amt)
def test_get_spot_delist_schedule(mocker, default_conf) -> None:
exchange = get_patched_exchange(mocker, default_conf, id='binance')
return_value = [{
'delistTime': '1712113200000',
'symbols': ['DREPBTC', 'DREPUSDT', 'MOBBTC', 'MOBUSDT', 'PNTUSDT']
}]
exchange._api.sapi_get_spot_delist_schedule = MagicMock(return_value=return_value)
assert exchange.get_spot_pair_delist_time('DREP/USDT') == 1712113200000
assert exchange.get_spot_pair_delist_time('BTC/USDT') is None