Files
stonks-oracle/services/trading/backtester.py
T
Celes Renata 4ffde8cc06 feat: autonomous trading engine — full implementation
- Database migration 018 with 13 tables for trading engine state
- Trading engine service (services/trading/) with 12 pure computation modules:
  position sizer, stop-loss manager, reserve pool, circuit breaker,
  risk tier controller, correlation matrix, tax lots, trading window,
  gradual entry, notifications, micro-trading, backtester
- Core TradingEngine with pre-trade evaluation pipeline and integration wiring
- FastAPI HTTP service with 14 endpoints (health, config, decisions, metrics, backtest)
- Performance tracker with Sharpe ratio, drawdown, profit factor computation
- 194 Python tests (165 property-based + 29 integration)
- Frontend: 13 TanStack Query hooks, 7 dashboard panels, tabbed Trading Engine page
- Helm chart entry, network policy, nginx proxy, ingress for trading-engine
- Shared infrastructure: enums, Redis keys, TradingConfig in AppConfig
2026-04-15 16:12:22 +00:00

116 lines
3.5 KiB
Python

"""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,
)