feat: market event

This commit is contained in:
OpenSquared
2026-06-29 21:40:44 +02:00
parent b223f50f8b
commit 4c85d9d23b
2 changed files with 49 additions and 10 deletions

View File

@@ -1014,11 +1014,12 @@ function CausalFrise({
// ── ExplanationScore ──────────────────────────────────────────────────────────
function ExplanationScore({
events, templates, causalInsts,
events, templates, causalInsts, onRefreshDone,
}: {
events: SnapshotEvent[]
templates: CausalTemplate[]
causalInsts: string[]
onRefreshDone?: () => void
}) {
const [refreshing, setRefreshing] = useState(false)
const [refreshDone, setRefreshDone] = useState(false)
@@ -1059,6 +1060,7 @@ function ExplanationScore({
try {
await api.post('/causal-lab/auto-analyze/refresh')
setRefreshDone(true)
onRefreshDone?.()
setTimeout(() => setRefreshDone(false), 3000)
} finally {
setRefreshing(false)
@@ -1137,16 +1139,8 @@ export default function InstrumentDashboard() {
api.get('/causal-lab/templates').then(r => setTemplates(r.data)).catch(() => {})
}, [])
useEffect(() => {
const fetchSnapshot = useCallback(() => {
setLoading(true)
setSnapshot(null)
setNarrative('')
setSelectedDate(null)
setMacroAtDate(null)
setTheoryCurve(null)
setShowTheory(false)
chartDateToXRef.current = null
setChartReady(0)
api.get(`/instruments/${instrumentId}/snapshot?period=${period}`)
.then(r => {
setSnapshot(r.data)
@@ -1157,6 +1151,18 @@ export default function InstrumentDashboard() {
.finally(() => setLoading(false))
}, [instrumentId, period])
useEffect(() => {
setSnapshot(null)
setNarrative('')
setSelectedDate(null)
setMacroAtDate(null)
setTheoryCurve(null)
setShowTheory(false)
chartDateToXRef.current = null
setChartReady(0)
fetchSnapshot()
}, [instrumentId, period])
const loadNarrative = useCallback(() => {
setLoadingNarr(true)
api.post(`/instruments/${instrumentId}/narrative`)
@@ -1525,6 +1531,7 @@ export default function InstrumentDashboard() {
events={snapshot.events}
templates={templates}
causalInsts={causalInsts}
onRefreshDone={fetchSnapshot}
/>
</div>
)

View File

@@ -1000,6 +1000,38 @@ function EventDetail({
{outputNodes.length > 0 && (
<div>
<div className="text-[10px] text-slate-600 uppercase tracking-wide mb-1">Prédictions</div>
{(() => {
const nodeScores = outputNodes
.map(n => {
const pred = anResult.preds[n.id]
const act = anResult.actuals[n.instrument || n.id] ?? anResult.actuals[n.id]
if (pred == null || act == null) return null
if (pred === 0 && act === 0) return 100
if (pred === 0) return 40
const sameDir = pred * act > 0
if (!sameDir) return 0
const ratio = Math.min(Math.abs(act), Math.abs(pred)) / Math.max(Math.abs(act), Math.abs(pred))
return Math.round(50 + ratio * 50)
})
.filter((s): s is number => s != null)
const graphScore = nodeScores.length > 0
? Math.round(nodeScores.reduce((a, b) => a + b, 0) / nodeScores.length)
: null
if (graphScore == null) return null
const barCls = graphScore >= 70 ? 'bg-emerald-500' : graphScore >= 40 ? 'bg-amber-500' : 'bg-red-500'
const txtCls = graphScore >= 70 ? 'text-emerald-400' : graphScore >= 40 ? 'text-amber-400' : 'text-red-400'
return (
<div className="mb-2 pb-2 border-b border-slate-800/60">
<div className="flex items-center justify-between text-[9px] text-slate-600 mb-0.5">
<span>Précision</span>
<span className={txtCls}>{graphScore}%</span>
</div>
<div className="h-1 bg-slate-800 rounded-full overflow-hidden">
<div className={clsx('h-full rounded-full', barCls)} style={{ width: `${graphScore}%` }} />
</div>
</div>
)
})()}
{outputNodes.map(n => {
const pred = anResult.preds[n.id]
const actual = anResult.actuals[n.instrument || n.id] ?? anResult.actuals[n.id]