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
+169
View File
@@ -0,0 +1,169 @@
"""Property-based tests for risk tier default parameters.
Feature: autonomous-trading-engine
Validates that the three risk tier defaults (conservative, moderate, aggressive)
have valid parameter ranges and correct ordering relationships.
**Validates: Requirements 5.1**
"""
from __future__ import annotations
from hypothesis import given, settings
from hypothesis import strategies as st
from services.trading.models import RISK_TIER_DEFAULTS, RiskTierConfig
# ---------------------------------------------------------------------------
# Hypothesis strategies
# ---------------------------------------------------------------------------
def _risk_tier_config_strategy() -> st.SearchStrategy[RiskTierConfig]:
"""Generate random RiskTierConfig objects with valid parameter ranges."""
return st.builds(
RiskTierConfig,
name=st.sampled_from(["conservative", "moderate", "aggressive"]),
min_confidence=st.floats(min_value=0.0, max_value=1.0, allow_nan=False),
max_position_pct=st.floats(
min_value=0.01, max_value=1.0, allow_nan=False
),
stop_loss_atr_multiplier=st.floats(
min_value=0.01, max_value=10.0, allow_nan=False
),
reward_risk_ratio=st.floats(
min_value=0.01, max_value=10.0, allow_nan=False
),
max_sector_pct=st.floats(
min_value=0.01, max_value=1.0, allow_nan=False
),
max_portfolio_heat=st.floats(
min_value=0.01, max_value=1.0, allow_nan=False
),
)
# ---------------------------------------------------------------------------
# Property 29 (partial): Risk tier default parameter validation
# ---------------------------------------------------------------------------
EXPECTED_TIERS = {"conservative", "moderate", "aggressive"}
def test_all_three_tiers_exist() -> None:
"""All three risk tiers must be present in RISK_TIER_DEFAULTS.
**Validates: Requirements 5.1**
"""
assert set(RISK_TIER_DEFAULTS.keys()) == EXPECTED_TIERS
def test_each_tier_has_valid_parameter_ranges() -> None:
"""Each tier's parameters must fall within valid ranges.
**Validates: Requirements 5.1**
"""
for tier_name, cfg in RISK_TIER_DEFAULTS.items():
assert cfg.name == tier_name, (
f"Tier name mismatch: key={tier_name}, cfg.name={cfg.name}"
)
# min_confidence in [0, 1]
assert 0.0 <= cfg.min_confidence <= 1.0, (
f"{tier_name}: min_confidence={cfg.min_confidence} not in [0, 1]"
)
# max_position_pct in (0, 1]
assert 0.0 < cfg.max_position_pct <= 1.0, (
f"{tier_name}: max_position_pct={cfg.max_position_pct} not in (0, 1]"
)
# stop_loss_atr_multiplier > 0
assert cfg.stop_loss_atr_multiplier > 0.0, (
f"{tier_name}: stop_loss_atr_multiplier={cfg.stop_loss_atr_multiplier} not > 0"
)
# reward_risk_ratio > 0
assert cfg.reward_risk_ratio > 0.0, (
f"{tier_name}: reward_risk_ratio={cfg.reward_risk_ratio} not > 0"
)
# max_sector_pct in (0, 1]
assert 0.0 < cfg.max_sector_pct <= 1.0, (
f"{tier_name}: max_sector_pct={cfg.max_sector_pct} not in (0, 1]"
)
# max_portfolio_heat in (0, 1]
assert 0.0 < cfg.max_portfolio_heat <= 1.0, (
f"{tier_name}: max_portfolio_heat={cfg.max_portfolio_heat} not in (0, 1]"
)
def test_min_confidence_ordering() -> None:
"""Conservative has highest min_confidence, aggressive has lowest.
conservative.min_confidence > moderate.min_confidence > aggressive.min_confidence
**Validates: Requirements 5.1**
"""
c = RISK_TIER_DEFAULTS["conservative"]
m = RISK_TIER_DEFAULTS["moderate"]
a = RISK_TIER_DEFAULTS["aggressive"]
assert c.min_confidence > m.min_confidence > a.min_confidence, (
f"min_confidence ordering violated: "
f"conservative={c.min_confidence}, "
f"moderate={m.min_confidence}, "
f"aggressive={a.min_confidence}"
)
def test_max_position_pct_ordering() -> None:
"""Conservative has lowest max_position_pct, aggressive has highest.
conservative.max_position_pct < moderate.max_position_pct < aggressive.max_position_pct
**Validates: Requirements 5.1**
"""
c = RISK_TIER_DEFAULTS["conservative"]
m = RISK_TIER_DEFAULTS["moderate"]
a = RISK_TIER_DEFAULTS["aggressive"]
assert c.max_position_pct < m.max_position_pct < a.max_position_pct, (
f"max_position_pct ordering violated: "
f"conservative={c.max_position_pct}, "
f"moderate={m.max_position_pct}, "
f"aggressive={a.max_position_pct}"
)
def test_max_portfolio_heat_ordering() -> None:
"""Conservative has lowest max_portfolio_heat, aggressive has highest.
conservative.max_portfolio_heat < moderate.max_portfolio_heat < aggressive.max_portfolio_heat
**Validates: Requirements 5.1**
"""
c = RISK_TIER_DEFAULTS["conservative"]
m = RISK_TIER_DEFAULTS["moderate"]
a = RISK_TIER_DEFAULTS["aggressive"]
assert c.max_portfolio_heat < m.max_portfolio_heat < a.max_portfolio_heat, (
f"max_portfolio_heat ordering violated: "
f"conservative={c.max_portfolio_heat}, "
f"moderate={m.max_portfolio_heat}, "
f"aggressive={a.max_portfolio_heat}"
)
@settings(max_examples=100)
@given(cfg=_risk_tier_config_strategy())
def test_random_risk_tier_config_parameter_ranges(cfg: RiskTierConfig) -> None:
"""Any randomly generated RiskTierConfig with valid inputs satisfies range invariants.
This property test verifies that the parameter range constraints hold for
arbitrary RiskTierConfig instances, not just the defaults.
**Validates: Requirements 5.1**
"""
assert 0.0 <= cfg.min_confidence <= 1.0
assert 0.0 < cfg.max_position_pct <= 1.0
assert cfg.stop_loss_atr_multiplier > 0.0
assert cfg.reward_risk_ratio > 0.0
assert 0.0 < cfg.max_sector_pct <= 1.0
assert 0.0 < cfg.max_portfolio_heat <= 1.0