Files
OpenFin/backend/routers/analytics.py
OpenSquared 246deaf631 feat: Phase 4 — Moteur Probabiliste & Apprentissage Automatique
Sprint 4.1 — Bayesian Updating
- database.py: update_bayesian_posteriors() — Beta(α,β) posteriors sur trades matures
- database.py: get_bayesian_posteriors() — posteriors + IC 95% + dérive prior GPT vs posterior
- Colonnes Bayésiennes ajoutées : bayesian_alpha, bayesian_beta, bayesian_win_rate, bayesian_sample_size
- auto_cycle.py: appel update_bayesian_posteriors() en Step 5.5 (après scoring)

Sprint 4.2 — Détection Automatique de Régimes (K-Means numpy pur)
- database.py: detect_and_save_regime_clusters() — K-Means sur 7 gauges macro (VIX, slope, DXY…)
- database.py: get_regime_cluster_history() — timeline des clusters
- database.py: get_regime_transition_matrix() — P(cluster j | cluster i) sur N transitions
- Table regime_clusters avec anomaly_flag (points > 3σ)
- auto_cycle.py: appel detect_and_save_regime_clusters() en Step 5.6

Sprint 4.3 — Embeddings Sémantiques (remplace Jaccard)
- database.py: get_or_create_pattern_embedding() — OpenAI text-embedding-3-small, stocké en DB
- database.py: max_cosine_similarity_vs_existing() — similarité cosinus vs patterns existants
- Table pattern_embeddings avec vecteur JSON + model_version
- auto_cycle.py: _is_duplicate_pattern() — cosinus seuil 0.75 avec fallback Jaccard automatique

Sprint 4.4 — Tableau de Bord Analytique Avancé
- AnalyticsAdvanced.tsx: nouvelle page /analytics-advanced
  • BayesianTable : prior GPT vs WR bayésien ± IC 95%, dérive, niveau de confiance
  • ClusterTimeline : timeline colorée des clusters + anomalies
  • TransitionMatrix : heatmap P(j|i) avec diagonale auto-transition
  • EmbeddingsSummary : liste des patterns vectorisés
  • Boutons "Bayesian update" et "Détecter régime" avec mutation React Query
- analytics.py router : 5 nouveaux endpoints (bayesian, regime-clusters, transitions, detect, embeddings)
- useApi.ts : 4 nouveaux hooks (useBayesianPosteriors, useRegimeClusters, useRegimeTransitions, usePatternEmbeddings)
- App.tsx + Sidebar.tsx : route /analytics-advanced + entrée menu

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 17:46:34 +02:00

103 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from fastapi import APIRouter, Query
from services.database import (
get_pattern_reliability,
get_all_pattern_reliability_map,
get_calibration_data,
decay_kb_confidence,
get_custom_patterns,
# Phase 4
get_bayesian_posteriors,
update_bayesian_posteriors,
detect_and_save_regime_clusters,
get_regime_cluster_history,
get_regime_transition_matrix,
get_all_pattern_embeddings_summary,
)
router = APIRouter(prefix="/api/analytics", tags=["analytics"])
@router.get("/reliability")
def reliability_all():
"""Pattern reliability scores — all patterns with mature trade history."""
return {"reliability": get_pattern_reliability()}
@router.get("/reliability/{pattern_id}")
def reliability_one(pattern_id: str):
result = get_pattern_reliability(pattern_id=pattern_id)
if not result:
return {"reliability": None}
return {"reliability": result[0]}
@router.get("/calibration")
def calibration(days: int = Query(default=365, ge=30, le=1000)):
"""Predicted probability vs realized outcomes (Brier score + buckets)."""
return get_calibration_data(days=days)
@router.post("/kb/decay")
def run_kb_decay():
"""Manually trigger KB confidence decay (also runs at cycle start)."""
updated = decay_kb_confidence()
return {"updated": updated, "message": f"{updated} entrée(s) KB mise(s) à jour"}
@router.get("/invalidation-alerts")
def invalidation_alerts():
"""List all active patterns that have an invalidation trigger defined."""
patterns = get_custom_patterns()
alerts = [
{
"pattern_id": p["id"],
"pattern_name": p["name"],
"invalidation_trigger": p.get("invalidation_trigger"),
"invalidation_probability": p.get("invalidation_probability"),
"counter_thesis": p.get("counter_thesis"),
}
for p in patterns
if p.get("invalidation_trigger")
]
return {"alerts": alerts, "count": len(alerts)}
# ── Phase 4 endpoints ────────────────────────────────────────────────────────
@router.get("/bayesian")
def bayesian_posteriors():
"""Posteriors bayésiens Beta(α,β) par pattern avec IC 95%."""
return {"posteriors": get_bayesian_posteriors()}
@router.post("/bayesian/update")
def trigger_bayesian_update():
"""Force la mise à jour des posteriors bayésiens."""
n = update_bayesian_posteriors()
return {"updated": n, "message": f"{n} pattern(s) mis à jour"}
@router.get("/regime-clusters")
def regime_cluster_history(days: int = Query(default=90, ge=7, le=365)):
"""Historique des assignations de clusters de régimes macro."""
return {"clusters": get_regime_cluster_history(days=days)}
@router.get("/regime-transitions")
def regime_transition_matrix(days: int = Query(default=180, ge=30, le=730)):
"""Matrice de transition entre clusters de régimes."""
return get_regime_transition_matrix(days=days)
@router.post("/regime-detect")
def trigger_regime_detect(n_clusters: int = Query(default=4, ge=2, le=6)):
"""Force la détection et sauvegarde du cluster de régime courant."""
result = detect_and_save_regime_clusters(n_clusters=n_clusters)
return result
@router.get("/embeddings")
def pattern_embeddings_summary():
"""Liste des patterns avec embedding vectoriel disponible."""
return {"embeddings": get_all_pattern_embeddings_summary()}