feat: market event
This commit is contained in:
@@ -940,6 +940,7 @@ def analyze_event(body: AnalyzeRequest):
|
||||
"yields": yields,
|
||||
"activation": activation,
|
||||
"drift": drift_by_inst.get(primary_inst, {}),
|
||||
"drift_by_inst": drift_by_inst,
|
||||
"prices_mode": prices.get("mode", "none"),
|
||||
"effective_lag_min": effective_lag,
|
||||
"effective_lag_days": effective_lag_days,
|
||||
@@ -961,7 +962,7 @@ def analyze_event(body: AnalyzeRequest):
|
||||
""", (
|
||||
primary_inst, json.dumps(inputs), json.dumps(body.coef_overrides),
|
||||
json.dumps(node_values), json.dumps(actual_moves), activation.get("score"),
|
||||
json.dumps(drift_by_inst.get(primary_inst, {})), analyzed_at,
|
||||
json.dumps(drift_by_inst), analyzed_at,
|
||||
body.market_event_id, body.template_id,
|
||||
))
|
||||
else:
|
||||
@@ -976,7 +977,7 @@ def analyze_event(body: AnalyzeRequest):
|
||||
json.dumps(inputs), json.dumps(body.coef_overrides),
|
||||
json.dumps(node_values), json.dumps(actual_moves),
|
||||
activation.get("score"),
|
||||
json.dumps(drift_by_inst.get(primary_inst, {})), analyzed_at,
|
||||
json.dumps(drift_by_inst), analyzed_at,
|
||||
))
|
||||
conn.commit()
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ interface CausalAnalysis {
|
||||
inputs_json: Record<string, number>
|
||||
prediction_json: Record<string, number>
|
||||
actual_json: Record<string, number>
|
||||
drift_json?: Record<string, any>
|
||||
graph_json?: { nodes: GraphNode[]; edges: GraphEdge[] }
|
||||
}
|
||||
|
||||
@@ -395,8 +396,12 @@ function EventDetail({
|
||||
const [instMsg, setInstMsg] = useState<string | null>(null)
|
||||
const [anResult, setAnResult] = useState<{
|
||||
score: number | null
|
||||
preds: Record<string, number>
|
||||
actuals: Record<string, number>
|
||||
preds: Record<string, number>
|
||||
actuals: Record<string, number>
|
||||
drift?: Record<string, { pre_pips: number | null; post_pips: number | null; lag_min: number; lag_days: number }>
|
||||
pricesMode?: string
|
||||
lagMin?: number
|
||||
lagDays?: number
|
||||
} | null>(null)
|
||||
const [graphData, setGraphData] = useState<GraphData | null>(null)
|
||||
|
||||
@@ -417,10 +422,19 @@ function EventDetail({
|
||||
const latest = data[0]
|
||||
if (latest.template_id) setSelTmpl(latest.template_id)
|
||||
if (Object.keys(latest.inputs_json || {}).length > 0) setInstInputs(latest.inputs_json)
|
||||
const driftRaw = latest.drift_json || {}
|
||||
// drift_json peut être { EURUSD: {...}, XAUUSD: {...} } (nouveau) ou { post_pips: ..., ... } (ancien)
|
||||
const isDriftByInst = driftRaw && !('post_pips' in driftRaw) && Object.keys(driftRaw).length > 0
|
||||
const driftByInst = isDriftByInst
|
||||
? driftRaw
|
||||
: latest.instrument && Object.keys(driftRaw).length > 0
|
||||
? { [latest.instrument]: driftRaw }
|
||||
: undefined
|
||||
setAnResult({
|
||||
score: latest.activation_score,
|
||||
preds: latest.prediction_json || {},
|
||||
actuals: latest.actual_json || {},
|
||||
score: latest.activation_score,
|
||||
preds: latest.prediction_json || {},
|
||||
actuals: latest.actual_json || {},
|
||||
drift: driftByInst,
|
||||
})
|
||||
if (latest.graph_json?.nodes?.length) {
|
||||
setGraphData(latest.graph_json as GraphData)
|
||||
@@ -535,7 +549,15 @@ function EventDetail({
|
||||
})
|
||||
const d = await r.json()
|
||||
if (!r.ok) throw new Error(d.detail || r.statusText)
|
||||
setAnResult({ score: d.activation?.score ?? null, preds: d.node_values ?? {}, actuals: d.actual_moves ?? {} })
|
||||
setAnResult({
|
||||
score: d.activation?.score ?? null,
|
||||
preds: d.node_values ?? {},
|
||||
actuals: d.actual_moves ?? {},
|
||||
drift: d.drift_by_inst ?? (d.drift ? { [d.instrument]: d.drift } : undefined),
|
||||
pricesMode: d.prices_mode,
|
||||
lagMin: d.effective_lag_min,
|
||||
lagDays: d.effective_lag_days,
|
||||
})
|
||||
if (d.graph_json?.nodes?.length) setGraphData(d.graph_json)
|
||||
await loadAnalyses()
|
||||
} catch (e: any) { setAnResult({ score: null, preds: {}, actuals: {} }) }
|
||||
@@ -850,6 +872,52 @@ function EventDetail({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Bandeau de détail du calcul réel */}
|
||||
{anResult && (anResult.pricesMode || anResult.drift) && (() => {
|
||||
const mode = anResult.pricesMode
|
||||
const lagMin = anResult.lagMin ?? 0
|
||||
const lagDays = anResult.lagDays ?? 0
|
||||
const driftEntries = Object.entries(anResult.drift ?? {})
|
||||
return (
|
||||
<div className="bg-slate-900/70 border border-slate-700/40 rounded px-3 py-2 space-y-1.5 text-[10px] font-mono">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<span className={clsx('px-1.5 py-0.5 rounded font-semibold',
|
||||
mode === 'intraday_5m' ? 'bg-cyan-900/40 text-cyan-400 border border-cyan-700/40'
|
||||
: 'bg-amber-900/40 text-amber-400 border border-amber-700/40')}>
|
||||
{mode === 'intraday_5m' ? '5m intraday' : mode === 'daily' ? 'journalier' : mode ?? '—'}
|
||||
</span>
|
||||
{mode === 'intraday_5m' && lagMin > 0 && (
|
||||
<span className="text-slate-400">lag <span className="text-cyan-300">{lagMin}min</span></span>
|
||||
)}
|
||||
{mode !== 'intraday_5m' && lagDays > 0 && (
|
||||
<span className="text-slate-400">lag <span className="text-amber-300">{lagDays}j</span></span>
|
||||
)}
|
||||
{mode !== 'intraday_5m' && lagDays === 0 && (
|
||||
<span className="text-slate-600 italic">lag J = 0 (jour J)</span>
|
||||
)}
|
||||
</div>
|
||||
{driftEntries.map(([inst, d]) => (
|
||||
<div key={inst} className="flex items-center gap-2 flex-wrap border-t border-slate-800/60 pt-1.5">
|
||||
<span className="text-slate-400 w-16 truncate">{inst}</span>
|
||||
{d.pre_pips != null && (
|
||||
<span className="text-slate-500">
|
||||
pré: <span className={d.pre_pips > 0 ? 'text-emerald-500' : 'text-red-500'}>{d.pre_pips > 0 ? '+' : ''}{d.pre_pips}p</span>
|
||||
</span>
|
||||
)}
|
||||
<span className="text-slate-500">
|
||||
post: <span className={d.post_pips == null ? 'text-slate-600' : d.post_pips > 0 ? 'text-emerald-400' : 'text-red-400'}>
|
||||
{d.post_pips == null ? '—' : `${d.post_pips > 0 ? '+' : ''}${d.post_pips}p`}
|
||||
</span>
|
||||
</span>
|
||||
{d.post_pips == null && (
|
||||
<span className="text-red-500 italic">données manquantes (yfinance)</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
|
||||
{/* Inline result — full-width graph + left recap */}
|
||||
{anResult && graphData?.nodes?.length && (() => {
|
||||
const inputNodes = graphData.nodes.filter(n => n.type === 'input')
|
||||
|
||||
Reference in New Issue
Block a user