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:
Celes Renata
2026-04-15 16:12:22 +00:00
parent da86132f0c
commit 4ffde8cc06
58 changed files with 14168 additions and 1 deletions
+112
View File
@@ -0,0 +1,112 @@
"""Reserve Pool Controller — pure computation module.
Manages the untouchable cash reserve that grows from realized profits.
All methods are pure computations; persistence is handled by the caller.
"""
from __future__ import annotations
from services.trading.models import ReservePoolState # noqa: F401 — re-export for convenience
class ReservePoolController:
"""Compute reserve-pool operations without touching the database.
Parameters
----------
siphon_pct:
Fraction of realized profit transferred to the reserve on each
profitable position close (default 20 %).
high_water_pct:
When the reserve exceeds this fraction of total portfolio value
the risk-tier controller should consider upgrading (default 30 %).
"""
def __init__(
self,
siphon_pct: float = 0.20,
high_water_pct: float = 0.30,
) -> None:
self.siphon_pct = siphon_pct
self.high_water_pct = high_water_pct
# ------------------------------------------------------------------
# Profit siphoning
# ------------------------------------------------------------------
def siphon_profit(
self,
realized_profit: float,
current_balance: float,
) -> tuple[float, float]:
"""Compute the amount to transfer into the reserve pool.
Only positive profits are siphoned.
Returns
-------
(transfer_amount, new_balance)
*transfer_amount* is ``realized_profit * siphon_pct`` when
the profit is positive, otherwise ``0.0``.
*new_balance* is ``current_balance + transfer_amount``.
"""
if realized_profit <= 0:
return 0.0, current_balance
transfer = realized_profit * self.siphon_pct
return transfer, current_balance + transfer
# ------------------------------------------------------------------
# Emergency liquidation
# ------------------------------------------------------------------
def emergency_liquidate(self, current_balance: float) -> float:
"""Return the full reserve balance to be released into the active pool.
The caller is responsible for zeroing the persisted balance and
recording the ledger entry.
Returns
-------
float
The amount to release (equal to *current_balance*).
"""
return current_balance
# ------------------------------------------------------------------
# Active pool computation
# ------------------------------------------------------------------
def compute_active_pool(
self,
total_portfolio_value: float,
reserve_balance: float,
) -> float:
"""Active Pool = total portfolio value reserve balance."""
return total_portfolio_value - reserve_balance
# ------------------------------------------------------------------
# High-water mark detection
# ------------------------------------------------------------------
def is_high_water(
self,
reserve_balance: float,
total_portfolio_value: float,
) -> bool:
"""Return ``True`` when the reserve exceeds *high_water_pct* of total portfolio."""
if total_portfolio_value <= 0:
return False
return reserve_balance > self.high_water_pct * total_portfolio_value
# ------------------------------------------------------------------
# Emergency liquidation trigger check
# ------------------------------------------------------------------
def should_emergency_liquidate(
self,
current_drawdown_pct: float,
emergency_threshold_pct: float,
) -> bool:
"""Return ``True`` when drawdown exceeds the emergency threshold."""
return current_drawdown_pct > emergency_threshold_pct