From c1e938ccda226dd5f558fd91aea51b046da058b8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 21 Jun 2024 14:01:25 +0200 Subject: [PATCH] Add "BacktestnigSupport" method to pairlists --- freqtrade/plugins/pairlist/IPairList.py | 13 +++++++++ freqtrade/plugins/pairlistmanager.py | 38 ++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/IPairList.py b/freqtrade/plugins/pairlist/IPairList.py index a2e70e649..c3d1a10ad 100644 --- a/freqtrade/plugins/pairlist/IPairList.py +++ b/freqtrade/plugins/pairlist/IPairList.py @@ -5,6 +5,7 @@ PairList Handler base class import logging from abc import ABC, abstractmethod from copy import deepcopy +from enum import Enum from typing import Any, Dict, List, Literal, Optional, TypedDict, Union from freqtrade.constants import Config @@ -51,8 +52,20 @@ PairlistParameter = Union[ ] +class SupportsBacktesting(str, Enum): + """ + Enum to indicate if a Pairlist Handler supports backtesting. + """ + + YES = "yes" + NO = "no" + NO_ACTION = "no_action" + TODAYS_DATA = "todays_data" + + class IPairList(LoggingMixin, ABC): is_pairlist_generator = False + supports_backtesting: SupportsBacktesting = SupportsBacktesting.NO def __init__( self, diff --git a/freqtrade/plugins/pairlistmanager.py b/freqtrade/plugins/pairlistmanager.py index a6afd5e64..30b3d74f8 100644 --- a/freqtrade/plugins/pairlistmanager.py +++ b/freqtrade/plugins/pairlistmanager.py @@ -11,10 +11,11 @@ from cachetools import TTLCache, cached from freqtrade.constants import Config, ListPairsWithTimeframes from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import CandleType +from freqtrade.enums.runmode import RunMode from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Tickers from freqtrade.mixins import LoggingMixin -from freqtrade.plugins.pairlist.IPairList import IPairList +from freqtrade.plugins.pairlist.IPairList import IPairList, SupportsBacktesting from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist from freqtrade.resolvers import PairListResolver @@ -57,9 +58,44 @@ class PairListManager(LoggingMixin): f"{invalid}." ) + self._check_backtest() + refresh_period = config.get("pairlist_refresh_period", 3600) LoggingMixin.__init__(self, logger, refresh_period) + def _check_backtest(self) -> None: + if self._config["runmode"] not in (RunMode.BACKTEST, RunMode.EDGE, RunMode.HYPEROPT): + return + + pairlist_errors: List[str] = [] + noaction_pairlists: List[str] = [] + biased_pairlists: List[str] = [] + for pairlist_handler in self._pairlist_handlers: + if pairlist_handler.supports_backtesting == SupportsBacktesting.NO: + pairlist_errors.append(pairlist_handler.name) + if pairlist_handler.supports_backtesting == SupportsBacktesting.NO_ACTION: + noaction_pairlists.append(pairlist_handler.name) + if pairlist_handler.supports_backtesting == SupportsBacktesting.TODAYS_DATA: + biased_pairlists.append(pairlist_handler.name) + + if noaction_pairlists: + logger.warning( + f"Pairlist Handlers {', '.join(noaction_pairlists)} do not generate " + "any changes during backtesting. While it's safe to leave them enabled, they will " + "not behave like in dry/live modes. " + ) + + if biased_pairlists: + logger.warning( + f"Pairlist Handlers {', '.join(biased_pairlists)} will introduce a lookahead bias " + "to your backtest results, as they use today's data - which inheritly suffers from " + "'winner bias'." + ) + if pairlist_errors: + raise OperationalException( + f"Pairlist Handlers {', '.join(pairlist_errors)} do not support backtesting." + ) + @property def whitelist(self) -> List[str]: """The current whitelist"""