Merge pull request #9861 from freqtrade/feat/sort_volatility
Add sorting to volatility and RangeStability pairlists
This commit is contained in:
+4
-4
@@ -142,8 +142,8 @@ def generate_trades_history(n_rows, start_date: Optional[datetime] = None, days=
|
||||
return df
|
||||
|
||||
|
||||
def generate_test_data(timeframe: str, size: int, start: str = '2020-07-05'):
|
||||
np.random.seed(42)
|
||||
def generate_test_data(timeframe: str, size: int, start: str = '2020-07-05', random_seed=42):
|
||||
np.random.seed(random_seed)
|
||||
|
||||
base = np.random.normal(20, 2, size=size)
|
||||
if timeframe == '1y':
|
||||
@@ -174,9 +174,9 @@ def generate_test_data(timeframe: str, size: int, start: str = '2020-07-05'):
|
||||
return df
|
||||
|
||||
|
||||
def generate_test_data_raw(timeframe: str, size: int, start: str = '2020-07-05'):
|
||||
def generate_test_data_raw(timeframe: str, size: int, start: str = '2020-07-05', random_seed=42):
|
||||
""" Generates data in the ohlcv format used by ccxt """
|
||||
df = generate_test_data(timeframe, size, start)
|
||||
df = generate_test_data(timeframe, size, start, random_seed)
|
||||
df['date'] = df.loc[:, 'date'].astype(np.int64) // 1000 // 1000
|
||||
return list(list(x) for x in zip(*(df[x].values.tolist() for x in df.columns)))
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist,
|
||||
from freqtrade.plugins.pairlistmanager import PairListManager
|
||||
from freqtrade.resolvers import PairListResolver
|
||||
from freqtrade.util.datetime_helpers import dt_now
|
||||
from tests.conftest import (EXMS, create_mock_trades_usdt, get_patched_exchange,
|
||||
from tests.conftest import (EXMS, create_mock_trades_usdt, generate_test_data, get_patched_exchange,
|
||||
get_patched_freqtradebot, log_has, log_has_re, num_log_has)
|
||||
|
||||
|
||||
@@ -748,6 +748,104 @@ def test_PerformanceFilter_error(mocker, whitelist_conf, caplog) -> None:
|
||||
assert log_has("PerformanceFilter is not available in this mode.", caplog)
|
||||
|
||||
|
||||
def test_VolatilityFilter_error(mocker, whitelist_conf) -> None:
|
||||
volatility_filter = {"method": "VolatilityFilter", "lookback_days": -1}
|
||||
whitelist_conf['pairlists'] = [{"method": "StaticPairList"}, volatility_filter]
|
||||
|
||||
mocker.patch(f'{EXMS}.exchange_has', MagicMock(return_value=True))
|
||||
exchange_mock = MagicMock()
|
||||
exchange_mock.ohlcv_candle_limit = MagicMock(return_value=1000)
|
||||
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"VolatilityFilter requires lookback_days to be >= 1*"):
|
||||
PairListManager(exchange_mock, whitelist_conf, MagicMock())
|
||||
|
||||
volatility_filter = {"method": "VolatilityFilter", "lookback_days": 2000}
|
||||
whitelist_conf['pairlists'] = [{"method": "StaticPairList"}, volatility_filter]
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"VolatilityFilter requires lookback_days to not exceed exchange max"):
|
||||
PairListManager(exchange_mock, whitelist_conf, MagicMock())
|
||||
|
||||
volatility_filter = {"method": "VolatilityFilter", "sort_direction": "Random"}
|
||||
whitelist_conf['pairlists'] = [{"method": "StaticPairList"}, volatility_filter]
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"VolatilityFilter requires sort_direction to be either "
|
||||
r"None .*'asc'.*'desc'"):
|
||||
PairListManager(exchange_mock, whitelist_conf, MagicMock())
|
||||
|
||||
|
||||
@pytest.mark.parametrize('pairlist,expected_pairlist', [
|
||||
({"method": "VolatilityFilter", "sort_direction": "asc"},
|
||||
['XRP/BTC', 'ETH/BTC', 'LTC/BTC', 'TKN/BTC']),
|
||||
({"method": "VolatilityFilter", "sort_direction": "desc"},
|
||||
['TKN/BTC', 'LTC/BTC', 'ETH/BTC', 'XRP/BTC']),
|
||||
({"method": "VolatilityFilter", "sort_direction": "desc", 'min_volatility': 0.4},
|
||||
['TKN/BTC', 'LTC/BTC', 'ETH/BTC']),
|
||||
({"method": "VolatilityFilter", "sort_direction": "asc", 'min_volatility': 0.4},
|
||||
['ETH/BTC', 'LTC/BTC', 'TKN/BTC']),
|
||||
({"method": "VolatilityFilter", "sort_direction": "desc", 'max_volatility': 0.5},
|
||||
['LTC/BTC', 'ETH/BTC', 'XRP/BTC']),
|
||||
({"method": "VolatilityFilter", "sort_direction": "asc", 'max_volatility': 0.5},
|
||||
['XRP/BTC', 'ETH/BTC', 'LTC/BTC']),
|
||||
({"method": "RangeStabilityFilter", "sort_direction": "asc"},
|
||||
['ETH/BTC', 'XRP/BTC', 'LTC/BTC', 'TKN/BTC']),
|
||||
({"method": "RangeStabilityFilter", "sort_direction": "desc"},
|
||||
['TKN/BTC', 'LTC/BTC', 'XRP/BTC', 'ETH/BTC']),
|
||||
({"method": "RangeStabilityFilter", "sort_direction": "asc", 'min_rate_of_change': 0.4},
|
||||
['XRP/BTC', 'LTC/BTC', 'TKN/BTC']),
|
||||
({"method": "RangeStabilityFilter", "sort_direction": "desc", 'min_rate_of_change': 0.4},
|
||||
['TKN/BTC', 'LTC/BTC', 'XRP/BTC']),
|
||||
])
|
||||
def test_VolatilityFilter_RangeStabilityFilter_sort(
|
||||
mocker, whitelist_conf, tickers, time_machine, pairlist, expected_pairlist) -> None:
|
||||
whitelist_conf['pairlists'] = [
|
||||
{'method': 'VolumePairList', 'number_assets': 10},
|
||||
pairlist
|
||||
]
|
||||
|
||||
df1 = generate_test_data('1d', 10, '2022-01-05 00:00:00+00:00', random_seed=42)
|
||||
df2 = generate_test_data('1d', 10, '2022-01-05 00:00:00+00:00', random_seed=2)
|
||||
df3 = generate_test_data('1d', 10, '2022-01-05 00:00:00+00:00', random_seed=3)
|
||||
df4 = generate_test_data('1d', 10, '2022-01-05 00:00:00+00:00', random_seed=4)
|
||||
df5 = generate_test_data('1d', 10, '2022-01-05 00:00:00+00:00', random_seed=5)
|
||||
df6 = generate_test_data('1d', 10, '2022-01-05 00:00:00+00:00', random_seed=6)
|
||||
|
||||
assert not df1.equals(df2)
|
||||
time_machine.move_to('2022-01-15 00:00:00+00:00')
|
||||
|
||||
ohlcv_data = {
|
||||
('ETH/BTC', '1d', CandleType.SPOT): df1,
|
||||
('TKN/BTC', '1d', CandleType.SPOT): df2,
|
||||
('LTC/BTC', '1d', CandleType.SPOT): df3,
|
||||
('XRP/BTC', '1d', CandleType.SPOT): df4,
|
||||
('HOT/BTC', '1d', CandleType.SPOT): df5,
|
||||
('BLK/BTC', '1d', CandleType.SPOT): df6,
|
||||
|
||||
}
|
||||
ohlcv_mock = MagicMock(return_value=ohlcv_data)
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
exchange_has=MagicMock(return_value=True),
|
||||
refresh_latest_ohlcv=ohlcv_mock,
|
||||
get_tickers=tickers
|
||||
|
||||
)
|
||||
|
||||
exchange = get_patched_exchange(mocker, whitelist_conf)
|
||||
exchange.ohlcv_candle_limit = MagicMock(return_value=1000)
|
||||
plm = PairListManager(exchange, whitelist_conf, MagicMock())
|
||||
|
||||
assert exchange.ohlcv_candle_limit.call_count == 2
|
||||
plm.refresh_pairlist()
|
||||
assert ohlcv_mock.call_count == 1
|
||||
assert exchange.ohlcv_candle_limit.call_count == 2
|
||||
assert plm.whitelist == expected_pairlist
|
||||
|
||||
plm.refresh_pairlist()
|
||||
assert exchange.ohlcv_candle_limit.call_count == 2
|
||||
assert ohlcv_mock.call_count == 1
|
||||
|
||||
|
||||
def test_ShuffleFilter_init(mocker, whitelist_conf, caplog) -> None:
|
||||
whitelist_conf['pairlists'] = [
|
||||
{"method": "StaticPairList"},
|
||||
@@ -1095,6 +1193,13 @@ def test_rangestabilityfilter_checks(mocker, default_conf, markets, tickers):
|
||||
match='RangeStabilityFilter requires lookback_days to be >= 1'):
|
||||
get_patched_freqtradebot(mocker, default_conf)
|
||||
|
||||
default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10},
|
||||
{'method': 'RangeStabilityFilter', 'sort_direction': 'something'}]
|
||||
|
||||
with pytest.raises(OperationalException,
|
||||
match='RangeStabilityFilter requires sort_direction to be either None.*'):
|
||||
get_patched_freqtradebot(mocker, default_conf)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('min_rate_of_change,max_rate_of_change,expected_length', [
|
||||
(0.01, 0.99, 5),
|
||||
|
||||
Reference in New Issue
Block a user