Files
stonks-oracle/services/trading/risk_tier_controller.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

88 lines
2.7 KiB
Python

"""Risk tier auto-adjustment controller for the autonomous trading engine.
Pure computation module — no DB access. Persistence of tier changes is
handled by the caller. All methods operate on values passed in as
arguments and return deterministic results.
Tier ordering: conservative → moderate → aggressive
"""
from __future__ import annotations
from services.trading.models import PerformanceMetrics
# Ordered from lowest to highest risk.
TIER_ORDER: list[str] = ["conservative", "moderate", "aggressive"]
class RiskTierController:
"""Evaluates performance metrics and determines whether the active
risk tier should change.
Downgrade conditions (any one triggers a downgrade by one level):
- Trailing 30-day win rate < 40%
- Current drawdown > 15%
Upgrade conditions (ALL must be true):
- Trailing 30-day win rate > 55%
- Reserve pool > 20% of total portfolio
- Current drawdown < 5%
"""
def __init__(self) -> None:
# No configuration needed — uses TIER_ORDER for ordering.
pass
def evaluate(
self,
current_tier: str,
metrics: PerformanceMetrics,
reserve_pct: float,
) -> str | None:
"""Evaluate whether the tier should change based on performance.
Parameters
----------
current_tier:
The currently active tier name (e.g. ``"moderate"``).
metrics:
Latest portfolio performance metrics.
reserve_pct:
Reserve pool balance as a fraction of total portfolio value
(e.g. 0.25 means 25%).
Returns
-------
str | None
The new tier name if a change is needed, or ``None`` if the
current tier should remain.
"""
current_index = TIER_ORDER.index(current_tier)
# --- Downgrade check (any condition triggers) ---
should_downgrade = (
metrics.win_rate < 0.40 or metrics.current_drawdown_pct > 0.15
)
if should_downgrade:
if current_index > 0:
return TIER_ORDER[current_index - 1]
# Already at the lowest tier — no change.
return None
# --- Upgrade check (all conditions must be true) ---
should_upgrade = (
metrics.win_rate > 0.55
and reserve_pct > 0.20
and metrics.current_drawdown_pct < 0.05
)
if should_upgrade:
if current_index < len(TIER_ORDER) - 1:
return TIER_ORDER[current_index + 1]
# Already at the highest tier — no change.
return None
# Neither condition met — stay at current tier.
return None