From 4986f356ee45b55f4cf65d8d12a35ce0ec6e7fed Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Apr 2025 14:38:51 +0200 Subject: [PATCH] refactor: Type backtest results --- freqtrade/ft_types/__init__.py | 2 ++ freqtrade/ft_types/backtest_result_type.py | 22 +++++++++++++++++++ freqtrade/optimize/backtesting.py | 14 ++++++++---- .../optimize/hyperopt/hyperopt_optimizer.py | 3 ++- .../optimize_reports/optimize_reports.py | 12 ++++++---- 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/freqtrade/ft_types/__init__.py b/freqtrade/ft_types/__init__.py index 2eb4e5dda..1915a8549 100644 --- a/freqtrade/ft_types/__init__.py +++ b/freqtrade/ft_types/__init__.py @@ -1,5 +1,7 @@ # flake8: noqa: F401 from freqtrade.ft_types.backtest_result_type import ( + BacktestContentType, + BacktestContentTypeIcomplete, BacktestHistoryEntryType, BacktestMetadataType, BacktestResultType, diff --git a/freqtrade/ft_types/backtest_result_type.py b/freqtrade/ft_types/backtest_result_type.py index b8ddbb4b6..1e42d64aa 100644 --- a/freqtrade/ft_types/backtest_result_type.py +++ b/freqtrade/ft_types/backtest_result_type.py @@ -1,8 +1,11 @@ from copy import deepcopy from typing import Any, cast +from pandas import DataFrame from typing_extensions import TypedDict +from freqtrade.constants import Config + class BacktestMetadataType(TypedDict): run_id: str @@ -36,3 +39,22 @@ class BacktestHistoryEntryType(BacktestMetadataType): backtest_end_ts: int | None timeframe: str | None timeframe_detail: str | None + + +class BacktestContentTypeIcomplete(TypedDict, total=False): + results: DataFrame + config: Config + locks: Any + rejected_signals: int + timedout_entry_orders: int + timedout_exit_orders: int + canceled_trade_entries: int + canceled_entry_orders: int + replaced_entry_orders: int + final_balance: float + backtest_start_time: int + backtest_end_time: int + run_id: str + + +class BacktestContentType(BacktestContentTypeIcomplete, total=True): ... diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 5d83d4981..9326c0840 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -8,7 +8,6 @@ import logging from collections import defaultdict from copy import deepcopy from datetime import datetime, timedelta -from typing import Any from numpy import nan from pandas import DataFrame @@ -37,7 +36,12 @@ from freqtrade.exchange import ( timeframe_to_seconds, ) from freqtrade.exchange.exchange import Exchange -from freqtrade.ft_types import BacktestResultType, get_BacktestResultType_default +from freqtrade.ft_types import ( + BacktestContentType, + BacktestContentTypeIcomplete, + BacktestResultType, + get_BacktestResultType_default, +) from freqtrade.leverage.liquidation_price import update_liquidation_prices from freqtrade.mixins import LoggingMixin from freqtrade.optimize.backtest_caching import get_strategy_run_id @@ -119,7 +123,7 @@ class Backtesting: config["dry_run"] = True self.run_ids: dict[str, str] = {} self.strategylist: list[IStrategy] = [] - self.all_results: dict[str, dict] = {} + self.all_results: dict[str, BacktestContentType] = {} self.analysis_results: dict[str, dict[str, DataFrame]] = { "signals": {}, "rejected": {}, @@ -1611,7 +1615,9 @@ class Backtesting: yield current_time_det, pair, row, is_last_row, trade_dir self.progress.increment() - def backtest(self, processed: dict, start_date: datetime, end_date: datetime) -> dict[str, Any]: + def backtest( + self, processed: dict, start_date: datetime, end_date: datetime + ) -> BacktestContentTypeIcomplete: """ Implement backtesting functionality diff --git a/freqtrade/optimize/hyperopt/hyperopt_optimizer.py b/freqtrade/optimize/hyperopt/hyperopt_optimizer.py index 9e4995aca..d46859ee7 100644 --- a/freqtrade/optimize/hyperopt/hyperopt_optimizer.py +++ b/freqtrade/optimize/hyperopt/hyperopt_optimizer.py @@ -19,6 +19,7 @@ from freqtrade.data.history import get_timerange from freqtrade.data.metrics import calculate_market_change from freqtrade.enums import HyperoptState from freqtrade.exceptions import OperationalException +from freqtrade.ft_types import BacktestContentType from freqtrade.misc import deep_merge_dicts from freqtrade.optimize.backtesting import Backtesting @@ -324,7 +325,7 @@ class HyperOptimizer: def _get_results_dict( self, - backtesting_results: dict[str, Any], + backtesting_results: BacktestContentType, min_date: datetime, max_date: datetime, params_dict: dict[str, Any], diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index e6cadaab4..560e91476 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -18,7 +18,11 @@ from freqtrade.data.metrics import ( calculate_sortino, calculate_sqn, ) -from freqtrade.ft_types import BacktestResultType, get_BacktestResultType_default +from freqtrade.ft_types import ( + BacktestContentType, + BacktestResultType, + get_BacktestResultType_default, +) from freqtrade.util import decimals_per_coin, fmt_coin, get_dry_run_wallet @@ -26,7 +30,7 @@ logger = logging.getLogger(__name__) def generate_trade_signal_candles( - preprocessed_df: dict[str, DataFrame], bt_results: dict[str, Any], date_col: str + preprocessed_df: dict[str, DataFrame], bt_results: BacktestContentType, date_col: str ) -> dict[str, DataFrame]: signal_candles_only = {} for pair in preprocessed_df.keys(): @@ -407,7 +411,7 @@ def generate_daily_stats(results: DataFrame) -> dict[str, Any]: def generate_strategy_stats( pairlist: list[str], strategy: str, - content: dict[str, Any], + content: BacktestContentType, min_date: datetime, max_date: datetime, market_change: float, @@ -632,7 +636,7 @@ def generate_strategy_stats( def generate_backtest_stats( btdata: dict[str, DataFrame], - all_results: dict[str, dict[str, DataFrame | dict]], + all_results: dict[str, BacktestContentType], min_date: datetime, max_date: datetime, ) -> BacktestResultType: