mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-11-29 08:33:07 +00:00
Merge pull request #12077 from mrpabloyeah/allow-pairs-with-prefix-in-marketcap-pairList
Allow pairs with prefix in MarketCapPairList
This commit is contained in:
@@ -389,6 +389,8 @@ The `refresh_period` setting defines the interval (in seconds) at which the mark
|
||||
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.
|
||||
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.
|
||||
|
||||
Coins like 1000PEPE/USDT or KPEPE/USDT:USDT are detected on a best effort basis, with the prefixes `1000` and `K` being used to identify them.
|
||||
|
||||
!!! Warning "Many categories"
|
||||
Each added category corresponds to one API call to CoinGecko. The more categories you add, the longer the pairlist generation will take, potentially causing rate limit issues.
|
||||
|
||||
|
||||
@@ -117,6 +117,16 @@ class MarketCapPairList(IPairList):
|
||||
},
|
||||
}
|
||||
|
||||
def get_markets_exchange(self):
|
||||
markets = [
|
||||
k
|
||||
for k in self._exchange.get_markets(
|
||||
quote_currencies=[self._stake_currency], tradable_only=True, active_only=True
|
||||
).keys()
|
||||
]
|
||||
|
||||
return markets
|
||||
|
||||
def gen_pairlist(self, tickers: Tickers) -> list[str]:
|
||||
"""
|
||||
Generate the pairlist
|
||||
@@ -132,12 +142,8 @@ class MarketCapPairList(IPairList):
|
||||
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.get_markets_exchange()
|
||||
|
||||
# No point in testing for blacklisted pairs...
|
||||
_pairlist = self.verify_blacklist(_pairlist, logger.info)
|
||||
|
||||
@@ -146,6 +152,31 @@ class MarketCapPairList(IPairList):
|
||||
|
||||
return pairlist
|
||||
|
||||
# Prefixes to test to discover coins like 1000PEPE/USDDT:USDT or KPEPE/USDC (hyperliquid)
|
||||
prefixes = ("1000", "K")
|
||||
|
||||
def resolve_marketcap_pair(
|
||||
self,
|
||||
pair: str,
|
||||
pairlist: list[str],
|
||||
markets: list[str],
|
||||
filtered_pairlist: list[str],
|
||||
) -> str | None:
|
||||
if pair in filtered_pairlist:
|
||||
return None
|
||||
|
||||
if pair in pairlist:
|
||||
return pair
|
||||
|
||||
if pair not in markets:
|
||||
for prefix in self.prefixes:
|
||||
test_prefix = f"{prefix}{pair}"
|
||||
|
||||
if test_prefix in pairlist:
|
||||
return test_prefix
|
||||
|
||||
return None
|
||||
|
||||
def filter_pairlist(self, pairlist: list[str], tickers: dict) -> list[str]:
|
||||
"""
|
||||
Filters and sorts pairlist and returns the whitelist again.
|
||||
@@ -188,7 +219,7 @@ class MarketCapPairList(IPairList):
|
||||
self._marketcap_cache["marketcap"] = marketcap_list
|
||||
|
||||
if marketcap_list:
|
||||
filtered_pairlist = []
|
||||
filtered_pairlist: list[str] = []
|
||||
|
||||
market = self._exchange._config["trading_mode"]
|
||||
pair_format = f"{self._stake_currency.upper()}"
|
||||
@@ -196,13 +227,17 @@ class MarketCapPairList(IPairList):
|
||||
pair_format += f":{self._stake_currency.upper()}"
|
||||
|
||||
top_marketcap = marketcap_list[: self._max_rank :]
|
||||
markets = self.get_markets_exchange()
|
||||
|
||||
for mc_pair in top_marketcap:
|
||||
test_pair = f"{mc_pair.upper()}/{pair_format}"
|
||||
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
|
||||
pair = f"{mc_pair.upper()}/{pair_format}"
|
||||
resolved = self.resolve_marketcap_pair(pair, pairlist, markets, filtered_pairlist)
|
||||
|
||||
if resolved:
|
||||
filtered_pairlist.append(resolved)
|
||||
|
||||
if len(filtered_pairlist) == self._number_assets:
|
||||
break
|
||||
|
||||
if len(filtered_pairlist) > 0:
|
||||
return filtered_pairlist
|
||||
|
||||
@@ -2409,7 +2409,7 @@ def test_MarketCapPairList_timing(mocker, default_conf_usdt, markets, time_machi
|
||||
pm = PairListManager(exchange, default_conf_usdt)
|
||||
markets_mock.reset_mock()
|
||||
pm.refresh_pairlist()
|
||||
assert markets_mock.call_count == 3
|
||||
assert markets_mock.call_count == 4
|
||||
markets_mock.reset_mock()
|
||||
|
||||
time_machine.move_to(start_dt + timedelta(hours=20))
|
||||
@@ -2421,7 +2421,52 @@ def test_MarketCapPairList_timing(mocker, default_conf_usdt, markets, time_machi
|
||||
time_machine.move_to(start_dt + timedelta(days=2))
|
||||
pm.refresh_pairlist()
|
||||
# No longer cached pairlist ...
|
||||
assert markets_mock.call_count == 3
|
||||
assert markets_mock.call_count == 4
|
||||
|
||||
|
||||
def test_MarketCapPairList_1000_K_fillup(mocker, default_conf_usdt, markets, time_machine):
|
||||
test_value = [
|
||||
{"symbol": "btc"},
|
||||
{"symbol": "eth"},
|
||||
{"symbol": "usdt"},
|
||||
{"symbol": "bnb"},
|
||||
{"symbol": "sol"},
|
||||
{"symbol": "xrp"},
|
||||
{"symbol": "usdc"},
|
||||
{"symbol": "steth"},
|
||||
{"symbol": "ada"},
|
||||
{"symbol": "avax"},
|
||||
]
|
||||
|
||||
default_conf_usdt["trading_mode"] = "spot"
|
||||
default_conf_usdt["exchange"]["pair_whitelist"] = []
|
||||
default_conf_usdt["pairlists"] = [{"method": "MarketCapPairList", "number_assets": 3}]
|
||||
markets["1000ETH/USDT"] = markets["ETH/USDT"]
|
||||
markets["KXRP/USDT"] = markets["XRP/USDT"]
|
||||
del markets["ETH/USDT"]
|
||||
del markets["XRP/USDT"]
|
||||
|
||||
markets_mock = MagicMock(return_value=markets)
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
get_markets=markets_mock,
|
||||
exchange_has=MagicMock(return_value=True),
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"freqtrade.plugins.pairlist.MarketCapPairList.FtCoinGeckoApi.get_coins_markets",
|
||||
return_value=test_value,
|
||||
)
|
||||
|
||||
start_dt = dt_now()
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf_usdt)
|
||||
time_machine.move_to(start_dt)
|
||||
|
||||
pm = PairListManager(exchange, default_conf_usdt)
|
||||
markets_mock.reset_mock()
|
||||
pm.refresh_pairlist()
|
||||
assert pm.whitelist == ["BTC/USDT", "1000ETH/USDT", "KXRP/USDT"]
|
||||
|
||||
|
||||
def test_MarketCapPairList_filter_special_no_pair_from_coingecko(
|
||||
|
||||
Reference in New Issue
Block a user