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,253 @@
|
||||
"""Core data models for the autonomous trading engine.
|
||||
|
||||
Defines dataclasses for risk tier configuration, portfolio state,
|
||||
trading decisions, position sizing results, stop levels, and
|
||||
performance metrics used across all trading engine components.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Risk Tier Configuration
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class RiskTierConfig:
|
||||
"""Parameters for a named risk tier (conservative/moderate/aggressive)."""
|
||||
|
||||
name: str
|
||||
min_confidence: float
|
||||
max_position_pct: float
|
||||
stop_loss_atr_multiplier: float
|
||||
reward_risk_ratio: float
|
||||
max_sector_pct: float
|
||||
max_portfolio_heat: float
|
||||
|
||||
|
||||
RISK_TIER_DEFAULTS: dict[str, RiskTierConfig] = {
|
||||
"conservative": RiskTierConfig(
|
||||
name="conservative",
|
||||
min_confidence=0.75,
|
||||
max_position_pct=0.05,
|
||||
stop_loss_atr_multiplier=1.5,
|
||||
reward_risk_ratio=2.0,
|
||||
max_sector_pct=0.20,
|
||||
max_portfolio_heat=0.10,
|
||||
),
|
||||
"moderate": RiskTierConfig(
|
||||
name="moderate",
|
||||
min_confidence=0.55,
|
||||
max_position_pct=0.10,
|
||||
stop_loss_atr_multiplier=2.0,
|
||||
reward_risk_ratio=1.5,
|
||||
max_sector_pct=0.30,
|
||||
max_portfolio_heat=0.20,
|
||||
),
|
||||
"aggressive": RiskTierConfig(
|
||||
name="aggressive",
|
||||
min_confidence=0.40,
|
||||
max_position_pct=0.15,
|
||||
stop_loss_atr_multiplier=2.5,
|
||||
reward_risk_ratio=1.2,
|
||||
max_sector_pct=0.40,
|
||||
max_portfolio_heat=0.30,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Portfolio State
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class PortfolioState:
|
||||
"""Snapshot of the current portfolio used for decision-making."""
|
||||
|
||||
positions: list = field(default_factory=list)
|
||||
total_value: float = 0.0
|
||||
cash: float = 0.0
|
||||
active_pool: float = 0.0
|
||||
reserve_pool: float = 0.0
|
||||
sector_exposure: dict[str, float] = field(default_factory=dict)
|
||||
portfolio_heat: float = 0.0
|
||||
open_position_count: int = 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Trading Decision
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class TradingDecision:
|
||||
"""Record of a trading decision (act or skip), persisted for audit trail."""
|
||||
|
||||
id: str
|
||||
recommendation_id: str | None
|
||||
decision: str
|
||||
skip_reason: str | None
|
||||
ticker: str
|
||||
computed_position_size: float | None
|
||||
computed_share_quantity: int | None
|
||||
risk_tier_at_decision: str
|
||||
portfolio_heat_at_decision: float | None
|
||||
active_pool_at_decision: float | None
|
||||
reserve_pool_at_decision: float | None
|
||||
circuit_breaker_status: str
|
||||
correlation_check_result: dict = field(default_factory=dict)
|
||||
sector_exposure_check_result: dict = field(default_factory=dict)
|
||||
earnings_proximity_flag: bool = False
|
||||
is_micro_trade: bool = False
|
||||
decision_trace: dict = field(default_factory=dict)
|
||||
created_at: datetime = field(default_factory=datetime.utcnow)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Position Sizing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class PositionSizeResult:
|
||||
"""Output of the position sizer computation."""
|
||||
|
||||
dollar_amount: float
|
||||
share_quantity: int
|
||||
allocation_pct: float
|
||||
adjustments: list[str] = field(default_factory=list)
|
||||
rejected: bool = False
|
||||
rejection_reason: str = ""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stop-Loss / Take-Profit Levels
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class StopLevels:
|
||||
"""Current stop-loss and take-profit levels for an open position."""
|
||||
|
||||
stop_loss_price: float
|
||||
take_profit_price: float
|
||||
trailing_stop_active: bool
|
||||
atr_value: float
|
||||
atr_multiplier: float
|
||||
reward_risk_ratio: float
|
||||
last_updated: datetime = field(default_factory=datetime.utcnow)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Open / Closed Positions
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class OpenPosition:
|
||||
"""Representation of a currently held position."""
|
||||
|
||||
ticker: str
|
||||
quantity: int
|
||||
entry_price: float
|
||||
current_price: float
|
||||
unrealized_pnl: float
|
||||
market_value: float
|
||||
sector: str
|
||||
stop_loss_price: float
|
||||
take_profit_price: float
|
||||
signal_confidence: float
|
||||
is_micro_trade: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class ClosedTrade:
|
||||
"""Record of a completed (closed) trade."""
|
||||
|
||||
ticker: str
|
||||
entry_price: float
|
||||
exit_price: float
|
||||
quantity: int
|
||||
pnl: float
|
||||
pnl_pct: float
|
||||
hold_duration: timedelta
|
||||
recommendation_id: str | None = None
|
||||
is_micro_trade: bool = False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Performance Metrics
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class PerformanceMetrics:
|
||||
"""Portfolio-wide performance metrics computed periodically."""
|
||||
|
||||
total_portfolio_value: float
|
||||
active_pool: float
|
||||
reserve_pool: float
|
||||
unrealized_pnl: float
|
||||
realized_pnl: float
|
||||
daily_pnl: float
|
||||
win_count: int
|
||||
loss_count: int
|
||||
win_rate: float
|
||||
avg_win: float
|
||||
avg_loss: float
|
||||
profit_factor: float
|
||||
sharpe_ratio: float
|
||||
max_drawdown: float
|
||||
current_drawdown_pct: float
|
||||
portfolio_heat: float
|
||||
computed_at: datetime = field(default_factory=datetime.utcnow)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Circuit Breaker
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class CircuitBreakerState:
|
||||
"""Current state of the circuit breaker safety mechanism."""
|
||||
|
||||
active: bool = False
|
||||
trigger_type: str | None = None
|
||||
triggered_at: datetime | None = None
|
||||
cooldown_expires: datetime | None = None
|
||||
ticker_cooldowns: dict[str, datetime] = field(default_factory=dict)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Reserve Pool
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReservePoolState:
|
||||
"""Current state of the reserve pool."""
|
||||
|
||||
balance: float = 0.0
|
||||
total_deposits: float = 0.0
|
||||
total_withdrawals: float = 0.0
|
||||
last_updated: datetime = field(default_factory=datetime.utcnow)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stop Trigger
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class StopTrigger:
|
||||
"""A triggered stop-loss or take-profit event for a position."""
|
||||
|
||||
ticker: str
|
||||
trigger_type: str # "stop_loss" or "take_profit"
|
||||
current_price: float
|
||||
trigger_price: float
|
||||
Reference in New Issue
Block a user