Commit Graph

167 Commits

Author SHA1 Message Date
OpenSquared
a69ced8fff chore: ignore forex_factory_cache.csv (already imported in DB) 2026-06-26 17:08:15 +02:00
OpenSquared
808bd2b564 fix: FF auto-import always runs if CSV present, deletes CSV after success
- Remove count>0 guard — import runs regardless of existing rows (idempotent upsert)
- After successful import, CSV is deleted from /app so next restart skips cleanly
- No more need to manually trigger import or remove CSV from git separately

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 17:05:01 +02:00
OpenSquared
600b86e75f Calendar 2026-06-26 17:00:41 +02:00
OpenSquared
7aca0c307a feat: FF CSV bundled in image + auto-import on startup
- forex_factory_cache.csv moved to backend/ (Docker build context)
  → available at /app/forex_factory_cache.csv inside container
- main.py startup: auto-imports CSV in background thread if ff_calendar empty
  (idempotent — skips if rows already present)
- CSV path candidates: /app/ (Docker) → /tmp/ (upload) → local dev paths
- CalendarPage: remove Upload CSV + Import into DB buttons
  → replaced by auto-import status indicator + Sync Live only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 16:58:31 +02:00
OpenSquared
afbfeff468 feat: FF→FRED series mapping + MacroSeries visualization page
Backend:
- ff_calendar: add series_id column (migration) + FF_TO_FRED mapping dict
  (NFP→PAYEMS, CPI→CPIAUCSL, Jobless Claims→ICSA, GDP→GDPC1, FEDFUNDS, PCE)
- import_csv + sync_live now populate series_id on each FF event
- New GET /api/eco/series/{id}/history: FRED time series + linked FF events
  (surprises, forecast, actual) merged by date — enables context queries

Frontend:
- New MacroSeriesPage.tsx: sidebar with 11 FRED series grouped by category,
  recharts ComposedChart with area + z-score surprise reference lines (|z|≥1.5),
  KPI cards (latest/prev/min/max), FF events table (actual vs forecast coloring),
  z-score bar chart for recent surprises, range selector (1Y/2Y/5Y/10Y/All)
- Route /macro-series + Sidebar entry "Macro Series"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 16:51:55 +02:00
OpenSquared
b187107468 fix: FF calendar upload endpoint + path resolution for server deployment
- Add POST /api/eco/ff-upload to receive CSV from browser (saves to /tmp)
- _find_csv() checks /tmp, /app, project root — works local + Docker
- _run_ff_import now takes explicit csv_path parameter
- Frontend: Upload CSV button (file input) → uploads to server first,
  then Import into DB — with upload progress messages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 16:36:31 +02:00
OpenSquared
4053e0669d feat: Forex Factory calendar — unified past+future list style Trading Economics
- New ff_calendar table (event_date, time, currency, impact, actual, forecast, previous)
- New service ff_calendar.py: bulk CSV import (83K events 2007-2025) + live sync
  from faireconomy.media JSON endpoint (this week / next week)
- New API endpoints: POST /api/eco/ff-import, POST /api/eco/ff-sync,
  GET /api/eco/calendar (period filter), GET /api/eco/ff-stats
- CalendarPage.tsx full rewrite: period tabs (Recent/Today/Tomorrow/This Week…),
  currency flags filter, impact filter, unified date-grouped table with
  Time·Flag·Currency·Impact·Event·Actual·Forecast·Previous columns,
  green/red actual vs forecast, TODAY badge, auto-refresh 60s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 16:32:26 +02:00
OpenSquared
915560bc31 feat: upcoming economic releases panel on Calendar page
- eco.py: GET /api/eco/upcoming — estimates next release date per series
  from last stored date + frequency + typical publication lag; returns
  status (imminent/due_soon/upcoming/scheduled/overdue/no_data)
- CalendarPage.tsx: UpcomingPanel component in right sidebar showing
  next expected dates, J-N countdown, last value + direction signal;
  color-coded by urgency (orange=cette semaine, yellow=attendu, blue=ce mois)

