Updating delisting function to return the time when it will delist if it is delisting

This commit is contained in:
Jakub Werner (jakubikan) 2024-03-22 21:17:17 +01:00
parent 4ef45314dd
commit cbc98d384e
2 changed files with 39 additions and 34 deletions

View File

@ -5,6 +5,8 @@ from pathlib import Path
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Optional, Tuple
import ccxt import ccxt
from cachetools import TTLCache
from threading import Lock
from freqtrade.enums import CandleType, MarginMode, PriceType, TradingMode from freqtrade.enums import CandleType, MarginMode, PriceType, TradingMode
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
@ -50,6 +52,11 @@ class Binance(Exchange):
(TradingMode.FUTURES, MarginMode.ISOLATED) (TradingMode.FUTURES, MarginMode.ISOLATED)
] ]
def __init__(self, *args, **kwargs) -> None:
super(Binance, self).__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: def get_tickers(self, symbols: Optional[List[str]] = None, cached: bool = False) -> Tickers:
tickers = super().get_tickers(symbols=symbols, cached=cached) tickers = super().get_tickers(symbols=symbols, cached=cached)
if self.trading_mode == TradingMode.FUTURES: if self.trading_mode == TradingMode.FUTURES:
@ -218,43 +225,41 @@ class Binance(Exchange):
else: else:
return {} return {}
def get_spot_delist_schedule(self, refresh: bool) -> list: def get_spot_pair_delist_time(self, pair, refresh: bool) -> int | None:
""" """
Calculates bid/ask target Get the delisting time for a pair if it will be delisted
bid rate - between current ask price and last price :param pair: Pair to get the delisting time for
ask rate - either using ticker bid or first bid based on orderbook :param refresh: true if you need fresh data
or remain static in any other case since it's not updating. :return: int: delisting time None if not delisting
:param pair: Pair to get rate for
:param refresh: allow cached data
:param side: "buy" or "sell"
:return: float: Price
:raises PricingError if orderbook price could not be determined.
""" """
cache = self._spot_delist_schedule_cache
lock = self._spot_delist_schedule_cache_lock
if not refresh: if not refresh:
with self._cache_lock: with lock:
rate = cache_rate.get(pair) delist_time = cache.get(f"{pair}")
# Check if cache has been invalidated if delist_time:
if rate: return delist_time
logger.debug(f"Using cached {side} rate for {pair}.")
return rate
try:
delist_schedule = self._api.sapi_get_spot_delist_schedule()
if conf_strategy.get('use_order_book', False): if delist_schedule is None:
return
order_book_top = conf_strategy.get('order_book_top', 1) with lock:
if order_book is None: for schedule in delist_schedule:
order_book = self.fetch_l2_order_book(pair, order_book_top) for pair in schedule['symbols']:
rate = self._get_rate_from_ob(pair, side, order_book, name, price_side, cache[f"{pair}"] = int(schedule['delistTime'])
order_book_top)
else:
logger.debug(f"Using Last {price_side.capitalize()} / Last Price")
if ticker is None:
ticker = self.fetch_ticker(pair)
rate = self._get_rate_from_ticker(side, ticker, conf_strategy, price_side)
if rate is None: with lock:
raise PricingError(f"{name}-Rate for {pair} was empty.") return cache.get(f"{pair}")
with self._cache_lock:
cache_rate[pair] = rate
return delistings 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

View File

@ -621,7 +621,7 @@ def test_get_maintenance_ratio_and_amt_binance(
def test_get_spot_delist_schedule(mocker, default_conf) -> None: def test_get_spot_delist_schedule(mocker, default_conf) -> None:
exchange = get_patched_exchange(mocker, default_conf, id='binance') exchange = get_patched_exchange(mocker, default_conf, id='binance')
exchange._api.sapi_get_spot_delist_schedule = get_mock_coro([{'delistTime': '1712113200000', 'symbols': ['DREPBTC', 'DREPUSDT', 'MOBBTC', 'MOBUSDT', 'PNTUSDT']}]) exchange._api.sapi_get_spot_delist_schedule = MagicMock(return_value=[{'delistTime': '1712113200000', 'symbols': ['DREPBTC', 'DREPUSDT', 'MOBBTC', 'MOBUSDT', 'PNTUSDT']}])
assert exchange.get_spot_delist_schedule(True) == ['DREP/BTC', 'DREP/USDT', 'MOB/BTC', 'MOB/USDT', 'PNT/USDT'] assert exchange.get_spot_pair_delist_time('DREP/USDT', False) == 1712113200000