import os from typing import Dict, Any import strategies from optimizer import run_optimized_backtest class Optimizer: """Base optimizer wrapper tying a strategy to an optimization grid.""" name = 'BaseOptimizer' strategy_cls = None optimize_kwargs: Dict[str, Any] = None @classmethod def run(cls, symbol): # pass optimizer name as report_tag so output files are unique per optimizer return run_optimized_backtest(symbol, strategy_cls=cls.strategy_cls, optimize_kwargs=cls.optimize_kwargs, report_tag=cls.name) class RsiOptimizer(Optimizer): name = 'RsiOptimizer' strategy_cls = strategies.RsiStrategy def constraint(p): return p.rsi_upper > p.rsi_lower optimize_kwargs = dict( rsi_period=range(7, 30, 2), rsi_lower=range(20, 40, 5), rsi_upper=range(60, 80, 5), maximize='Return [%]', constraint=constraint, atr_period=range(10, 20, 2), stop_loss_atr_multiplier=[2.0, 2.5, 3.0, 3.5, 4.0], ) class CrossEmaOptimizer(Optimizer): name = 'CrossEmaOptimizer' strategy_cls = strategies.CrossEmaStrategy def constraint(p): # ensure long EMA period is greater than short EMA period return p.long_ema > p.short_ema optimize_kwargs = dict( short_ema=range(5, 20, 3), long_ema=range(20, 60, 5), maximize='Return [%]', constraint=constraint, atr_period=range(10, 20, 2), stop_loss_atr_multiplier=[2.0, 2.5, 3.0, 3.5], finalize_trades=False, # run final backtest with best params ) def get_optimizer_by_name(name: str): mapping = {cls.name: cls for cls in (RsiOptimizer, CrossEmaOptimizer)} # allow passing class name as well if name in mapping: return mapping[name] # try attribute lookup in module cls = getattr(__import__('optimizers'), name, None) return cls def run_optimizer(symbol: str, optimizer_name: str): cls = get_optimizer_by_name(optimizer_name) if cls is None: raise ValueError(f"Optimizer '{optimizer_name}' not found") return cls.run(symbol)