Merge pull request #12927 from stash86/main-stash

number_assets in RemotePairlist becomes optional
This commit is contained in:
Matthias
2026-03-13 06:31:43 +01:00
committed by GitHub
3 changed files with 64 additions and 28 deletions
+2
View File
@@ -304,6 +304,8 @@ The optional `mode` option specifies if the pairlist should be used as a `blackl
The optional `processing_mode` option in the RemotePairList configuration determines how the retrieved pairlist is processed. It can have two values: "filter" or "append". The default value is "filter".
The optional `number_assets` option in the RemotePairList configuration determines how many pairs will be returned if used in whitelist `mode`. By default, all pairs will be returned. In blacklist `mode`, this option will be ignored.
In "filter" mode, the retrieved pairlist is used as a filter. Only the pairs present in both the original pairlist and the retrieved pairlist are included in the final pairlist. Other pairs are filtered out.
In "append" mode, the retrieved pairlist is added to the original pairlist. All pairs from both lists are included in the final pairlist without any filtering.
+5 -9
View File
@@ -31,12 +31,6 @@ class RemotePairList(IPairList):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
if "number_assets" not in self._pairlistconfig:
raise OperationalException(
"`number_assets` not specified. Please check your configuration "
'for "pairlist.config.number_assets"'
)
if "pairlist_url" not in self._pairlistconfig:
raise OperationalException(
"`pairlist_url` not specified. Please check your configuration "
@@ -45,7 +39,7 @@ class RemotePairList(IPairList):
self._mode = self._pairlistconfig.get("mode", "whitelist")
self._processing_mode = self._pairlistconfig.get("processing_mode", "filter")
self._number_pairs = self._pairlistconfig["number_assets"]
self._number_pairs: int | None = self._pairlistconfig.get("number_assets", None)
self._refresh_period: int = self._pairlistconfig.get("refresh_period", 1800)
self._keep_pairlist_on_failure = self._pairlistconfig.get("keep_pairlist_on_failure", True)
self._pair_cache: FtTTLCache = FtTTLCache(maxsize=1, ttl=self._refresh_period)
@@ -248,7 +242,8 @@ class RemotePairList(IPairList):
pairlist = expand_pairlist(pairlist, list(self._exchange.get_markets().keys()))
pairlist = self._whitelist_for_active_markets(pairlist)
pairlist = pairlist[: self._number_pairs]
if self._number_pairs and (self._mode == "whitelist"):
pairlist = pairlist[: self._number_pairs]
if pairlist:
self._pair_cache["pairlist"] = pairlist.copy()
@@ -305,5 +300,6 @@ class RemotePairList(IPairList):
if filtered:
self.log_once(f"Blacklist - Filtered out pairs: {filtered}", logger.info)
merged_list = merged_list[: self._number_pairs]
if self._number_pairs and (self._mode == "whitelist"):
merged_list = merged_list[: self._number_pairs]
return merged_list
+57 -19
View File
@@ -137,25 +137,6 @@ def test_remote_pairlist_init_no_pairlist_url(mocker, rpl_config):
get_patched_freqtradebot(mocker, rpl_config)
def test_remote_pairlist_init_no_number_assets(mocker, rpl_config):
rpl_config["pairlists"] = [
{
"method": "RemotePairList",
"pairlist_url": "http://example.com/pairlist",
"keep_pairlist_on_failure": True,
}
]
get_patched_exchange(mocker, rpl_config)
with pytest.raises(
OperationalException,
match=r"`number_assets` not specified. "
'Please check your configuration for "pairlist.config.number_assets"',
):
get_patched_freqtradebot(mocker, rpl_config)
def test_fetch_pairlist_mock_response_valid(mocker, rpl_config):
rpl_config["pairlists"] = [
{
@@ -341,3 +322,60 @@ def test_remote_pairlist_whitelist(mocker, rpl_config, processing_mode, markets,
whitelist = remote_pairlist.filter_pairlist(rpl_config["exchange"]["pair_whitelist"], {})
assert whitelist == (["XRP/USDT"] if processing_mode == "filter" else ["ETH/USDT", "XRP/USDT"])
@pytest.mark.parametrize(
"number_assets, result",
[
(1, ["ETH/USDT"]),
(2, ["ETH/USDT", "XRP/USDT"]),
(500, ["ETH/USDT", "XRP/USDT"]),
(None, ["ETH/USDT", "XRP/USDT"]),
],
)
def test_remote_pairlist_whitelist_number_assets(
mocker, rpl_config, number_assets, result, markets, tickers
):
mock_response = MagicMock()
mock_response.json.return_value = {
"pairs": ["ETH/USDT", "XRP/USDT", "TKN/USDT"],
"refresh_period": 60,
}
mock_response.headers = {"content-type": "application/json"}
rpl_config["pairlists"] = [
{
"method": "RemotePairList",
"mode": "whitelist",
"pairlist_url": "http://example.com/pairlist",
"number_assets": number_assets,
},
]
mocker.patch.multiple(
EXMS,
markets=PropertyMock(return_value=markets),
exchange_has=MagicMock(return_value=True),
get_tickers=tickers,
)
mocker.patch(
"freqtrade.plugins.pairlist.RemotePairList.requests.get", return_value=mock_response
)
exchange = get_patched_exchange(mocker, rpl_config)
pairlistmanager = PairListManager(exchange, rpl_config)
remote_pairlist = RemotePairList(
exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][0], 0
)
pairs, _ = remote_pairlist.fetch_pairlist()
assert pairs == ["ETH/USDT", "XRP/USDT", "TKN/USDT"]
whitelist = remote_pairlist.filter_pairlist(rpl_config["exchange"]["pair_whitelist"], {})
assert whitelist == result