Merge pull request #5245 from sauces1313/RangeStabilityFilterMax

Range stability filter max
This commit is contained in:
Matthias 2021-08-02 08:06:36 +02:00 committed by GitHub
commit 5ead95b06b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 9 deletions

View File

@ -221,10 +221,10 @@ If `DOGE/BTC` maximum bid is 0.00000026 and minimum ask is 0.00000027, the ratio
#### RangeStabilityFilter #### 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: 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 ```json
"pairlists": [ "pairlists": [
@ -232,6 +232,7 @@ If the trading range over the last 10 days is <1%, remove the pair from the whit
"method": "RangeStabilityFilter", "method": "RangeStabilityFilter",
"lookback_days": 10, "lookback_days": 10,
"min_rate_of_change": 0.01, "min_rate_of_change": 0.01,
"max_rate_of_change": 0.99,
"refresh_period": 1440 "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 !!! 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. 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 #### VolatilityFilter

View File

@ -26,6 +26,7 @@ class RangeStabilityFilter(IPairList):
self._days = pairlistconfig.get('lookback_days', 10) self._days = pairlistconfig.get('lookback_days', 10)
self._min_rate_of_change = pairlistconfig.get('min_rate_of_change', 0.01) 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._refresh_period = pairlistconfig.get('refresh_period', 1440)
self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period) 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 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 " 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]: 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}.", f"which is below the threshold of {self._min_rate_of_change}.",
logger.info) logger.info)
result = False 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 self._pair_cache[pair] = result
return result return result

View File

@ -427,6 +427,10 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
{"method": "RangeStabilityFilter", "lookback_days": 10, {"method": "RangeStabilityFilter", "lookback_days": 10,
"min_rate_of_change": 0.01, "refresh_period": 1440}], "min_rate_of_change": 0.01, "refresh_period": 1440}],
"BTC", ['ETH/BTC', 'TKN/BTC', 'HOT/BTC']), "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": "StaticPairList"},
{"method": "VolatilityFilter", "lookback_days": 3, {"method": "VolatilityFilter", "lookback_days": 3,
"min_volatility": 0.002, "max_volatility": 0.004, "refresh_period": 1440}], "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) get_patched_freqtradebot(mocker, default_conf)
@pytest.mark.parametrize('min_rate_of_change,expected_length', [ @pytest.mark.parametrize('min_rate_of_change,max_rate_of_change,expected_length', [
(0.01, 5), (0.01, 0.99, 5),
(0.05, 0), # Setting rate_of_change to 5% removes all pairs from the whitelist. (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, 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}, default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10},
{'method': 'RangeStabilityFilter', 'lookback_days': 2, {'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', mocker.patch.multiple('freqtrade.exchange.Exchange',
markets=PropertyMock(return_value=markets), markets=PropertyMock(return_value=markets),
@ -984,11 +989,18 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo
None, None,
"PriceFilter requires max_value to be >= 0" "PriceFilter requires max_value to be >= 0"
), # OperationalException expected ), # 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 " "[{'RangeStabilityFilter': 'RangeStabilityFilter - Filtering pairs with rate of change below "
"0.01 over the last days.'}]", "0.01 over the last days.'}]",
None 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, def test_pricefilter_desc(mocker, whitelist_conf, markets, pairlistconfig,
desc_expected, exception_expected): desc_expected, exception_expected):