diff --git a/frontend/src/pages/MarketEvents.tsx b/frontend/src/pages/MarketEvents.tsx index c3569f6..e1ba30f 100644 --- a/frontend/src/pages/MarketEvents.tsx +++ b/frontend/src/pages/MarketEvents.tsx @@ -51,6 +51,13 @@ interface MarketEvent { evaluated?: boolean } +function resolveFormula(formula: string, coefs: Record): string { + return formula.replace(/\{\{(\w+)\}\}/g, (_, k) => { + const v = coefs[k]?.value + return v !== undefined ? String(v) : `[${k}]` + }) +} + interface CausalAnalysis { id: number market_event_id: number @@ -63,7 +70,7 @@ interface CausalAnalysis { prediction_json: Record actual_json: Record drift_json?: Record - graph_json?: { nodes: GraphNode[]; edges: GraphEdge[] } + graph_json?: GraphData } interface TemplateRef { @@ -73,9 +80,10 @@ interface TemplateRef { instruments: string[] } -interface GraphNode { id: string; label: string; type: string; x: number; y: number; unit?: string; instrument?: string } -interface GraphEdge { from: string; to: string; style?: string; sign?: string } -interface GraphData { nodes: GraphNode[]; edges: GraphEdge[] } +interface GraphNode { id: string; label: string; type: string; x: number; y: number; unit?: string; instrument?: string; formula?: string } +interface GraphEdge { from: string; to: string; style?: string; sign?: string; label?: string } +interface GraphCoef { value: number; description?: string } +interface GraphData { nodes: GraphNode[]; edges: GraphEdge[]; coefficients?: Record } // ── Constants ───────────────────────────────────────────────────────────────── @@ -764,6 +772,7 @@ function EventDetail({ const isExpanded = expandedAnalysis === a.id const gNodes = a.graph_json?.nodes || [] const gEdges = a.graph_json?.edges || [] + const gCoefs = a.graph_json?.coefficients || {} const allVals: Record = { ...(a.inputs_json || {}), ...(a.prediction_json || {}) } const inputNodes = gNodes.filter(n => n.type === 'input') const outputNodes = gNodes.filter(n => n.type === 'market_asset' || n.type === 'output') @@ -842,17 +851,24 @@ function EventDetail({ {midNodes.length > 0 && (
Propagation
-
+
{midNodes.map(n => { const v = allVals[n.id] if (v === undefined) return null const fmt = n.unit === 'pips' ? Math.round(v).toString() - : n.unit === '%' ? v.toFixed(2) + '%' - : v.toFixed(3) + : n.unit === '%' ? v.toFixed(3) + '%' + : v.toFixed(4) + const formula = n.formula ? resolveFormula(n.formula, gCoefs) : null return ( - - {n.label}: 0 ? 'text-teal-400' : v < 0 ? 'text-orange-400' : 'text-slate-500'}>{v > 0 ? '+' : ''}{fmt} - +
+ {n.label}: + 0 ? 'text-teal-400' : v < 0 ? 'text-orange-400' : 'text-slate-500')}> + {v > 0 ? '+' : ''}{fmt} + + {formula && ( + = {formula} + )} +
) })}
@@ -862,21 +878,29 @@ function EventDetail({ {outputNodes.length > 0 && (
Prédictions vs Réel
-
+
{outputNodes.map(n => { const pred = allVals[n.id] - const actual = (a.actual_json || {})[n.id] ?? (a.actual_json || {})[n.instrument || ''] - const fmt = (x: number) => `${x > 0 ? '+' : ''}${Math.round(x)}${n.unit === 'pips' ? 'p' : ''}` + const actual = (a.actual_json || {})[n.id] + ?? (a.actual_json || {})[n.instrument || ''] + ?? (a.actual_json || {})[(n.instrument || '').toUpperCase()] + const fmt = (x: number) => `${x > 0 ? '+' : ''}${Math.round(x)}${n.unit === 'pips' ? 'p' : n.unit === '$/oz' ? '$' : ''}` + const formula = n.formula ? resolveFormula(n.formula, gCoefs) : null return ( - 0 ? 'bg-emerald-900/20 border-emerald-700/30 text-emerald-400' : - pred < 0 ? 'bg-red-900/20 border-red-700/30 text-red-400' : - 'bg-slate-800/60 border-slate-700/40 text-slate-400')}> - {n.label}: {pred !== undefined ? fmt(pred) : '—'} - {actual !== undefined && ( - / réel {fmt(actual)} +
+ 0 ? 'bg-emerald-900/20 border-emerald-700/30 text-emerald-400' : + pred < 0 ? 'bg-red-900/20 border-red-700/30 text-red-400' : + 'bg-slate-800/60 border-slate-700/40 text-slate-400')}> + {n.label}: {pred !== undefined ? fmt(pred) : '—'} + {actual !== undefined && ( + / réel {fmt(actual)} + )} + + {formula && ( + = {formula} )} - +
) })}