class StrategyTuner(): def __init__(self, raw_data: pd.DataFrame, strategy_creator, fields_to_tune: tuple, need_recompute_ti=False): self.data = raw_data self.strategy_creator = strategy_creator self.fields_to_tune = fields_to_tune self.need_recompute_ti = need_recompute_ti self.strategy = None self.backtest = None self.report = None self.timer = Timer() self.initialize() def initialize(self): self.update_strategy_param() self.initialize_backtest() self.report = StrategyTunerReport(self.strategy, self.strategy_creator) def initialize_backtest(self): self.backtest = Backtest(self.data, [self.strategy]) self.backtest.initialize() self.backtest.data_preprocess() self.backtest.compute_technical_indicator() def simulate(self, param_values: list): self.update_strategy_param(param_values) param_config = self.strategy_creator.__dict__ if self.report.param_already_exists(param_config): logging.warn( f'[StrategyTuner] Skipping this param because it is already simulated: {param_config}' ) return self.backtest.strategies = [self.strategy] self.backtest.initialize() if self.need_recompute_ti: self.initialize_backtest() self.backtest.back_test() self.backtest.gen_report(only='general_report') self.record_simulation() def record_simulation(self): self.report.write(backtest_result=dict( self.backtest.report.general_report.df.iloc[0]), param_config=self.strategy_creator.__dict__) def update_strategy_param(self, param_values=None): if not param_values: for field in self.fields_to_tune: self.strategy_creator.__dict__[field.name] = field.low_bound else: for i in range(len(self.fields_to_tune)): field = self.fields_to_tune[i] self.strategy_creator.__dict__[field.name] = field.values[ param_values[i]] # TO DO: param_values list self.strategy = self.strategy_creator.create() # recursion def do_step(self, counters, lengths, level): if level == len(counters): self.simulate(param_values=counters) msg = self.write_step_msg(counters) self.timer.time(msg) else: counters[level] = 0 while True: if counters[level] >= lengths[level]: break # do something self.do_step(counters, lengths, level + 1) counters[level] += 1 def write_step_msg(self, param_values: list): d = dict() for i in range(len(self.fields_to_tune)): field = self.fields_to_tune[i] d[field.name] = field.values[param_values[i]] return str(d) def run(self): counters = [0] * len(self.fields_to_tune) lengths = [len(field.values) for field in self.fields_to_tune] self.do_step(counters, lengths, 0)