Skip to content

Commit d1014ce

Browse files
authored
Merge pull request freqtrade#12448 from stash86/main-stash
Add blacklist mode to MarketcapPairlist
2 parents a2ec085 + 3bad6d3 commit d1014ce

File tree

3 files changed

+66
-16
lines changed

3 files changed

+66
-16
lines changed

docs/includes/pairlists.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ The optional `bearer_token` will be included in the requests Authorization Heade
367367

368368
#### MarketCapPairList
369369

370-
`MarketCapPairList` employs sorting/filtering of pairs by their marketcap rank based of CoinGecko. The returned pairlist will be sorted based of their marketcap ranks.
370+
`MarketCapPairList` employs sorting/filtering of pairs by their marketcap rank based of CoinGecko. The returned pairlist will be sorted based of their marketcap ranks if used in whitelist `mode`.
371371

372372
```json
373373
"pairlists": [
@@ -376,16 +376,21 @@ The optional `bearer_token` will be included in the requests Authorization Heade
376376
"number_assets": 20,
377377
"max_rank": 50,
378378
"refresh_period": 86400,
379+
"mode": "whitelist",
379380
"categories": ["layer-1"]
380381
}
381382
]
382383
```
383384

384-
`number_assets` defines the maximum number of pairs returned by the pairlist. `max_rank` will determine the maximum rank used in creating/filtering the pairlist. It's expected that some coins within the top `max_rank` marketcap will not be included in the resulting pairlist since not all pairs will have active trading pairs in your preferred market/stake/exchange combination.
385+
`number_assets` defines the maximum number of pairs returned by the pairlist if used in whitelist `mode`. In blacklist `mode`, this setting will be ignored.
386+
387+
`max_rank` will determine the maximum rank used in creating/filtering the pairlist. It's expected that some coins within the top `max_rank` marketcap will not be included in the resulting pairlist since not all pairs will have active trading pairs in your preferred market/stake/exchange combination.
385388
While using a `max_rank` bigger than 250 is supported, it's not recommended, as it'll cause multiple API calls to CoinGecko, which can lead to rate limit issues.
386389

387390
The `refresh_period` setting defines the interval (in seconds) at which the marketcap rank data will be refreshed. The default is 86,400 seconds (1 day). The pairlist cache (`refresh_period`) applies to both generating pairlists (when in the first position in the list) and filtering instances (when not in the first position in the list).
388391

