diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 8727cc3fc..995e49a2d 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -221,10 +221,10 @@ If `DOGE/BTC` maximum bid is 0.00000026 and minimum ask is 0.00000027, the ratio #### RangeStabilityFilter -Removes pairs where the difference between lowest low and highest high over `lookback_days` days is below `min_rate_of_change`. Since this is a filter that requires additional data, the results are cached for `refresh_period`. +Removes pairs where the difference between lowest low and highest high over `lookback_days` days is below `min_rate_of_change` or above `max_rate_of_change`. Since this is a filter that requires additional data, the results are cached for `refresh_period`. In the below example: -If the trading range over the last 10 days is <1%, remove the pair from the whitelist. +If the trading range over the last 10 days is <1% or >99%, remove the pair from the whitelist. ```json "pairlists": [ @@ -232,6 +232,7 @@ If the trading range over the last 10 days is <1%, remove the pair from the whit "method": "RangeStabilityFilter", "lookback_days": 10, "min_rate_of_change": 0.01, + "max_rate_of_change": 0.99, "refresh_period": 1440 } ] @@ -239,6 +240,7 @@ If the trading range over the last 10 days is <1%, remove the pair from the whit !!! Tip This Filter can be used to automatically remove stable coin pairs, which have a very low trading range, and are therefore extremely difficult to trade with profit. + Additionally, it can also be used to automatically remove pairs with extreme high/low variance over a given amount of time. #### VolatilityFilter diff --git a/freqtrade/plugins/pairlist/rangestabilityfilter.py b/freqtrade/plugins/pairlist/rangestabilityfilter.py index a6d1820de..ef7f2cbcb 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfilter.py +++ b/freqtrade/plugins/pairlist/rangestabilityfilter.py @@ -26,6 +26,7 @@ class RangeStabilityFilter(IPairList): self._days = pairlistconfig.get('lookback_days', 10) self._min_rate_of_change = pairlistconfig.get('min_rate_of_change', 0.01) + self._max_rate_of_change = pairlistconfig.get('max_rate_of_change', None) self._refresh_period = pairlistconfig.get('refresh_period', 1440) self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period) @@ -50,8 +51,12 @@ class RangeStabilityFilter(IPairList): """ Short whitelist method description - used for startup-messages """ + max_rate_desc = "" + if self._max_rate_of_change: + max_rate_desc = (f" and above {self._max_rate_of_change}") return (f"{self.name} - Filtering pairs with rate of change below " - f"{self._min_rate_of_change} over the last {plural(self._days, 'day')}.") + f"{self._min_rate_of_change}{max_rate_desc} over the " + f"last {plural(self._days, 'day')}.") def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: """ @@ -104,6 +109,16 @@ class RangeStabilityFilter(IPairList): f"which is below the threshold of {self._min_rate_of_change}.", logger.info) result = False + if self._max_rate_of_change: + if pct_change <= self._max_rate_of_change: + result = True + else: + self.log_once( + f"Removed {pair} from whitelist, because rate of change " + f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, " + f"which is above the threshold of {self._max_rate_of_change}.", + logger.info) + result = False self._pair_cache[pair] = result return result diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index b15126a33..5f0701a22 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -427,6 +427,10 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): {"method": "RangeStabilityFilter", "lookback_days": 10, "min_rate_of_change": 0.01, "refresh_period": 1440}], "BTC", ['ETH/BTC', 'TKN/BTC', 'HOT/BTC']), + ([{"method": "StaticPairList"}, + {"method": "RangeStabilityFilter", "lookback_days": 10, + "max_rate_of_change": 0.01, "refresh_period": 1440}], + "BTC", []), # All removed because of max_rate_of_change being 0.017 ([{"method": "StaticPairList"}, {"method": "VolatilityFilter", "lookback_days": 3, "min_volatility": 0.002, "max_volatility": 0.004, "refresh_period": 1440}], @@ -874,15 +878,16 @@ def test_rangestabilityfilter_checks(mocker, default_conf, markets, tickers): get_patched_freqtradebot(mocker, default_conf) -@pytest.mark.parametrize('min_rate_of_change,expected_length', [ - (0.01, 5), - (0.05, 0), # Setting rate_of_change to 5% removes all pairs from the whitelist. +@pytest.mark.parametrize('min_rate_of_change,max_rate_of_change,expected_length', [ + (0.01, 0.99, 5), + (0.05, 0.0, 0), # Setting min rate_of_change to 5% removes all pairs from the whitelist. ]) def test_rangestabilityfilter_caching(mocker, markets, default_conf, tickers, ohlcv_history, - min_rate_of_change, expected_length): + min_rate_of_change, max_rate_of_change, expected_length): default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10}, {'method': 'RangeStabilityFilter', 'lookback_days': 2, - 'min_rate_of_change': min_rate_of_change}] + 'min_rate_of_change': min_rate_of_change, + "max_rate_of_change": max_rate_of_change}] mocker.patch.multiple('freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets), @@ -984,11 +989,18 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo None, "PriceFilter requires max_value to be >= 0" ), # OperationalException expected - ({"method": "RangeStabilityFilter", "lookback_days": 10, "min_rate_of_change": 0.01}, + ({"method": "RangeStabilityFilter", "lookback_days": 10, + "min_rate_of_change": 0.01}, "[{'RangeStabilityFilter': 'RangeStabilityFilter - Filtering pairs with rate of change below " "0.01 over the last days.'}]", None ), + ({"method": "RangeStabilityFilter", "lookback_days": 10, + "min_rate_of_change": 0.01, "max_rate_of_change": 0.99}, + "[{'RangeStabilityFilter': 'RangeStabilityFilter - Filtering pairs with rate of change below " + "0.01 and above 0.99 over the last days.'}]", + None + ), ]) def test_pricefilter_desc(mocker, whitelist_conf, markets, pairlistconfig, desc_expected, exception_expected):