From 481f9ba6d6f10befedcddaddd63d973ed3aa7cb0 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 15 May 2020 03:00:55 +0300 Subject: [PATCH 01/14] Use list comprehension instead of filter() --- freqtrade/pairlist/VolumePairList.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/freqtrade/pairlist/VolumePairList.py b/freqtrade/pairlist/VolumePairList.py index eb44fe725..7ac350de9 100644 --- a/freqtrade/pairlist/VolumePairList.py +++ b/freqtrade/pairlist/VolumePairList.py @@ -26,6 +26,7 @@ class VolumePairList(IPairList): raise OperationalException( f'`number_assets` not specified. Please check your configuration ' 'for "pairlist.config.number_assets"') + self._number_pairs = self._pairlistconfig['number_assets'] self._sort_key = self._pairlistconfig.get('sort_key', 'quoteVolume') self._min_value = self._pairlistconfig.get('min_value', 0) @@ -36,9 +37,11 @@ class VolumePairList(IPairList): 'Exchange does not support dynamic whitelist.' 'Please edit your config and restart the bot' ) + if not self._validate_keys(self._sort_key): raise OperationalException( f'key {self._sort_key} not in {SORT_VALUES}') + if self._sort_key != 'quoteVolume': logger.warning( "DEPRECATED: using any key other than quoteVolume for VolumePairList is deprecated." @@ -81,7 +84,9 @@ class VolumePairList(IPairList): self._sort_key, self._min_value) else: pairs = pairlist + self.log_on_refresh(logger.info, f"Searching {self._number_pairs} pairs: {pairs}") + return pairs def _gen_pair_whitelist(self, pairlist: List[str], tickers: Dict, @@ -100,11 +105,11 @@ class VolumePairList(IPairList): if (self._exchange.get_pair_quote_currency(k) == base_currency and v[key] is not None)] else: - # If other pairlist is in front, use the incomming pairlist. + # If other pairlist is in front, use the incoming pairlist. filtered_tickers = [v for k, v in tickers.items() if k in pairlist] if min_val > 0: - filtered_tickers = list(filter(lambda t: t[key] > min_val, filtered_tickers)) + filtered_tickers = [v for v in filtered_tickers if v[key] > min_val] sorted_tickers = sorted(filtered_tickers, reverse=True, key=lambda t: t[key]) From 2924b70fd70861555cfb664e0ce8c5ab3d6d359c Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 15 May 2020 03:41:41 +0300 Subject: [PATCH 02/14] Cosmetics in tests/pairlist/ --- tests/pairlist/test_pairlist.py | 48 ++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/pairlist/test_pairlist.py b/tests/pairlist/test_pairlist.py index f9e2893c3..10886d87c 100644 --- a/tests/pairlist/test_pairlist.py +++ b/tests/pairlist/test_pairlist.py @@ -69,44 +69,44 @@ def test_log_on_refresh(mocker, static_pl_conf, markets, tickers): def test_load_pairlist_noexist(mocker, markets, default_conf): - bot = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) - plm = PairListManager(bot.exchange, default_conf) + plm = PairListManager(freqtrade.exchange, default_conf) with pytest.raises(OperationalException, match=r"Impossible to load Pairlist 'NonexistingPairList'. " r"This class does not exist or contains Python code errors."): - PairListResolver.load_pairlist('NonexistingPairList', bot.exchange, plm, + PairListResolver.load_pairlist('NonexistingPairList', freqtrade.exchange, plm, default_conf, {}, 1) def test_refresh_market_pair_not_in_whitelist(mocker, markets, static_pl_conf): - freqtradebot = get_patched_freqtradebot(mocker, static_pl_conf) + freqtrade = get_patched_freqtradebot(mocker, static_pl_conf) mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) - freqtradebot.pairlists.refresh_pairlist() + freqtrade.pairlists.refresh_pairlist() # List ordered by BaseVolume whitelist = ['ETH/BTC', 'TKN/BTC'] # Ensure all except those in whitelist are removed - assert set(whitelist) == set(freqtradebot.pairlists.whitelist) + assert set(whitelist) == set(freqtrade.pairlists.whitelist) # Ensure config dict hasn't been changed assert (static_pl_conf['exchange']['pair_whitelist'] == - freqtradebot.config['exchange']['pair_whitelist']) + freqtrade.config['exchange']['pair_whitelist']) def test_refresh_static_pairlist(mocker, markets, static_pl_conf): - freqtradebot = get_patched_freqtradebot(mocker, static_pl_conf) + 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), ) - freqtradebot.pairlists.refresh_pairlist() + freqtrade.pairlists.refresh_pairlist() # List ordered by BaseVolume whitelist = ['ETH/BTC', 'TKN/BTC'] # Ensure all except those in whitelist are removed - assert set(whitelist) == set(freqtradebot.pairlists.whitelist) - assert static_pl_conf['exchange']['pair_blacklist'] == freqtradebot.pairlists.blacklist + assert set(whitelist) == set(freqtrade.pairlists.whitelist) + assert static_pl_conf['exchange']['pair_blacklist'] == freqtrade.pairlists.blacklist def test_refresh_pairlist_dynamic(mocker, shitcoinmarkets, tickers, whitelist_conf): @@ -116,7 +116,7 @@ def test_refresh_pairlist_dynamic(mocker, shitcoinmarkets, tickers, whitelist_co get_tickers=tickers, exchange_has=MagicMock(return_value=True), ) - bot = get_patched_freqtradebot(mocker, whitelist_conf) + freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) # Remock markets with shitcoinmarkets since get_patched_freqtradebot uses the markets fixture mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -124,9 +124,9 @@ def test_refresh_pairlist_dynamic(mocker, shitcoinmarkets, tickers, whitelist_co ) # argument: use the whitelist dynamically by exchange-volume whitelist = ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'XRP/BTC', 'HOT/BTC'] - bot.pairlists.refresh_pairlist() + freqtrade.pairlists.refresh_pairlist() - assert whitelist == bot.pairlists.whitelist + assert whitelist == freqtrade.pairlists.whitelist whitelist_conf['pairlists'] = [{'method': 'VolumePairList', 'config': {} @@ -136,7 +136,7 @@ def test_refresh_pairlist_dynamic(mocker, shitcoinmarkets, tickers, whitelist_co with pytest.raises(OperationalException, match=r'`number_assets` not specified. Please check your configuration ' r'for "pairlist.config.number_assets"'): - PairListManager(bot.exchange, whitelist_conf) + PairListManager(freqtrade.exchange, whitelist_conf) def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): @@ -144,13 +144,13 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): 'freqtrade.exchange.Exchange', exchange_has=MagicMock(return_value=True), ) - freqtradebot = get_patched_freqtradebot(mocker, whitelist_conf) + freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets_empty)) # argument: use the whitelist dynamically by exchange-volume whitelist = [] whitelist_conf['exchange']['pair_whitelist'] = [] - freqtradebot.pairlists.refresh_pairlist() + freqtrade.pairlists.refresh_pairlist() pairslist = whitelist_conf['exchange']['pair_whitelist'] assert set(whitelist) == set(pairslist) @@ -312,18 +312,18 @@ def test_volumepairlist_caching(mocker, markets, whitelist_conf, tickers): exchange_has=MagicMock(return_value=True), get_tickers=tickers ) - bot = get_patched_freqtradebot(mocker, whitelist_conf) - assert bot.pairlists._pairlists[0]._last_refresh == 0 + freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) + assert freqtrade.pairlists._pairlists[0]._last_refresh == 0 assert tickers.call_count == 0 - bot.pairlists.refresh_pairlist() + freqtrade.pairlists.refresh_pairlist() assert tickers.call_count == 1 - assert bot.pairlists._pairlists[0]._last_refresh != 0 - lrf = bot.pairlists._pairlists[0]._last_refresh - bot.pairlists.refresh_pairlist() + assert freqtrade.pairlists._pairlists[0]._last_refresh != 0 + lrf = freqtrade.pairlists._pairlists[0]._last_refresh + freqtrade.pairlists.refresh_pairlist() assert tickers.call_count == 1 # Time should not be updated. - assert bot.pairlists._pairlists[0]._last_refresh == lrf + assert freqtrade.pairlists._pairlists[0]._last_refresh == lrf def test_pairlistmanager_no_pairlist(mocker, markets, whitelist_conf, caplog): From f0c3a0d2f88ce57443c3313100cdb796f8c46f4a Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 15 May 2020 03:59:13 +0300 Subject: [PATCH 03/14] Simplify VolumePairList --- freqtrade/pairlist/VolumePairList.py | 25 ++++++++++++------------- tests/pairlist/test_pairlist.py | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/freqtrade/pairlist/VolumePairList.py b/freqtrade/pairlist/VolumePairList.py index 7ac350de9..602a2e7cb 100644 --- a/freqtrade/pairlist/VolumePairList.py +++ b/freqtrade/pairlist/VolumePairList.py @@ -27,6 +27,7 @@ class VolumePairList(IPairList): f'`number_assets` not specified. Please check your configuration ' 'for "pairlist.config.number_assets"') + self._stake_currency = config['stake_currency'] self._number_pairs = self._pairlistconfig['number_assets'] self._sort_key = self._pairlistconfig.get('sort_key', 'quoteVolume') self._min_value = self._pairlistconfig.get('min_value', 0) @@ -79,9 +80,7 @@ class VolumePairList(IPairList): (self._last_refresh + self.refresh_period < datetime.now().timestamp())): self._last_refresh = int(datetime.now().timestamp()) - pairs = self._gen_pair_whitelist(pairlist, tickers, - self._config['stake_currency'], - self._sort_key, self._min_value) + pairs = self._gen_pair_whitelist(pairlist, tickers) else: pairs = pairlist @@ -89,29 +88,29 @@ class VolumePairList(IPairList): return pairs - def _gen_pair_whitelist(self, pairlist: List[str], tickers: Dict, - base_currency: str, key: str, min_val: int) -> List[str]: + def _gen_pair_whitelist(self, pairlist: List[str], tickers: Dict) -> List[str]: """ Updates the whitelist with with a dynamically generated list - :param base_currency: base currency as str - :param key: sort key (defaults to 'quoteVolume') + :param pairlist: pairlist to filter or sort :param tickers: Tickers (from exchange.get_tickers()). :return: List of pairs """ if self._pairlist_pos == 0: # If VolumePairList is the first in the list, use fresh pairlist # Check if pair quote currency equals to the stake currency. - filtered_tickers = [v for k, v in tickers.items() - if (self._exchange.get_pair_quote_currency(k) == base_currency - and v[key] is not None)] + filtered_tickers = [ + v for k, v in tickers.items() + if (self._exchange.get_pair_quote_currency(k) == self._stake_currency + and v[self._sort_key] is not None)] else: # If other pairlist is in front, use the incoming pairlist. filtered_tickers = [v for k, v in tickers.items() if k in pairlist] - if min_val > 0: - filtered_tickers = [v for v in filtered_tickers if v[key] > min_val] + if self._min_value > 0: + filtered_tickers = [ + v for v in filtered_tickers if v[self._sort_key] > self._min_value] - sorted_tickers = sorted(filtered_tickers, reverse=True, key=lambda t: t[key]) + sorted_tickers = sorted(filtered_tickers, reverse=True, key=lambda t: t[self._sort_key]) # Validate whitelist to only have active market pairs pairs = self._whitelist_for_active_markets([s['symbol'] for s in sorted_tickers]) diff --git a/tests/pairlist/test_pairlist.py b/tests/pairlist/test_pairlist.py index 10886d87c..61f6b4bd5 100644 --- a/tests/pairlist/test_pairlist.py +++ b/tests/pairlist/test_pairlist.py @@ -206,6 +206,7 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t pairlists, base_currency, whitelist_result, caplog) -> None: whitelist_conf['pairlists'] = pairlists + whitelist_conf['stake_currency'] = base_currency mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) @@ -215,7 +216,6 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t markets=PropertyMock(return_value=shitcoinmarkets), ) - freqtrade.config['stake_currency'] = base_currency freqtrade.pairlists.refresh_pairlist() whitelist = freqtrade.pairlists.whitelist From afa7a5846b29eaf0794a06e5ad9ec8514aca0fac Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 15 May 2020 04:05:31 +0300 Subject: [PATCH 04/14] Simplify PriceFilter --- freqtrade/pairlist/PriceFilter.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/freqtrade/pairlist/PriceFilter.py b/freqtrade/pairlist/PriceFilter.py index 2f7e98e24..a7c2bd2d9 100644 --- a/freqtrade/pairlist/PriceFilter.py +++ b/freqtrade/pairlist/PriceFilter.py @@ -38,14 +38,12 @@ class PriceFilter(IPairList): :return: True if the pair can stay, false if it should be removed """ if ticker['last'] is None: - self.log_on_refresh(logger.info, f"Removed {ticker['symbol']} from whitelist, because " "ticker['last'] is empty (Usually no trade in the last 24h).") return False - compare = ticker['last'] + self._exchange.price_get_one_pip(ticker['symbol'], - ticker['last']) - changeperc = (compare - ticker['last']) / ticker['last'] + compare = self._exchange.price_get_one_pip(ticker['symbol'], ticker['last']) + changeperc = compare / ticker['last'] if changeperc > self._low_price_ratio: self.log_on_refresh(logger.info, f"Removed {ticker['symbol']} from whitelist, " f"because 1 unit is {changeperc * 100:.3f}%") From 794ed304b1a3356d010feafe7d57816dc07b657e Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 15 May 2020 04:17:23 +0300 Subject: [PATCH 05/14] Make stoploss an attribute --- freqtrade/pairlist/PrecisionFilter.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/freqtrade/pairlist/PrecisionFilter.py b/freqtrade/pairlist/PrecisionFilter.py index 2a2ba46b7..35eed5392 100644 --- a/freqtrade/pairlist/PrecisionFilter.py +++ b/freqtrade/pairlist/PrecisionFilter.py @@ -1,6 +1,6 @@ import logging from copy import deepcopy -from typing import Dict, List +from typing import Any, Dict, List from freqtrade.pairlist.IPairList import IPairList @@ -9,6 +9,16 @@ logger = logging.getLogger(__name__) class PrecisionFilter(IPairList): + def __init__(self, exchange, pairlistmanager, + config: Dict[str, Any], pairlistconfig: Dict[str, Any], + pairlist_pos: int) -> None: + super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + + self._stoploss = self._config.get('stoploss') + if self._stoploss is not None: + # Precalculate sanitized stoploss value to avoid recalculation for every pair + self._stoploss = 1 - abs(self._stoploss) + @property def needstickers(self) -> bool: """ @@ -49,15 +59,12 @@ class PrecisionFilter(IPairList): """ Filters and sorts pairlists and assigns and returns them again. """ - stoploss = self._config.get('stoploss') - if stoploss is not None: - # Precalculate sanitized stoploss value to avoid recalculation for every pair - stoploss = 1 - abs(stoploss) # Copy list since we're modifying this list for p in deepcopy(pairlist): ticker = tickers.get(p) # Filter out assets which would not allow setting a stoploss - if not ticker or (stoploss and not self._validate_precision_filter(ticker, stoploss)): + if not ticker or (self._stoploss + and not self._validate_precision_filter(ticker, self._stoploss)): pairlist.remove(p) continue From 2aa80f915d5a494fc30637c5f785d9a92f05c72f Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 15 May 2020 04:24:18 +0300 Subject: [PATCH 06/14] Cosmetics: improve readability --- freqtrade/pairlist/PrecisionFilter.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/freqtrade/pairlist/PrecisionFilter.py b/freqtrade/pairlist/PrecisionFilter.py index 35eed5392..7491cf1b0 100644 --- a/freqtrade/pairlist/PrecisionFilter.py +++ b/freqtrade/pairlist/PrecisionFilter.py @@ -44,15 +44,19 @@ class PrecisionFilter(IPairList): :return: True if the pair can stay, false if it should be removed """ stop_price = ticker['ask'] * stoploss + # Adjust stop-prices to precision sp = self._exchange.price_to_precision(ticker["symbol"], stop_price) + stop_gap_price = self._exchange.price_to_precision(ticker["symbol"], stop_price * 0.99) logger.debug(f"{ticker['symbol']} - {sp} : {stop_gap_price}") + if sp <= stop_gap_price: self.log_on_refresh(logger.info, f"Removed {ticker['symbol']} from whitelist, " f"because stop price {sp} would be <= stop limit {stop_gap_price}") return False + return True def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: From cbb2ce3708b7f1a88e6b255ef2006ac98b360788 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 15 May 2020 04:55:28 +0300 Subject: [PATCH 07/14] Simplify PriceFilter --- freqtrade/pairlist/PriceFilter.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/freqtrade/pairlist/PriceFilter.py b/freqtrade/pairlist/PriceFilter.py index a7c2bd2d9..166515148 100644 --- a/freqtrade/pairlist/PriceFilter.py +++ b/freqtrade/pairlist/PriceFilter.py @@ -58,14 +58,12 @@ class PriceFilter(IPairList): :param tickers: Tickers (from exchange.get_tickers()). May be cached. :return: new whitelist """ - # Copy list since we're modifying this list - for p in deepcopy(pairlist): - ticker = tickers.get(p) - if not ticker: - pairlist.remove(p) - - # Filter out assets which would not allow setting a stoploss - if self._low_price_ratio and not self._validate_ticker_lowprice(ticker): - pairlist.remove(p) + if self._low_price_ratio: + # Copy list since we're modifying this list + for p in deepcopy(pairlist): + ticker = tickers[p] + # Filter out assets which would not allow setting a stoploss + if not self._validate_ticker_lowprice(ticker): + pairlist.remove(p) return pairlist From 143e6f52af7d9f177975226e4a699d23338d51d4 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 15 May 2020 05:14:06 +0300 Subject: [PATCH 08/14] Simplify SpreadFilter --- freqtrade/pairlist/PrecisionFilter.py | 17 +++++++------- freqtrade/pairlist/SpreadFilter.py | 33 ++++++++++++++++----------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/freqtrade/pairlist/PrecisionFilter.py b/freqtrade/pairlist/PrecisionFilter.py index 7491cf1b0..422cd865c 100644 --- a/freqtrade/pairlist/PrecisionFilter.py +++ b/freqtrade/pairlist/PrecisionFilter.py @@ -41,7 +41,7 @@ class PrecisionFilter(IPairList): :param ticker: ticker dict as returned from ccxt.load_markets() :param stoploss: stoploss value as set in the configuration (already cleaned to be 1 - stoploss) - :return: True if the pair can stay, false if it should be removed + :return: True if the pair can stay, False if it should be removed """ stop_price = ticker['ask'] * stoploss @@ -63,13 +63,12 @@ class PrecisionFilter(IPairList): """ Filters and sorts pairlists and assigns and returns them again. """ - # Copy list since we're modifying this list - for p in deepcopy(pairlist): - ticker = tickers.get(p) - # Filter out assets which would not allow setting a stoploss - if not ticker or (self._stoploss - and not self._validate_precision_filter(ticker, self._stoploss)): - pairlist.remove(p) - continue + if self._stoploss: + # Copy list since we're modifying this list + for p in deepcopy(pairlist): + ticker = tickers[p] + # Filter out assets which would not allow setting a stoploss + if not self._validate_precision_filter(ticker, self._stoploss): + pairlist.remove(p) return pairlist diff --git a/freqtrade/pairlist/SpreadFilter.py b/freqtrade/pairlist/SpreadFilter.py index 49731ef11..5b886135f 100644 --- a/freqtrade/pairlist/SpreadFilter.py +++ b/freqtrade/pairlist/SpreadFilter.py @@ -31,8 +31,24 @@ class SpreadFilter(IPairList): return (f"{self.name} - Filtering pairs with ask/bid diff above " f"{self._max_spread_ratio * 100}%.") - def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: + def _validate_spread(self, ticker: dict) -> bool: + """ + Validate spread for the ticker + :param ticker: ticker dict as returned from ccxt.load_markets() + :return: True if the pair can stay, False if it should be removed + """ + if 'bid' in ticker and 'ask' in ticker: + spread = 1 - ticker['bid'] / ticker['ask'] + if spread > self._max_spread_ratio: + self.log_on_refresh(logger.info, f"Removed {ticker['symbol']} from whitelist, " + f"because spread {spread * 100:.3f}% >" + f"{self._max_spread_ratio * 100}%") + return False + else: + return True + return False + def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: """ Filters and sorts pairlist and returns the whitelist again. Called on each bot iteration - please use internal caching if necessary @@ -41,19 +57,10 @@ class SpreadFilter(IPairList): :return: new whitelist """ # Copy list since we're modifying this list - - spread = None for p in deepcopy(pairlist): - ticker = tickers.get(p) - assert ticker is not None - if 'bid' in ticker and 'ask' in ticker: - spread = 1 - ticker['bid'] / ticker['ask'] - if not ticker or spread > self._max_spread_ratio: - self.log_on_refresh(logger.info, f"Removed {ticker['symbol']} from whitelist, " - f"because spread {spread * 100:.3f}% >" - f"{self._max_spread_ratio * 100}%") - pairlist.remove(p) - else: + ticker = tickers[p] + # Filter out assets + if not self._validate_spread(ticker): pairlist.remove(p) return pairlist From 1b3864ebf8eb29f8e089e721d17377f22ed44384 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 16 May 2020 09:21:36 +0300 Subject: [PATCH 09/14] Make flake happy --- freqtrade/pairlist/VolumePairList.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/pairlist/VolumePairList.py b/freqtrade/pairlist/VolumePairList.py index 602a2e7cb..46ff4b9c8 100644 --- a/freqtrade/pairlist/VolumePairList.py +++ b/freqtrade/pairlist/VolumePairList.py @@ -101,7 +101,7 @@ class VolumePairList(IPairList): filtered_tickers = [ v for k, v in tickers.items() if (self._exchange.get_pair_quote_currency(k) == self._stake_currency - and v[self._sort_key] is not None)] + and v[self._sort_key] is not None)] else: # If other pairlist is in front, use the incoming pairlist. filtered_tickers = [v for k, v in tickers.items() if k in pairlist] From d457542d962796011a4dd23259d1a453182720d2 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 17 May 2020 11:11:49 +0300 Subject: [PATCH 10/14] Fix PrecisionFilter --- freqtrade/pairlist/PrecisionFilter.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/freqtrade/pairlist/PrecisionFilter.py b/freqtrade/pairlist/PrecisionFilter.py index 422cd865c..85fa592f8 100644 --- a/freqtrade/pairlist/PrecisionFilter.py +++ b/freqtrade/pairlist/PrecisionFilter.py @@ -14,10 +14,8 @@ class PrecisionFilter(IPairList): pairlist_pos: int) -> None: super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) - self._stoploss = self._config.get('stoploss') - if self._stoploss is not None: - # Precalculate sanitized stoploss value to avoid recalculation for every pair - self._stoploss = 1 - abs(self._stoploss) + # Precalculate sanitized stoploss value to avoid recalculation for every pair + self._stoploss = 1 - abs(self._config['stoploss']) @property def needstickers(self) -> bool: @@ -63,12 +61,11 @@ class PrecisionFilter(IPairList): """ Filters and sorts pairlists and assigns and returns them again. """ - if self._stoploss: - # Copy list since we're modifying this list - for p in deepcopy(pairlist): - ticker = tickers[p] - # Filter out assets which would not allow setting a stoploss - if not self._validate_precision_filter(ticker, self._stoploss): - pairlist.remove(p) + # Copy list since we're modifying this list + for p in deepcopy(pairlist): + ticker = tickers[p] + # Filter out assets which would not allow setting a stoploss + if not self._validate_precision_filter(ticker, self._stoploss): + pairlist.remove(p) return pairlist From ce185a3b193429a0ca00cb8119ad8a99e786f422 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 17 May 2020 11:39:18 +0300 Subject: [PATCH 11/14] Remove pairs with no ticker available when it's needed --- freqtrade/pairlist/pairlistmanager.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/freqtrade/pairlist/pairlistmanager.py b/freqtrade/pairlist/pairlistmanager.py index 5b4c5b602..07dec912e 100644 --- a/freqtrade/pairlist/pairlistmanager.py +++ b/freqtrade/pairlist/pairlistmanager.py @@ -5,6 +5,7 @@ Provides lists as configured in config.json """ import logging +from copy import deepcopy from typing import Dict, List from cachetools import TTLCache, cached @@ -79,13 +80,14 @@ class PairListManager(): Run pairlist through all configured pairlists. """ - pairlist = self._whitelist.copy() - # tickers should be cached to avoid calling the exchange on each call. tickers: Dict = {} if self._tickers_needed: tickers = self._get_cached_tickers() + # Adjust whitelist if filters are using tickers + pairlist = self._prepare_whitelist(self._whitelist.copy(), tickers) + # Process all pairlists in chain for pl in self._pairlists: pairlist = pl.filter_pairlist(pairlist, tickers) @@ -94,3 +96,17 @@ class PairListManager(): pairlist = IPairList.verify_blacklist(pairlist, self.blacklist, True) self._whitelist = pairlist + + def _prepare_whitelist(self, pairlist: List[str], tickers) -> List[str]: + """ + Prepare pairlist for Pairlist Filters that use tickers data - remove + pairs that do not have ticker available + """ + if self._tickers_needed: + # Copy list since we're modifying this list + for p in deepcopy(pairlist): + ticker = tickers.get(p) + if not ticker: + pairlist.remove(p) + + return pairlist From 97c50f86e984c7b48ab6ec192c1835ba9b9ef178 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 17 May 2020 14:10:11 +0300 Subject: [PATCH 12/14] Cleanup pairlistmanager --- freqtrade/pairlist/pairlistmanager.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/freqtrade/pairlist/pairlistmanager.py b/freqtrade/pairlist/pairlistmanager.py index 07dec912e..b01f1342b 100644 --- a/freqtrade/pairlist/pairlistmanager.py +++ b/freqtrade/pairlist/pairlistmanager.py @@ -1,9 +1,6 @@ """ -Static List provider - -Provides lists as configured in config.json - - """ +PairList manager class +""" import logging from copy import deepcopy from typing import Dict, List @@ -14,6 +11,7 @@ from freqtrade.exceptions import OperationalException from freqtrade.pairlist.IPairList import IPairList from freqtrade.resolvers import PairListResolver + logger = logging.getLogger(__name__) @@ -79,8 +77,7 @@ class PairListManager(): """ Run pairlist through all configured pairlists. """ - - # tickers should be cached to avoid calling the exchange on each call. + # Tickers should be cached to avoid calling the exchange on each call. tickers: Dict = {} if self._tickers_needed: tickers = self._get_cached_tickers() @@ -92,21 +89,21 @@ class PairListManager(): for pl in self._pairlists: pairlist = pl.filter_pairlist(pairlist, tickers) - # Validation against blacklist happens after the pairlists to ensure blacklist is respected. + # Validation against blacklist happens after the pairlists to ensure + # blacklist is respected. pairlist = IPairList.verify_blacklist(pairlist, self.blacklist, True) self._whitelist = pairlist def _prepare_whitelist(self, pairlist: List[str], tickers) -> List[str]: """ - Prepare pairlist for Pairlist Filters that use tickers data - remove + Prepare sanitized pairlist for Pairlist Filters that use tickers data - remove pairs that do not have ticker available """ if self._tickers_needed: # Copy list since we're modifying this list for p in deepcopy(pairlist): - ticker = tickers.get(p) - if not ticker: + if p not in tickers: pairlist.remove(p) return pairlist From ae69d31095bb5241690036c39acb2ca768b96f26 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 17 May 2020 14:13:26 +0300 Subject: [PATCH 13/14] Cosmetics in IPairList --- freqtrade/pairlist/IPairList.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/freqtrade/pairlist/IPairList.py b/freqtrade/pairlist/IPairList.py index e089e546c..e2eb364bc 100644 --- a/freqtrade/pairlist/IPairList.py +++ b/freqtrade/pairlist/IPairList.py @@ -1,9 +1,6 @@ """ -Static List provider - -Provides lists as configured in config.json - - """ +PairList base class +""" import logging from abc import ABC, abstractmethod, abstractproperty from copy import deepcopy @@ -13,6 +10,7 @@ from cachetools import TTLCache, cached from freqtrade.exchange import market_is_active + logger = logging.getLogger(__name__) From 16622bbfadc26561fe134a08f7bfeaba89134390 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 17 May 2020 14:26:21 +0300 Subject: [PATCH 14/14] Cosmetics in pair lists --- freqtrade/pairlist/PrecisionFilter.py | 7 +++++-- freqtrade/pairlist/PriceFilter.py | 7 +++++-- freqtrade/pairlist/SpreadFilter.py | 4 ++++ freqtrade/pairlist/StaticPairList.py | 8 ++++---- freqtrade/pairlist/VolumePairList.py | 9 +++++---- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/freqtrade/pairlist/PrecisionFilter.py b/freqtrade/pairlist/PrecisionFilter.py index 85fa592f8..6bd9c594e 100644 --- a/freqtrade/pairlist/PrecisionFilter.py +++ b/freqtrade/pairlist/PrecisionFilter.py @@ -1,9 +1,13 @@ +""" +Precision pair list filter +""" import logging from copy import deepcopy from typing import Any, Dict, List from freqtrade.pairlist.IPairList import IPairList + logger = logging.getLogger(__name__) @@ -63,9 +67,8 @@ class PrecisionFilter(IPairList): """ # Copy list since we're modifying this list for p in deepcopy(pairlist): - ticker = tickers[p] # Filter out assets which would not allow setting a stoploss - if not self._validate_precision_filter(ticker, self._stoploss): + if not self._validate_precision_filter(tickers[p], self._stoploss): pairlist.remove(p) return pairlist diff --git a/freqtrade/pairlist/PriceFilter.py b/freqtrade/pairlist/PriceFilter.py index 166515148..167717656 100644 --- a/freqtrade/pairlist/PriceFilter.py +++ b/freqtrade/pairlist/PriceFilter.py @@ -1,9 +1,13 @@ +""" +Price pair list filter +""" import logging from copy import deepcopy from typing import Any, Dict, List from freqtrade.pairlist.IPairList import IPairList + logger = logging.getLogger(__name__) @@ -61,9 +65,8 @@ class PriceFilter(IPairList): if self._low_price_ratio: # Copy list since we're modifying this list for p in deepcopy(pairlist): - ticker = tickers[p] # Filter out assets which would not allow setting a stoploss - if not self._validate_ticker_lowprice(ticker): + if not self._validate_ticker_lowprice(tickers[p]): pairlist.remove(p) return pairlist diff --git a/freqtrade/pairlist/SpreadFilter.py b/freqtrade/pairlist/SpreadFilter.py index 5b886135f..88e143a50 100644 --- a/freqtrade/pairlist/SpreadFilter.py +++ b/freqtrade/pairlist/SpreadFilter.py @@ -1,9 +1,13 @@ +""" +Spread pair list filter +""" import logging from copy import deepcopy from typing import Dict, List from freqtrade.pairlist.IPairList import IPairList + logger = logging.getLogger(__name__) diff --git a/freqtrade/pairlist/StaticPairList.py b/freqtrade/pairlist/StaticPairList.py index 0050fbd5c..07e559168 100644 --- a/freqtrade/pairlist/StaticPairList.py +++ b/freqtrade/pairlist/StaticPairList.py @@ -1,14 +1,14 @@ """ -Static List provider +Static Pair List provider -Provides lists as configured in config.json - - """ +Provides pair white list as it configured in config +""" import logging from typing import Dict, List from freqtrade.pairlist.IPairList import IPairList + logger = logging.getLogger(__name__) diff --git a/freqtrade/pairlist/VolumePairList.py b/freqtrade/pairlist/VolumePairList.py index 46ff4b9c8..e20fb3577 100644 --- a/freqtrade/pairlist/VolumePairList.py +++ b/freqtrade/pairlist/VolumePairList.py @@ -1,9 +1,8 @@ """ Volume PairList provider -Provides lists as configured in config.json - - """ +Provides dynamic pair list based on trade volumes +""" import logging from datetime import datetime from typing import Any, Dict, List @@ -11,8 +10,10 @@ from typing import Any, Dict, List from freqtrade.exceptions import OperationalException from freqtrade.pairlist.IPairList import IPairList + logger = logging.getLogger(__name__) + SORT_VALUES = ['askVolume', 'bidVolume', 'quoteVolume'] @@ -115,7 +116,7 @@ class VolumePairList(IPairList): # Validate whitelist to only have active market pairs pairs = self._whitelist_for_active_markets([s['symbol'] for s in sorted_tickers]) pairs = self._verify_blacklist(pairs, aswarning=False) - # Limit to X number of pairs + # Limit pairlist to the requested number of pairs pairs = pairs[:self._number_pairs] return pairs