392+
The `mode` setting defines whether the plugin will filters in (whitelist `mode`) or filters out (blacklist `mode`) top marketcap ranked coins. By default, the plugin will be in whitelist mode.
393+
389394
The `categories` setting specifies the [coingecko categories](https://www.coingecko.com/en/categories) from which to select coins from. The default is an empty list `[]`, meaning no category filtering is applied.
390395
If an incorrect category string is chosen, the plugin will print the available categories from CoinGecko and fail. The category should be the ID of the category, for example, for `https://www.coingecko.com/en/categories/layer-1`, the category ID would be `layer-1`. You can pass multiple categories such as `["layer-1", "meme-token"]` to select from several categories.
391396

freqtrade/plugins/pairlist/MarketCapPairList.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ class MarketCapPairList(IPairList):
2525
def __init__(self, *args, **kwargs) -> None:
2626
super().__init__(*args, **kwargs)
2727

28-
if "number_assets" not in self._pairlistconfig:
28+
self._mode = self._pairlistconfig.get("mode", "whitelist")
29+
30+
if (self._mode == "whitelist") and ("number_assets" not in self._pairlistconfig):
2931
raise OperationalException(
3032
"`number_assets` not specified. Please check your configuration "
3133
'for "pairlist.config.number_assets"'
3234
)
3335

3436
self._stake_currency = self._config["stake_currency"]
35-
self._number_assets = self._pairlistconfig["number_assets"]
37+
self._number_assets = self._pairlistconfig.get("number_assets", 30)
3638
self._max_rank = self._pairlistconfig.get("max_rank", 30)
3739
self._refresh_period = self._pairlistconfig.get("refresh_period", 86400)
3840
self._categories = self._pairlistconfig.get("categories", [])
@@ -78,7 +80,9 @@ def short_desc(self) -> str:
7880
"""
7981
num = self._number_assets
8082
rank = self._max_rank
81-
msg = f"{self.name} - {num} pairs placed within top {rank} market cap."
83+
mode = self._mode
84+
pair_text = num if (mode == "whitelist") else "blacklisting"
85+
msg = f"{self.name} - {pair_text} pairs placed within top {rank} market cap."
8286
return msg
8387

8488
@staticmethod
@@ -115,6 +119,13 @@ def available_parameters() -> dict[str, PairlistParameter]:
115119
"description": "Refresh period",
116120
"help": "Refresh period in seconds",
117121
},
122+
"mode": {
123+
"type": "option",
124+
"default": "whitelist",
125+
"options": ["whitelist", "blacklist"],
126+
"description": "Mode of operation",
127+
"help": "Mode of operation (whitelist/blacklist)",
128+
},
118129
}
119130

120131
def get_markets_exchange(self):
@@ -186,6 +197,9 @@ def filter_pairlist(self, pairlist: list[str], tickers: dict) -> list[str]:
186197
:return: new whitelist
187198
"""
188199
marketcap_list = self._marketcap_cache.get("marketcap")
200+
mode = self._mode
201+
is_whitelist_mode = mode == "whitelist"
202+
filtered_pairlist: list[str] = []
189203

190204
default_kwargs = {
191205
"vs_currency": "usd",
@@ -219,12 +233,10 @@ def filter_pairlist(self, pairlist: list[str], tickers: dict) -> list[str]:
219233
self._marketcap_cache["marketcap"] = marketcap_list
220234

221235
if marketcap_list:
222-
filtered_pairlist: list[str] = []
223-
224236
market = self._exchange._config["trading_mode"]
225-
pair_format = f"{self._stake_currency.upper()}"
226-
if market == "futures":
227-
pair_format += f":{self._stake_currency.upper()}"
237+
pair_format = f"{self._stake_currency.upper()}" + (
238+
f":{self._stake_currency.upper()}" if market == "futures" else ""
239+
)
228240

229241
top_marketcap = marketcap_list[: self._max_rank :]
230242
markets = self.get_markets_exchange()
@@ -234,13 +246,16 @@ def filter_pairlist(self, pairlist: list[str], tickers: dict) -> list[str]:
234246
resolved = self.resolve_marketcap_pair(pair, pairlist, markets, filtered_pairlist)
235247

236248
if resolved:
237-
filtered_pairlist.append(resolved)
249+
if not is_whitelist_mode:
250+
pairlist.remove(resolved)
251+
continue
238252

239-
if len(filtered_pairlist) == self._number_assets:
240-
break
253+
filtered_pairlist.append(resolved)
254+
if len(filtered_pairlist) == self._number_assets:
255+
break
241256

242-
if len(filtered_pairlist) > 0:
243-
return filtered_pairlist
257+
if not is_whitelist_mode:
258+
return pairlist
244259

245260
# If no pairs are found, return the original pairlist
246-
return []
261+
return filtered_pairlist

tests/plugins/test_pairlist.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,6 +2334,36 @@ def test_FullTradesFilter(mocker, default_conf_usdt, fee, caplog) -> None:
23342334
["ETH/USDT:USDT", "ADA/USDT:USDT"],
23352335
["layer-1", "protocol"],
23362336
),
2337+
(
2338+
[
2339+
# Blacklist high MC pairs
2340+
{"method": "StaticPairList", "allow_inactive": True},
2341+
{"method": "MarketCapPairList", "mode": "blacklist"},
2342+
],
2343+
"spot",
2344+
["LTC/USDT", "NEO/USDT", "TKN/USDT", "ETC/USDT"],
2345+
1,
2346+
),
2347+
(
2348+
[
2349+
# Blacklist high MC pairs
2350+
{"method": "StaticPairList", "allow_inactive": True},
2351+
{"method": "MarketCapPairList", "mode": "blacklist", "max_rank": 2},
2352+
],
2353+
"spot",
2354+
["LTC/USDT", "XRP/USDT", "NEO/USDT", "TKN/USDT", "ETC/USDT", "ADA/USDT"],
2355+
1,
2356+
),
2357+
(
2358+
[
2359+
# Blacklist top 6 MarketCap pairs - removes XRP which is at spot 6.
2360+
{"method": "StaticPairList", "allow_inactive": True},
2361+
{"method": "MarketCapPairList", "mode": "blacklist", "max_rank": 6},
2362+
],
2363+
"spot",
2364+
["LTC/USDT", "NEO/USDT", "TKN/USDT", "ETC/USDT", "ADA/USDT"],
2365+
1,
2366+
),
23372367
],
23382368
)
23392369
def test_MarketCapPairList_filter(

0 commit comments

Comments
 (0)