Files
stonks-oracle/services/trading/models.py
T
Celes Renata c85c0068a2 fix: clean up utcnow deprecation warnings, fix 12 failing tests, add CI/CD pipeline manifests
- Replace all datetime.utcnow() with datetime.now(tz=timezone.utc) across 8 files
- Fix 12 failing tests to match current implementation behavior
- Fix pytest_plugins in non-top-level conftest (moved to root conftest.py)
- Auto-fix 189 lint issues (import sorting, unused imports)
- Add CI/CD pipeline infrastructure (ARC, ArgoCD, Kargo manifests)
- Add values-beta.yaml and values-paper.yaml for staged deployments
- Update GitHub Actions workflow to use self-hosted-gremlin runners
- Add integration-test job to CI pipeline

Result: 1596 passed, 0 failed, 0 warnings
2026-04-18 03:59:28 +00:00

254 lines
7.1 KiB
Python

"""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, timezone
# ---------------------------------------------------------------------------
# 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=lambda: datetime.now(tz=timezone.utc))
# ---------------------------------------------------------------------------
# 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=lambda: datetime.now(tz=timezone.utc))
# ---------------------------------------------------------------------------
# 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=lambda: datetime.now(tz=timezone.utc))
# ---------------------------------------------------------------------------
# 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=lambda: datetime.now(tz=timezone.utc))
# ---------------------------------------------------------------------------
# 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