feat: risk
This commit is contained in:
@@ -827,8 +827,7 @@ export default function Dashboard() {
|
||||
.filter(d => d.value > 0)
|
||||
.sort((a, b) => b.value - a.value)
|
||||
const radarAxes = ((riskRadarData as any)?.axes ?? []).map((a: any) => ({ ...a, value: a.value ?? 0 }))
|
||||
const dominantScenario = (scenarioExposure as any)?.dominant_scenario
|
||||
const scenarioWarning = (scenarioExposure as any)?.warning
|
||||
const scenarioRanking: any[] = ((scenarioExposure as any)?.concentration ?? []).slice(0, 3)
|
||||
|
||||
return (
|
||||
<Link to="/risk" className="card flex flex-col overflow-y-auto hover:border-slate-600/60 transition-all cursor-pointer"
|
||||
@@ -840,10 +839,22 @@ export default function Dashboard() {
|
||||
<div className={clsx('text-[10px] font-bold mt-0.5 flex items-center gap-2', alertCount > 0 ? 'text-red-400' : 'text-emerald-400')}>
|
||||
{alertCount > 0 ? `${alertCount} alert${alertCount > 1 ? 's' : ''}` : 'OK'}
|
||||
</div>
|
||||
{scenarioWarning && dominantScenario && (
|
||||
<div className="mt-1 text-[9px] leading-snug text-amber-400 bg-amber-900/10 border border-amber-700/20 rounded px-1.5 py-1">
|
||||
⚠ {dominantScenario.pct_of_portfolio.toFixed(0)}% du book sur le même pari macro
|
||||
(<span className="font-semibold">{dominantScenario.label}</span>)
|
||||
{scenarioRanking.length > 0 && (
|
||||
<div className="mt-1.5 space-y-1">
|
||||
<div className="text-[8px] uppercase tracking-wide text-slate-600">Portefeuille aligné sur</div>
|
||||
{scenarioRanking.map((s: any, i: number) => (
|
||||
<div key={s.key}>
|
||||
<div className="flex items-center justify-between text-[9px] mb-0.5">
|
||||
<span className="text-slate-300 truncate flex items-center gap-1">
|
||||
<span>{s.emoji}</span>{s.label}
|
||||
</span>
|
||||
<span className="font-mono font-bold shrink-0" style={{ color: s.color }}>{s.pct_of_portfolio}%</span>
|
||||
</div>
|
||||
<div className="h-1 bg-dark-700 rounded-full overflow-hidden">
|
||||
<div className="h-full rounded-full" style={{ width: `${Math.min(s.pct_of_portfolio, 100)}%`, background: s.color, opacity: i === 0 ? 1 : 0.6 }} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{radarAxes.length > 0 && (
|
||||
|
||||
@@ -145,11 +145,12 @@ function ScenarioExposureCard() {
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="text-sm font-semibold text-white mb-1 flex items-center gap-2">
|
||||
<Layers className="w-4 h-4 text-purple-400" /> Concentration par scénario macro
|
||||
<Layers className="w-4 h-4 text-purple-400" /> Alignement du portefeuille sur le Régime Macro
|
||||
</div>
|
||||
<div className="text-[10px] text-slate-500 mb-3">
|
||||
Repricing Black-Scholes réel (pricing Saxo-first) de chaque position sous 5 scénarios —
|
||||
révèle quand plusieurs positions différentes sont en réalité le même pari répété.
|
||||
Repricing Black-Scholes réel (pricing Saxo-first) de chaque position sous les 8 scénarios du
|
||||
régime macro — révèle quand plusieurs positions différentes sont en réalité le même pari répété.
|
||||
Vue distincte des Risk Factors ci-dessous (thème/classe d'actif touché, pas le sens du pari).
|
||||
</div>
|
||||
|
||||
{exp.warning && (
|
||||
@@ -160,7 +161,7 @@ function ScenarioExposureCard() {
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
||||
{/* Concentration bars */}
|
||||
{/* Concentration ranking */}
|
||||
<div>
|
||||
<div className="text-xs font-semibold text-slate-400 mb-2">
|
||||
% du book dont c'est le scénario le plus favorable
|
||||
@@ -169,13 +170,15 @@ function ScenarioExposureCard() {
|
||||
{concentration.map((c: any) => (
|
||||
<div key={c.key}>
|
||||
<div className="flex justify-between text-xs mb-1">
|
||||
<span className="text-slate-300">{c.label}</span>
|
||||
<span className={clsx('font-mono font-bold', c.pct_of_portfolio >= 60 ? 'text-amber-400' : 'text-slate-300')}>
|
||||
<span className="text-slate-300 flex items-center gap-1.5">
|
||||
<span>{c.emoji}</span>{c.label}
|
||||
</span>
|
||||
<span className="font-mono font-bold" style={{ color: c.color }}>
|
||||
{c.pct_of_portfolio}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-2 bg-dark-700 rounded-full overflow-hidden">
|
||||
<div className="h-full rounded-full bg-purple-500/70" style={{ width: `${Math.min(c.pct_of_portfolio, 100)}%` }} />
|
||||
<div className="h-full rounded-full" style={{ width: `${Math.min(c.pct_of_portfolio, 100)}%`, background: c.color }} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -189,7 +192,9 @@ function ScenarioExposureCard() {
|
||||
<tbody>
|
||||
{scenarios.map((s: any) => (
|
||||
<tr key={s.key} className="border-b border-slate-800/40">
|
||||
<td className="py-1.5 pr-3 text-slate-300">{s.label}</td>
|
||||
<td className="py-1.5 pr-3 text-slate-300 flex items-center gap-1.5">
|
||||
<span>{s.emoji}</span>{s.label}
|
||||
</td>
|
||||
<td className={clsx('py-1.5 text-right font-mono font-bold whitespace-nowrap',
|
||||
s.portfolio_pnl_pct >= 0 ? 'text-emerald-400' : 'text-red-400')}>
|
||||
{s.portfolio_pnl_pct >= 0 ? '+' : ''}{s.portfolio_pnl_pct}%
|
||||
|
||||
Reference in New Issue
Block a user