Renamd volatilityFilter to RangeStabilityFilter

This commit is contained in:
Matthias 2020-11-22 19:47:27 +01:00
parent 29c6a9263d
commit 8f1d2ff070
5 changed files with 41 additions and 41 deletions

View File

@ -69,8 +69,8 @@
{"method": "PriceFilter", "low_price_ratio": 0.01, "min_price": 0.00000010}, {"method": "PriceFilter", "low_price_ratio": 0.01, "min_price": 0.00000010},
{"method": "SpreadFilter", "max_spread_ratio": 0.005}, {"method": "SpreadFilter", "max_spread_ratio": 0.005},
{ {
"method": "VolatilityFilter", "method": "RangeStabilityFilter",
"volatility_over_days": 10, "lookback_days": 10,
"min_volatility": 0.01, "min_volatility": 0.01,
"refresh_period": 1440 "refresh_period": 1440
} }

View File

@ -19,7 +19,7 @@ Inactive markets are always removed from the resulting pairlist. Explicitly blac
* [`PriceFilter`](#pricefilter) * [`PriceFilter`](#pricefilter)
* [`ShuffleFilter`](#shufflefilter) * [`ShuffleFilter`](#shufflefilter)
* [`SpreadFilter`](#spreadfilter) * [`SpreadFilter`](#spreadfilter)
* [`VolatilityFilter`](#volatilityfilter) * [`RangeStabilityFilter`](#rangestabilityfilter)
!!! Tip "Testing pairlists" !!! Tip "Testing pairlists"
Pairlist configurations can be quite tricky to get right. Best use the [`test-pairlist`](utils.md#test-pairlist) utility sub-command to test your configuration quickly. Pairlist configurations can be quite tricky to get right. Best use the [`test-pairlist`](utils.md#test-pairlist) utility sub-command to test your configuration quickly.
@ -119,26 +119,26 @@ Example:
If `DOGE/BTC` maximum bid is 0.00000026 and minimum ask is 0.00000027, the ratio is calculated as: `1 - bid/ask ~= 0.037` which is `> 0.005` and this pair will be filtered out. If `DOGE/BTC` maximum bid is 0.00000026 and minimum ask is 0.00000027, the ratio is calculated as: `1 - bid/ask ~= 0.037` which is `> 0.005` and this pair will be filtered out.
#### VolatilityFilter #### RangeStabilityFilter
Removes pairs where the difference between lowest low and highest high over `volatility_over_days` days is below `min_volatility`. 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`. 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 volatility over the last 10 days is <1%, remove the pair from the whitelist. If the trading range over the last 10 days is <1%, remove the pair from the whitelist.
```json ```json
"pairlists": [ "pairlists": [
{ {
"method": "VolatilityFilter", "method": "RangeStabilityFilter",
"volatility_over_days": 10, "lookback_days": 10,
"min_volatility": 0.01, "min_rate_of_change": 0.01,
"refresh_period": 1440 "refresh_period": 1440
} }
] ]
``` ```
!!! Tip !!! Tip
This Filter can be used to automatically remove stable coin pairs, which have a very low volatility, 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.
### Full example of Pairlist Handlers ### Full example of Pairlist Handlers
@ -160,9 +160,9 @@ The below example blacklists `BNB/BTC`, uses `VolumePairList` with `20` assets,
{"method": "PriceFilter", "low_price_ratio": 0.01}, {"method": "PriceFilter", "low_price_ratio": 0.01},
{"method": "SpreadFilter", "max_spread_ratio": 0.005}, {"method": "SpreadFilter", "max_spread_ratio": 0.005},
{ {
"method": "VolatilityFilter", "method": "RangeStabilityFilter",
"volatility_over_days": 10, "lookback_days": 10,
"min_volatility": 0.01, "min_rate_of_change": 0.01,
"refresh_period": 1440 "refresh_period": 1440
}, },
{"method": "ShuffleFilter", "seed": 42} {"method": "ShuffleFilter", "seed": 42}

View File

@ -25,7 +25,7 @@ HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss',
'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily'] 'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily']
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList',
'AgeFilter', 'PrecisionFilter', 'PriceFilter', 'AgeFilter', 'PrecisionFilter', 'PriceFilter',
'ShuffleFilter', 'SpreadFilter', 'VolatilityFilter'] 'RangeStabilityFilter', 'ShuffleFilter', 'SpreadFilter']
AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5'] AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5']
DRY_RUN_WALLET = 1000 DRY_RUN_WALLET = 1000
DATETIME_PRINT_FORMAT = '%Y-%m-%d %H:%M:%S' DATETIME_PRINT_FORMAT = '%Y-%m-%d %H:%M:%S'

View File

@ -15,23 +15,23 @@ from freqtrade.pairlist.IPairList import IPairList
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class VolatilityFilter(IPairList): class RangeStabilityFilter(IPairList):
def __init__(self, exchange, pairlistmanager, def __init__(self, exchange, pairlistmanager,
config: Dict[str, Any], pairlistconfig: Dict[str, Any], config: Dict[str, Any], pairlistconfig: Dict[str, Any],
pairlist_pos: int) -> None: pairlist_pos: int) -> None:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
self._days = pairlistconfig.get('volatility_over_days', 10) self._days = pairlistconfig.get('lookback_days', 10)
self._min_volatility = pairlistconfig.get('min_volatility', 0.01) self._min_rate_of_change = pairlistconfig.get('min_rate_of_change', 0.01)
self._refresh_period = pairlistconfig.get('refresh_period', 1440) self._refresh_period = pairlistconfig.get('refresh_period', 1440)
self._pair_cache: TTLCache = TTLCache(maxsize=100, ttl=self._refresh_period) self._pair_cache: TTLCache = TTLCache(maxsize=100, ttl=self._refresh_period)
if self._days < 1: if self._days < 1:
raise OperationalException("VolatilityFilter requires volatility_over_days to be >= 1") raise OperationalException("RangeStabilityFilter requires lookback_days to be >= 1")
if self._days > exchange.ohlcv_candle_limit: if self._days > exchange.ohlcv_candle_limit:
raise OperationalException("VolatilityFilter requires volatility_over_days to not " raise OperationalException("RangeStabilityFilter requires lookback_days to not "
"exceed exchange max request size " "exceed exchange max request size "
f"({exchange.ohlcv_candle_limit})") f"({exchange.ohlcv_candle_limit})")
@ -48,12 +48,12 @@ class VolatilityFilter(IPairList):
""" """
Short whitelist method description - used for startup-messages Short whitelist method description - used for startup-messages
""" """
return (f"{self.name} - Filtering pairs with volatility below {self._min_volatility} " return (f"{self.name} - Filtering pairs with rate of change below "
f"over the last {plural(self._days, 'day')}.") f"{self._min_rate_of_change} over the last {plural(self._days, 'day')}.")
def _validate_pair(self, ticker: Dict) -> bool: def _validate_pair(self, ticker: Dict) -> bool:
""" """
Validate volatility Validate trading range
:param ticker: ticker dict as returned from ccxt.load_markets() :param ticker: ticker dict as returned from ccxt.load_markets()
: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
""" """
@ -75,14 +75,14 @@ class VolatilityFilter(IPairList):
highest_high = daily_candles['high'].max() highest_high = daily_candles['high'].max()
lowest_low = daily_candles['low'].min() lowest_low = daily_candles['low'].min()
pct_change = ((highest_high - lowest_low) / lowest_low) if lowest_low > 0 else 0 pct_change = ((highest_high - lowest_low) / lowest_low) if lowest_low > 0 else 0
if pct_change >= self._min_volatility: if pct_change >= self._min_rate_of_change:
result = True result = True
else: else:
self.log_on_refresh(logger.info, self.log_on_refresh(logger.info,
f"Removed {pair} from whitelist, " f"Removed {pair} from whitelist, "
f"because volatility over {plural(self._days, 'day')} is " f"because rate of change over {plural(self._days, 'day')} is "
f"{pct_change:.3f}, which is below the " f"{pct_change:.3f}, which is below the "
f"threshold of {self._min_volatility}.") f"threshold of {self._min_rate_of_change}.")
result = False result = False
self._pair_cache[pair] = result self._pair_cache[pair] = result

View File

@ -341,8 +341,8 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
{"method": "PriceFilter", "low_price_ratio": 0.02}], {"method": "PriceFilter", "low_price_ratio": 0.02}],
"USDT", ['ETH/USDT', 'NANO/USDT']), "USDT", ['ETH/USDT', 'NANO/USDT']),
([{"method": "StaticPairList"}, ([{"method": "StaticPairList"},
{"method": "VolatilityFilter", "volatility_over_days": 10, {"method": "RangeStabilityFilter", "lookback_days": 10,
"min_volatility": 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']),
]) ])
def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, tickers, def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, tickers,
@ -586,9 +586,9 @@ def test_agefilter_caching(mocker, markets, whitelist_conf_agefilter, tickers, o
assert freqtrade.exchange.get_historic_ohlcv.call_count == previous_call_count assert freqtrade.exchange.get_historic_ohlcv.call_count == previous_call_count
def test_volatilityfilter_checks(mocker, default_conf, markets, tickers): def test_rangestabilityfilter_checks(mocker, default_conf, markets, tickers):
default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10}, default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10},
{'method': 'VolatilityFilter', 'volatility_over_days': 99999}] {'method': 'RangeStabilityFilter', 'lookback_days': 99999}]
mocker.patch.multiple('freqtrade.exchange.Exchange', mocker.patch.multiple('freqtrade.exchange.Exchange',
markets=PropertyMock(return_value=markets), markets=PropertyMock(return_value=markets),
@ -597,27 +597,27 @@ def test_volatilityfilter_checks(mocker, default_conf, markets, tickers):
) )
with pytest.raises(OperationalException, with pytest.raises(OperationalException,
match=r'VolatilityFilter requires volatility_over_days to not exceed ' match=r'RangeStabilityFilter requires lookback_days to not exceed '
r'exchange max request size \([0-9]+\)'): r'exchange max request size \([0-9]+\)'):
get_patched_freqtradebot(mocker, default_conf) get_patched_freqtradebot(mocker, default_conf)
default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10}, default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10},
{'method': 'VolatilityFilter', 'volatility_over_days': 0}] {'method': 'RangeStabilityFilter', 'lookback_days': 0}]
with pytest.raises(OperationalException, with pytest.raises(OperationalException,
match='VolatilityFilter requires volatility_over_days to be >= 1'): match='RangeStabilityFilter requires lookback_days to be >= 1'):
get_patched_freqtradebot(mocker, default_conf) get_patched_freqtradebot(mocker, default_conf)
@pytest.mark.parametrize('min_volatility,expected_length', [ @pytest.mark.parametrize('min_rate_of_change,expected_length', [
(0.01, 5), (0.01, 5),
(0.05, 0), # Setting volatility to 5% removes all pairs from the whitelist. (0.05, 0), # Setting rate_of_change to 5% removes all pairs from the whitelist.
]) ])
def test_volatilityfilter_caching(mocker, markets, default_conf, tickers, ohlcv_history_list, def test_rangestabilityfilter_caching(mocker, markets, default_conf, tickers, ohlcv_history_list,
min_volatility, expected_length): min_rate_of_change, expected_length):
default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10}, default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10},
{'method': 'VolatilityFilter', 'volatility_over_days': 2, {'method': 'RangeStabilityFilter', 'lookback_days': 2,
'min_volatility': min_volatility}] 'min_rate_of_change': min_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),
@ -677,9 +677,9 @@ def test_volatilityfilter_caching(mocker, markets, default_conf, tickers, ohlcv_
None, None,
"PriceFilter requires max_price to be >= 0" "PriceFilter requires max_price to be >= 0"
), # OperationalException expected ), # OperationalException expected
({"method": "VolatilityFilter", "volatility_over_days": 10, "min_volatility": 0.01}, ({"method": "RangeStabilityFilter", "lookback_days": 10, "min_rate_of_change": 0.01},
"[{'VolatilityFilter': 'VolatilityFilter - Filtering pairs with volatility below 0.01 " "[{'RangeStabilityFilter': 'RangeStabilityFilter - Filtering pairs with rate of change below "
"over the last days.'}]", "0.01 over the last days.'}]",
None None
), ),
]) ])