mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-11-29 08:33:07 +00:00
feat: Support split directory and filename for backteststats loading
This commit is contained in:
@@ -155,7 +155,7 @@ def load_backtest_metadata(filename: Path | str) -> dict[str, Any]:
|
|||||||
raise OperationalException("Unexpected error while loading backtest metadata.") from e
|
raise OperationalException("Unexpected error while loading backtest metadata.") from e
|
||||||
|
|
||||||
|
|
||||||
def _normalize_filename(file_or_directory: Path | str, filename: Path | str | None = None) -> Path:
|
def _normalize_filename(file_or_directory: Path | str, filename: Path | str | None) -> Path:
|
||||||
"""
|
"""
|
||||||
Normalize the filename by ensuring it is a Path object.
|
Normalize the filename by ensuring it is a Path object.
|
||||||
:param file_or_directory: The directory or file to normalize.
|
:param file_or_directory: The directory or file to normalize.
|
||||||
@@ -384,16 +384,21 @@ def _load_backtest_data_df_compatibility(df: pd.DataFrame) -> pd.DataFrame:
|
|||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
def load_backtest_data(filename: Path | str, strategy: str | None = None) -> pd.DataFrame:
|
def load_backtest_data(
|
||||||
|
file_or_directory: Path | str, strategy: str | None = None, filename: Path | str | None = None
|
||||||
|
) -> pd.DataFrame:
|
||||||
"""
|
"""
|
||||||
Load backtest data file, returns a dataframe with the individual trades.
|
Load backtest data file, returns a dataframe with the individual trades.
|
||||||
:param filename: pathlib.Path object, or string pointing to a file or directory
|
:param file_or_directory: pathlib.Path object, or string pointing to the directory,
|
||||||
|
or absolute/relative path to the backtest results file.
|
||||||
:param strategy: Strategy to load - mainly relevant for multi-strategy backtests
|
:param strategy: Strategy to load - mainly relevant for multi-strategy backtests
|
||||||
Can also serve as protection to load the correct result.
|
Can also serve as protection to load the correct result.
|
||||||
|
:param filename: Optional filename to load from (if different from the main filename).
|
||||||
|
Only valid when loading from a directory.
|
||||||
:return: a dataframe with the analysis results
|
:return: a dataframe with the analysis results
|
||||||
:raise: ValueError if loading goes wrong.
|
:raise: ValueError if loading goes wrong.
|
||||||
"""
|
"""
|
||||||
data = load_backtest_stats(filename)
|
data = load_backtest_stats(file_or_directory, filename)
|
||||||
if not isinstance(data, list):
|
if not isinstance(data, list):
|
||||||
# new, nested format
|
# new, nested format
|
||||||
if "strategy" not in data:
|
if "strategy" not in data:
|
||||||
@@ -452,20 +457,29 @@ def load_file_from_zip(zip_path: Path, filename: str) -> bytes:
|
|||||||
raise ValueError(f"Bad zip file: {zip_path}.") from None
|
raise ValueError(f"Bad zip file: {zip_path}.") from None
|
||||||
|
|
||||||
|
|
||||||
def load_backtest_analysis_data(backtest_dir: Path, name: Literal["signals", "rejected", "exited"]):
|
def load_backtest_analysis_data(
|
||||||
|
file_or_directory: Path,
|
||||||
|
name: Literal["signals", "rejected", "exited"],
|
||||||
|
filename: Path | str | None = None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Load backtest analysis data either from a pickle file or from within a zip file
|
Load backtest analysis data either from a pickle file or from within a zip file
|
||||||
:param backtest_dir: Directory containing backtest results
|
:param file_or_directory: pathlib.Path object, or string pointing to the directory,
|
||||||
|
or absolute/relative path to the backtest results file.
|
||||||
:param name: Name of the analysis data to load (signals, rejected, exited)
|
:param name: Name of the analysis data to load (signals, rejected, exited)
|
||||||
|
:param filename: Optional filename to load from (if different from the main filename).
|
||||||
|
Only valid when loading from a directory.
|
||||||
:return: Analysis data
|
:return: Analysis data
|
||||||
"""
|
"""
|
||||||
import joblib
|
import joblib
|
||||||
|
|
||||||
if backtest_dir.is_dir():
|
zip_path = _normalize_filename(file_or_directory, None)
|
||||||
lbf = Path(get_latest_backtest_filename(backtest_dir))
|
|
||||||
zip_path = backtest_dir / lbf
|
if file_or_directory.is_dir():
|
||||||
|
lbf = Path(get_latest_backtest_filename(file_or_directory))
|
||||||
|
zip_path = file_or_directory / lbf
|
||||||
else:
|
else:
|
||||||
zip_path = backtest_dir
|
zip_path = file_or_directory
|
||||||
|
|
||||||
if zip_path.suffix == ".zip":
|
if zip_path.suffix == ".zip":
|
||||||
# Load from zip file
|
# Load from zip file
|
||||||
@@ -480,10 +494,10 @@ def load_backtest_analysis_data(backtest_dir: Path, name: Literal["signals", "re
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
# Load from separate pickle file
|
# Load from separate pickle file
|
||||||
if backtest_dir.is_dir():
|
if file_or_directory.is_dir():
|
||||||
scpf = Path(backtest_dir, f"{zip_path.stem}_{name}.pkl")
|
scpf = Path(file_or_directory, f"{zip_path.stem}_{name}.pkl")
|
||||||
else:
|
else:
|
||||||
scpf = Path(backtest_dir.parent / f"{backtest_dir.stem}_{name}.pkl")
|
scpf = Path(file_or_directory.parent / f"{file_or_directory.stem}_{name}.pkl")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with scpf.open("rb") as scp:
|
with scpf.open("rb") as scp:
|
||||||
|
|||||||
@@ -344,21 +344,29 @@ def process_entry_exit_reasons(config: Config):
|
|||||||
None if config.get("timerange") is None else str(config.get("timerange"))
|
None if config.get("timerange") is None else str(config.get("timerange"))
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
backtest_stats = load_backtest_stats(config["exportdirectory"])
|
backtest_stats = load_backtest_stats(
|
||||||
|
config["exportdirectory"], config["exportfilename"]
|
||||||
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise ConfigurationError(e) from e
|
raise ConfigurationError(e) from e
|
||||||
|
|
||||||
for strategy_name, results in backtest_stats["strategy"].items():
|
for strategy_name, results in backtest_stats["strategy"].items():
|
||||||
trades = load_backtest_data(config["exportdirectory"], strategy_name)
|
trades = load_backtest_data(
|
||||||
|
config["exportdirectory"], strategy_name, config["exportfilename"]
|
||||||
|
)
|
||||||
|
|
||||||
if trades is not None and not trades.empty:
|
if trades is not None and not trades.empty:
|
||||||
signal_candles = load_backtest_analysis_data(config["exportdirectory"], "signals")
|
signal_candles = load_backtest_analysis_data(
|
||||||
exit_signals = load_backtest_analysis_data(config["exportdirectory"], "exited")
|
config["exportdirectory"], "signals", config["exportfilename"]
|
||||||
|
)
|
||||||
|
exit_signals = load_backtest_analysis_data(
|
||||||
|
config["exportdirectory"], "exited", config["exportfilename"]
|
||||||
|
)
|
||||||
|
|
||||||
rej_df = None
|
rej_df = None
|
||||||
if do_rejected:
|
if do_rejected:
|
||||||
rejected_signals_dict = load_backtest_analysis_data(
|
rejected_signals_dict = load_backtest_analysis_data(
|
||||||
config["exportdirectory"], "rejected"
|
config["exportdirectory"], "rejected", config["exportfilename"]
|
||||||
)
|
)
|
||||||
rej_df = prepare_results(
|
rej_df = prepare_results(
|
||||||
rejected_signals_dict,
|
rejected_signals_dict,
|
||||||
|
|||||||
Reference in New Issue
Block a user