mirror of
https://github.com/freqtrade/freqtrade.git
synced 2026-01-25 16:30:26 +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.
|
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.
|
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"
|
!!! 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.
|
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]:
|
def gen_pairlist(self, tickers: Tickers) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Generate the pairlist
|
Generate the pairlist
|
||||||
@@ -132,12 +142,8 @@ class MarketCapPairList(IPairList):
|
|||||||
else:
|
else:
|
||||||
# Use fresh pairlist
|
# Use fresh pairlist
|
||||||
# Check if pair quote currency equals to the stake currency.
|
# Check if pair quote currency equals to the stake currency.
|
||||||
_pairlist = [
|
_pairlist = self.get_markets_exchange()
|
||||||
k
|
|
||||||
for k in self._exchange.get_markets(
|
|
||||||
quote_currencies=[self._stake_currency], tradable_only=True, active_only=True
|
|
||||||
).keys()
|
|
||||||
]
|
|
||||||
# No point in testing for blacklisted pairs...
|
# No point in testing for blacklisted pairs...
|
||||||
_pairlist = self.verify_blacklist(_pairlist, logger.info)
|
_pairlist = self.verify_blacklist(_pairlist, logger.info)
|
||||||
|
|
||||||
@@ -146,6 +152,31 @@ class MarketCapPairList(IPairList):
|
|||||||
|
|
||||||
return pairlist
|
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]:
|
def filter_pairlist(self, pairlist: list[str], tickers: dict) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Filters and sorts pairlist and returns the whitelist again.
|
Filters and sorts pairlist and returns the whitelist again.
|
||||||
@@ -188,7 +219,7 @@ class MarketCapPairList(IPairList):
|
|||||||
self._marketcap_cache["marketcap"] = marketcap_list
|
self._marketcap_cache["marketcap"] = marketcap_list
|
||||||
|
|
||||||
if marketcap_list:
|
if marketcap_list:
|
||||||
filtered_pairlist = []
|
filtered_pairlist: list[str] = []
|
||||||
|
|
||||||
market = self._exchange._config["trading_mode"]
|
market = self._exchange._config["trading_mode"]
|
||||||
pair_format = f"{self._stake_currency.upper()}"
|
pair_format = f"{self._stake_currency.upper()}"
|
||||||
@@ -196,13 +227,17 @@ class MarketCapPairList(IPairList):
|
|||||||
pair_format += f":{self._stake_currency.upper()}"
|
pair_format += f":{self._stake_currency.upper()}"
|
||||||
|
|
||||||
top_marketcap = marketcap_list[: self._max_rank :]
|
top_marketcap = marketcap_list[: self._max_rank :]
|
||||||
|
markets = self.get_markets_exchange()
|
||||||
|
|
||||||
for mc_pair in top_marketcap:
|
for mc_pair in top_marketcap:
|
||||||
test_pair = f"{mc_pair.upper()}/{pair_format}"
|
pair = f"{mc_pair.upper()}/{pair_format}"
|
||||||
if test_pair in pairlist and test_pair not in filtered_pairlist:
|
resolved = self.resolve_marketcap_pair(pair, pairlist, markets, filtered_pairlist)
|
||||||
filtered_pairlist.append(test_pair)
|
|
||||||
if len(filtered_pairlist) == self._number_assets:
|
if resolved:
|
||||||
break
|
filtered_pairlist.append(resolved)
|
||||||
|
|
||||||
|
if len(filtered_pairlist) == self._number_assets:
|
||||||
|
break
|
||||||
|
|
||||||
if len(filtered_pairlist) > 0:
|
if len(filtered_pairlist) > 0:
|
||||||
return filtered_pairlist
|
return filtered_pairlist
|
||||||
|
|||||||
@@ -2409,7 +2409,7 @@ def test_MarketCapPairList_timing(mocker, default_conf_usdt, markets, time_machi
|
|||||||
pm = PairListManager(exchange, default_conf_usdt)
|
pm = PairListManager(exchange, default_conf_usdt)
|
||||||
markets_mock.reset_mock()
|
markets_mock.reset_mock()
|
||||||
pm.refresh_pairlist()
|
pm.refresh_pairlist()
|
||||||
assert markets_mock.call_count == 3
|
assert markets_mock.call_count == 4
|
||||||
markets_mock.reset_mock()
|
markets_mock.reset_mock()
|
||||||
|
|
||||||
time_machine.move_to(start_dt + timedelta(hours=20))
|
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))
|
time_machine.move_to(start_dt + timedelta(days=2))
|
||||||
pm.refresh_pairlist()
|
pm.refresh_pairlist()
|
||||||
# No longer cached 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(
|
def test_MarketCapPairList_filter_special_no_pair_from_coingecko(
|
||||||
|
|||||||
Reference in New Issue
Block a user