feat: market event
This commit is contained in:
@@ -940,6 +940,7 @@ def analyze_event(body: AnalyzeRequest):
|
|||||||
"yields": yields,
|
"yields": yields,
|
||||||
"activation": activation,
|
"activation": activation,
|
||||||
"drift": drift_by_inst.get(primary_inst, {}),
|
"drift": drift_by_inst.get(primary_inst, {}),
|
||||||
|
"drift_by_inst": drift_by_inst,
|
||||||
"prices_mode": prices.get("mode", "none"),
|
"prices_mode": prices.get("mode", "none"),
|
||||||
"effective_lag_min": effective_lag,
|
"effective_lag_min": effective_lag,
|
||||||
"effective_lag_days": effective_lag_days,
|
"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),
|
primary_inst, json.dumps(inputs), json.dumps(body.coef_overrides),
|
||||||
json.dumps(node_values), json.dumps(actual_moves), activation.get("score"),
|
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,
|
body.market_event_id, body.template_id,
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
@@ -976,7 +977,7 @@ def analyze_event(body: AnalyzeRequest):
|
|||||||
json.dumps(inputs), json.dumps(body.coef_overrides),
|
json.dumps(inputs), json.dumps(body.coef_overrides),
|
||||||
json.dumps(node_values), json.dumps(actual_moves),
|
json.dumps(node_values), json.dumps(actual_moves),
|
||||||
activation.get("score"),
|
activation.get("score"),
|
||||||
json.dumps(drift_by_inst.get(primary_inst, {})), analyzed_at,
|
json.dumps(drift_by_inst), analyzed_at,
|
||||||
))
|
))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ interface CausalAnalysis {
|
|||||||
inputs_json: Record<string, number>
|
inputs_json: Record<string, number>
|
||||||
prediction_json: Record<string, number>
|
prediction_json: Record<string, number>
|
||||||
actual_json: Record<string, number>
|
actual_json: Record<string, number>
|
||||||
|
drift_json?: Record<string, any>
|
||||||
graph_json?: { nodes: GraphNode[]; edges: GraphEdge[] }
|
graph_json?: { nodes: GraphNode[]; edges: GraphEdge[] }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,8 +396,12 @@ function EventDetail({
|
|||||||
const [instMsg, setInstMsg] = useState<string | null>(null)
|
const [instMsg, setInstMsg] = useState<string | null>(null)
|
||||||
const [anResult, setAnResult] = useState<{
|
const [anResult, setAnResult] = useState<{
|
||||||
score: number | null
|
score: number | null
|
||||||
preds: Record<string, number>
|
preds: Record<string, number>
|
||||||
actuals: 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)
|
} | null>(null)
|
||||||
const [graphData, setGraphData] = useState<GraphData | null>(null)
|
const [graphData, setGraphData] = useState<GraphData | null>(null)
|
||||||
|
|
||||||
@@ -417,10 +422,19 @@ function EventDetail({
|
|||||||
const latest = data[0]
|
const latest = data[0]
|
||||||
if (latest.template_id) setSelTmpl(latest.template_id)
|
if (latest.template_id) setSelTmpl(latest.template_id)
|
||||||
if (Object.keys(latest.inputs_json || {}).length > 0) setInstInputs(latest.inputs_json)
|
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({
|
setAnResult({
|
||||||
score: latest.activation_score,
|
score: latest.activation_score,
|
||||||
preds: latest.prediction_json || {},
|
preds: latest.prediction_json || {},
|
||||||
actuals: latest.actual_json || {},
|
actuals: latest.actual_json || {},
|
||||||
|
drift: driftByInst,
|
||||||
})
|
})
|
||||||
if (latest.graph_json?.nodes?.length) {
|
if (latest.graph_json?.nodes?.length) {
|
||||||
setGraphData(latest.graph_json as GraphData)
|
setGraphData(latest.graph_json as GraphData)
|
||||||
@@ -535,7 +549,15 @@ function EventDetail({
|
|||||||
})
|
})
|
||||||
const d = await r.json()
|
const d = await r.json()
|
||||||
if (!r.ok) throw new Error(d.detail || r.statusText)
|
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)
|
if (d.graph_json?.nodes?.length) setGraphData(d.graph_json)
|
||||||
await loadAnalyses()
|
await loadAnalyses()
|
||||||
} catch (e: any) { setAnResult({ score: null, preds: {}, actuals: {} }) }
|
} catch (e: any) { setAnResult({ score: null, preds: {}, actuals: {} }) }
|
||||||
@@ -850,6 +872,52 @@ function EventDetail({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</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 */}
|
{/* Inline result — full-width graph + left recap */}
|
||||||
{anResult && graphData?.nodes?.length && (() => {
|
{anResult && graphData?.nodes?.length && (() => {
|
||||||
const inputNodes = graphData.nodes.filter(n => n.type === 'input')
|
const inputNodes = graphData.nodes.filter(n => n.type === 'input')
|
||||||
|
|||||||
Reference in New Issue
Block a user