4ffde8cc06
- 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
88 lines
2.7 KiB
Python
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
|