feat: InstrumentDashboard — event timeline strip + crosshair date-aware cards
- InstrumentChart: onDateHover callback via subscribeCrosshairMove (useRef pattern) - EventTimelineStrip: 3 rows LT/MT/CT with CSS-% bars aligned to chart X axis - Cards date-aware: crosshair drives selectedDate; dateTrend + dateSignals computed client-side from lookup maps (priceMap/indMap/sortedDates) without extra API calls - TrendCard: price, RSI, ATR, slopes, momentum, 52W range all at selected date - RegimeCard: 5 signals recomputed at selected date; regime label from server - Date badge above cards; blue tint when browsing history, grey on last date - instrument_service.py: end_date in events; price_data built before events block Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -505,11 +505,19 @@ def _get_relevant_events(
|
||||
asset_hit = any(ra in ev_assets for ra in related)
|
||||
|
||||
if keyword_hit or asset_hit:
|
||||
filtered.append(ev)
|
||||
filtered.append({
|
||||
"date": ev_start,
|
||||
"end_date": ev.get("end_date") or None,
|
||||
"title": ev.get("name") or ev.get("event_name") or "",
|
||||
"level": ev.get("level", "medium"),
|
||||
"category": ev.get("category", ""),
|
||||
"description": (ev.get("description") or "")[:120],
|
||||
"impact_score": float(ev.get("impact_score") or 0.5),
|
||||
})
|
||||
|
||||
# Sort by start_date desc, cap at 15
|
||||
filtered.sort(key=lambda e: str(e.get("start_date", "") or ""), reverse=True)
|
||||
return filtered[:15]
|
||||
# Sort by start_date asc for timeline display, cap at 30
|
||||
filtered.sort(key=lambda e: str(e.get("date", "") or ""))
|
||||
return filtered[:30]
|
||||
|
||||
|
||||
# ── Main snapshot ──────────────────────────────────────────────────────────────
|
||||
@@ -557,15 +565,6 @@ async def get_snapshot(
|
||||
change_abs = _round(last - prev)
|
||||
change_pct = _round((last - prev) / abs(prev) * 100, 3)
|
||||
|
||||
# Events (last 1 year)
|
||||
try:
|
||||
from_date = (pd.Timestamp.now() - pd.Timedelta(days=365)).strftime("%Y-%m-%d")
|
||||
to_date = pd.Timestamp.now().strftime("%Y-%m-%d")
|
||||
events = _get_relevant_events(config, from_date=from_date, to_date=to_date)
|
||||
except Exception as e:
|
||||
logger.warning(f"[instrument_service] Event filtering error: {e}")
|
||||
events = []
|
||||
|
||||
# Price data for chart (time + ohlcv)
|
||||
price_data = []
|
||||
if records:
|
||||
@@ -579,6 +578,15 @@ async def get_snapshot(
|
||||
"volume": r.get("volume", 0),
|
||||
})
|
||||
|
||||
# Events spanning the chart period (built after price_data so dates are known)
|
||||
try:
|
||||
chart_start = price_data[0]["time"] if price_data else None
|
||||
chart_end = price_data[-1]["time"] if price_data else None
|
||||
events = _get_relevant_events(config, from_date=chart_start, to_date=chart_end)
|
||||
except Exception as e:
|
||||
logger.warning(f"[instrument_service] Event filtering error: {e}")
|
||||
events = []
|
||||
|
||||
return {
|
||||
"instrument": config,
|
||||
"price_data": price_data,
|
||||
|
||||
Reference in New Issue
Block a user