From 0dbe507b26ffc45ec2d4f07ff41720781c9873d9 Mon Sep 17 00:00:00 2001 From: "Jakub Werner (jakubikan)" Date: Wed, 25 Sep 2024 21:11:52 +0200 Subject: [PATCH] making list of categories available --- freqtrade/plugins/pairlist/IPairList.py | 20 +++--- .../plugins/pairlist/MarketCapPairList.py | 63 ++++++++++++------- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/freqtrade/plugins/pairlist/IPairList.py b/freqtrade/plugins/pairlist/IPairList.py index 755f52b06..7be86df7e 100644 --- a/freqtrade/plugins/pairlist/IPairList.py +++ b/freqtrade/plugins/pairlist/IPairList.py @@ -38,6 +38,11 @@ class __OptionPairlistParameter(__PairlistParameterBase): default: Union[str, None] options: List[str] +class __ListPairListParamenter(__PairlistParameterBase): + type: Literal["list"] + default: Union[List[str], None] + options: List[str] + class __BoolPairlistParameter(__PairlistParameterBase): type: Literal["boolean"] @@ -49,6 +54,7 @@ PairlistParameter = Union[ __StringPairlistParameter, __OptionPairlistParameter, __BoolPairlistParameter, + __ListPairListParamenter ] @@ -68,12 +74,12 @@ class IPairList(LoggingMixin, ABC): supports_backtesting: SupportsBacktesting = SupportsBacktesting.NO def __init__( - self, - exchange: Exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, + self, + exchange: Exchange, + pairlistmanager, + config: Config, + pairlistconfig: Dict[str, Any], + pairlist_pos: int, ) -> None: """ :param exchange: Exchange instance @@ -213,7 +219,7 @@ class IPairList(LoggingMixin, ABC): return self._pairlistmanager.verify_blacklist(pairlist, logmethod) def verify_whitelist( - self, pairlist: List[str], logmethod, keep_invalid: bool = False + self, pairlist: List[str], logmethod, keep_invalid: bool = False ) -> List[str]: """ Proxy method to verify_whitelist for easy access for child classes. diff --git a/freqtrade/plugins/pairlist/MarketCapPairList.py b/freqtrade/plugins/pairlist/MarketCapPairList.py index 96edf81b5..b9461589d 100644 --- a/freqtrade/plugins/pairlist/MarketCapPairList.py +++ b/freqtrade/plugins/pairlist/MarketCapPairList.py @@ -14,7 +14,6 @@ from freqtrade.exchange.exchange_types import Tickers from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter, SupportsBacktesting from freqtrade.util.coin_gecko import FtCoinGeckoApi - logger = logging.getLogger(__name__) @@ -35,7 +34,7 @@ class MarketCapPairList(IPairList): self._number_assets = self._pairlistconfig["number_assets"] self._max_rank = self._pairlistconfig.get("max_rank", 30) self._refresh_period = self._pairlistconfig.get("refresh_period", 86400) - self._category = self._pairlistconfig.get("category", None) + self._categories = self._pairlistconfig.get("categories", []) self._marketcap_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period) self._def_candletype = self._config["candle_type_def"] @@ -46,14 +45,14 @@ class MarketCapPairList(IPairList): is_demo=_coingecko_config.get("is_demo", True), ) - if self._category: + if self._categories: categories = self._coingecko.get_coins_categories_list() - category_ids = [cat["category_id"] for cat in categories] + category_ids = [cat['category_id'] for cat in categories] - if self._category not in category_ids: - raise OperationalException( - f"category not in coingecko category list you can choose from {category_ids}" - ) + for category in self._categories: + if category not in category_ids: + raise OperationalException( + f"category not in coingecko category list you can choose from {category_ids}") if self._max_rank > 250: raise OperationalException("This filter only support marketcap rank up to 250.") @@ -95,11 +94,11 @@ class MarketCapPairList(IPairList): "description": "Max rank of assets", "help": "Maximum rank of assets to use from the pairlist", }, - "category": { - "type": "string", - "default": None, - "description": "The Category", - "help": "Th Category of the coin e.g layer-1 default None", + "categories": { + "type": "list", + "default": [], + "description": "The Categories to be set", + "help": "The Category of the coin e.g layer-1 default [] (https://www.coingecko.com/en/categories)", }, "refresh_period": { "type": "number", @@ -148,16 +147,32 @@ class MarketCapPairList(IPairList): """ marketcap_list = self._marketcap_cache.get("marketcap") + default_kwargs = { + "vs_currency": "usd", + "order": "market_cap_desc", + "per_page": "250", + "page": "1", + "sparkline": "false", + "locale": "en", + } + if marketcap_list is None: - data = self._coingecko.get_coins_markets( - vs_currency="usd", - order="market_cap_desc", - per_page="250", - page="1", - sparkline="false", - locale="en", - **({"category": self._category} if self._category else {}), - ) + data = [] + + if not self._categories: + data = self._coingecko.get_coins_markets( + **default_kwargs + ) + else: + for category in self._categories: + category_data = self._coingecko.get_coins_markets( + **default_kwargs, + **({"category": category} if category else {}) + ) + data += category_data + + data.sort(key=lambda d: float(d['market_cap'] or 0.0), reverse=True) + if data: marketcap_list = [row["symbol"] for row in data] self._marketcap_cache["marketcap"] = marketcap_list @@ -170,11 +185,11 @@ class MarketCapPairList(IPairList): if market == "futures": pair_format += f":{self._stake_currency.upper()}" - top_marketcap = marketcap_list[: self._max_rank :] + top_marketcap = marketcap_list[: self._max_rank:] for mc_pair in top_marketcap: test_pair = f"{mc_pair.upper()}/{pair_format}" - if test_pair in pairlist: + if test_pair in pairlist and test_pair not in filtered_pairlist: filtered_pairlist.append(test_pair) if len(filtered_pairlist) == self._number_assets: break