add crossmarketfilter

This commit is contained in:
Stefano
2026-02-25 15:51:56 +09:00
parent 6d04874e26
commit 9719f28795
3 changed files with 108 additions and 1 deletions
+8 -1
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).
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.
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.
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)
* [`MarketCapPairList`](#marketcappairlist)
* [`AgeFilter`](#agefilter)
* [`CrossMarketFilter`](#crossmarketfilter)
* [`DelistFilter`](#delistfilter)
* [`FullTradesFilter`](#fulltradesfilter)
* [`OffsetFilter`](#offsetfilter)
@@ -412,6 +413,12 @@ 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:
+1
View File
@@ -62,6 +62,7 @@ AVAILABLE_PAIRLISTS = [
"RemotePairList",
"MarketCapPairList",
"AgeFilter",
"CrossMarketFilter",
"DelistFilter",
"FullTradesFilter",
"OffsetFilter",
@@ -0,0 +1,99 @@
"""
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
logger = logging.getLogger(__name__)
class CrossMarketFilter(IPairList):
supports_backtesting = SupportsBacktesting.BIASED
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self._mode: str = self._pairlistconfig.get("mode", "whitelist")
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"
@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
"""
mode = self._mode
target_mode = self._target_mode
msg = f"{self.name} - {mode.capitalize()} pairs that exists on {target_mode} market."
return msg
@staticmethod
def description() -> str:
return "Filter pairs if they exist on another market."
@staticmethod
def available_parameters() -> dict[str, PairlistParameter]:
return {
"mode": {
"type": "option",
"default": "whitelist",
"options": ["whitelist", "blacklist"],
"description": "Mode of operation",
"help": "Mode of operation (whitelist/blacklist)",
},
}
def get_base_list(self):
target_mode = self._target_mode
spot_only = True if target_mode == "spot" else False
futures_only = True if target_mode == "futures" else False
bases = [
v.get("base", "")
for k, v in self._exchange.get_markets(
quote_currencies=[self._stake_currency],
tradable_only=False,
active_only=True,
spot_only=spot_only,
futures_only=futures_only,
).items()
]
return bases
prefixes = ("1000", "1000000", "1M", "K", "M")
def filter_pairlist(self, pairlist: list[str], tickers: Tickers) -> list[str]:
bases = self.get_base_list()
is_whitelist_mode = self._mode == "whitelist"
whitelisted_pairlist: list[str] = []
filtered_pairlist = pairlist.copy()
for pair in pairlist:
base = self._exchange.get_pair_base_currency(pair)
found_in_bases = base in bases
if not found_in_bases:
for prefix in self.prefixes:
test_prefix = f"{prefix}{base}"
if test_prefix in bases:
found_in_bases = True
break
if found_in_bases:
whitelisted_pairlist.append(pair)
filtered_pairlist.remove(pair)
return whitelisted_pairlist if is_whitelist_mode else filtered_pairlist