feat: Phase 2 — economic event surprise tracker (FRED actuals + z-score)
- economic_events table in DB (series_id, actual, forecast_baseline, surprise_pct, surprise_zscore, direction) - DB helpers: save_economic_event(), get_recent_economic_surprises(), get_economic_events_for_calendar() - fred_fetcher.py: _compute_zscore_surprise() computes 12-period MA as implied consensus + z-score deviation; save_fred_releases_to_db() persists releases per cycle; build_economic_surprise_block() formats significant surprises for AI prompt - auto_cycle.py: saves FRED releases to economic_events each cycle, appends surprise block to fred_block for injection into both suggestion and scoring prompts - data_fetcher.py: get_economic_calendar() now merges static upcoming events with past FRED actuals from DB (Prev/Fcst/Actual/z-score fields populated) - CalendarPage.tsx: past events show colored z-score badge (⚡ for |z|≥1.5, bullish/bearish colors) - EconomicEvent type: added surprise_zscore, surprise_direction, source fields Activates automatically once fred_api_key is set in Configuration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,11 +49,27 @@ function EventCard({ ev }: { ev: EconomicEvent }) {
|
||||
{format(evDate, "EEEE, MMM d yyyy")} · {ev.country}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right shrink-0">
|
||||
<div className="text-right shrink-0 space-y-0.5">
|
||||
<div className={clsx('text-xs font-semibold', cfg.color.split(' ')[0])}>{cfg.label}</div>
|
||||
{ev.previous && <div className="text-xs text-slate-600 mt-0.5">Prev: {ev.previous}</div>}
|
||||
{ev.previous && <div className="text-xs text-slate-600">Prev: {ev.previous}</div>}
|
||||
{ev.forecast && <div className="text-xs text-slate-500">Fcst: {ev.forecast}</div>}
|
||||
{ev.actual && <div className="text-xs text-emerald-400 font-bold">Actual: {ev.actual}</div>}
|
||||
{ev.actual && (
|
||||
<div className="text-xs text-emerald-400 font-bold">Actual: {ev.actual}</div>
|
||||
)}
|
||||
{ev.surprise_zscore !== undefined && Math.abs(ev.surprise_zscore) >= 0.8 && (
|
||||
<div className={clsx(
|
||||
'text-xs font-bold px-1.5 py-0.5 rounded border',
|
||||
ev.surprise_direction === 'bullish'
|
||||
? 'text-emerald-400 border-emerald-700/40 bg-emerald-900/20'
|
||||
: ev.surprise_direction === 'bearish'
|
||||
? 'text-red-400 border-red-700/40 bg-red-900/20'
|
||||
: 'text-slate-400 border-slate-700/40'
|
||||
)}>
|
||||
z={ev.surprise_zscore > 0 ? '+' : ''}{ev.surprise_zscore?.toFixed(1)} {
|
||||
Math.abs(ev.surprise_zscore) >= 1.5 ? '⚡' : ''
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{ev.asset_impact && ev.asset_impact.length > 0 && (
|
||||
|
||||
@@ -124,6 +124,9 @@ export interface EconomicEvent {
|
||||
previous?: string
|
||||
forecast?: string
|
||||
actual?: string
|
||||
surprise_zscore?: number
|
||||
surprise_direction?: 'bullish' | 'bearish' | 'neutral'
|
||||
source?: string
|
||||
}
|
||||
|
||||
export interface HistoricalCandle {
|
||||
|
||||
Reference in New Issue
Block a user