diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index f28809d89..40a5c8925 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -409,7 +409,9 @@ This filter allows freqtrade to ignore pairs until they have been listed for at #### DelistFilter -Removes pairs that will be delisted on the exchange maximum `max_days_from_now` days from now (defaults to `0` which remove all future delisted pairs no matter how far from now). +Removes pairs that will be delisted on the exchange maximum `max_days_from_now` days from now (defaults to `0` which remove all future delisted pairs no matter how far from now). Currently this filter only supports following exchanges: + +* Binance (Spot and Futures) !!! Warning "Backtesting" `DelistFilter` does not support backtesting mode. diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 38e3bc90f..09897bd7b 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -84,6 +84,7 @@ Check the [configuration documentation](configuration.md) about how to set the b **Always use dry mode when testing as this gives you an idea of how your strategy will work in reality without risking capital.** ## Diving in deeper + **For the following section we will use the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_strategy.py) file as reference.** @@ -99,9 +100,9 @@ file as reference.** Some common patterns for this are listed in the [Common Mistakes](#common-mistakes-when-developing-strategies) section of this document. ??? Hint "Lookahead and recursive analysis" - Freqtrade includes two helpful commands to help assess common lookahead (using future data) and - recursive bias (variance in indicator values) issues. Before running a strategy in dry or live more, - you should always use these commands first. Please check the relevant documentation for + Freqtrade includes two helpful commands to help assess common lookahead (using future data) and + recursive bias (variance in indicator values) issues. Before running a strategy in dry or live more, + you should always use these commands first. Please check the relevant documentation for [lookahead](lookahead-analysis.md) and [recursive](recursive-analysis.md) analysis. ### Dataframe @@ -154,7 +155,7 @@ Vectorized operations perform calculations across the whole range of data and ar !!! Warning "Trade order assumptions" In backtesting, signals are generated on candle close. Trades are then initiated immeditely on next candle open. - + In dry and live, this may be delayed due to all pair dataframes needing to be analysed first, then trade processing for each of those pairs happens. This means that in dry/live you need to be mindful of having as low a computation delay as possible, usually by running a low number of pairs and having a CPU with a good clock speed. @@ -284,7 +285,7 @@ It's important to always return the dataframe without removing/modifying the col This method will also define a new column, `"enter_long"` (`"enter_short"` for shorts), which needs to contain `1` for entries, and `0` for "no action". `enter_long` is a mandatory column that must be set even if the strategy is shorting only. -You can name your entry signals by using the `"enter_tag"` column, which can help debug and assess your strategy later. +You can name your entry signals by using the `"enter_tag"` column, which can help debug and assess your strategy later. Sample from `user_data/strategies/sample_strategy.py`: @@ -555,7 +556,7 @@ A full sample can be found [in the DataProvider section](#complete-dataprovider- ??? Note "Alternative candle types" Informative_pairs can also provide a 3rd tuple element defining the candle type explicitly. - Availability of alternative candle-types will depend on the trading-mode and the exchange. + Availability of alternative candle-types will depend on the trading-mode and the exchange. In general, spot pairs cannot be used in futures markets, and futures candles can't be used as informative pairs for spot bots. Details about this may vary, if they do, this can be found in the exchange documentation. @@ -775,6 +776,7 @@ Please always check the mode of operation to select the correct method to get da ### Possible options for DataProvider - [`available_pairs`](#available_pairs) - Property with tuples listing cached pairs with their timeframe (pair, timeframe). +- [`check_delisting(pair)`](#check_delisting) - Return Datetime of the pair delisting schedule if any, otherwise return None - [`current_whitelist()`](#current_whitelist) - Returns a current list of whitelisted pairs. Useful for accessing dynamic whitelists (i.e. VolumePairlist) - [`get_pair_dataframe(pair, timeframe)`](#get_pair_dataframepair-timeframe) - This is a universal method, which returns either historical data (for backtesting) or cached live data (for the Dry-Run and Live-Run modes). - [`get_analyzed_dataframe(pair, timeframe)`](#get_analyzed_dataframepair-timeframe) - Returns the analyzed dataframe (after calling `populate_indicators()`, `populate_buy()`, `populate_sell()`) and the time of the latest analysis. @@ -795,6 +797,15 @@ for pair, timeframe in self.dp.available_pairs: print(f"available {pair}, {timeframe}") ``` +### *check_delisting(pair)* + +```python +def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs): + delisting_dt = self.dp.check_delisting(pair) + if delisting_dt is not None: + return "delist" +``` + ### *current_whitelist()* Imagine you've developed a strategy that trades the `5m` timeframe using signals generated from a `1d` timeframe on the top 10 exchange pairs by volume. diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 1d45d7c79..275bd5b3f 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -41,6 +41,7 @@ class Binance(Exchange): "fetch_orders_limit_minutes": None, "l2_limit_range": [5, 10, 20, 50, 100, 500, 1000], "ws_enabled": True, + "has_delisting": True, } _ft_has_futures: FtHas = { "funding_fee_candle_limit": 1000, diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index de414dbbe..5e76170db 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -166,6 +166,7 @@ class Exchange: "proxy_coin_mapping": {}, # Mapping for proxy coins # Expected to be in the format {"fetchOHLCV": True} or {"fetchOHLCV": False} "ws_enabled": False, # Set to true for exchanges with tested websocket support + "has_delisting": False, # Set to true for exchanges that have delisting pair checks } _ft_has: FtHas = {} _ft_has_futures: FtHas = {} diff --git a/freqtrade/exchange/exchange_types.py b/freqtrade/exchange/exchange_types.py index cd2bd0059..d2c2b46e2 100644 --- a/freqtrade/exchange/exchange_types.py +++ b/freqtrade/exchange/exchange_types.py @@ -63,6 +63,9 @@ class FtHas(TypedDict, total=False): # Websocket control ws_enabled: bool + # Delisting check + has_delisting: bool + class Ticker(TypedDict): symbol: str diff --git a/freqtrade/plugins/pairlist/DelistFilter.py b/freqtrade/plugins/pairlist/DelistFilter.py index 3fc1f60ef..62b1e0bad 100644 --- a/freqtrade/plugins/pairlist/DelistFilter.py +++ b/freqtrade/plugins/pairlist/DelistFilter.py @@ -5,7 +5,7 @@ Delist pair list filter import logging from datetime import UTC, datetime, timedelta -from freqtrade.exceptions import OperationalException +from freqtrade.exceptions import ConfigurationError from freqtrade.exchange.exchange_types import Ticker from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter, SupportsBacktesting @@ -21,8 +21,11 @@ class DelistFilter(IPairList): self._max_days_from_now = self._pairlistconfig.get("max_days_from_now", 0) if self._max_days_from_now < 0: - raise OperationalException("DelistFilter requires max_days_from_now to be >= 0") - self._enabled = self._max_days_from_now >= 0 + raise ConfigurationError("DelistFilter requires max_days_from_now to be >= 0") + if not self._exchange._ft_has["has_delisting"]: + raise ConfigurationError( + "DelistFilter doesn't support this exchange and trading mode combination.", + ) @property def needstickers(self) -> bool: @@ -49,7 +52,7 @@ class DelistFilter(IPairList): @staticmethod def description() -> str: - return "Filter pairs that will bbe delisted on exchange." + return "Filter pairs that will be delisted on exchange." @staticmethod def available_parameters() -> dict[str, PairlistParameter]: