diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index 8601f8176..ecedc55db 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -3,7 +3,7 @@ Helpers when analyzing backtest data """ import logging from pathlib import Path -from typing import Dict, Union, Tuple +from typing import Dict, Union, Tuple, Any import numpy as np import pandas as pd @@ -20,6 +20,23 @@ BT_DATA_COLUMNS = ["pair", "profit_percent", "open_time", "close_time", "index", "open_rate", "close_rate", "open_at_end", "sell_reason"] +def load_backtest_stats(filename: Union[Path, str]) -> Dict[str, Any]: + """ + Load backtest statistics file. + :param filename: pathlib.Path object, or string pointing to the file. + :return: a dictionary containing the resulting file. + """ + if isinstance(filename, str): + filename = Path(filename) + if not filename.is_file(): + raise ValueError(f"File {filename} does not exist.") + + with filename.open() as file: + data = json_load(file) + + return data + + def load_backtest_data(filename: Union[Path, str]) -> pd.DataFrame: """ Load backtest data file. diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 847434789..e4df80a82 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -11,9 +11,9 @@ from typing import Any, Dict, List, NamedTuple, Optional, Tuple import arrow from pandas import DataFrame -from freqtrade.constants import DATETIME_PRINT_FORMAT from freqtrade.configuration import (TimeRange, remove_credentials, validate_config_consistency) +from freqtrade.constants import DATETIME_PRINT_FORMAT from freqtrade.data import history from freqtrade.data.converter import trim_dataframe from freqtrade.data.dataprovider import DataProvider @@ -21,7 +21,8 @@ from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.optimize.optimize_reports import (generate_backtest_stats, show_backtest_results, - store_backtest_result) + store_backtest_result, + store_backtest_stats) from freqtrade.pairlist.pairlistmanager import PairListManager from freqtrade.persistence import Trade from freqtrade.resolvers import ExchangeResolver, StrategyResolver @@ -420,3 +421,4 @@ class Backtesting: stats = generate_backtest_stats(self.config, data, all_results, min_date=min_date, max_date=max_date) show_backtest_results(self.config, stats) + store_backtest_stats(self.config['exportfilename'], stats) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index b334917ba..3c0bfcb96 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -14,6 +14,18 @@ from freqtrade.misc import file_dump_json logger = logging.getLogger(__name__) +def store_backtest_stats(recordfilename: Path, stats: Dict[str, DataFrame]) -> None: + + filename = Path.joinpath(recordfilename.parent, + f'{recordfilename.stem}-{datetime.now().isoformat()}' + ).with_suffix(recordfilename.suffix) + file_dump_json(filename, stats) + + latest_filename = Path.joinpath(recordfilename.parent, + '.last_result.json') + file_dump_json(latest_filename, {'latest_backtest': str(filename.name)}) + + def store_backtest_result(recordfilename: Path, all_results: Dict[str, DataFrame]) -> None: """ Stores backtest results to file (one file per strategy) @@ -224,7 +236,7 @@ def generate_backtest_stats(config: Dict, btdata: Dict[str, DataFrame], backtest_days = (max_date - min_date).days strat_stats = { - 'trades': backtest_result_to_list(results), + 'trades': results.to_dict(orient='records'), 'results_per_pair': pair_results, 'sell_reason_summary': sell_reason_stats, 'left_open_trades': left_open_results, @@ -338,12 +350,11 @@ def text_table_strategy(strategy_results, stake_currency: str) -> str: def text_table_add_metrics(strategy_results: Dict) -> str: if len(strategy_results['trades']) > 0: - min_trade = min(strategy_results['trades'], key=lambda x: x[2]) + min_trade = min(strategy_results['trades'], key=lambda x: x['open_time']) metrics = [ ('Total trades', strategy_results['total_trades']), - ('First trade', datetime.fromtimestamp(min_trade[2], - tz=timezone.utc).strftime(DATETIME_PRINT_FORMAT)), - ('First trade Pair', min_trade[0]), + ('First trade', min_trade['open_time'].strftime(DATETIME_PRINT_FORMAT)), + ('First trade Pair', min_trade['pair']), ('Backtesting from', strategy_results['backtest_start'].strftime(DATETIME_PRINT_FORMAT)), ('Backtesting to', strategy_results['backtest_end'].strftime(DATETIME_PRINT_FORMAT)), ('Trades per day', strategy_results['trades_per_day']),