From 704cf143835fb6bb1e12fb2a3b611415894f0126 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 30 Dec 2020 09:55:44 +0100 Subject: [PATCH] Add expand_pairlist method --- freqtrade/exchange/exchange.py | 2 +- .../plugins/pairlist/pairlist_helpers.py | 18 +++++++++++ freqtrade/plugins/pairlistmanager.py | 10 +++++- tests/plugins/test_pairlist.py | 32 +++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 freqtrade/plugins/pairlist/pairlist_helpers.py diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 6f495e605..11a0ef8e6 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -208,7 +208,7 @@ class Exchange: return self._api.precisionMode def get_markets(self, base_currencies: List[str] = None, quote_currencies: List[str] = None, - pairs_only: bool = False, active_only: bool = False) -> Dict: + pairs_only: bool = False, active_only: bool = False) -> Dict[str, Any]: """ Return exchange ccxt markets, filtered out by base currency and quote currency if this was requested in parameters. diff --git a/freqtrade/plugins/pairlist/pairlist_helpers.py b/freqtrade/plugins/pairlist/pairlist_helpers.py new file mode 100644 index 000000000..7d365a344 --- /dev/null +++ b/freqtrade/plugins/pairlist/pairlist_helpers.py @@ -0,0 +1,18 @@ +import re +from typing import List + + +def expand_pairlist(wildcardpl: List[str], available_pairs: List[str]) -> List[str]: + """ + TODO: Add docstring here + """ + result = [] + for pair_wc in wildcardpl: + try: + comp = re.compile(pair_wc) + result += [ + pair for pair in available_pairs if re.match(comp, pair) + ] + except re.error as err: + raise ValueError(f"Wildcard error in {pair_wc}, {err}") + return result diff --git a/freqtrade/plugins/pairlistmanager.py b/freqtrade/plugins/pairlistmanager.py index b71f02898..ea1f4ecc7 100644 --- a/freqtrade/plugins/pairlistmanager.py +++ b/freqtrade/plugins/pairlistmanager.py @@ -1,6 +1,7 @@ """ PairList manager class """ +from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist import logging from copy import deepcopy from typing import Any, Dict, List @@ -55,6 +56,13 @@ class PairListManager(): """ return self._blacklist + @property + def expanded_blacklist(self) -> List[str]: + """ + Has the expanded blacklist (including wildcard expansion) + """ + return expand_pairlist(self._blacklist, self._exchange.get_markets().keys()) + @property def name_list(self) -> List[str]: """ @@ -121,7 +129,7 @@ class PairListManager(): :return: pairlist - blacklisted pairs """ for pair in deepcopy(pairlist): - if pair in self._blacklist: + if pair in self.expanded_blacklist: logmethod(f"Pair {pair} in your blacklist. Removing it from whitelist...") pairlist.remove(pair) return pairlist diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 1795fc27f..25597ef93 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -6,6 +6,7 @@ import pytest from freqtrade.constants import AVAILABLE_PAIRLISTS from freqtrade.exceptions import OperationalException +from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist from freqtrade.plugins.pairlistmanager import PairListManager from freqtrade.resolvers import PairListResolver from tests.conftest import get_patched_freqtradebot, log_has, log_has_re @@ -804,3 +805,34 @@ def test_performance_filter(mocker, whitelist_conf, pairlists, pair_allowlist, o freqtrade.pairlists.refresh_pairlist() allowlist = freqtrade.pairlists.whitelist assert allowlist == allowlist_result + + +@pytest.mark.parametrize('wildcardlist,pairs,expected', [ + (['BTC/USDT'], + ['BTC/USDT'], + ['BTC/USDT']), + (['BTC/USDT', 'ETH/USDT'], + ['BTC/USDT', 'ETH/USDT'], + ['BTC/USDT', 'ETH/USDT']), + (['BTC/USDT', 'ETH/USDT'], + ['BTC/USDT'], ['BTC/USDT']), # Test one too many + (['.*/USDT'], + ['BTC/USDT', 'ETH/USDT'], ['BTC/USDT', 'ETH/USDT']), # Wildcard simple + (['.*C/USDT'], + ['BTC/USDT', 'ETC/USDT', 'ETH/USDT'], ['BTC/USDT', 'ETC/USDT']), # Wildcard exclude one + (['.*UP/USDT', 'BTC/USDT', 'ETH/USDT'], + ['BTC/USDT', 'ETC/USDT', 'ETH/USDT', 'BTCUP/USDT', 'XRPUP/USDT', 'XRPDOWN/USDT'], + ['BTC/USDT', 'ETH/USDT', 'BTCUP/USDT', 'XRPUP/USDT']), # Wildcard exclude one + (['BTC/.*', 'ETH/.*'], + ['BTC/USDT', 'ETC/USDT', 'ETH/USDT', 'BTC/USD', 'ETH/EUR', 'BTC/GBP'], + ['BTC/USDT', 'ETH/USDT', 'BTC/USD', 'ETH/EUR', 'BTC/GBP']), # Wildcard exclude one + (['*UP/USDT', 'BTC/USDT', 'ETH/USDT'], + ['BTC/USDT', 'ETC/USDT', 'ETH/USDT', 'BTCUP/USDT', 'XRPUP/USDT', 'XRPDOWN/USDT'], + None), +]) +def test_expand_pairlist(wildcardlist, pairs, expected): + if expected is None: + with pytest.raises(ValueError, match=r'Wildcard error in \*UP/USDT,'): + expand_pairlist(wildcardlist, pairs) + else: + assert sorted(expand_pairlist(wildcardlist, pairs)) == sorted(expected)