change into pairlist plugin

This commit is contained in:
Stefano
2026-02-25 17:17:26 +09:00
parent 9719f28795
commit 8fb21e8f50
3 changed files with 55 additions and 17 deletions

View File

@@ -2,11 +2,11 @@
Pairlist Handlers define the list of pairs (pairlist) that the bot should trade. They are configured in the `pairlists` section of the configuration settings.
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), [`CrossMarketPairlist`](#crossmarketpairlist), [`MarketCapPairlist`](#marketcappairlist) and [`PercentChangePairList`](#percent-change-pair-list) Pairlist Handlers).
Additionally, [`AgeFilter`](#agefilter), [`CrossMarketFilter`](#crossmarketfilter), [`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.
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`, `PercentChangePairList` or `CrossMarketPairList` as the starting Pairlist Handler.
Inactive markets are always removed from the resulting pairlist. Explicitly blacklisted pairs (those in the `pair_blacklist` configuration setting) are also always removed from the resulting pairlist.
@@ -26,8 +26,8 @@ You may also use something like `.*DOWN/BTC` or `.*UP/BTC` to exclude leveraged
* [`ProducerPairList`](#producerpairlist)
* [`RemotePairList`](#remotepairlist)
* [`MarketCapPairList`](#marketcappairlist)
* [`CrossMarketPairlist`](#crossmarketpairlist)
* [`AgeFilter`](#agefilter)
* [`CrossMarketFilter`](#crossmarketfilter)
* [`DelistFilter`](#delistfilter)
* [`FullTradesFilter`](#fulltradesfilter)
* [`OffsetFilter`](#offsetfilter)
@@ -403,6 +403,12 @@ Coins like 1000PEPE/USDT or KPEPE/USDT:USDT are detected on a best effort basis,
!!! Danger "Duplicate symbols in coingecko"
Coingecko often has duplicate symbols, where the same symbol is used for different coins. Freqtrade will use the symbol as is and try to search for it on the exchange. If the symbol exists - it will be used. Freqtrade will however not check if the _intended_ symbol is the one coingecko meant. This can sometimes lead to unexpected results, especially on low volume coins or with meme coin categories.
#### CrossMarketPairlist
Generate or filter pairs based of their availability on the opposite market. So for spot pairs, it will be checked against futures market, and vice versa.
The `mode` setting defines whether the plugin will filters in (whitelist `mode`) or filters out (blacklist `mode`) pairs if they are active on opposite market. By default, the plugin will be in whitelist mode.
#### AgeFilter
Removes pairs that have been listed on the exchange for less than `min_days_listed` days (defaults to `10`) or more than `max_days_listed` days (defaults `None` mean infinity).
@@ -413,12 +419,6 @@ 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`.
#### CrossMarketFilter
Filter pairs based of their availability on the opposite market. So for spot pairs, it will be checked against futures market, and vice versa.
The `mode` setting defines whether the plugin will filters in (whitelist `mode`) or filters out (blacklist `mode`) based of the availability on the opposite market. By default, the plugin will be in whitelist mode.
#### 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). Currently this filter only supports following exchanges:

View File

@@ -61,8 +61,8 @@ AVAILABLE_PAIRLISTS = [
"ProducerPairList",
"RemotePairList",
"MarketCapPairList",
"CrossMarketPairlist",
"AgeFilter",
"CrossMarketFilter",
"DelistFilter",
"FullTradesFilter",
"OffsetFilter",

View File

@@ -4,17 +4,15 @@ Price pair list filter
import logging
import ccxt.pro as ccxt_pro
from freqtrade.exceptions import OperationalException
from freqtrade.exchange.exchange_types import Tickers
from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter, SupportsBacktesting
from freqtrade.util import FtTTLCache
logger = logging.getLogger(__name__)
class CrossMarketFilter(IPairList):
class CrossMarketPairlist(IPairList):
supports_backtesting = SupportsBacktesting.BIASED
def __init__(self, *args, **kwargs) -> None:
@@ -24,6 +22,8 @@ class CrossMarketFilter(IPairList):
self._trading_mode: str = self._config["trading_mode"]
self._stake_currency: str = self._config["stake_currency"]
self._target_mode = "futures" if self._trading_mode == "spot" else "spot"
self._refresh_period = self._pairlistconfig.get("refresh_period", 1800)
self._pair_cache: FtTTLCache = FtTTLCache(maxsize=1, ttl=self._refresh_period)
@property
def needstickers(self) -> bool:
@@ -57,6 +57,7 @@ class CrossMarketFilter(IPairList):
"description": "Mode of operation",
"help": "Mode of operation (whitelist/blacklist)",
},
**IPairList.refresh_period_parameter(),
}
def get_base_list(self):
@@ -77,6 +78,35 @@ class CrossMarketFilter(IPairList):
prefixes = ("1000", "1000000", "1M", "K", "M")
def gen_pairlist(self, tickers: Tickers) -> list[str]:
"""
Generate the pairlist
:param tickers: Tickers (from exchange.get_tickers). May be cached.
:return: List of pairs
"""
# Generate dynamic whitelist
# Must always run if this pairlist is the first in the list.
pairlist = self._pair_cache.get("pairlist")
if pairlist:
# Item found - no refresh necessary
return pairlist.copy()
else:
# Use fresh pairlist
# Check if pair quote currency equals to the stake currency.
_pairlist = [
k
for k in self._exchange.get_markets(
quote_currencies=[self._stake_currency], tradable_only=True, active_only=True
).keys()
]
_pairlist = self.verify_blacklist(_pairlist, logger.info)
pairlist = self.filter_pairlist(_pairlist, tickers)
self._pair_cache["pairlist"] = pairlist.copy()
return pairlist
def filter_pairlist(self, pairlist: list[str], tickers: Tickers) -> list[str]:
bases = self.get_base_list()
is_whitelist_mode = self._mode == "whitelist"
@@ -88,10 +118,18 @@ class CrossMarketFilter(IPairList):
found_in_bases = base in bases
if not found_in_bases:
for prefix in self.prefixes:
# Check in case of PEPE needs to be changed to 1000PEPE for example
test_prefix = f"{prefix}{base}"
if test_prefix in bases:
found_in_bases = True
found_in_bases = test_prefix in bases
if found_in_bases:
break
# Check in case of 1000PEPE needs to be changed to PEPE for example
if base.startswith(prefix):
temp_base = base.removeprefix(prefix)
found_in_bases = temp_base in bases
if found_in_bases:
break
if found_in_bases:
whitelisted_pairlist.append(pair)
filtered_pairlist.remove(pair)