diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 76e2c5491..dfab29c08 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -604,3 +604,19 @@ class DataProvider: if always_send or message not in self.__msg_cache: self._msg_queue.append(message) self.__msg_cache[message] = True + + def check_delisting(self, pair: str) -> datetime | None: + """ + Check if a pair gonna be delisted on the exchange. + Will only return datetime if the pair is gonna be delisted. + :param pair: Pair to check + :return: Datetime of the pair's delisting, None otherwise + """ + if self._exchange is None: + raise OperationalException(NO_EXCHANGE_EXCEPTION) + + try: + return self._exchange.check_delisting_time(pair) + except ExchangeError: + logger.warning(f"Could not fetch market data for {pair}. Assuming no delisting.") + return None diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index d18f53e6d..8f18f4de0 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -59,7 +59,6 @@ class Binance(Exchange): "BNFCR": "USDC", "BFUSD": "USDT", }, - "delivery_column": "deliveryDate", } _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ @@ -433,3 +432,31 @@ class Binance(Exchange): return await super()._async_get_trade_history_id( pair, until=until, since=since, from_id=from_id ) + + def check_delisting_futures(self, pair: str) -> datetime | None: + delivery_time = self.markets.get(pair, {}).get("info", {}).get("deliveryDate", None) + if delivery_time: + if isinstance(delivery_time, str) and (delivery_time != ""): + delivery_time = int(delivery_time) + + # Binance set a very high delivery time for all perpetuals. + # We compare with delivery time of BTC/USDT:USDT which assumed to never be delisted + btc_delivery_time = ( + self.markets.get("BTC/USDT:USDT", {}).get("info", {}).get("deliveryDate", None) + ) + + if delivery_time == btc_delivery_time: + return None + + delivery_time = dt_from_ts(delivery_time) + + return delivery_time + + def check_delisting_spot(self, pair: str) -> datetime | None: + return None + + def check_delisting_time(self, pair: str) -> datetime | None: + if self.trading_mode == TradingMode.SPOT: + return self.check_delisting_spot(pair) + + return self.check_delisting_futures(pair) diff --git a/freqtrade/exchange/bybit.py b/freqtrade/exchange/bybit.py index 08106e89e..93e62d25e 100644 --- a/freqtrade/exchange/bybit.py +++ b/freqtrade/exchange/bybit.py @@ -59,7 +59,6 @@ class Bybit(Exchange): "exchange_has_overrides": { "fetchOrder": True, }, - "delivery_column": "deliveryTime", } _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 7e1b8ca7d..6ccfdbebb 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -3912,31 +3912,13 @@ class Exchange: else: raise ExchangeError(f"Cannot get maintenance ratio using {self.name}") - def check_delivery_time(self, pair: str) -> int: + def check_delisting_time(self, pair: str) -> datetime | None: """ - Check if the futures contract is a delivery contract + Check if the pair gonna be delisted. + This function should be overridden by the exchange class if the exchange + provides such information. + By default, it returns None. :param pair: Market symbol - :return: True if the contract is a delivery contract, False otherwise + :return: Datetime if the pair gonna be delisted, None otherwise """ - if self.trading_mode != TradingMode.FUTURES: - return 0 - - column_to_check = self._ft_has.get("delivery_column", "") - - delivery_time = self.markets.get(pair, {}).get("info", {}).get(column_to_check, None) - if delivery_time: - if isinstance(delivery_time, str) and (delivery_time != ""): - delivery_time = int(delivery_time) - - if self.name == "Binance": - # Binance set a very high delivery time for all perpetuals. - # We compare with delivery time of BTC/USDT:USDT which assumed to never be delisted - btc_delivery_time = ( - self.markets.get("BTC/USDT:USDT", {}).get("info", {}).get(column_to_check, None) - ) - - if delivery_time == btc_delivery_time: - return 0 - - return delivery_time - return 0 + return None diff --git a/freqtrade/exchange/exchange_types.py b/freqtrade/exchange/exchange_types.py index 9038edb13..cd2bd0059 100644 --- a/freqtrade/exchange/exchange_types.py +++ b/freqtrade/exchange/exchange_types.py @@ -63,9 +63,6 @@ class FtHas(TypedDict, total=False): # Websocket control ws_enabled: bool - # Delisting checks - delivery_column: str - class Ticker(TypedDict): symbol: str diff --git a/freqtrade/exchange/okx.py b/freqtrade/exchange/okx.py index 910f912b9..847f8a284 100644 --- a/freqtrade/exchange/okx.py +++ b/freqtrade/exchange/okx.py @@ -46,7 +46,6 @@ class Okx(Exchange): }, "stoploss_blocks_assets": False, "ws_enabled": True, - "delivery_column": "expTime", } _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index d7a19090a..fed9a6533 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -661,19 +661,6 @@ class FreqtradeBot(LoggingMixin): """ logger.debug(f"create_trade for pair {pair}") - delivery_time = self.exchange.check_delivery_time(pair) - if delivery_time: - delivery_date = dt_from_ts(delivery_time) - logger.info( - f"Pair {pair} has a delivery time of " - f"{delivery_date.strftime(constants.DATETIME_PRINT_FORMAT)}." - ) - - if pair not in self.pairlists.blacklist: - self.pairlists.blacklist.append(pair) - - return False - analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(pair, self.strategy.timeframe) nowtime = analyzed_df.iloc[-1]["date"] if len(analyzed_df) > 0 else None