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
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
"""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,
|
||||
)
|
||||
Reference in New Issue
Block a user