Dates are approximate (freq + typical lag), not official FRED schedule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 16:01:26 +02:00
OpenSquared
b058b7297a fix: use FRED official JSON API (bypasses CloudFlare) + API key UI
- fred_bootstrap.py: switch from CSV graph endpoint (CloudFlare-blocked)
  to api.stlouisfed.org/fred/series/observations JSON API; reads key
  from DB config 'fred_api_key'; returns clear error if key missing
- eco.py: add GET/POST /api/eco/fred-key to check/save the FRED API key
- CalendarPage.tsx: BootstrapPanel shows API key section with status,
  input to paste key + save button, disables Launch if key missing

Free key: fred.stlouisfed.org -> My Account -> API Keys

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 15:47:16 +02:00
OpenSquared
202a8ce97c fix: FRED fetch — add browser User-Agent + print diagnostics to stdout
FRED blocks requests without a valid User-Agent. Added headers matching
a real browser. Also added print() calls so fetch errors appear in
docker logs (logger.warning goes to system_logs table, not stdout).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 15:33:06 +02:00
OpenSquared
d25ca92694 fix: FRED data quality — GDP growth %, CPI/PCE YoY%, ICSA in K
- GDPC1 replaces A191RL1Q225SBEA: compute annualized QoQ growth from
  GDP level ((val/prev)^4 - 1)*100 → displays proper ~2-3% not 31 819
- CPIAUCSL/CPILFESL/PCEPILFE: yoy_pct transform (val/val_12m_ago-1)*100
  → displays 3.x% YoY inflation, not raw index level 334
- ICSA: div1000 transform → displays 226 K claims, not 226 000 K
- delta_absolute flag: pp change for rate/% series, % change for levels
- SurprisePct component: shows 'pp' suffix for %, '%' for K/levels
- Column header renamed from 'Δ%' to 'Δ vs préc.' with tooltip
- Deprecated A191RL1Q225SBEA rows cleaned from DB on next bootstrap
- Warm-up periods: 2yr for yoy_pct, 3yr for qoq_annualized, 1yr others

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 15:16:44 +02:00
OpenSquared
2469b76769 fix: CalendarPage API calls use relative URLs (not localhost:8000)
All fetch() calls now use relative paths (/api/eco/...) so nginx
can proxy them to the backend container in production.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 15:00:56 +02:00
OpenSquared
77b3a27495 fix: TS2322 in CalendarPage — typed bootstrap result vars outside JSX
Record<string, T> index type prevents 'error' in narrowing, causing
string | T to leak into ReactNode positions. Fix: precompute
bootstrapError (string|null) and bootstrapEntries ([string, T][])
before JSX so TypeScript can resolve types correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 14:50:07 +02:00
OpenSquared
64ff777da6 feat: FRED bootstrap + Calendar page complete rebuild
- backend/services/fred_bootstrap.py: fetch 11 FRED series (PAYEMS, UNRATE, CPI, PCE, FEDFUNDS, ICSA, GDP, HY spread, T10Y2Y, T10Y3M) from public CSV endpoint — no API key needed; computes rolling z-scores and upserts into economic_events table
- backend/routers/eco.py: new /api/eco router with bootstrap (POST + status GET), events list with full filtering (date range, category, series, min z-score, direction, sort/pagination), series catalog, and db status endpoints
- backend/main.py: register eco router
- frontend/src/pages/CalendarPage.tsx: complete rewrite — real data table from /api/eco/events, Bootstrap FRED button with live polling, filter bar (date range, category, series chips, |z| threshold, direction), sort by date/z-score/series, pagination, z-score badges with color coding, sidebar with series inventory + geo alerts + z-score guide

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 14:42:44 +02:00
OpenSquared
325d52b3b9 feat: market events db migration + generation date filter
Database migration:
- Add 'origin' and 'source_refs' columns to market_events ALTER TABLE migration
  (sub_type/actual_value/expected_value/surprise_pct were already there)
- All new tables (macro_gauge_snapshots, ai_desks) created via CREATE TABLE IF NOT EXISTS
  on next init_db() call (container restart)

Backend:
- GET /api/market-events/db-status — health check returning row counts,
  latest dates, and missing columns for all 6 tables needed by the detector
- list_events() now accepts gen_date_from / gen_date_to query params
  filtering by date(created_at) — separate from start_date event date filters

Frontend (MarketEvents.tsx):
- MarketEvent interface: add created_at field
- EventRow: show generation date as MM-DD next to event date
- Extended filters: new ' Date de génération' section with from/to inputs
  filtered independently from the event date range
- Clear-all button includes genFrom/genTo reset

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 14:23:09 +02:00
OpenSquared
d0816fd9ff feat: Sentiment desk — gauge threshold alerts (Option B)
Each macro gauge selected in the Sentiment desk can now be independently
configured with three alert criteria:
- Seuil bas (≤): alert when value crosses below
- Seuil haut (≥): alert when value crosses above
- Variation % (Δ%): alert when N-day % change exceeds threshold

Frontend: GaugeThresholdConfig component — one row per selected gauge,
compact grid layout with enable toggle + 3 numeric inputs.
Stored in config.gauge_thresholds[gauge_id].

Backend: _check_sentiment() extended — after CBOE signals, reads
macro_gauge_snapshots history, checks each enabled gauge threshold,
emits sentiment market_events with options_note for each breach.
Gauge → affected_assets mapping covers all 32 gauge keys.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 13:59:40 +02:00
OpenSquared
27ee4410cf refactor: separate Sentiment and Technical desks — no signal overlap
- Mark 6 price signals (ma_cross, rsi_extreme, bb_squeeze, new_52w_extreme, price_gap, volume_spike) as desk_type=technical so they no longer appear in the Sentiment desk
- Sentiment desk now shows MacroGaugeSelector (32 gauges grouped by bloc: Liquidité, Crédit, Volatilité, Métaux…) instead of the stock/ETF instrument picker
- Sentiment desk instruments seed updated to macro gauge keys (vix, vvix, skew, hyg, dxy, slope_10y3m, gold_copper_ratio)
- Signal init useEffect extended to cover sentiment desk as well as technical

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 13:48:14 +02:00
OpenSquared
85eb864584 feat: macro gauge DB + regime triggers + date-aware instrument snapshot
DB:
- New table macro_gauge_snapshots (daily snapshot of all 28+ gauges + dominant + scores)
- save_macro_gauge_snapshot / get_macro_gauge_snapshot_at / get_macro_gauge_history
- Auto-save once per calendar day on every macro-regime fetch (not just force=True)

API:
- GET /api/market/macro-gauges/at?date=YYYY-MM-DD — nearest snapshot ≤ date
- GET /api/market/macro-gauges/history?days=N

Detector (_check_macro_gauges in Eco Desk):
- Regime transition events (goldilocks→stagflation etc.) with severity scoring
- Yield curve inversion / désinversion (slope_10y3m sign change)
- DXY shock (% change over lookback window)
- Credit stress (HYG drop threshold)
- Gold/Copper ratio regime crossings

InstrumentDashboard:
- macroAtDate state: fetches /api/market/macro-gauges/at when crosshair date ≠ last date
- RegimeCard uses historical macro regime when on a past date
- MacroGaugePanel: full breakdown of all gauges by bloc (liquidité, crédit, énergie...)
  visible only when on a historical date — shows value + change_pct + regime scores bar

AIDesks: added fundamental + sentiment to AIDesk type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 09:34:35 +02:00
OpenSquared
a12d7a1ef3 feat: 6 AI desks complets — fundamental, report, sentiment (options triggers)
- Fundamental Desk: filtre corporate news (layoffs, M&A, earnings, credit)
  avec prompt dédié + dedup sémantique
- Report Desk: wiring desk config (days, min_importance, system_prompt)
- Sentiment Desk: 5 signaux VIX/SKEW pour options lab
  (vix_level thresholds, vix_spike %, vix_term_structure, vvix_extreme, skew_extreme)
  Chaque event contient options_note actionnable (vente puts, straddles, calendar spreads)
- check_new_market_events() couvre les 6 sources, charge desk configs dynamiquement
- Signal catalog: 12 signaux (7 technical + 5 sentiment), filtrés par desk_type dans UI
- AIDesks.tsx: FundamentalConfig + ReportConfig + SignalToggle filtré par desk type
- 3 nouveaux desks seedés dans init_db (idempotent)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 09:21:29 +02:00
OpenSquared
97706dea7b feat: AI Desks — configurable agent system for news/technical/eco processing
- New ai_desks table with CRUD (get_all/by_type/upsert/delete)
- ai_desks router: REST API + GET /signal-catalog (7 extensible signals)
- News Desk: semantic dedup via AI (±N days window, system_prompt hint)
- Technical Desk: 4 signal detectors driven by desk config
  (ma_cross, rsi_extreme, bb_squeeze, new_52w_extreme)
- 3 more signals in catalog ready to enable: price_gap, volume_spike, macd_crossover
- market_event_detector.py loads desk configs at runtime, falls back to legacy params
- AIDesks.tsx: full editor UI with signal toggles, param sliders, instrument multi-select
- Sidebar: Bot icon + /ai-desks route

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 23:08:14 +02:00
OpenSquared
069b398d75 feat: apply category defaults first then AI adjusts + remove chips
Backend: merge strategy — category defaults are applied as baseline
(ai_generated=0), AI impacts override/extend them (ai_generated=1).
All N category defaults appear in the impacts list; AI covers them
explicitly with event-specific score/direction adjustments.
Prompt: instruct AI to cover ALL default instruments.

Frontend: remove default impact chips under category selector —
they now appear directly in the instrument impacts list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 22:23:38 +02:00
OpenSquared
06a27e4492 fix: add sub_type to update_market_event SQL — was silently dropped
The UPDATE statement listed all columns except sub_type, so the
AI-matched category auto-set was written to the object but never
persisted to the DB. The category dropdown stayed blank after evaluate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 22:18:09 +02:00
OpenSquared
4071193385 fix: AI returns type instead of category name — clearer prompt + guard
The AI was returning 'geopolitical' (the event type/family) instead of
a category name like 'Guerre — Moyen-Orient'. Two fixes:
1. Prompt: rename 'Type' → 'Famille', list categories as CAT_N: "name",
   explicit warning not to return a generic type, show valid name examples
2. Code: reject any response that matches a known type string before matching

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 22:14:21 +02:00
OpenSquared
91a448e8c9 fix: robust category matching — normalize dashes/accents + partial match
The AI often returns 'Guerre - Moyen-Orient' (ASCII dash) instead of
'Guerre — Moyen-Orient' (em-dash), causing exact match to fail silently.
Fix: normalize unicode, collapse all dash variants, then try
exact → normalized → partial substring matching.
Also log the raw AI response for debugging, increase max_tokens 1200→2000.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 22:07:56 +02:00
OpenSquared
7eb8c24719 feat: AI category matching — pass all categories to GPT for best-fit selection
Previously: heuristic text matching (sub_type in category.name) — failed
for semantic cases like 'Iran Ceasefire Talks' → 'Guerre — Moyen-Orient'.

Now: all EventCategories are sent in the prompt (name + type + description).
The AI picks matched_category by semantic understanding, returns it in JSON.
- Validates returned name against known categories (case-insensitive)
- Auto-sets event sub_type when AI finds a match and sub_type was empty
- Returns matched_category in evaluate response + shown in UI eval message

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 22:02:47 +02:00
OpenSquared
bf938067a1 fix: cat_name not defined in impact_service evaluate_event_impacts
_find_cat() returned only default_impacts, never the category name.
Now returns (name, defaults) tuple; cat_name → matched_cat_name.
Fixes: 'name cat_name is not defined' on AI evaluate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:55:43 +02:00
OpenSquared
acab69b65c fix: always GROUP BY me.id in market_events list query
The LEFT JOIN on instrument_impacts (added for the evaluated subquery)
generates N rows per event when the event has N impacts. GROUP BY was
only applied when the instrument filter was active — now it's always on.
Fixes: total=2 but list shows 6+ duplicates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:53:14 +02:00
OpenSquared
332d5ad743 fix: evaluated field on event list + abort race condition
- Backend: add evaluated subquery column to list SELECT so each event
  returns evaluated=1/0 (was missing, causing all events to appear
  as unevaluated regardless of filter)
