diff --git a/freqtrade/plugins/pairlistmanager.py b/freqtrade/plugins/pairlistmanager.py index face79729..1e79dc743 100644 --- a/freqtrade/plugins/pairlistmanager.py +++ b/freqtrade/plugins/pairlistmanager.py @@ -2,13 +2,14 @@ PairList manager class """ import logging -from copy import deepcopy +from functools import partial from typing import Dict, List from cachetools import TTLCache, cached from freqtrade.constants import ListPairsWithTimeframes from freqtrade.exceptions import OperationalException +from freqtrade.mixins import LoggingMixin from freqtrade.plugins.pairlist.IPairList import IPairList from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist from freqtrade.resolvers import PairListResolver @@ -17,7 +18,7 @@ from freqtrade.resolvers import PairListResolver logger = logging.getLogger(__name__) -class PairListManager(): +class PairListManager(LoggingMixin): def __init__(self, exchange, config: dict) -> None: self._exchange = exchange @@ -41,6 +42,9 @@ class PairListManager(): if not self._pairlist_handlers: raise OperationalException("No Pairlist Handlers defined") + refresh_period = config.get('pairlist_refresh_period', 3600) + LoggingMixin.__init__(self, logger, refresh_period) + @property def whitelist(self) -> List[str]: """The current whitelist""" @@ -108,9 +112,10 @@ class PairListManager(): except ValueError as err: logger.error(f"Pair blacklist contains an invalid Wildcard: {err}") return [] - for pair in deepcopy(pairlist): + log_once = partial(self.log_once, logmethod=logmethod) + for pair in pairlist.copy(): if pair in blacklist: - logmethod(f"Pair {pair} in your blacklist. Removing it from whitelist...") + log_once(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 b85484a60..dec6ca726 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -1,5 +1,6 @@ # pragma pylint: disable=missing-docstring,C0103,protected-access +import logging import time from unittest.mock import MagicMock, PropertyMock @@ -217,6 +218,40 @@ def test_invalid_blacklist(mocker, markets, static_pl_conf, caplog): log_has_re(r"Pair blacklist contains an invalid Wildcard.*", caplog) +def test_remove_logs_for_pairs_already_in_blacklist(mocker, markets, static_pl_conf, caplog): + logger = logging.getLogger(__name__) + freqtrade = get_patched_freqtradebot(mocker, static_pl_conf) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + exchange_has=MagicMock(return_value=True), + markets=PropertyMock(return_value=markets), + ) + freqtrade.pairlists.refresh_pairlist() + whitelist = ['ETH/BTC', 'TKN/BTC'] + caplog.clear() + caplog.set_level(logging.INFO) + + # Ensure all except those in whitelist are removed. + assert set(whitelist) == set(freqtrade.pairlists.whitelist) + assert static_pl_conf['exchange']['pair_blacklist'] == freqtrade.pairlists.blacklist + # Ensure that log message wasn't generated. + assert not log_has('Pair BLK/BTC in your blacklist. Removing it from whitelist...', caplog) + + new_whitelist = freqtrade.pairlists.verify_blacklist(whitelist + ['BLK/BTC'], logger.warning) + # Ensure that the pair is removed from the white list, and properly logged. + assert set(whitelist) == set(new_whitelist) + matches = sum(1 for message in caplog.messages + if message == 'Pair BLK/BTC in your blacklist. Removing it from whitelist...') + assert matches == 1 + + new_whitelist = freqtrade.pairlists.verify_blacklist(whitelist + ['BLK/BTC'], logger.warning) + # Ensure that the pair is not logged anymore when being removed from the pair list. + assert set(whitelist) == set(new_whitelist) + matches = sum(1 for message in caplog.messages + if message == 'Pair BLK/BTC in your blacklist. Removing it from whitelist...') + assert matches == 1 + + def test_refresh_pairlist_dynamic(mocker, shitcoinmarkets, tickers, whitelist_conf): mocker.patch.multiple(