Fix ShuffleFilter behavior in backtesting

This commit is contained in:
mrpabloyeah
2025-09-07 23:28:33 +02:00
parent 6de7c70e7e
commit e749051dbc
3 changed files with 30 additions and 9 deletions

View File

@@ -173,6 +173,7 @@ class Backtesting:
self.disable_database_use() self.disable_database_use()
self.init_backtest_detail() self.init_backtest_detail()
self.pairlists = PairListManager(self.exchange, self.config, self.dataprovider) self.pairlists = PairListManager(self.exchange, self.config, self.dataprovider)
self.dinamic_pairlist = False
self._validate_pairlists_for_backtesting() self._validate_pairlists_for_backtesting()
self.dataprovider.add_pairlisthandler(self.pairlists) self.dataprovider.add_pairlisthandler(self.pairlists)
@@ -226,6 +227,9 @@ class Backtesting:
"PrecisionFilter not allowed for backtesting multiple strategies." "PrecisionFilter not allowed for backtesting multiple strategies."
) )
if "ShuffleFilter" in self.pairlists.name_list:
self.dinamic_pairlist = True
def log_once(self, msg: str) -> None: def log_once(self, msg: str) -> None:
""" """
Partial reimplementation of log_once from the Login mixin. Partial reimplementation of log_once from the Login mixin.
@@ -1582,6 +1586,11 @@ class Backtesting:
for current_time in self._time_generator(start_date, end_date): for current_time in self._time_generator(start_date, end_date):
# Loop for each main candle. # Loop for each main candle.
self.check_abort() self.check_abort()
if self.dinamic_pairlist:
self.pairlists.refresh_pairlist()
pairs = self.pairlists.whitelist
# Reset open trade count for this candle # Reset open trade count for this candle
# Critical to avoid exceeding max_open_trades in backtesting # Critical to avoid exceeding max_open_trades in backtesting
# when timeframe-detail is used and trades close within the opening candle. # when timeframe-detail is used and trades close within the opening candle.

View File

@@ -93,6 +93,8 @@ class ShuffleFilter(IPairList):
return pairlist_new return pairlist_new
# Shuffle is done inplace # Shuffle is done inplace
self._random.shuffle(pairlist) self._random.shuffle(pairlist)
self.__pairlist_cache[pairlist_bef] = pairlist
if self._config.get("runmode") in (RunMode.LIVE, RunMode.DRY_RUN):
self.__pairlist_cache[pairlist_bef] = pairlist
return pairlist return pairlist

View File

@@ -1274,27 +1274,37 @@ def test_ShuffleFilter_init(mocker, whitelist_conf, caplog) -> None:
{"method": "StaticPairList"}, {"method": "StaticPairList"},
{"method": "ShuffleFilter", "seed": 43}, {"method": "ShuffleFilter", "seed": 43},
] ]
whitelist_conf["runmode"] = "backtest" whitelist_conf["runmode"] = RunMode.BACKTEST
exchange = get_patched_exchange(mocker, whitelist_conf) exchange = get_patched_exchange(mocker, whitelist_conf)
plm = PairListManager(exchange, whitelist_conf) plm = PairListManager(exchange, whitelist_conf)
assert log_has("Backtesting mode detected, applying seed value: 43", caplog) assert log_has("Backtesting mode detected, applying seed value: 43", caplog)
plm.refresh_pairlist()
pl1 = deepcopy(plm.whitelist)
plm.refresh_pairlist()
assert plm.whitelist != pl1
assert set(plm.whitelist) == set(pl1)
caplog.clear()
whitelist_conf["runmode"] = RunMode.DRY_RUN
plm = PairListManager(exchange, whitelist_conf)
assert not log_has("Backtesting mode detected, applying seed value: 42", caplog)
assert log_has("Live mode detected, not applying seed.", caplog)
with time_machine.travel("2021-09-01 05:01:00 +00:00") as t: with time_machine.travel("2021-09-01 05:01:00 +00:00") as t:
plm.refresh_pairlist() plm.refresh_pairlist()
pl1 = deepcopy(plm.whitelist) pl1 = deepcopy(plm.whitelist)
plm.refresh_pairlist() plm.refresh_pairlist()
assert plm.whitelist == pl1 assert plm.whitelist == pl1
target = plm._pairlist_handlers[1]._random
shuffle_mock = mocker.patch.object(target, "shuffle", wraps=target.shuffle)
t.shift(timedelta(minutes=10)) t.shift(timedelta(minutes=10))
plm.refresh_pairlist() plm.refresh_pairlist()
assert plm.whitelist != pl1 assert shuffle_mock.call_count == 1
assert set(plm.whitelist) == set(pl1)
caplog.clear()
whitelist_conf["runmode"] = RunMode.DRY_RUN
plm = PairListManager(exchange, whitelist_conf)
assert not log_has("Backtesting mode detected, applying seed value: 42", caplog)
assert log_has("Live mode detected, not applying seed.", caplog)
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")