feat: instrument analysis
This commit is contained in:
@@ -1176,7 +1176,7 @@ def list_analyses(
|
|||||||
|
|
||||||
rows = conn.execute(f"""
|
rows = conn.execute(f"""
|
||||||
SELECT a.*, e.name as event_name, e.category, e.start_date,
|
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
|
FROM causal_event_analyses a
|
||||||
LEFT JOIN market_events e ON e.id = a.market_event_id
|
LEFT JOIN market_events e ON e.id = a.market_event_id
|
||||||
LEFT JOIN causal_graph_templates t ON t.id = a.template_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 "{}")
|
d[f] = json.loads(d[f] or "{}")
|
||||||
except Exception:
|
except Exception:
|
||||||
d[f] = {}
|
d[f] = {}
|
||||||
|
try:
|
||||||
|
d["graph_json"] = json.loads(d.get("template_graph_json") or "{}")
|
||||||
|
except Exception:
|
||||||
|
d["graph_json"] = {}
|
||||||
result.append(d)
|
result.append(d)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ regime detection, trend summary, event filtering, and AI narrative.
|
|||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import logging
|
import logging
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
@@ -12,6 +13,11 @@ from pathlib import Path
|
|||||||
from typing import Dict, Any, List, Optional, Tuple
|
from typing import Dict, Any, List, Optional, Tuple
|
||||||
from datetime import datetime, date
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# ── Config loading ─────────────────────────────────────────────────────────────
|
# ── Config loading ─────────────────────────────────────────────────────────────
|
||||||
@@ -586,8 +592,10 @@ def _get_relevant_events(
|
|||||||
asset_hit = any(ra in ev_assets for ra in related)
|
asset_hit = any(ra in ev_assets for ra in related)
|
||||||
|
|
||||||
# Always include events that have a causal analysis for this instrument
|
# 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 ""
|
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:
|
if keyword_hit or asset_hit or analysis_hit:
|
||||||
filtered.append({
|
filtered.append({
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ interface CausalAnalysis {
|
|||||||
activation_score: number | null
|
activation_score: number | null
|
||||||
analyzed_at: string
|
analyzed_at: string
|
||||||
prediction_json: Record<string, number>
|
prediction_json: Record<string, number>
|
||||||
|
actual_json: Record<string, number>
|
||||||
|
graph_json?: { nodes: GraphNode[]; edges: GraphEdge[] }
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TemplateRef {
|
interface TemplateRef {
|
||||||
@@ -410,9 +412,18 @@ function EventDetail({
|
|||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
const data: CausalAnalysis[] = await r.json()
|
const data: CausalAnalysis[] = await r.json()
|
||||||
setAnalyses(data)
|
setAnalyses(data)
|
||||||
// Pre-select the most recent analysis's template so the form isn't blank on re-open
|
if (data.length > 0) {
|
||||||
if (data.length > 0 && data[0].template_id) {
|
const latest = data[0]
|
||||||
setSelTmpl(data[0].template_id)
|
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])
|
}, [event.id])
|
||||||
|
|||||||
Reference in New Issue
Block a user