- Frontend: AbortController cancels the previous in-flight fetch when
  a new load fires, preventing stale results from overwriting current
  filter state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:36:12 +02:00
OpenSquared
984e6860ad feat: event category selector in detail panel
- Always-visible 'Catégorie IA' block in EventDetail (view + edit mode)
- Dropdown lists all EventCategories; selecting one instantly saves sub_type
  and updates category type via PUT (no need to enter full edit mode)
- Shows matched category description + default impacts preview (ticker chips
  with direction arrows and sensitivity %)
- Edit mode: category dropdown also auto-sets the category type field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:32:02 +02:00
OpenSquared
f71e3be3ff feat: market events filters, no-auto-bootstrap, star label deoverlap
- MarketEvents: date presets (7j/30j/3m/6m/1an/Tout/Perso), instrument
  impact filter (ticker + min score + direction → auto-sort by inst score),
  sort controls (date/score/nom + asc/desc), clear-all button, count bar
- Market events list endpoint: full SQL JOIN rewrite supporting instrument
  filter, date range, origin, sort_by=instrument_score
- Disable auto-bootstrap on startup (macro/eco/categories) — manual only
- CycleActions: bootstrap group (Macro/Eco/Categories) with force checkbox,
  grouped layout (detection / bootstrap / data / ai / portfolio)
- cycle_actions router: /bootstrap-macro, /bootstrap-eco, /bootstrap-categories
  endpoints + group field on all action catalogue entries
- InstrumentChart: greedy row placement for star labels (4 rows × 20px)
  to eliminate horizontal overlap; labels now inline (★ + text)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:27:21 +02:00
OpenSquared
0b1fcff49c feat: Market Events — catégories, cleanup sidebar, rename
- Supprime Timeline & ImpactMonitor du routing (redirects /impact + /timeline → /market-events)
- Renomme 'Instrument Snap.' → 'Instrument Analysis' dans la sidebar
- Onglet 'Catégories & Defaults' dans Market Events: CRUD complet (créer/éditer/supprimer)
  chaque catégorie a une table d'impacts par défaut (instrument, sensibilité, direction, notes)
- impact_service: meilleur matching catégorie (sub_type fuzzy + type fallback)
- DB: delete_event_category() + DELETE /api/impact/categories/{name}

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:11:24 +02:00
OpenSquared
bd28b6a73a feat: origin tracing on all market_events
- DB: colonne origin (migration + UPDATE heuristique sur données legacy)
- save/update_market_event: persist origin
- Tous les points de création taguent leur origine:
    bootstrap_macro/eco/ma/legacy | detector_news/eco/technical/report | manual
- UI MarketEvents: badge d'origine avec icône + description dans le panneau détail,
  icône tooltip dans la liste gauche, message explicite si pas de source_refs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 20:58:05 +02:00
OpenSquared
d5da4737ef feat: Market Events admin page — source_refs + per-instrument impacts
- DB: colonne source_refs sur market_events (migration idempotente)
- save/update_market_event: persist source_refs (JSON array de {url,title,source})
- market_event_detector: stocke source_refs + évalue impacts instruments après chaque création
- Nouveau router /api/market-events: CRUD + evaluate + impacts CRUD
- Page MarketEvents.tsx: liste filtrée/triée + panneau détail (sources cliquables,
  tableau impacts par instrument avec score/direction/rationale inline-éditables,
  ajout manuel, bulk-evaluate)
