feat: Phase 3 — Portfolio Risk Engine (Exposition, Clusters, Kelly, Risk Dashboard)
Sprint 3.1 — Vue Portefeuille Consolidée
- database.py: get_portfolio_exposure() — exposition par classe d'actif + facteur de risque
- database.py: get_pnl_timeline() — courbe P&L cumulé pour equity curve
- Alertes concentration automatiques (>40% par classe, >50% par facteur)
- _RISK_FACTOR_MAP: classification géopolitique/inflation/récession/liquidité/dollar
Sprint 3.2 — Risk Cluster Engine
- database.py: get_risk_clusters() — saturation par facteur + risk_prompt_context
- database.py: get_pattern_correlations() — matrice Pearson sur trades matures
- auto_cycle.py: injection du contexte risque dans le prompt de scoring (Step 3.5)
- ai_analyzer.py: paramètre risk_context dans score_patterns_with_context()
- Pénalisation automatique des patterns sur facteurs saturés dans le scoring GPT
Sprint 3.3 — Position Sizing Kelly Fractionnel
- database.py: compute_kelly_sizing() — f* = (p×G - (1-p))/G, Kelly ×33% par défaut
- Ajustement cluster: sizing ÷2 si facteur saturé
- Ajustement fiabilité: sizing ÷2 si win_rate historique <40% (≥5 trades)
- JournalDeBord.tsx: colonne "Kelly" avec KellyCell (% + €, ajustements signalés)
- routers/risk.py: GET /api/risk/kelly/{pattern_id}
Sprint 3.4 — Tableau de Bord Risque Global
- database.py: get_risk_dashboard() — HHI, score diversification, drawdown attendu, recommandation
- database.py: _build_risk_recommendation() — alerte Risk Committee automatique
- RiskDashboard.tsx: nouvelle page — jauges concentration, courbe P&L, corrélations, recommandation
- Dashboard.tsx: banner d'alerte concentration sur le Cockpit avec lien vers /risk
- routers/risk.py: GET /api/risk/exposure|timeline|clusters|correlations|dashboard
- App.tsx + Sidebar.tsx: route /risk + entrée menu Risk Dashboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -280,7 +280,22 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
|
||||
summary["patterns_added"] = added_count
|
||||
|
||||
# ── Step 3.5: Collect IV context ─────────────────────────────────────
|
||||
# ── Step 3.5: Collect risk cluster context ───────────────────────────
|
||||
risk_cluster_context = ""
|
||||
try:
|
||||
from services.database import get_risk_clusters as _grc
|
||||
_clusters = _grc()
|
||||
risk_cluster_context = _clusters.get("risk_prompt_context", "")
|
||||
if risk_cluster_context:
|
||||
logger.info(
|
||||
f"[Cycle {run_id[:16]}] Risk clusters: "
|
||||
f"{len(_clusters.get('clusters', []))} facteurs, "
|
||||
f"saturés={_clusters.get('saturated_factors', [])}"
|
||||
)
|
||||
except Exception as _re:
|
||||
logger.warning(f"[Cycle] Risk cluster context failed (non-blocking): {_re}")
|
||||
|
||||
# ── Step 3.6: Collect IV context ─────────────────────────────────────
|
||||
iv_context = ""
|
||||
try:
|
||||
from services.iv_engine import get_iv_context_for_prompt, IV_WATCHLIST
|
||||
@@ -320,6 +335,7 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
macro_regime=macro_regime,
|
||||
portfolio_lessons=portfolio_lessons,
|
||||
iv_context=iv_context,
|
||||
risk_context=risk_cluster_context,
|
||||
)
|
||||
scored_with_id = [s for s in scored if s.get("pattern_id")]
|
||||
scored_without_id = [s for s in scored if not s.get("pattern_id")]
|
||||
|
||||
Reference in New Issue
Block a user