feat: instrument analysis

This commit is contained in:
OpenSquared
2026-06-29 00:06:14 +02:00
parent 3bcecdab09
commit 5e65424500
3 changed files with 28 additions and 5 deletions

View File

@@ -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

View File

@@ -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({

View File

@@ -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])