feat: instrument analysis
This commit is contained in:
@@ -762,17 +762,49 @@ function CausalFrise({
|
|||||||
} | null>(null)
|
} | null>(null)
|
||||||
const [popupScore, setPopupScore] = useState<number | null | 'loading'>('loading')
|
const [popupScore, setPopupScore] = useState<number | null | 'loading'>('loading')
|
||||||
|
|
||||||
// Fetch activation_score live when popup opens for a specific event
|
// Fetch analysis live when popup opens — compute same precision as MarketEvents expanded view
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const evId = activeChip?.ev.id
|
const evId = activeChip?.ev.id
|
||||||
if (!evId) { setPopupScore(null); return }
|
if (!evId) { setPopupScore(null); return }
|
||||||
setPopupScore('loading')
|
setPopupScore('loading')
|
||||||
api.get(`/causal-lab/analyses?market_event_id=${evId}&limit=1`)
|
api.get(`/causal-lab/analyses?market_event_id=${evId}&limit=1`)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
const score = r.data?.[0]?.activation_score
|
const analysis = r.data?.[0]
|
||||||
setPopupScore(score != null ? Math.round(score * 100) : null)
|
console.log('[CausalFrise] analysis for event', evId, analysis)
|
||||||
|
if (!analysis) { setPopupScore(null); return }
|
||||||
|
|
||||||
|
// Compute magnitude-weighted precision (same formula as MarketEvents expanded panel)
|
||||||
|
const preds: Record<string, number> = analysis.prediction_json || {}
|
||||||
|
const actuals: Record<string, number> = analysis.actual_json || {}
|
||||||
|
const graph = analysis.graph_json || {}
|
||||||
|
const outputNodes: { id: string; instrument?: string }[] = (graph.nodes || [])
|
||||||
|
.filter((n: { type?: string }) => n.type === 'market_asset' || n.type === 'output')
|
||||||
|
|
||||||
|
console.log('[CausalFrise] preds:', preds, 'actuals:', actuals, 'outputNodes:', outputNodes)
|
||||||
|
|
||||||
|
const nodeScores = outputNodes.map((n: { id: string; instrument?: string }) => {
|
||||||
|
const pred = preds[n.id]
|
||||||
|
const act = actuals[n.instrument || n.id] ?? 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)
|
||||||
|
|
||||||
|
console.log('[CausalFrise] nodeScores:', nodeScores)
|
||||||
|
|
||||||
|
if (nodeScores.length > 0) {
|
||||||
|
setPopupScore(Math.round(nodeScores.reduce((a, b) => a + b, 0) / nodeScores.length))
|
||||||
|
} else {
|
||||||
|
// Fallback: use stored activation_score if no prediction data
|
||||||
|
const stored = analysis.activation_score
|
||||||
|
setPopupScore(stored != null ? Math.round(stored * 100) : null)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(() => setPopupScore(null))
|
.catch(err => { console.error('[CausalFrise] fetch error:', err); setPopupScore(null) })
|
||||||
}, [activeChip?.ev.id])
|
}, [activeChip?.ev.id])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user