- Sidebar: entrée Market Events (Radio icon)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 18:29:09 +02:00
OpenSquared
e66d3ddb46 fix: use relative API URL in CycleActions (nginx proxy compat)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 18:12:01 +02:00
OpenSquared
9afc01c7f5 feat: isolated cycle action — Check New Market Events
Décompose le cycle en 8 actions appelables individuellement.
Action 1 implémentée : scan de 4 sources (news RSS, surprises FRED,
MA crossovers yfinance, rapports institutionnels) → création de
market_events avec déduplication. UI CycleActions page + sidebar link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 18:07:38 +02:00
OpenSquared
50a4a55b9e fix: stars higher above bars + bigger label (11px bold, 130px offset, capped top 30px) 2026-06-25 17:30:15 +02:00
OpenSquared
1d433b4a7a feat: replace arrowDown markers with ★ stars overlay on chart, colored by category
- InstrumentChart: remove setMarkers arrowDown (level-colored)
  Add DOM overlay div with ★ HTML characters positioned via timeToCoordinate +
  priceToCoordinate(candle.high) - 52px; sized by impact_score; colored by category
  Label (title, 22 chars) rendered above each star; redrawn on scroll/zoom via
  subscribeVisibleLogicalRangeChange; cleaned up on unmount
  ChartEvent interface: add category, impact_score, end_date, description
  Header legend: replace ▼ LT/MT/CT with ★ category color legend
- InstrumentDashboard: remove EventStarsStrip (now internal to chart)
  Remove unused dateToMs helper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 17:24:33 +02:00
OpenSquared
51a5531454 refactor: align market_events categories with driver types + event stars on chart
- database.py: idempotent migration — calendar→event_calendar, geopolitique→geopolitical,
  macro events classified by name pattern (FOMC/CPI/BOJ/OPEC+→event_calendar,
  NVIDIA→report, rest→fundamental)
- eco_calendar_bootstrap.py: calendar→event_calendar (75 events)
- macro_events_bootstrap.py: all 30 events now carry aligned category + sub_type
  (FOMC×9, CPI×3, BOJ×3, OPEC+×2 → event_calendar ; war/geo×6 → geopolitical ;
   ChatGPT/BTC/PBOC → fundamental ; NVIDIA×2 → report)
- InstrumentDashboard: replace EventTimelineStrip (horizontal bars below chart)
  with EventStarsStrip (★ icons above chart, sized by impact_score, colored by category)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 17:01:07 +02:00
OpenSquared
a68896cf67 feat: Impact Monitor — AI impact evaluation for eco events & geopolitical news
- New DB tables: event_categories (18 bootstrap categories) + instrument_impacts
- impact_categories_bootstrap.py: FOMC/NFP/CPI/GDP/PCE/ISM/BOJ/ECB/BOE/OPEC+ + 7 géopolitical categories with per-instrument sensitivity & direction defaults
- impact_service.py: GPT-4o-mini evaluation (0-1 score, direction, rationale) + monitor data aggregation
- routers/impact.py: GET/POST endpoints for evaluate/bulk/monitor/adjust/categories
- ImpactMonitor.tsx: two-tab page (Évalués / À évaluer), top instruments bar, inline score override modal, category browser
- Startup auto-bootstrap for impact categories
- Route /impact + sidebar nav entry

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 16:26:13 +02:00
OpenSquared
9ed59c5ca2 feat: auto-bootstrap macro + eco events on startup
Both bootstrap_macro_events() and bootstrap_eco_events() are now called
in the FastAPI startup event. Idempotent — skips already-existing events.
Logs how many events were inserted on first run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 00:13:22 +02:00
OpenSquared
3470205d54 feat: eco calendar bootstrap + driver types + timeline cursor
Backend:
- eco_calendar_bootstrap.py: 75 historical events 2020-2026 (FOMC, NFP,
  CPI, GDP, ISM, BOJ, ECB, BOE) with expected_value/actual_value/
  surprise_pct/unit/absorption_pct fields
- database.py: 5 new columns on market_events (expected_value,
  actual_value, surprise_pct, unit, sub_type) + updated save_market_event
- timeline.py: POST /api/timeline/bootstrap-eco endpoint
- instrument_service.py: _get_relevant_events now returns eco fields
- instruments.json: type field on all 90 drivers across 20 instruments
  (event_calendar | report | geopolitical | fundamental | sentiment | technical)

Frontend (InstrumentDashboard):
- EventTimelineStrip: vertical cursor line tracking crosshair selectedDate
- EventsCard: active-event highlight at hovered date + expected/actual/
  surprise_pct display + absorption progress bar
