add DelistFilter

This commit is contained in:
Stefano
2025-09-17 09:16:05 +09:00
parent d8d8260d76
commit 8e500d4601
5 changed files with 150 additions and 24 deletions

View File

@@ -587,6 +587,7 @@
"RemotePairList", "RemotePairList",
"MarketCapPairList", "MarketCapPairList",
"AgeFilter", "AgeFilter",
"DelistFilter",
"FullTradesFilter", "FullTradesFilter",
"OffsetFilter", "OffsetFilter",
"PerformanceFilter", "PerformanceFilter",

View File

@@ -25,10 +25,10 @@
"trading_mode": "spot", "trading_mode": "spot",
"margin_mode": "", "margin_mode": "",
"minimal_roi": { "minimal_roi": {
"40": 0.0, "40": 0.0,
"30": 0.01, "30": 0.01,
"20": 0.02, "20": 0.02,
"0": 0.04 "0": 0.04
}, },
"stoploss": -0.10, "stoploss": -0.10,
"unfilledtimeout": { "unfilledtimeout": {
@@ -47,7 +47,7 @@
"bids_to_ask_delta": 1 "bids_to_ask_delta": 1
} }
}, },
"exit_pricing":{ "exit_pricing": {
"price_side": "same", "price_side": "same",
"use_order_book": true, "use_order_book": true,
"order_book_top": 1, "order_book_top": 1,
@@ -70,18 +70,38 @@
"exit": "GTC" "exit": "GTC"
}, },
"pairlists": [ "pairlists": [
{"method": "StaticPairList"}, {
{"method": "FullTradesFilter"}, "method": "StaticPairList"
},
{
"method": "DelistFilter",
"max_days_from_now": 0,
},
{
"method": "FullTradesFilter"
},
{ {
"method": "VolumePairList", "method": "VolumePairList",
"number_assets": 20, "number_assets": 20,
"sort_key": "quoteVolume", "sort_key": "quoteVolume",
"refresh_period": 1800 "refresh_period": 1800
}, },
{"method": "AgeFilter", "min_days_listed": 10}, {
{"method": "PrecisionFilter"}, "method": "AgeFilter",
{"method": "PriceFilter", "low_price_ratio": 0.01, "min_price": 0.00000010}, "min_days_listed": 10
{"method": "SpreadFilter", "max_spread_ratio": 0.005}, },
{
"method": "PrecisionFilter"
},
{
"method": "PriceFilter",
"low_price_ratio": 0.01,
"min_price": 0.00000010
},
{
"method": "SpreadFilter",
"max_spread_ratio": 0.005
},
{ {
"method": "RangeStabilityFilter", "method": "RangeStabilityFilter",
"lookback_days": 10, "lookback_days": 10,
@@ -166,12 +186,12 @@
"external_message_consumer": { "external_message_consumer": {
"enabled": false, "enabled": false,
"producers": [ "producers": [
{ {
"name": "default", "name": "default",
"host": "127.0.0.2", "host": "127.0.0.2",
"port": 8080, "port": 8080,
"ws_token": "secret_ws_t0ken." "ws_token": "secret_ws_t0ken."
} }
], ],
"wait_timeout": 300, "wait_timeout": 300,
"ping_timeout": 10, "ping_timeout": 10,

View File

@@ -4,7 +4,7 @@ Pairlist Handlers define the list of pairs (pairlist) that the bot should trade.
In your configuration, you can use Static Pairlist (defined by the [`StaticPairList`](#static-pair-list) Pairlist Handler) and Dynamic Pairlist (defined by the [`VolumePairList`](#volume-pair-list) and [`PercentChangePairList`](#percent-change-pair-list) Pairlist Handlers). In your configuration, you can use Static Pairlist (defined by the [`StaticPairList`](#static-pair-list) Pairlist Handler) and Dynamic Pairlist (defined by the [`VolumePairList`](#volume-pair-list) and [`PercentChangePairList`](#percent-change-pair-list) Pairlist Handlers).
Additionally, [`AgeFilter`](#agefilter), [`PrecisionFilter`](#precisionfilter), [`PriceFilter`](#pricefilter), [`ShuffleFilter`](#shufflefilter), [`SpreadFilter`](#spreadfilter) and [`VolatilityFilter`](#volatilityfilter) act as Pairlist Filters, removing certain pairs and/or moving their positions in the pairlist. Additionally, [`AgeFilter`](#agefilter), [`DelistFilter`](#delistfilter), [`PrecisionFilter`](#precisionfilter), [`PriceFilter`](#pricefilter), [`ShuffleFilter`](#shufflefilter), [`SpreadFilter`](#spreadfilter) and [`VolatilityFilter`](#volatilityfilter) act as Pairlist Filters, removing certain pairs and/or moving their positions in the pairlist.
If multiple Pairlist Handlers are used, they are chained and a combination of all Pairlist Handlers forms the resulting pairlist the bot uses for trading and backtesting. Pairlist Handlers are executed in the sequence they are configured. You can define either `StaticPairList`, `VolumePairList`, `ProducerPairList`, `RemotePairList`, `MarketCapPairList` or `PercentChangePairList` as the starting Pairlist Handler. If multiple Pairlist Handlers are used, they are chained and a combination of all Pairlist Handlers forms the resulting pairlist the bot uses for trading and backtesting. Pairlist Handlers are executed in the sequence they are configured. You can define either `StaticPairList`, `VolumePairList`, `ProducerPairList`, `RemotePairList`, `MarketCapPairList` or `PercentChangePairList` as the starting Pairlist Handler.
@@ -27,6 +27,7 @@ You may also use something like `.*DOWN/BTC` or `.*UP/BTC` to exclude leveraged
* [`RemotePairList`](#remotepairlist) * [`RemotePairList`](#remotepairlist)
* [`MarketCapPairList`](#marketcappairlist) * [`MarketCapPairList`](#marketcappairlist)
* [`AgeFilter`](#agefilter) * [`AgeFilter`](#agefilter)
* [`DelistFilter`](#delistfilter)
* [`FullTradesFilter`](#fulltradesfilter) * [`FullTradesFilter`](#fulltradesfilter)
* [`OffsetFilter`](#offsetfilter) * [`OffsetFilter`](#offsetfilter)
* [`PerformanceFilter`](#performancefilter) * [`PerformanceFilter`](#performancefilter)
@@ -270,7 +271,6 @@ You can limit the length of the pairlist with the optional parameter `number_ass
], ],
``` ```
!!! Tip "Combining pairlists" !!! Tip "Combining pairlists"
This pairlist can be combined with all other pairlists and filters for further pairlist reduction, and can also act as an "additional" pairlist, on top of already defined pairs. This pairlist can be combined with all other pairlists and filters for further pairlist reduction, and can also act as an "additional" pairlist, on top of already defined pairs.
`ProducerPairList` can also be used multiple times in sequence, combining the pairs from multiple producers. `ProducerPairList` can also be used multiple times in sequence, combining the pairs from multiple producers.
@@ -407,6 +407,13 @@ be caught out buying before the pair has finished dropping in price.
This filter allows freqtrade to ignore pairs until they have been listed for at least `min_days_listed` days and listed before `max_days_listed`. This filter allows freqtrade to ignore pairs until they have been listed for at least `min_days_listed` days and listed before `max_days_listed`.
#### 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).
!!! Warning "Backtesting"
`DelistFilter` does not support backtesting mode.
#### FullTradesFilter #### FullTradesFilter
Shrink whitelist to consist only in-trade pairs when the trade slots are full (when `max_open_trades` isn't being set to `-1` in the config). Shrink whitelist to consist only in-trade pairs when the trade slots are full (when `max_open_trades` isn't being set to `-1` in the config).
@@ -601,7 +608,7 @@ Adding `"sort_direction": "asc"` or `"sort_direction": "desc"` enables sorting m
### Full example of Pairlist Handlers ### Full example of Pairlist Handlers
The below example blacklists `BNB/BTC`, uses `VolumePairList` with `20` assets, sorting pairs by `quoteVolume` and applies [`PrecisionFilter`](#precisionfilter) and [`PriceFilter`](#pricefilter), filtering all assets where 1 price unit is > 1%. Then the [`SpreadFilter`](#spreadfilter) and [`VolatilityFilter`](#volatilityfilter) is applied and pairs are finally shuffled with the random seed set to some predefined value. The below example blacklists `BNB/BTC`, uses `VolumePairList` with `20` assets, sorting pairs by `quoteVolume`, then filter future delisted pairs using [`DelistFilter`](#delistfilter) and [`AgeFilter`](#agefilter) to remove pairs that are listed less than 10 days ago. After that [`PrecisionFilter`](#precisionfilter) and [`PriceFilter`](#pricefilter) are applied, filtering all assets where 1 price unit is > 1%. Then the [`SpreadFilter`](#spreadfilter) and [`VolatilityFilter`](#volatilityfilter) are applied and pairs are finally shuffled with the random seed set to some predefined value.
```json ```json
"exchange": { "exchange": {
@@ -614,6 +621,10 @@ The below example blacklists `BNB/BTC`, uses `VolumePairList` with `20` assets,
"number_assets": 20, "number_assets": 20,
"sort_key": "quoteVolume" "sort_key": "quoteVolume"
}, },
{
"method": "DelistFilter",
"max_days_from_now": 0,
},
{"method": "AgeFilter", "min_days_listed": 10}, {"method": "AgeFilter", "min_days_listed": 10},
{"method": "PrecisionFilter"}, {"method": "PrecisionFilter"},
{"method": "PriceFilter", "low_price_ratio": 0.01}, {"method": "PriceFilter", "low_price_ratio": 0.01},

View File

@@ -49,6 +49,7 @@ AVAILABLE_PAIRLISTS = [
"RemotePairList", "RemotePairList",
"MarketCapPairList", "MarketCapPairList",
"AgeFilter", "AgeFilter",
"DelistFilter",
"FullTradesFilter", "FullTradesFilter",
"OffsetFilter", "OffsetFilter",
"PerformanceFilter", "PerformanceFilter",

View File

@@ -0,0 +1,93 @@
"""
Delist pair list filter
"""
import logging
from datetime import UTC, datetime, timedelta
from freqtrade.exceptions import OperationalException
from freqtrade.exchange.exchange_types import Ticker
from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter, SupportsBacktesting
logger = logging.getLogger(__name__)
class DelistFilter(IPairList):
supports_backtesting = SupportsBacktesting.NO
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
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
@property
def needstickers(self) -> bool:
"""
Boolean property defining if tickers are necessary.
If no Pairlist requires tickers, an empty Dict is passed
as tickers argument to filter_pairlist
"""
return False
def short_desc(self) -> str:
"""
Short whitelist method description - used for startup-messages
"""
return (
f"{self.name} - Filtering pairs that will be delisted"
+ (
f" in the next {self._max_days_from_now} days"
if self._max_days_from_now > 0
else ""
)
+ "."
)
@staticmethod
def description() -> str:
return "Filter pairs that will bbe delisted on exchange."
@staticmethod
def available_parameters() -> dict[str, PairlistParameter]:
return {
"max_days_from_now": {
"type": "number",
"default": 0,
"description": "Max days from now",
"help": (
"Remove pairs that will be delisted in the next X days. Set to 0 to remove all."
),
},
}
def _validate_pair(self, pair: str, ticker: Ticker | None) -> bool:
"""
Check if pair will be delisted.
:param pair: Pair that's currently validated
:param ticker: ticker dict as returned from ccxt.fetch_ticker
:return: True if the pair can stay, false if it should be removed
"""
delist_date = self._exchange.check_delisting_time(pair)
if delist_date is not None:
if self._max_days_from_now == 0:
self.log_once(
f"Removed {pair} from whitelist, because it will be delisted on {delist_date}.",
logger.info,
)
return False
else:
current_datetime = datetime.now(UTC)
max_delist_date = current_datetime + timedelta(days=self._max_days_from_now)
if delist_date <= max_delist_date:
self.log_once(
f"Removed {pair} from whitelist, because it will be delisted on {delist_date}.",
logger.info,
)
return False
return True