""" Grid-search optimizer. Iterates over a parameter grid, re-instantiates the strategy for each combination, runs the backtest on the in-sample slice, and ranks by a composite score (Sharpe × expectancy × return). """ from __future__ import annotations import itertools from dataclasses import dataclass from typing import Any, Type import numpy as np import pandas as pd from .backtest import BacktestEngine from .metrics import Metrics, compute @dataclass class OptResult: params: dict[str, Any] metrics: Metrics score: float def grid_search( StrategyClass: Type, param_grid: dict[str, list], df: pd.DataFrame, engine: BacktestEngine, *, verbose: bool = False, ) -> list[OptResult]: """ Run every combination of parameters in `param_grid`. Returns results sorted best-first by composite score. """ keys = list(param_grid.keys()) values = list(param_grid.values()) combos = list(itertools.product(*values)) results: list[OptResult] = [] for idx, combo in enumerate(combos): params = dict(zip(keys, combo)) strategy = StrategyClass(**params) trades, equity = engine.run(df, strategy) m = compute(trades, equity, engine.initial_balance) s = m.score() results.append(OptResult(params=params, metrics=m, score=s)) if verbose and (idx + 1) % 50 == 0: print(f" [{idx+1}/{len(combos)}] best so far: " f"{max(results, key=lambda r: r.score).score:.4f}") results.sort(key=lambda r: r.score, reverse=True) return results