- DriversPanel: type selector dropdown per driver
- DriverTypeBadge: colored micro-badge on driver labels in strip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 00:07:41 +02:00
OpenSquared
aec9ced74f feat: macro regime + 30 historical events + regime confidence fix
- Add macro_regime (goldilocks/stagflation/recession/etc.) to every instrument snapshot via get_macro_gauges() + score_macro_scenarios()
- RegimeCard now shows global macro cycle section (emoji + label + top-3 scenarios) above technical signals
- Fix _detect_regime() confidence: capped at 85% max; add late-bull (dist_MA200 > 20%) and correction-in-bull (MA50 > MA200 but momentum < -3%) detection so regime no longer locks at 100%
- Add macro_events_bootstrap.py with 30 curated historical events (FOMC 2022-2025, CPI surprises, Ukraine/Hamas/Iran geopolitics, BOJ pivots, Bitcoin ETF, Liberation Day tariffs, SVB crisis, etc.)
- POST /api/timeline/bootstrap-macro endpoint (idempotent, deduplicates by name)
- Fix event date filter in _get_relevant_events(): overlap logic instead of start-only filter — events extending into the chart window are now included
- EventTimelineStrip: add "Signaux Techniques" fallback row for events not matched by any driver keyword (MA crossovers are now always visible)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 23:24:24 +02:00
OpenSquared
aa81598278 feat: driver-based timeline strip, regime signal metrics, drivers editor
- instruments.json: add keywords array to every driver across 20 instruments
  (Fed, BCE, BOJ, OPEC, CPI, AI, EIA, etc.) for event-to-driver matching
- instrument_service.py: add update_instrument_drivers() persisting changes to JSON
  and refreshing in-memory cache
- instruments.py: add PUT /api/instruments/{id}/drivers endpoint (DriverUpdate model)
- InstrumentDashboard:
  * RegimeCard: replace regime score bars with 6-metric signal grid
    (MA50/MA200 position, MA50 slope, MA200 slope, momentum 20j, dist MA200, ATR vol ratio)
    with colour-coded values and contextual sub-labels (Golden cross, Surextension, etc.)
  * EventTimelineStrip: rows now keyed by top-4 instrument drivers (by weight)
    instead of LT/MT/CT; events matched via case-insensitive keyword scan against
    title + description + category; fallback dashed line when no events match
  * DriversPanel: inline edit panel (toggle via Drivers button in header);
    edit label, weight, keywords (comma-separated) per driver; add/remove drivers;
    saves via PUT /api/instruments/{id}/drivers; optimistic local state update

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 22:48:10 +02:00
OpenSquared
418d03254d 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>
2026-06-24 22:21:04 +02:00
OpenSquared
d47fc8f50d fix: move instruments.json from data/ to config/ — Docker volume was masking /app/data
The docker-compose mounts db_data named volume at /app/data which hid the
instruments.json file baked into the image. Moving to /app/config which is
not volume-overlaid resolves the FileNotFoundError on startup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 21:42:19 +02:00
OpenSquared
537fea8148 feat: Instrument Snapshot Dashboard — 5-layer synchronized view for 20 instruments
- 20 instruments configured (equity indices, metals, energy, bonds, FX, stocks, crypto)
  each with custom drivers, regime labels, MA periods, event keywords, ai_context
- InstrumentChart: TradingView lightweight-charts candlesticks + MA lines + Bollinger
  + volume histogram + macro event markers overlaid on price
- InstrumentDashboard: regime detection card (scores + signals), trend indicators
  (RSI gauge, MA slopes, momentum, 52W range), events card (links to Timeline),
  AI narrative via GPT-4o-mini (cached by day)
- Backend: instrument_service (OHLCV fetch, indicators, regime scoring, GPT narrative)
  + /api/instruments router (3 endpoints)
- Route: /instruments/:id with selector dropdown, period buttons (3M/6M/1Y/2Y/5Y)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 21:36:39 +02:00
OpenSquared
edfa90c9b7 feat: vertical 3-column timeline + MA regime bootstrap engine
- TimelineVertical: replace horizontal frise with Y=time vertical layout,
  3 columns (Long/Medium/Short), auto-scroll to selected date, sub-columns
  for overlapping events, today/selected-date horizontal lines
