Renamd volatilityFilter to RangeStabilityFilter
This commit is contained in:
parent
29c6a9263d
commit
8f1d2ff070
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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}
|
||||||
|
@ -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'
|
||||||
|
@ -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
|
||||||
|
|
@ -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
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
Loading…
Reference in New Issue
Block a user