2022-04-29 17:37:13 +00:00
|
|
|
import hashlib
|
|
|
|
from copy import deepcopy
|
|
|
|
from pathlib import Path
|
|
|
|
from typing import Union
|
|
|
|
|
|
|
|
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.
|
|
|
|
"""
|
|
|
|
digest = hashlib.sha1()
|
|
|
|
config = deepcopy(strategy.config)
|
|
|
|
|
|
|
|
# Options that have no impact on results of individual backtest.
|
|
|
|
not_important_keys = ('strategy_list', 'original_config', 'telegram', 'api_server')
|
|
|
|
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.
|
|
|
|
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
|
|
|
|
digest.update(rapidjson.dumps(
|
|
|
|
strategy._ft_params_from_file, default=str, number_mode=rapidjson.NM_NAN).encode('utf-8'))
|
2023-02-25 16:08:02 +00:00
|
|
|
with Path(strategy.__file__).open('rb') as fp:
|
2022-04-29 17:37:13 +00:00
|
|
|
digest.update(fp.read())
|
|
|
|
return digest.hexdigest().lower()
|
|
|
|
|
|
|
|
|
|
|
|
def get_backtest_metadata_filename(filename: Union[Path, str]) -> Path:
|
|
|
|
"""Return metadata filename for specified backtest results file."""
|
|
|
|
filename = Path(filename)
|
|
|
|
return filename.parent / Path(f'{filename.stem}.meta{filename.suffix}')
|