diff --git a/backend/services/instrument_service.py b/backend/services/instrument_service.py index d48b57d..a85a751 100644 --- a/backend/services/instrument_service.py +++ b/backend/services/instrument_service.py @@ -544,10 +544,11 @@ def _get_relevant_events( (SELECT GROUP_CONCAT(DISTINCT a.instrument) FROM causal_event_analyses a WHERE a.market_event_id = e.id) AS analyzed_instruments, - (SELECT a.template_id FROM causal_event_analyses a WHERE a.market_event_id = e.id ORDER BY a.id DESC LIMIT 1) AS cea_template_id, - (SELECT a.id FROM causal_event_analyses a WHERE a.market_event_id = e.id ORDER BY a.id DESC LIMIT 1) AS analysis_id, - (SELECT a.prediction_json FROM causal_event_analyses a WHERE a.market_event_id = e.id ORDER BY a.id DESC LIMIT 1) AS cea_prediction_json, - (SELECT a.actual_json FROM causal_event_analyses a WHERE a.market_event_id = e.id ORDER BY a.id DESC LIMIT 1) AS cea_actual_json + (SELECT a.template_id FROM causal_event_analyses a WHERE a.market_event_id = e.id ORDER BY a.id DESC LIMIT 1) AS cea_template_id, + (SELECT a.id FROM causal_event_analyses a WHERE a.market_event_id = e.id ORDER BY a.id DESC LIMIT 1) AS analysis_id, + (SELECT a.prediction_json FROM causal_event_analyses a WHERE a.market_event_id = e.id ORDER BY a.id DESC LIMIT 1) AS cea_prediction_json, + (SELECT a.actual_json FROM causal_event_analyses a WHERE a.market_event_id = e.id ORDER BY a.id DESC LIMIT 1) AS cea_actual_json, + (SELECT a.activation_score FROM causal_event_analyses a WHERE a.market_event_id = e.id ORDER BY a.id DESC LIMIT 1) AS cea_activation_score FROM market_events e ORDER BY e.start_date DESC """).fetchall() @@ -556,8 +557,9 @@ def _get_relevant_events( # any collision with an e.* column named template_id all_events = [ {**dict(r), "template_id": r["cea_template_id"], - "prediction_json": r["cea_prediction_json"], - "actual_json": r["cea_actual_json"]} + "prediction_json": r["cea_prediction_json"], + "actual_json": r["cea_actual_json"], + "activation_score": r["cea_activation_score"]} for r in rows ] except Exception as e: @@ -626,6 +628,7 @@ def _get_relevant_events( "absorption_pct": ev.get("absorption_pct"), "prediction_json": ev.get("prediction_json") or None, "actual_json": ev.get("actual_json") or None, + "activation_score": ev.get("activation_score"), }) # Sort by start_date asc for timeline display, cap at 30 diff --git a/frontend/src/pages/InstrumentDashboard.tsx b/frontend/src/pages/InstrumentDashboard.tsx index 1e78f62..49764c2 100644 --- a/frontend/src/pages/InstrumentDashboard.tsx +++ b/frontend/src/pages/InstrumentDashboard.tsx @@ -42,8 +42,9 @@ interface SnapshotEvent { category: string; sub_type?: string; description: string; impact_score: number expected_value?: string | null; actual_value?: string | null surprise_pct?: number | null; unit?: string | null; absorption_pct?: number | null - prediction_json?: string | null // JSON: {node_id: pips} from causal_event_analyses - actual_json?: string | null // JSON: {instrument: pips} from causal_event_analyses + prediction_json?: string | null // JSON: {node_id: pips} from causal_event_analyses + actual_json?: string | null // JSON: {instrument: pips} from causal_event_analyses + activation_score?: number | null // stored directional accuracy from causal_event_analyses } interface RegimeSignals { @@ -704,6 +705,9 @@ function templateAbsorptionDays(tmpl: CausalTemplate): number { /** Score 0-100 measuring how well the graph predicted actual pips for an instrument. */ function comprehensionScore(ev: SnapshotEvent, tmpl: CausalTemplate, instrument: string): number | null { + // Prefer the stored activation_score from causal_event_analyses (consistent with MarketEvents panel) + if (ev.activation_score != null) return Math.round(ev.activation_score * 100) + if (!ev.prediction_json || !ev.actual_json) return null try { const preds: Record = JSON.parse(ev.prediction_json) @@ -711,7 +715,6 @@ function comprehensionScore(ev: SnapshotEvent, tmpl: CausalTemplate, instrument: const actual = actuals[instrument] ?? actuals[instrument.toUpperCase()] ?? actuals[instrument.toLowerCase()] if (actual == null) return null - // Find the output node for this instrument (built-in templates use "output", auto-generated use "market_asset") const outputNode = tmpl.graph_json.nodes.find(n => (n.type === 'market_asset' || n.type === 'output') && n.instrument?.toUpperCase() === instrument.toUpperCase() )