diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 43ede568c..e5e4d28a0 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -490,6 +490,9 @@ class Configuration: self._args_to_config(config, argname='lookahead_analysis_exportfilename', logstring='Path to store lookahead-analysis-results: {}') + self._args_to_config(config, argname='startup_candle', + logstring='Startup candle to be used on recursive analysis: {}') + def _process_runmode(self, config: Config) -> None: self._args_to_config(config, argname='dry_run', diff --git a/freqtrade/optimize/base_analysis.py b/freqtrade/optimize/base_analysis.py new file mode 100644 index 000000000..190ac882f --- /dev/null +++ b/freqtrade/optimize/base_analysis.py @@ -0,0 +1,66 @@ +import logging +from copy import deepcopy +from datetime import datetime, timezone +from typing import Any, Dict, Optional + +from pandas import DataFrame + +from freqtrade.configuration import TimeRange + + +logger = logging.getLogger(__name__) + + +class VarHolder: + timerange: TimeRange + data: DataFrame + indicators: Dict[str, DataFrame] + result: DataFrame + compared: DataFrame + from_dt: datetime + to_dt: datetime + compared_dt: datetime + timeframe: str + startup_candle: int + + +class BaseAnalysis: + + def __init__(self, config: Dict[str, Any], strategy_obj: Dict): + self.failed_bias_check = True + self.full_varHolder = VarHolder() + self.exchange: Optional[Any] = None + self._fee = None + + # pull variables the scope of the lookahead_analysis-instance + self.local_config = deepcopy(config) + self.local_config['strategy'] = strategy_obj['name'] + self.strategy_obj = strategy_obj + + @staticmethod + def dt_to_timestamp(dt: datetime): + timestamp = int(dt.replace(tzinfo=timezone.utc).timestamp()) + return timestamp + + def fill_full_varholder(self): + self.full_varHolder = VarHolder() + + # define datetime in human-readable format + parsed_timerange = TimeRange.parse_timerange(self.local_config['timerange']) + + if parsed_timerange.startdt is None: + self.full_varHolder.from_dt = datetime.fromtimestamp(0, tz=timezone.utc) + else: + self.full_varHolder.from_dt = parsed_timerange.startdt + + if parsed_timerange.stopdt is None: + self.full_varHolder.to_dt = datetime.utcnow() + else: + self.full_varHolder.to_dt = parsed_timerange.stopdt + + self.prepare_data(self.full_varHolder, self.local_config['pairs']) + + def start(self) -> None: + + # first make a single backtest + self.fill_full_varholder() diff --git a/freqtrade/optimize/lookahead_analysis.py b/freqtrade/optimize/lookahead_analysis.py index 80418da95..924e43e07 100755 --- a/freqtrade/optimize/lookahead_analysis.py +++ b/freqtrade/optimize/lookahead_analysis.py @@ -1,35 +1,23 @@ import logging import shutil from copy import deepcopy -from datetime import datetime, timedelta, timezone +from datetime import datetime, timedelta from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List from pandas import DataFrame -from freqtrade.configuration import TimeRange from freqtrade.data.history import get_timerange from freqtrade.exchange import timeframe_to_minutes from freqtrade.loggers.set_log_levels import (reduce_verbosity_for_bias_tester, restore_verbosity_for_bias_tester) from freqtrade.optimize.backtesting import Backtesting +from freqtrade.optimize.base_analysis import BaseAnalysis, VarHolder logger = logging.getLogger(__name__) -class VarHolder: - timerange: TimeRange - data: DataFrame - indicators: Dict[str, DataFrame] - result: DataFrame - compared: DataFrame - from_dt: datetime - to_dt: datetime - compared_dt: datetime - timeframe: str - - class Analysis: def __init__(self) -> None: self.total_signals = 0 @@ -39,29 +27,18 @@ class Analysis: self.has_bias = False -class LookaheadAnalysis: +class LookaheadAnalysis(BaseAnalysis): def __init__(self, config: Dict[str, Any], strategy_obj: Dict): - self.failed_bias_check = True - self.full_varHolder = VarHolder() + + super().__init__(config, strategy_obj) self.entry_varHolders: List[VarHolder] = [] self.exit_varHolders: List[VarHolder] = [] - self.exchange: Optional[Any] = None - self._fee = None - # pull variables the scope of the lookahead_analysis-instance - self.local_config = deepcopy(config) - self.local_config['strategy'] = strategy_obj['name'] self.current_analysis = Analysis() self.minimum_trade_amount = config['minimum_trade_amount'] self.targeted_trade_amount = config['targeted_trade_amount'] - self.strategy_obj = strategy_obj - - @staticmethod - def dt_to_timestamp(dt: datetime): - timestamp = int(dt.replace(tzinfo=timezone.utc).timestamp()) - return timestamp @staticmethod def get_result(backtesting: Backtesting, processed: DataFrame): @@ -162,24 +139,6 @@ class LookaheadAnalysis: varholder.indicators = backtesting.strategy.advise_all_indicators(varholder.data) varholder.result = self.get_result(backtesting, varholder.indicators) - def fill_full_varholder(self): - self.full_varHolder = VarHolder() - - # define datetime in human-readable format - parsed_timerange = TimeRange.parse_timerange(self.local_config['timerange']) - - if parsed_timerange.startdt is None: - self.full_varHolder.from_dt = datetime.fromtimestamp(0, tz=timezone.utc) - else: - self.full_varHolder.from_dt = parsed_timerange.startdt - - if parsed_timerange.stopdt is None: - self.full_varHolder.to_dt = datetime.utcnow() - else: - self.full_varHolder.to_dt = parsed_timerange.stopdt - - self.prepare_data(self.full_varHolder, self.local_config['pairs']) - def fill_entry_and_exit_varHolders(self, result_row): # entry_varHolder entry_varHolder = VarHolder() @@ -246,8 +205,7 @@ class LookaheadAnalysis: def start(self) -> None: - # first make a single backtest - self.fill_full_varholder() + super().start() reduce_verbosity_for_bias_tester() diff --git a/freqtrade/optimize/recursive_analysis.py b/freqtrade/optimize/recursive_analysis.py index ae91d49d2..599fc4dda 100644 --- a/freqtrade/optimize/recursive_analysis.py +++ b/freqtrade/optimize/recursive_analysis.py @@ -1,56 +1,35 @@ import logging import shutil from copy import deepcopy -from datetime import datetime, timedelta, timezone +from datetime import timedelta from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List from pandas import DataFrame -from freqtrade.configuration import TimeRange from freqtrade.exchange import timeframe_to_minutes from freqtrade.loggers.set_log_levels import (reduce_verbosity_for_bias_tester, restore_verbosity_for_bias_tester) from freqtrade.optimize.backtesting import Backtesting +from freqtrade.optimize.base_analysis import BaseAnalysis, VarHolder logger = logging.getLogger(__name__) -class VarHolder: - timerange: TimeRange - data: DataFrame - indicators: Dict[str, DataFrame] - from_dt: datetime - to_dt: datetime - timeframe: str - startup_candle: int - - -class RecursiveAnalysis: +class RecursiveAnalysis(BaseAnalysis): def __init__(self, config: Dict[str, Any], strategy_obj: Dict): - self.failed_bias_check = True - self.full_varHolder = VarHolder() + + self._startup_candle = config.get('startup_candle', [199, 399, 499, 999, 1999]) + + super().__init__(config, strategy_obj) + self.partial_varHolder_array: List[VarHolder] = [] self.partial_varHolder_lookahead_array: List[VarHolder] = [] - self.entry_varHolders: List[VarHolder] = [] - self.exit_varHolders: List[VarHolder] = [] - self.exchange: Optional[Any] = None - - # pull variables the scope of the recursive_analysis-instance - self.local_config = deepcopy(config) - self.local_config['strategy'] = strategy_obj['name'] - self._startup_candle = config.get('startup_candle', [199, 399, 499, 999, 1999]) - self.strategy_obj = strategy_obj self.dict_recursive: Dict[str, Any] = dict() - @staticmethod - def dt_to_timestamp(dt: datetime): - timestamp = int(dt.replace(tzinfo=timezone.utc).timestamp()) - return timestamp - # For recursive bias check # analyzes two data frames with processed indicators and shows differences between them. def analyze_indicators(self): @@ -141,7 +120,6 @@ class RecursiveAnalysis: prepare_data_config['exchange']['pair_whitelist'] = pairs_to_load backtesting = Backtesting(prepare_data_config, self.exchange) - self.exchange = backtesting.exchange backtesting._set_strategy(backtesting.strategylist[0]) varholder.data, varholder.timerange = backtesting.load_bt_data() @@ -150,24 +128,6 @@ class RecursiveAnalysis: varholder.indicators = backtesting.strategy.advise_all_indicators(varholder.data) - def fill_full_varholder(self): - self.full_varHolder = VarHolder() - - # define datetime in human-readable format - parsed_timerange = TimeRange.parse_timerange(self.local_config['timerange']) - - if parsed_timerange.startdt is None: - self.full_varHolder.from_dt = datetime.fromtimestamp(0, tz=timezone.utc) - else: - self.full_varHolder.from_dt = parsed_timerange.startdt - - if parsed_timerange.stopdt is None: - self.full_varHolder.to_dt = datetime.utcnow() - else: - self.full_varHolder.to_dt = parsed_timerange.stopdt - - self.prepare_data(self.full_varHolder, self.local_config['pairs']) - def fill_partial_varholder(self, start_date, startup_candle): partial_varHolder = VarHolder() @@ -186,9 +146,6 @@ class RecursiveAnalysis: partial_varHolder.from_dt = self.full_varHolder.from_dt partial_varHolder.to_dt = end_date - # partial_varHolder.startup_candle = startup_candle - - # self.local_config['startup_candle_count'] = startup_candle self.prepare_data(partial_varHolder, self.local_config['pairs']) @@ -196,11 +153,9 @@ class RecursiveAnalysis: def start(self) -> None: - # first make a single backtest - self.fill_full_varholder() + super().start() reduce_verbosity_for_bias_tester() - start_date_full = self.full_varHolder.from_dt end_date_full = self.full_varHolder.to_dt