- ma_analyzer.py: detect MA50/MA200 crossovers + MA100 slope changes +
  MA20 direction swings on EUR/USD, Brent, Gold, S&P500, US10Y (5y history)
  with 5-bar confirmation, dedup, GPT-4o-mini enrichment, idempotent DB save
- POST /api/timeline/bootstrap-ma endpoint to trigger analysis
- Bootstrap MA button in Timeline page with loading state + result count

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 21:01:01 +02:00
OpenSquared
aeb5233deb feat: frise sub-lanes + event manager + MA indicators + absorption
Frise chronologique:
- Sub-lane stacking (assignSubLanes) — overlapping events se décalent verticalement
- Zone d'overlap semi-transparente sur la période commune entre 2 événements
- Hauteur dynamique selon nb de sub-lanes par niveau
- Événements en cours avec flèche ▶ à droite, gradient de fin
- Tri par start_date pour placement greedy

Event Manager (composant EventManager.tsx):
- Tableau filtrable par niveau (Long/Moyen/Court)
- Edit modal complet : tous les champs + absorption_pct éditable
- Bouton "IA — Enrichir" par événement → POST /api/timeline/events/{id}/ai-enrich
  → GPT-4o-mini suggère absorption_pct + indicateurs pertinents par niveau temporel
- Delete avec confirmation double-clic
- Expand row pour voir description + indicateurs
- Intégré Timeline page via bouton "Gérer événements"

Backend:
- Nouvelles colonnes market_events: absorption_pct + relevant_indicators (ALTER idempotent)
- DELETE /api/timeline/events/{id}
- POST /api/timeline/events/{id}/ai-enrich

Snapshot Externe:
- AbsorptionBar par événement dans cellule Géopolitique
- MA indicators : fetch 200j history, compute MA10/MA20/MA100 per level (short/med/long)
- Affichage prix vs MA + % écart dans CellMarkets
- Si relevant_indicators configurés sur l'event → utilise ces symbols au lieu des défauts
- Calendar : horizons exclusifs (short 0-7j, medium 8-30j, long 31-90j) — bug corrigé

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 20:31:57 +02:00
OpenSquared
43b4816596 refactor: Snapshot Externe — layout 3 lignes temporelles × 4 piliers
Remplace le 2×2 indépendant par une matrice cohérente avec le concept E1/E2/E3 :
- 3 lignes (Long/Moyen/Court), chacune ancrée sur l'événement actif de ce niveau
- 4 colonnes (Géopolitique / Prix & Marchés / Régimes Macro / Calendrier)
- Assets affichés dans la colonne Prix filtrés selon affected_assets de l'événement
- Barres macro normalisées sur max score (overflow corrigé, overflow-hidden)
- Calendrier filtré par horizon temporel (7j / 1M / 3M) selon le niveau
- En-têtes colonnes globaux pour lecture matricielle immédiate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 18:47:08 +02:00
OpenSquared
8afa06118c feat: frise chronologique + page Snapshot Externe 4 piliers
- TimelineFrise.tsx : visualisation horizontale scrollable COVID→aujourd'hui
  3 lanes (long/medium/short), événements cliquables, marqueur aujourd'hui/date, axe années
- ExternalSnapshot.tsx (/snapshot) : dashboard 4 piliers du contexte externe
  Pilier 1 Géopolitique : 3 badges L/M/C avec J+ depuis événement + lien /timeline?date=
  Pilier 2 Prix & Marchés : quotes SPY/QQQ/GLD/USO/TLT/UUP live + lien Specialist Desks
  Pilier 3 Régimes Macro : régime dominant + scores scénarios + jauges VIX/DXY/10Y
  Pilier 4 Calendrier Économique : prochains événements + récents, liens Calendar/Institutional
- Timeline.tsx : remplace mini-strip par TimelineFrise, lit ?date= query param
- Sidebar : Snapshot Externe (ScanEye) + Timeline (Layers)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 18:36:12 +02:00