feat(finlab): boutons d'analyse rapide (digest/analyse/plan/fondamental/opport./alertes) (#70)

Panneau « Actions rapides » sur le dashboard : 6 boutons one-click qui renvoient
une analyse finlab instantanée (déterministe, gratuite, sans LLM), dans une modale.

- 📊 Analyse (action sélectionnée) : technique (RSI/MACD/MM/signaux) + fondamental
  (PER/PEG/ROE/marges/dette/reco/cible) + plan R:R, combinés en une fiche
- 📐 Plan R:R, 📈 Fondamentaux (action sélectionnée) ; 📋 Digest, 🔭 Opportunités,
  🔔 Alertes (global)
- Backend : endpoints /api/digest, /api/fundamentals, /api/analyze
- Fix : fundamental.compare_portfolio() utilisait l'ancien schéma mono-compte
  (pf["positions"]) → cassé par le multi-comptes ; passe par data.portfolio_tickers()

Vérifié : endpoints OK (TestClient), rendu modale au navigateur (Playwright) —
fiche Analyse NVDA complète (technique + fondamental + plan). JS node --check OK.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ALI YESILKAYA 2026-06-30 11:51:40 +02:00 committed by GitHub
parent b09d3db1fa
commit 73e2ad30db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 122 additions and 3 deletions

View file

@ -21,6 +21,8 @@ from fastapi.staticfiles import StaticFiles
from finlab import (
alerts,
data,
digest as digest_mod,
fundamental,
indicators as ind,
plan as plan_mod,
scanner,
@ -168,6 +170,38 @@ def api_plan(ticker: str, capital: float = 1427.0):
return _clean({"plan": p, "render": plan_mod.render(p)})
# ── Analyses rapides (boutons one-click, finlab pur) ──────────────────────────
@app.get("/api/digest")
def api_digest(theme: str = "all", target: float = 5.0):
return {"text": digest_mod.build(theme, target)}
@app.get("/api/fundamentals")
def api_fundamentals(ticker: str):
return _clean({"snapshot": fundamental.snapshot(ticker)})
@app.get("/api/analyze")
def api_analyze(ticker: str, capital: float = 1427.0):
"""Analyse one-click d'une action : technique + fondamental + plan R:R (finlab pur)."""
out = {"ticker": ticker}
try:
out["technical"] = technical.analyze(ticker)
except Exception as e:
out["technical"] = {"error": str(e)}
try:
out["fundamental"] = fundamental.snapshot(ticker)
except Exception as e:
out["fundamental"] = {"error": str(e)}
try:
p = plan_mod.plan(ticker, capital)
out["plan"] = p
out["plan_render"] = plan_mod.render(p)
except Exception as e:
out["plan"] = {"error": str(e)}
return _clean(out)
# ── Import de relevés (image → analysée par la Console IA) ─────────────────────
IMPORTS_DIR = data.ROOT / "imports"
_IMG_EXT = {".png", ".jpg", ".jpeg", ".webp", ".gif"}