"""Backtester for the autonomous trading engine. Pure computation module that assembles backtest results from pre-computed trade data and daily returns. The actual replay logic (fetching historical data, simulating decisions) requires DB access and will be wired in the integration layer. This module provides the pure computation for result assembly. """ from __future__ import annotations import uuid from dataclasses import dataclass, field from datetime import date from services.trading.models import ClosedTrade from services.trading.performance_tracker import PerformanceComputer # --------------------------------------------------------------------------- # Data classes # --------------------------------------------------------------------------- @dataclass class BacktestConfig: """Configuration for a backtest run.""" start_date: date end_date: date initial_capital: float risk_tier: str # conservative | moderate | aggressive @dataclass class BacktestResult: """Output of a completed backtest run.""" backtest_id: str config: BacktestConfig total_return: float sharpe_ratio: float max_drawdown: float win_rate: float profit_factor: float trade_count: int trade_log: list[dict] = field(default_factory=list) equity_curve: list[dict] = field(default_factory=list) # --------------------------------------------------------------------------- # Engine # --------------------------------------------------------------------------- class BacktestEngine: """Assembles a BacktestResult from pre-computed trade data. Uses :class:`PerformanceComputer` to derive metrics from closed trades and daily returns, then packages everything into a :class:`BacktestResult`. """ def __init__(self) -> None: self._perf = PerformanceComputer() def compute_result( self, config: BacktestConfig, trades: list[ClosedTrade], daily_returns: list[float], equity_curve: list[dict], ) -> BacktestResult: """Build a :class:`BacktestResult` from raw simulation outputs. Args: config: The backtest configuration that was used. trades: Closed trades produced by the simulation. daily_returns: Daily return percentages for the simulated period. equity_curve: List of ``{"date": ..., "portfolio_value": ...}`` dicts representing the equity curve. Returns: A fully populated :class:`BacktestResult`. """ metrics = self._perf.compute_metrics( closed_trades=trades, portfolio_value=config.initial_capital, active_pool=config.initial_capital, reserve_pool=0.0, daily_pnl=0.0, unrealized_pnl=0.0, portfolio_heat=0.0, daily_returns=daily_returns, ) trade_log = [self._perf.compute_trade_metrics(t) for t in trades] total_return = ( sum(t.pnl for t in trades) / config.initial_capital if config.initial_capital > 0 else 0.0 ) return BacktestResult( backtest_id=str(uuid.uuid4()), config=config, total_return=total_return, sharpe_ratio=metrics.sharpe_ratio, max_drawdown=metrics.max_drawdown, win_rate=metrics.win_rate, profit_factor=metrics.profit_factor, trade_count=len(trades), trade_log=trade_log, equity_curve=equity_curve, )