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')) # 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')) with open(strategy.__file__, 'rb') as fp: 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}')