From 36b33fb40707c7c3b8b921543beac03e4d2d8de0 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Sat, 24 Jun 2023 12:38:31 +0200 Subject: [PATCH 01/12] add mode to set the pairlist to blacklist additional to whitelist adhere to _number_pairs --- docs/includes/pairlists.md | 3 +++ freqtrade/plugins/pairlist/RemotePairList.py | 24 ++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 5fda038bd..e0aa3278c 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -184,6 +184,7 @@ The RemotePairList is defined in the pairlists section of the configuration sett "pairlists": [ { "method": "RemotePairList", + "mode": "whitelist", "pairlist_url": "https://example.com/pairlist", "number_assets": 10, "refresh_period": 1800, @@ -194,6 +195,8 @@ The RemotePairList is defined in the pairlists section of the configuration sett ] ``` +The `mode` option specifies if the pairlist should be used as a `blacklist` or as a `whitelist`. The default value is "whitelist". + The `pairlist_url` option specifies the URL of the remote server where the pairlist is located, or the path to a local file (if file:/// is prepended). This allows the user to use either a remote server or a local file as the source for the pairlist. The user is responsible for providing a server or local file that returns a JSON object with the following structure: diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 372f9a593..39eed9f34 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -40,6 +40,7 @@ class RemotePairList(IPairList): '`pairlist_url` not specified. Please check your configuration ' 'for "pairlist.config.pairlist_url"') + self._mode = self._pairlistconfig.get('mode', 'whitelist') self._number_pairs = self._pairlistconfig['number_assets'] self._refresh_period: int = self._pairlistconfig.get('refresh_period', 1800) self._keep_pairlist_on_failure = self._pairlistconfig.get('keep_pairlist_on_failure', True) @@ -250,6 +251,25 @@ class RemotePairList(IPairList): :return: new whitelist """ rpl_pairlist = self.gen_pairlist(tickers) - merged_list = pairlist + rpl_pairlist - merged_list = sorted(set(merged_list), key=merged_list.index) + merged_list = [] + filtered = [] + + if self._mode == "whitelist": + merged_list = pairlist + rpl_pairlist + merged_list = sorted(set(merged_list), key=merged_list.index) + elif self._mode == "blacklist": + for pair in pairlist: + if pair not in rpl_pairlist: + merged_list.append(pair) + else: + filtered.append(pair) + if filtered: + self.log_once(f"Blacklist - Filtered out pairs: {filtered}", logger.info) + + else: + raise OperationalException( + '`mode` not configured correctly. Supported Modes: ' + 'are "whitelist","blacklist"') + + merged_list = merged_list[:self._number_pairs] return merged_list From caca070c1af44176a9263569f8134284425462fe Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Sat, 24 Jun 2023 14:31:30 +0200 Subject: [PATCH 02/12] added tests --- freqtrade/plugins/pairlist/RemotePairList.py | 12 ++-- tests/plugins/test_remotepairlist.py | 67 +++++++++++++++++++- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 39eed9f34..0d7a9dc1b 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -51,6 +51,11 @@ class RemotePairList(IPairList): self._init_done = False self._last_pairlist: List[Any] = list() + if self._mode not in ['whitelist', 'blacklist']: + raise OperationalException( + '`mode` not configured correctly. Supported Modes ' + 'are "whitelist","blacklist"') + @property def needstickers(self) -> bool: """ @@ -257,7 +262,7 @@ class RemotePairList(IPairList): if self._mode == "whitelist": merged_list = pairlist + rpl_pairlist merged_list = sorted(set(merged_list), key=merged_list.index) - elif self._mode == "blacklist": + else: for pair in pairlist: if pair not in rpl_pairlist: merged_list.append(pair) @@ -266,10 +271,5 @@ class RemotePairList(IPairList): if filtered: self.log_once(f"Blacklist - Filtered out pairs: {filtered}", logger.info) - else: - raise OperationalException( - '`mode` not configured correctly. Supported Modes: ' - 'are "whitelist","blacklist"') - merged_list = merged_list[:self._number_pairs] return merged_list diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index ac1d1f5ed..1ab455712 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -16,11 +16,12 @@ def rpl_config(default_conf): default_conf['exchange']['pair_whitelist'] = [ 'ETH/USDT', - 'BTC/USDT', + 'XRP/USDT', ] default_conf['exchange']['pair_blacklist'] = [ 'BLK/USDT' ] + return default_conf @@ -183,3 +184,67 @@ def test_fetch_pairlist_mock_response_valid(mocker, rpl_config): assert pairs == ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"] assert time_elapsed == 0.4 assert remote_pairlist._refresh_period == 60 + + +def test_remote_pairlist_init_wrong_mode(mocker, rpl_config): + + rpl_config['pairlists'] = [ + { + "method": "RemotePairList", + "mode": "blacklis", + "number_assets": 20, + "pairlist_url": "http://example.com/pairlist", + "keep_pairlist_on_failure": True, + } + ] + + get_patched_exchange(mocker, rpl_config) + with pytest.raises(OperationalException, match=r'`mode` not configured correctly.' + r' Supported Modes are "whitelist","blacklist"'): + get_patched_freqtradebot(mocker, rpl_config) + + +def test_remote_pairlist_blacklist(mocker, rpl_config, caplog): + + mock_response = MagicMock() + + mock_response.json.return_value = { + "pairs": ["XRP/USDT"], + "refresh_period": 60 + } + + mock_response.headers = { + "content-type": "application/json" + } + + rpl_config['pairlists'] = [ + { + "method": "RemotePairList", + "mode": "blacklist", + "pairlist_url": "http://example.com/pairlist", + "number_assets": 3 + } + ] + + mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get", + return_value=mock_response) + + exchange = get_patched_exchange(mocker, rpl_config) + + freqtrade = get_patched_freqtradebot(mocker, rpl_config) + freqtrade.pairlists.refresh_pairlist() + whitelist = freqtrade.pairlists.whitelist + + pairlistmanager = PairListManager(exchange, rpl_config) + + remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, + rpl_config['pairlists'][0], 0) + + pairs, time_elapsed = remote_pairlist.fetch_pairlist() + + assert pairs == ["XRP/USDT"] + + whitelist = remote_pairlist.filter_pairlist(["XRP/USDT", "ETH/USDT"], {}) + assert whitelist == ["ETH/USDT"] + + assert log_has(f"Blacklist - Filtered out pairs: {pairs}", caplog) From d534f88d1c4f326c04ddb2736406be5f2c9349d9 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Sat, 24 Jun 2023 14:36:31 +0200 Subject: [PATCH 03/12] unnecessary lines removed. --- tests/plugins/test_remotepairlist.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index 1ab455712..0dc343dc8 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -231,10 +231,6 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog): exchange = get_patched_exchange(mocker, rpl_config) - freqtrade = get_patched_freqtradebot(mocker, rpl_config) - freqtrade.pairlists.refresh_pairlist() - whitelist = freqtrade.pairlists.whitelist - pairlistmanager = PairListManager(exchange, rpl_config) remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, @@ -244,7 +240,7 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog): assert pairs == ["XRP/USDT"] - whitelist = remote_pairlist.filter_pairlist(["XRP/USDT", "ETH/USDT"], {}) + whitelist = remote_pairlist.filter_pairlist(rpl_config['exchange']['pair_whitelist'], {}) assert whitelist == ["ETH/USDT"] assert log_has(f"Blacklist - Filtered out pairs: {pairs}", caplog) From ce1b90885e701336a6bd295badd0e0ff76f85ed0 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Sat, 24 Jun 2023 21:32:20 +0200 Subject: [PATCH 04/12] support wildcards --- freqtrade/plugins/pairlist/RemotePairList.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 0d7a9dc1b..0133db802 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -16,6 +16,7 @@ from freqtrade.constants import Config from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Tickers from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter +from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist logger = logging.getLogger(__name__) @@ -229,6 +230,7 @@ class RemotePairList(IPairList): self.log_once(f"Fetched pairs: {pairlist}", logger.debug) + pairlist = expand_pairlist(pairlist, list(self._exchange.get_markets().keys())) pairlist = self._whitelist_for_active_markets(pairlist) pairlist = pairlist[:self._number_pairs] From 1c5ea317e64075273f75774f16d761c57e5f99ac Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 8 Jul 2023 07:31:55 +0200 Subject: [PATCH 05/12] Add mode as parameter for the UI --- freqtrade/plugins/pairlist/RemotePairList.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 0133db802..a5c953d19 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -79,6 +79,13 @@ class RemotePairList(IPairList): @staticmethod def available_parameters() -> Dict[str, PairlistParameter]: return { + "mode": { + "type": "option", + "default": "whitelist", + "options": ["whitelist", "blacklist"], + "description": "Pairlist mode", + "help": "Should this pairlist operate as a whitelist or blacklist?", + }, "number_assets": { "type": "number", "default": 0, From ee1fa34df275bba1ac42cf8774ee03ad3e820720 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Sat, 8 Jul 2023 18:05:46 +0200 Subject: [PATCH 06/12] Add 'processing_mode' , blacklist checks --- docs/includes/pairlists.md | 9 ++++- freqtrade/plugins/pairlist/RemotePairList.py | 23 ++++++++++- tests/plugins/test_remotepairlist.py | 42 ++++++++++++++++---- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index e0aa3278c..1babeca1f 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -185,6 +185,7 @@ The RemotePairList is defined in the pairlists section of the configuration sett { "method": "RemotePairList", "mode": "whitelist", + "processing_mode": "filter", "pairlist_url": "https://example.com/pairlist", "number_assets": 10, "refresh_period": 1800, @@ -195,7 +196,13 @@ The RemotePairList is defined in the pairlists section of the configuration sett ] ``` -The `mode` option specifies if the pairlist should be used as a `blacklist` or as a `whitelist`. The default value is "whitelist". +The optional `mode` option specifies if the pairlist should be used as a `blacklist` or as a `whitelist`. The default value is "whitelist". + +The optional `processing_mode` option in the RemotePairList configuration determines how the retrieved pairlist is processed. It can have two values: "filter" or "append". + +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. The `pairlist_url` option specifies the URL of the remote server where the pairlist is located, or the path to a local file (if file:/// is prepended). This allows the user to use either a remote server or a local file as the source for the pairlist. diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index a5c953d19..ba602efd1 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -42,6 +42,7 @@ class RemotePairList(IPairList): 'for "pairlist.config.pairlist_url"') self._mode = self._pairlistconfig.get('mode', 'whitelist') + self._processing_mode = self._pairlistconfig.get('processing_mode', 'filter') self._number_pairs = self._pairlistconfig['number_assets'] self._refresh_period: int = self._pairlistconfig.get('refresh_period', 1800) self._keep_pairlist_on_failure = self._pairlistconfig.get('keep_pairlist_on_failure', True) @@ -57,6 +58,23 @@ class RemotePairList(IPairList): '`mode` not configured correctly. Supported Modes ' 'are "whitelist","blacklist"') + if self._processing_mode not in ['filter', 'append']: + raise OperationalException( + '`processing_mode` not configured correctly. Supported Modes ' + 'are "filter","append"') + + pairlists = self._config['pairlists'] + + if len(pairlists) == 1 and self._mode == 'blacklist': + raise OperationalException( + 'At `blacklist` mode RemotePairList requires an additional ' + 'Pairlist and cannot be used on its own.') + + if pairlists[0]['method'] == "RemotePairList" and self._mode == 'blacklist': + raise OperationalException( + 'At `blacklist` mode RemotePairList can not be on the first ' + 'position of your pairlist.') + @property def needstickers(self) -> bool: """ @@ -269,7 +287,10 @@ class RemotePairList(IPairList): filtered = [] if self._mode == "whitelist": - merged_list = pairlist + rpl_pairlist + if self._processing_mode == "filter": + merged_list = [pair for pair in pairlist if pair in rpl_pairlist] + elif self._processing_mode == "append": + merged_list = pairlist + rpl_pairlist merged_list = sorted(set(merged_list), key=merged_list.index) else: for pair in pairlist: diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index 0dc343dc8..189fc0d5f 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -1,5 +1,5 @@ import json -from unittest.mock import MagicMock +from unittest.mock import MagicMock, PropertyMock import pytest import requests @@ -7,7 +7,7 @@ import requests from freqtrade.exceptions import OperationalException from freqtrade.plugins.pairlist.RemotePairList import RemotePairList from freqtrade.plugins.pairlistmanager import PairListManager -from tests.conftest import get_patched_exchange, get_patched_freqtradebot, log_has +from tests.conftest import EXMS, get_patched_exchange, get_patched_freqtradebot, log_has @pytest.fixture(scope="function") @@ -187,7 +187,6 @@ def test_fetch_pairlist_mock_response_valid(mocker, rpl_config): def test_remote_pairlist_init_wrong_mode(mocker, rpl_config): - rpl_config['pairlists'] = [ { "method": "RemotePairList", @@ -199,12 +198,34 @@ def test_remote_pairlist_init_wrong_mode(mocker, rpl_config): ] get_patched_exchange(mocker, rpl_config) - with pytest.raises(OperationalException, match=r'`mode` not configured correctly.' - r' Supported Modes are "whitelist","blacklist"'): + with pytest.raises( + OperationalException, + match=r'`mode` not configured correctly. Supported Modes are "whitelist","blacklist"' + ): get_patched_freqtradebot(mocker, rpl_config) -def test_remote_pairlist_blacklist(mocker, rpl_config, caplog): +def test_remote_pairlist_init_wrong_proc_mode(mocker, rpl_config): + rpl_config['pairlists'] = [ + { + "method": "RemotePairList", + "processing_mode": "filler", + "mode": "whitelist", + "number_assets": 20, + "pairlist_url": "http://example.com/pairlist", + "keep_pairlist_on_failure": True, + } + ] + + get_patched_exchange(mocker, rpl_config) + with pytest.raises( + OperationalException, + match=r'`processing_mode` not configured correctly. Supported Modes are "filter","append"' + ): + get_patched_freqtradebot(mocker, rpl_config) + + +def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers): mock_response = MagicMock() @@ -218,6 +239,7 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog): } rpl_config['pairlists'] = [ + {'method': 'StaticPairList'}, { "method": "RemotePairList", "mode": "blacklist", @@ -226,6 +248,12 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog): } ] + 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) @@ -234,7 +262,7 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog): pairlistmanager = PairListManager(exchange, rpl_config) remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, - rpl_config['pairlists'][0], 0) + rpl_config['pairlists'][1], 0) pairs, time_elapsed = remote_pairlist.fetch_pairlist() From 0b68ca6cb31ffd643e8ab17f087abf7f4e6cc87d Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Sun, 9 Jul 2023 09:42:33 +0200 Subject: [PATCH 07/12] use pairlist_pos remove unused check, fixed Test --- freqtrade/plugins/pairlist/RemotePairList.py | 9 +-------- tests/plugins/test_remotepairlist.py | 8 ++++++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index ba602efd1..16b1939bb 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -63,14 +63,7 @@ class RemotePairList(IPairList): '`processing_mode` not configured correctly. Supported Modes ' 'are "filter","append"') - pairlists = self._config['pairlists'] - - if len(pairlists) == 1 and self._mode == 'blacklist': - raise OperationalException( - 'At `blacklist` mode RemotePairList requires an additional ' - 'Pairlist and cannot be used on its own.') - - if pairlists[0]['method'] == "RemotePairList" and self._mode == 'blacklist': + if self._pairlist_pos == 0 and self._mode == 'blacklist': raise OperationalException( 'At `blacklist` mode RemotePairList can not be on the first ' 'position of your pairlist.') diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index 189fc0d5f..6e675eca2 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -239,7 +239,9 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers) } rpl_config['pairlists'] = [ - {'method': 'StaticPairList'}, + { + "method": "StaticPairList", + }, { "method": "RemotePairList", "mode": "blacklist", @@ -262,7 +264,9 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers) pairlistmanager = PairListManager(exchange, rpl_config) remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, - rpl_config['pairlists'][1], 0) + rpl_config["pairlists"][1], 1) + + print(remote_pairlist._pairlistconfig) pairs, time_elapsed = remote_pairlist.fetch_pairlist() From 4d4ec11a8a5989833620557235c29b7acbd3d1a2 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Sun, 9 Jul 2023 09:53:31 +0200 Subject: [PATCH 08/12] - print --- tests/plugins/test_remotepairlist.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index 6e675eca2..4c69f6b8b 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -266,8 +266,6 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers) remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][1], 1) - print(remote_pairlist._pairlistconfig) - pairs, time_elapsed = remote_pairlist.fetch_pairlist() assert pairs == ["XRP/USDT"] From 5da5369ca4e1d75dcb916f8af0944912cb1f32dd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 9 Jul 2023 11:09:59 +0200 Subject: [PATCH 09/12] Update parameter sequence to make more sense --- freqtrade/plugins/pairlist/RemotePairList.py | 29 ++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 16b1939bb..6a16671f6 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -90,6 +90,18 @@ class RemotePairList(IPairList): @staticmethod def available_parameters() -> Dict[str, PairlistParameter]: return { + "pairlist_url": { + "type": "string", + "default": "", + "description": "URL to fetch pairlist from", + "help": "URL to fetch pairlist from", + }, + "number_assets": { + "type": "number", + "default": 30, + "description": "Number of assets", + "help": "Number of assets to use from the pairlist.", + }, "mode": { "type": "option", "default": "whitelist", @@ -97,17 +109,12 @@ class RemotePairList(IPairList): "description": "Pairlist mode", "help": "Should this pairlist operate as a whitelist or blacklist?", }, - "number_assets": { - "type": "number", - "default": 0, - "description": "Number of assets", - "help": "Number of assets to use from the pairlist.", - }, - "pairlist_url": { - "type": "string", - "default": "", - "description": "URL to fetch pairlist from", - "help": "URL to fetch pairlist from", + "processing_mode": { + "type": "option", + "default": "filter", + "options": ["filter", "append"], + "description": "Processing mode", + "help": "Append pairs to incomming pairlist or filter them?", }, **IPairList.refresh_period_parameter(), "keep_pairlist_on_failure": { From 4dda9c6daab0e5fcd4ff882244c760c0047140fb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 9 Jul 2023 11:36:13 +0200 Subject: [PATCH 10/12] Add explicit test for short-desc --- tests/plugins/test_pairlist.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index bc8fe84f1..cbbf1e6da 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -1200,6 +1200,10 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo "[{'ProducerPairList': 'ProducerPairList - default'}]", None ), + ({"method": "RemotePairList", "number_assets": 10, "pairlist_url": "https://example.com"}, + "[{'RemotePairList': 'RemotePairList - 10 pairs from RemotePairlist.'}]", + None + ), ]) def test_pricefilter_desc(mocker, whitelist_conf, markets, pairlistconfig, desc_expected, exception_expected): From e6ee55a69b8aa12015b61bc06c2ab6bc9e8e8809 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 9 Jul 2023 11:37:06 +0200 Subject: [PATCH 11/12] Improve some test coverage --- freqtrade/plugins/pairlist/RemotePairList.py | 4 ++-- tests/plugins/test_remotepairlist.py | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 6a16671f6..66b7d9496 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -65,7 +65,7 @@ class RemotePairList(IPairList): if self._pairlist_pos == 0 and self._mode == 'blacklist': raise OperationalException( - 'At `blacklist` mode RemotePairList can not be on the first ' + 'A `blacklist` mode RemotePairList can not be on the first ' 'position of your pairlist.') @property @@ -85,7 +85,7 @@ class RemotePairList(IPairList): @staticmethod def description() -> str: - return "Retrieve pairs from a remote API." + return "Retrieve pairs from a remote API or local file." @staticmethod def available_parameters() -> Dict[str, PairlistParameter]: diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index 4c69f6b8b..56f62f227 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -197,13 +197,28 @@ def test_remote_pairlist_init_wrong_mode(mocker, rpl_config): } ] - get_patched_exchange(mocker, rpl_config) with pytest.raises( OperationalException, match=r'`mode` not configured correctly. Supported Modes are "whitelist","blacklist"' ): get_patched_freqtradebot(mocker, rpl_config) + rpl_config['pairlists'] = [ + { + "method": "RemotePairList", + "mode": "blacklist", + "number_assets": 20, + "pairlist_url": "http://example.com/pairlist", + "keep_pairlist_on_failure": True, + } + ] + + with pytest.raises( + OperationalException, + match=r'A `blacklist` mode RemotePairList can not be.*first.*' + ): + get_patched_freqtradebot(mocker, rpl_config) + def test_remote_pairlist_init_wrong_proc_mode(mocker, rpl_config): rpl_config['pairlists'] = [ From af5fc76dc6827ec4a92b03cf6881a9f9bea90717 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 9 Jul 2023 11:40:38 +0200 Subject: [PATCH 12/12] Add test for different processing modes --- tests/plugins/test_remotepairlist.py | 51 ++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index 56f62f227..5e6f5cbf1 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -289,3 +289,54 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers) assert whitelist == ["ETH/USDT"] assert log_has(f"Blacklist - Filtered out pairs: {pairs}", caplog) + + +@pytest.mark.parametrize("processing_mode", ["filter", "append"]) +def test_remote_pairlist_whitelist(mocker, rpl_config, processing_mode, markets, tickers): + + mock_response = MagicMock() + + mock_response.json.return_value = { + "pairs": ["XRP/USDT"], + "refresh_period": 60 + } + + mock_response.headers = { + "content-type": "application/json" + } + + rpl_config['pairlists'] = [ + { + "method": "StaticPairList", + }, + { + "method": "RemotePairList", + "mode": "whitelist", + "processing_mode": processing_mode, + "pairlist_url": "http://example.com/pairlist", + "number_assets": 3 + } + ] + + 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"][1], 1) + + pairs, time_elapsed = remote_pairlist.fetch_pairlist() + + assert pairs == ["XRP/USDT"] + + whitelist = remote_pairlist.filter_pairlist(rpl_config['exchange']['pair_whitelist'], {}) + assert whitelist == (["XRP/USDT"] if processing_mode == "filter" else ['ETH/USDT', 'XRP/USDT'])