// ── AccuracyView ───────────────────────────────────────────────────────────── // Accuratezza delle previsioni (misurata su outcome già verificati vs prezzo // reale) + pannello salute dati. Cuore della validazione: niente è "affidabile" // finché non è misurato. // Legenda numero tool → nome (Tool2 rimosso: i suoi output li produce Tool3). const TOOL_LEGEND = [ { n: "Tool0", name: "Rischio", desc: "regime di rischio di mercato (allerta crolli)" }, { n: "Tool1", name: "Collector", desc: "scarica i prezzi (OHLCV)" }, { n: "Tool3", name: "Tecnico", desc: "analisi tecnica → segnale, forecast, target/stop" }, { n: "Tool4", name: "News", desc: "news + sentiment → segnale combinato" }, { n: "Tool5", name: "Calendario", desc: "eventi finanziari / earnings" }, { n: "Tool6", name: "Telegram", desc: "notifiche" }, { n: "Tool7", name: "Settori", desc: "rotazione settoriale (11 settori SPDR)" }, { n: "Tool8", name: "Pre-market", desc: "gap / momentum pre-apertura" }, { n: "Tool9", name: "Top Picks", desc: "scoperta titoli promettenti" }, { n: "Tool10", name: "Chain", desc: "catene lead-lag (A sale → B segue)" }, { n: "Tool11", name: "Simulatore", desc: "paper-trading + Portfolio Manager AI" }, ]; function AccuracyView() { const [acc, setAcc] = useState(null); const [health, setHealth] = useState(null); const [sim, setSim] = useState(null); const [pm, setPm] = useState(null); const [sc, setSc] = useState(null); // pagella (admin-only; 401 → null) const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [help, setHelp] = useState(false); const [win, setWin] = useState(30); // finestra previsioni in giorni (0 = tutto) const load = async (spin) => { if (spin) setLoading(true); try { const [a, h, s, p, scd] = await Promise.all([ fetch("/api/accuracy?days=" + (win || 0)).then(r => r.json()), fetch("/api/health").then(r => r.json()), fetch("/api/simulator").then(r => r.json()).catch(() => null), fetch("/api/pm").then(r => r.json()).catch(() => null), fetch("/api/scorecard?days=" + (win || 0)).then(r => r.json()).catch(() => null), ]); setAcc(a || {}); setHealth(h.health || []); setSim(s); setPm(p); setSc(scd && scd.tools ? scd : null); setError(a?.error || h?.error || null); } catch (e) { setError(String(e)); } finally { if (spin) setLoading(false); } }; useEffect(() => { load(true); const t = setInterval(() => load(false), 5 * 60 * 1000); return () => clearInterval(t); }, [win]); const HORIZONS = ["1h", "3h", "6h", "24h"]; const num = (v, d = 2) => (v == null ? "—" : (v >= 0 && d ? "" : "") + v.toFixed(d)); const dirColor = (v) => v == null ? "var(--text-muted)" : v >= 55 ? "var(--green)" : v >= 50 ? "var(--amber)" : "var(--red)"; const biasColor = (v) => v == null ? "var(--text-muted)" : Math.abs(v) <= 0.3 ? "var(--green)" : Math.abs(v) <= 0.8 ? "var(--amber)" : "var(--red)"; const totalN = (stats) => HORIZONS.reduce((s, h) => s + ((stats?.[h]?.n) || 0), 0); const ForecastCard = ({ title, color, stats }) => { const n = totalN(stats); return (
{title} {n} outcome
{n === 0 ? (
Nessun outcome ancora. Maturano ~25h dopo ogni previsione (poi compaiono qui).
) : ( {HORIZONS.map(h => { const s = stats?.[h] || {}; return ( ); })}
Oriz. n Dir.Acc MBE MAE RMSE
{h} {s.n || 0} {s.dir_acc == null ? "—" : s.dir_acc + "%"} {s.mbe == null ? "—" : (s.mbe >= 0 ? "+" : "") + s.mbe} {num(s.mae)} {num(s.rmse)}
)}
); }; const MiniStat = ({ title, children, hint }) => (
{title}
{children}
); const t9 = acc?.tool9 || {}; const t8 = acc?.tool8 || {}; const t10 = acc?.tool10 || {}; const signed = (v, suf = "%") => v == null ? "—" : `${v >= 0 ? "+" : ""}${v}${suf}`; const greenRed = (v) => v == null ? "var(--text-muted)" : v >= 0 ? "var(--green)" : "var(--red)"; return (

Accuratezza previsioni

Misurata su previsioni già verificate vs prezzo reale. Direction-accuracy > 55% = segnale; MBE ≈ bias sistematico.
{/* Finestra temporale: di default RECENTE (non a vita) così riflette lo stato attuale dei modelli dopo i miglioramenti settimanali. */}
Finestra previsioni (Tool3/4): {[{ v: 7, l: "7 giorni" }, { v: 30, l: "30 giorni" }, { v: 0, l: "Tutto" }].map(o => ( ))} {win ? `· solo le previsioni degli ultimi ${win} giorni` : "· media di sempre (diluisce i miglioramenti)"}
{help && (
Legenda tool — numero → nome
{TOOL_LEGEND.map(t => ( ))}
{t.n} {t.name} {t.desc}
Tool2 è stato rimosso: i suoi output (target/stop/probabilità) ora li produce il Tool3.
)} {loading && !acc &&
Caricamento…
} {error &&
Errore: {error}
} {acc && <> {/* Pagella tool — costo vs valore vs soldi (admin-only) */} {sc && sc.tools && sc.tools.length > 0 && (() => { const VLABEL = { earns_its_keep: { t: "Si ripaga", c: "var(--green)" }, costs_no_value: { t: "Costa, 0 valore", c: "var(--red)" }, unproven: { t: "Da dimostrare", c: "var(--amber)" }, free_informative: { t: "Gratis · utile", c: "#6ea8fe" }, free_low_value: { t: "Gratis · poco valore", c: "var(--text-muted)" }, }; return (
Pagella tool — costo vs valore vs soldi “mi fa guadagnare?”
Money = proxy di triage (long-only, una posizione per volta, niente concorrenza/slippage) → sovrastima: serve solo a capire quali tool meritano un backtest serio. Costo per trade ~{sc.assumptions?.cost_per_trade_pp}pp.
{sc.tools.map(t => { const v = t.value || {}, m = t.money || {}; const val = v.dir_acc != null ? v.dir_acc + "%" : (v.alpha != null ? signed(v.alpha) : "—"); const valCol = v.dir_acc != null ? dirColor(v.dir_acc) : greenRed(v.alpha); const vlab = VLABEL[t.verdict] || { t: t.verdict, c: "var(--text-muted)" }; return ( ); })}
Tool €/mese Valore Money net% (n) Verdetto
{t.label} 0 ? "var(--text)" : "var(--text-faint)" }}> {t.cost_month_eur > 0 ? "€" + t.cost_month_eur.toFixed(2) : "—"} {val} {m.net_ret_pct == null ? "—" : signed(m.net_ret_pct) + (m.n_signals ? ` (${m.n_signals})` : "")} {vlab.t}
); })()} {/* Forecast Tool3 / Tool4 */}
{/* Discovery / pre-market / chain */}
{t9.n_validated ? (
{signed(t9.avg_alpha)}
{t9.n_validated} pick · hit {t9.hit_rate ?? "—"}%
) :
In maturazione (30gg)
}
{t8.n ? (
{t8.hit_rate}%
{t8.n} verdetti
) :
Nessun dato
}
{t10.n ? (
{t10.pct_stable}%
{t10.n} edge · score medio {t10.avg_edge ?? "—"}
) :
Nessun dato
}
{/* Paper-trading — il "bottom line": quanto rendono davvero le decisioni */}

Paper-trading (settimana)

denaro fittizio
{sim?.active && sim?.summary ? (
{signed(sim.summary.return_pct)}
Δ buy&hold {signed((sim.summary.return_pct || 0) - (sim.summary.bh_return_pct || 0))}
) :
Non attivo
}
{pm?.active && pm?.summary ? (
{signed(pm.summary.return_pct)}
Δ b&h {signed((pm.summary.return_pct || 0) - (pm.summary.bh_return_pct || 0))} · {pm.summary.n_pos || 0} pos
) :
Parte lunedì
}
{pm?.active && pm?.summary ? (
{signed((pm.summary.return_pct || 0) - (pm.summary.spy_return_pct || 0))}
) :
}
{/* Salute dati */}

Salute dati

{(health || []).map(h => (
{h.label} {h.age_min == null ? "nessun dato" : h.age_min < 90 ? `${h.age_min} min fa` : `${(h.age_min / 60).toFixed(1)}h fa`} {h.rows_today == null ? "" : `${h.rows_today} righe oggi`} {(h.anomalies || []).map((a, i) => ( {a} ))} {h.ok && ok}
))}
}
); } window.AccuracyView = AccuracyView;