Files
OpenFin/backend/routers/analytics.py
OpenSquared 66f6607568 feat: Data Management tab — purge endpoints for all major data stores
Backend — new DELETE /purge-all endpoints:
  - /api/logs/purge-all          → truncate system_logs (immediate, no 30d wait)
  - /api/reports/purge-all       → cycle_reports + ai_reports
  - /api/portfolio/purge-all     → portfolio + trade_entry_prices
  - /api/analytics/purge-all     → pattern_score_history, regime_clusters,
                                    pattern_embeddings, cycle_runs,
                                    macro_regime_history, geo_alert_history
  - /api/var/purge-all           → var_snapshots + pnl_snapshots
  - /api/pattern-lab/purge-all   → backtest_lab_runs

Frontend — Config.tsx: new 'Data Management' tab
  - PurgeButton component with inline double-confirm (click Purge → confirm)
  - Shows table names affected + row count deleted
  - 6 purge actions: Logs, AI Reports, Portfolio, Analytics, VaR, Pattern Lab

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 18:25:01 +02:00

120 lines
4.0 KiB
Python
Raw Permalink 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()}
@router.delete("/purge-all")
def purge_all_analytics():
"""Purge all analytics history: score history, regime clusters, embeddings, cycle runs."""
from services.database import get_conn
conn = get_conn()
conn.execute("DELETE FROM pattern_score_history")
conn.execute("DELETE FROM regime_clusters")
conn.execute("DELETE FROM pattern_embeddings")
conn.execute("DELETE FROM cycle_runs")
conn.execute("DELETE FROM macro_regime_history")
conn.execute("DELETE FROM geo_alert_history")
n = conn.total_changes
conn.commit()
conn.close()
return {"deleted": n}