2022-04-29 17:37:13 +00:00
|
|
|
import hashlib
|
|
|
|
from copy import deepcopy
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
import rapidjson
|
|
|
|
|
|
|
|
|
|
|
|
def get_strategy_run_id(strategy) -> str:
|
|
|
|
"""
|
|
|
|
Generate unique identification hash for a backtest run. Identical config and strategy file will
|
|
|
|
always return an identical hash.
|
|
|
|
:param strategy: strategy object.
|
|
|
|
:return: hex string id.
|
|
|
|
"""
|
2024-06-08 07:27:40 +00:00
|
|
|
digest = hashlib.sha1() # noqa: S324
|
2022-04-29 17:37:13 +00:00
|
|
|
config = deepcopy(strategy.config)
|
|
|
|
|
|
|
|
# Options that have no impact on results of individual backtest.
|
2024-05-12 15:20:36 +00:00
|
|
|
not_important_keys = ("strategy_list", "original_config", "telegram", "api_server")
|
2022-04-29 17:37:13 +00:00
|
|
|
for k in not_important_keys:
|
|
|
|
if k in config:
|
|
|
|
del config[k]
|
|
|
|
|
|
|
|
# Explicitly allow NaN values (e.g. max_open_trades).
|
|
|
|
# as it does not matter for getting the hash.
|
2024-05-12 15:20:36 +00:00
|
|
|
digest.update(
|
|
|
|
rapidjson.dumps(config, default=str, number_mode=rapidjson.NM_NAN).encode("utf-8")
|
|
|
|
)
|
2022-04-29 17:44:17 +00:00
|
|
|
# Include _ft_params_from_file - so changing parameter files cause cache eviction
|
2024-05-12 15:20:36 +00:00
|
|
|
digest.update(
|
|
|
|
rapidjson.dumps(
|
|
|
|
strategy._ft_params_from_file, default=str, number_mode=rapidjson.NM_NAN
|
|
|
|
).encode("utf-8")
|
|
|
|
)
|
|
|
|
with Path(strategy.__file__).open("rb") as fp:
|
2022-04-29 17:37:13 +00:00
|
|
|
digest.update(fp.read())
|
|
|
|
return digest.hexdigest().lower()
|
|
|
|
|
|
|
|
|
2024-11-07 20:37:33 +00:00
|
|
|
def get_backtest_metadata_filename(filename: Path | str) -> Path:
|
2022-04-29 17:37:13 +00:00
|
|
|
"""Return metadata filename for specified backtest results file."""
|
|
|
|
filename = Path(filename)
|
2024-05-12 15:20:36 +00:00
|
|
|
return filename.parent / Path(f"{filename.stem}.meta{filename.suffix}")
|