feat: instrument analysis
This commit is contained in:
@@ -1176,7 +1176,7 @@ def list_analyses(
|
||||
|
||||
rows = conn.execute(f"""
|
||||
SELECT a.*, e.name as event_name, e.category, e.start_date,
|
||||
t.name as template_name
|
||||
t.name as template_name, t.graph_json as template_graph_json
|
||||
FROM causal_event_analyses a
|
||||
LEFT JOIN market_events e ON e.id = a.market_event_id
|
||||
LEFT JOIN causal_graph_templates t ON t.id = a.template_id
|
||||
@@ -1194,6 +1194,10 @@ def list_analyses(
|
||||
d[f] = json.loads(d[f] or "{}")
|
||||
except Exception:
|
||||
d[f] = {}
|
||||
try:
|
||||
d["graph_json"] = json.loads(d.get("template_graph_json") or "{}")
|
||||
except Exception:
|
||||
d["graph_json"] = {}
|
||||
result.append(d)
|
||||
return result
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ regime detection, trend summary, event filtering, and AI narrative.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
@@ -12,6 +13,11 @@ from pathlib import Path
|
||||
from typing import Dict, Any, List, Optional, Tuple
|
||||
from datetime import datetime, date
|
||||
|
||||
|
||||
def _base_ticker(t: str) -> str:
|
||||
"""Normalize Yahoo Finance tickers for comparison: EURUSD=X → EURUSD, BZ=F → BZ, ^GSPC → GSPC."""
|
||||
return re.sub(r'(=X|=F|=RR|-USD|\^)$', '', t.strip().upper())
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ── Config loading ─────────────────────────────────────────────────────────────
|
||||
@@ -586,8 +592,10 @@ def _get_relevant_events(
|
||||
asset_hit = any(ra in ev_assets for ra in related)
|
||||
|
||||
# Always include events that have a causal analysis for this instrument
|
||||
# Normalize tickers: strip Yahoo Finance suffixes (=X, =F, ^, -USD, =RR) for comparison
|
||||
analyzed = ev.get("analyzed_instruments") or ""
|
||||
analysis_hit = inst_upper and inst_upper in [i.strip().upper() for i in analyzed.split(",") if i.strip()]
|
||||
analyzed_bases = [_base_ticker(s) for s in analyzed.split(",") if s.strip()]
|
||||
analysis_hit = bool(inst_upper and _base_ticker(inst_upper) in analyzed_bases)
|
||||
|
||||
if keyword_hit or asset_hit or analysis_hit:
|
||||
filtered.append({
|
||||
|
||||
@@ -60,6 +60,8 @@ interface CausalAnalysis {
|
||||
activation_score: number | null
|
||||
analyzed_at: string
|
||||
prediction_json: Record<string, number>
|
||||
actual_json: Record<string, number>
|
||||
graph_json?: { nodes: GraphNode[]; edges: GraphEdge[] }
|
||||
}
|
||||
|
||||
interface TemplateRef {
|
||||
@@ -410,9 +412,18 @@ function EventDetail({
|
||||
if (r.ok) {
|
||||
const data: CausalAnalysis[] = await r.json()
|
||||
setAnalyses(data)
|
||||
// Pre-select the most recent analysis's template so the form isn't blank on re-open
|
||||
if (data.length > 0 && data[0].template_id) {
|
||||
setSelTmpl(data[0].template_id)
|
||||
if (data.length > 0) {
|
||||
const latest = data[0]
|
||||
if (latest.template_id) setSelTmpl(latest.template_id)
|
||||
// Reconstruct graph state from stored analysis so it's always visible on reopen
|
||||
setAnResult({
|
||||
score: latest.activation_score,
|
||||
preds: latest.prediction_json || {},
|
||||
actuals: latest.actual_json || {},
|
||||
})
|
||||
if (latest.graph_json?.nodes?.length) {
|
||||
setGraphData(latest.graph_json as GraphData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [event.id])
|
||||
|
||||
Reference in New Issue
Block a user