diff --git a/freqtrade/constants.py b/freqtrade/constants.py index c6a2ab5d3..d21020a3f 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -380,6 +380,7 @@ CONF_SCHEMA = { }, 'position_adjustment_enable': {'type': 'boolean'}, 'max_entry_position_adjustment': {'type': ['integer', 'number'], 'minimum': -1}, + 'backtest_signal_candle_export_enable': {'type': 'boolean'}, }, 'definitions': { 'exchange': { diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 11704a70b..b8f63d006 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -19,7 +19,7 @@ from freqtrade.data import history from freqtrade.data.btanalysis import find_existing_backtest_stats, trade_list_to_dataframe from freqtrade.data.converter import trim_dataframe, trim_dataframes from freqtrade.data.dataprovider import DataProvider -from freqtrade.enums import BacktestState, CandleType, ExitCheckTuple, ExitType, TradingMode +from freqtrade.enums import BacktestState, CandleType, ExitCheckTuple, ExitType, TradingMode, RunMode from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.misc import get_strategy_run_id @@ -74,7 +74,7 @@ class Backtesting: self.strategylist: List[IStrategy] = [] self.all_results: Dict[str, Dict] = {} self.processed_dfs: Dict[str, Dict] = {} - + self._exchange_name = self.config['exchange']['name'] self.exchange = ExchangeResolver.load_exchange(self._exchange_name, self.config) self.dataprovider = DataProvider(self.config, self.exchange) @@ -129,9 +129,7 @@ class Backtesting: self.config['startup_candle_count'] = self.required_startup self.exchange.validate_required_startup_candles(self.required_startup, self.timeframe) - self.enable_backtest_signal_candle_export = False - if self.config.get('enable_backtest_signal_candle_export', None) is not None: - self.enable_backtest_signal_candle_export = bool(self.config.get('enable_backtest_signal_candle_export')) + self.backtest_signal_candle_export_enable = self.config.get('backtest_signal_candle_export_enable', False) self.trading_mode: TradingMode = config.get('trading_mode', TradingMode.SPOT) # strategies which define "can_short=True" will fail to load in Spot mode. @@ -1076,7 +1074,7 @@ class Backtesting: }) self.all_results[self.strategy.get_strategy_name()] = results - if self.enable_backtest_signal_candle_export: + if self.backtest_signal_candle_export_enable and self.dataprovider.runmode == RunMode.BACKTEST: signal_candles_only = {} for pair in preprocessed_tmp.keys(): signal_candles_only_df = DataFrame() diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index c08fa07a1..a97a6cf0f 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -44,6 +44,25 @@ def store_backtest_stats(recordfilename: Path, stats: Dict[str, DataFrame]) -> N latest_filename = Path.joinpath(filename.parent, LAST_BT_RESULT_FN) file_dump_json(latest_filename, {'latest_backtest': str(filename.name)}) +def store_backtest_signal_candles(recordfilename: Path, candles: Dict[str, Dict]) -> None: + """ + Stores backtest trade signal candles + :param recordfilename: Path object, which can either be a filename or a directory. + Filenames will be appended with a timestamp right before the suffix + while for directories, /backtest-result-_signals.pkl will be used as filename + :param stats: Dict containing the backtesting signal candles + """ + if recordfilename.is_dir(): + filename = (recordfilename / + f'backtest-result-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}_signals.pkl') + else: + filename = Path.joinpath( + recordfilename.parent, + f'{recordfilename.stem}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}' + ).with_suffix(recordfilename.suffix) + + with open(filename, 'wb') as f: + pickle.dump(candles, f) def _get_line_floatfmt(stake_currency: str) -> List[str]: """