Initial commit: multi-symbol bot with backtest engine and RSI trend strategy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-28 21:09:12 +02:00
commit ad8dfa27d7
32 changed files with 2644 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
"""
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