+
+ {label}
+ {stressed && (
+
+ Stressed ×1.5
+
+ )}
+
+
{method}
+
+
+ VaR 1J ({horizon === 1 ? '1j' : `${horizon}j`})
+
+ {pct(horizon === 1 ? var1d : varNd)}
+
+
+
+ CVaR (ES)
+
+ {pct(cvar)}
+
+
+
+
+ )
+}
+
+function ControlBar({
+ confidence, setConfidence,
+ horizon, setHorizon,
+ lookback, setLookback,
+ iv, setIv,
+ loading, onRefresh,
+}: {
+ confidence: number
+ setConfidence: (v: number) => void
+ horizon: number
+ setHorizon: (v: number) => void
+ lookback: number
+ setLookback: (v: number) => void
+ iv: number
+ setIv: (v: number) => void
+ loading: boolean
+ onRefresh: () => void
+}) {
+ return (
+
+ {/* Confidence */}
+
+
+
+ {[0.90, 0.95, 0.99].map(c => (
+
+ ))}
+
+
+
+ {/* Horizon */}
+
+
+
+ {[1, 5, 10, 21].map(h => (
+
+ ))}
+
+
+
+ {/* Lookback */}
+
+
+
+ {[63, 126, 252].map(l => (
+
+ ))}
+
+
+
+ {/* IV */}
+
+
+
+ {[0.15, 0.20, 0.30, 0.40].map(v => (
+
+ ))}
+
+
+
+
+
+ )
+}
+
+// ─── Custom tooltip for histogram ─────────────────────────────────────────────
+
+function HistoTooltip({ active, payload }: any) {
+ if (!active || !payload?.length) return null
+ const { x, count } = payload[0].payload
+ return (
+
+ {/* Header */}
+
+
+
+
+
Analyse VaR — Value at Risk
+
+
+ Approche delta Black-Scholes · {portfolio ? `${portfolio.n_positions} positions · Notionnel ${portfolio.total_notional_eur.toLocaleString('fr-FR')} EUR` : '—'}
+ {portfolio?.data_source === 'simulated' && (
+ ⚠ Données simulées (marché indisponible)
+ )}
+
+
+
setRefreshKey(k => k + 1)}
+ />
+
+
+ {/* Error state */}
+ {error && (
+
+ )}
+
+ {data?.error && (
+
+
+ {data.error}
+
+ )}
+
+ {/* VaR metric cards */}
+ {varData && (
+
+
+
+
+
+ )}
+
+ {/* EUR amounts */}
+ {varData && portfolio && (
+
+ {[
+ { label: 'VaR Hist. 1J', val: varData.historical.var_1d_eur, color: 'blue' },
+ { label: 'CVaR Hist.', val: varData.historical.cvar_eur, color: 'blue' },
+ { label: 'VaR Param. 1J', val: varData.parametric.var_1d_eur, color: 'violet' },
+ { label: 'CVaR Param.', val: varData.parametric.cvar_eur, color: 'violet' },
+ { label: 'VaR MC Stressed', val: varData.monte_carlo.var_1d_eur, color: 'orange' },
+ { label: 'CVaR MC', val: varData.monte_carlo.cvar_eur, color: 'orange' },
+ ].map(({ label, val, color }) => (
+
+
{label}
+
+ {val < 0 ? '' : '+'}{val.toLocaleString('fr-FR', { maximumFractionDigits: 0 })} €
+
+
+ ))}
+
+ )}
+
+ {/* Charts row */}
+
+ {/* Returns distribution histogram */}
+
+
+
+
Distribution des Retours
+
— trait rouge = VaR hist.
+
+ {histogram.length > 0 ? (
+
+
+
+
+
+ } />
+
+
+ {histogram.map((entry, i) => (
+ |
+ ))}
+
+
+
+ ) : (
+
+ {isLoading ? 'Calcul en cours…' : 'Aucune donnée'}
+
+ )}
+
+
+ {/* Rolling VaR */}
+
+
+
+
VaR 95% Glissante (fenêtre 30J)
+
+ {rolling.length > 0 ? (
+
+
+
+ d.slice(5)} // MM-DD
+ interval={Math.floor(rolling.length / 6)}
+ />
+
+ } />
+
+
+
+
+ ) : (
+
+ {isLoading ? 'Calcul en cours…' : 'Historique insuffisant'}
+
+ )}
+
+
+
+ {/* Bottom row: positions + backtest */}
+
+ {/* Positions deltas */}
+
+
Positions & Deltas
+ {positions.length === 0 ? (
+
{isLoading ? 'Chargement…' : 'Aucune position'}
+ ) : (
+
+ {positions.map((p, i) => (
+
+
+
{p.ticker}
+
{p.strategy}
+ {p.pattern &&
{p.pattern}
}
+
+
+ 0 ? 'text-emerald-400' : 'text-red-400'
+ )}>
+ Δ {p.delta > 0 ? '+' : ''}{p.delta.toFixed(3)}
+
+ {p.notional.toLocaleString('fr-FR', { maximumFractionDigits: 0 })} €
+
+
+ ))}
+
+ )}
+
+
+ {/* Backtest Kupiec */}
+ {backtest && (
+
+
Backtest — Test de Kupiec
+
+
+ Observations
+ {backtest.n_observations}
+
+
+ Violations VaR
+ {backtest.n_breaches}
+
+
+ Taux réel
+
+ {backtest.breach_rate_pct}%
+
+
+
+ Taux attendu
+ {backtest.expected_breach_rate_pct}%
+
+
+ {backtest.kupiec_ok
+ ? '✓ Modèle validé — violations dans la tolérance'
+ : '✗ Excès de violations — modèle sous-estime le risque'}
+
+
+ Règle : taux réel ≤ 2× taux attendu ({backtest.expected_breach_rate_pct * 2}%)
+
+
+
+ )}
+
+
+ )
+}