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>
- 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>
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>
- 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>
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>
- 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>
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>
- 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>
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>
- Detect yfinance quote_type (CURRENCY→forex, FUTURE→energy, INDEX→indices,
ETF→etfs, EQUITY→equities) when adding a custom ticker and persist it in
market_watchlist.asset_class
- get_all_quotes() merges custom tickers into their proper group (e.g. EURUSD=X
appears under Forex) instead of always under a separate "Custom" group
- "Custom" tab only shows tickers whose type couldn't be detected
- Add market_watchlist.asset_class migration; ensure backtest_lab_runs and
market_watchlist are always created at init_db() time
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add ability to add/remove custom tickers (e.g. EURCHF=X) on the
Markets & Prices page without editing config. Tickers are validated
via yfinance, persisted in market_watchlist SQLite table, merged into
the quotes feed as a 'custom' group, and shown in a dedicated tab
with per-card remove buttons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove all built-in patterns (no proof of legitimacy); seed_builtin_patterns is now a no-op
- DB: add backtest_lab_runs table + backtest_hits/runs_count columns on patterns
- services/pattern_lab.py: build_historical_context (yfinance + RSI/MA200),
run_ai_backtest (GPT-4o as historical analyst), evaluate_outcomes (actual moves at T+horizon)
- routers/pattern_lab.py: POST /run, POST /evaluate/{id}, GET /runs, DELETE /runs/{id},
POST /save-pattern (promotes hit pattern to library with reliability counters)
- PatternLab.tsx: 34 preset events 2015-2025 (macro/geo/credit/fx/commodities/volatility/tech),
3-panel layout — preset selector + wizard + run history, market data table,
AI pattern cards with hit/miss outcome display, Save to Library button
- useApi.ts: usePatternLabRuns, useRunPatternLab, useEvaluatePatternLab, useSaveLabPattern, useDeleteLabRun
- Sidebar + App.tsx: /pattern-lab route + FlaskConical nav link
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- database.py: add trade_budget_eur / preferred_horizon_min/max config
defaults and include them in cycle config migrations
- auto_cycle.py: read trade params from config and inject into cycle_meta
- ai_analyzer.py: inject INVESTOR TRADE MANDATE block into scoring and
suggestion prompts so GPT-4o penalises horizon mismatches and sizes
within the capital cap
- Config.tsx: Trade Parameters card with budget + horizon sliders and live
mandate summary
- TradeIdeas.tsx: horizon filter pills (< 1M / 1-3M / 3-6M / > 6M) and
budget/horizon indicator pulled from saved config
- useApi.ts: extend useUpdateCycleConfig type with new config fields
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- database.py: add timedelta to datetime import (used in
get_recent_economic_surprises, was raising NameError)
- ai_analyzer.py: scoring split was searching French string
'Retourne UNIQUEMENT ce JSON valide:' but prompt is now in English
'Return ONLY this valid JSON:' — caused IndexError crashing every cycle
- requirements.txt: add lxml>=5.0.0 (yfinance earnings_dates dependency,
was silently failing all 23 ticker fetches every hour)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- New institutional_reports table (DB) with importance, signals per asset class, key points, absorption tracking
- cot_fetcher.py: CFTC Socrata API (6dca-aqww), 7 instruments (Gold/Silver/Copper/WTI/NatGas/SP500/EURUSD), net positioning + 52-week z-score
- eia_fetcher.py: EIA API v2, 4 series (crude/Cushing/gasoline/distillates), WoW surprise detection
- institutional.py router: GET /reports, GET /reports/{id}, POST /refresh, GET /stats
- institutional_scheduler.py: weekly auto-fetch (COT Saturdays, EIA Wednesday afternoons)
- ai_analyzer.py: build_institutional_block() + institutional_block param injected into AI scoring prompt
- auto_cycle.py: inject institutional block into suggestion + scoring, absorption tracking via keyword overlap after each cycle commentary
- InstitutionalReports.tsx: full page with filter bar (type/category/importance/period), cards with key point bullets, EXTREME alerts highlighted, signal badges, absorption badge, trading implications, expandable detail
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 1 — Catégorisation:
- database.py: ADD COLUMN category + signal_direction on custom_patterns (migration);
save_custom_pattern persists category/signal_direction; new helpers:
get_unclassified_patterns(), update_pattern_classification(),
get_patterns_with_last_score()
- ai_analyzer.py: PATTERN_CATEGORIES dict (8 categories: géopolitique, macro_monétaire,
technique, commodités_supply, risk_off, flux_saisonnier, géo_économique, crédit_stress);
classify_patterns_batch() → GPT-4o-mini batch classification
- suggest schema: added category + signal_direction fields so new patterns are
classified from birth
- auto_cycle.py: Step 3.1 classifies all unclassified patterns after each suggestion
Phase 2 — Convergence layer (post-scoring, no extra AI call):
- ai_analyzer.py: _compute_convergence() groups scored patterns by (underlying, signal_direction);
conviction_bonus = min(20, +5 per additional agreeing pattern); adds conviction_score,
conviction_bonus, convergence_count, convergence_underlying, convergence_partners to each result;
called at end of score_patterns_with_context(), re-sorts by conviction_score
- auto_cycle.py: logs convergence summary after scoring; propagates category/signal_direction
to scored results for display
Phase optionnelle — Convergence in suggestion prompt:
- ai_analyzer.py: suggest_patterns_from_market_context() accepts convergence_block param;
injected into prompt so AI knows which underlyings have multi-pattern agreement
- auto_cycle.py: before suggestion, loads last-cycle scores via get_patterns_with_last_score(),
calls _compute_convergence() to build convergence block, passes to suggester
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- auto_cycle.py: detect weekend/market session, build cycle_meta with day_of_week/is_weekend/market_note;
IVGate skips iv_rank>=99 on weekends to avoid artificial weekend option premium cascade;
inject portfolio context (open trades + price moves + concentration) before AI scoring;
pass portfolio_context_block + run_id to both AI scorer and suggester
- ai_analyzer.py: _build_temporal_news_block injects market session banner (WEEKEND warning,
pre/after-market note, or open session label) so AI knows markets are closed and defers execution to Monday
- iv_engine.py: add WHEAT/EUR/USD ticker aliases; skip saving IV snapshots on weekends to protect history;
resolve aliases before slash-format conversion in _resolve_ticker
- technical_indicators.py: fix pandas MultiIndex from yfinance>=0.2 (droplevel+squeeze);
use period proportional to lookback instead of fixed period=1d
- database.py: asset_class ticker-based fallback (_asset_class_from_ticker); one-time backfill migration
for all NULL asset_class rows; ai_call_logs table + save/get helpers; normalize_ticker public function
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Portfolio context (portfolio_context.py):
- get_open_trades_with_moves(): fetches open trades + 1d/5d yfinance price moves
- get_portfolio_concentration(): counts by asset_class
- build_portfolio_context_block(): formatted prompt block with strict AI instructions
(no double positions, flag contradictions, avoid overweight classes)
AI call logging:
- ai_call_logs table in DB (run_id, call_type, system/user prompt, response, tokens, ms)
- _chat() now accepts log_meta dict → saves call to DB non-blocking after each call
- suggest and score_batch calls pass run_id + call_type for full traceability
auto_cycle.py:
- Builds portfolio context before snapshot and both AI calls
- Context snapshot now includes portfolio_open_positions key
SystemLogs.tsx:
- "Contexte IA" tab gains sub-tabs: Contexte / Appels IA
- AiCallRow: expandable with 3 panes (user prompt / system prompt / response)
shows model, tokens breakdown, duration, call type badge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _normalize_asset_class() now accepts ticker param and infers class from
a full ticker→class lookup table (energy/metals/agri/indices/equities/forex)
- init_db() runs one-time UPDATE to backfill all NULL asset_class rows in
trade_entry_prices and skipped_trades using known ticker lists
- log_trade_entries and log_skipped_trade pass ticker to normalizer
- Frontend _normalizeAssetClass() gets same ticker lookup + pattern fallbacks
for =F futures, NSE: prefixed equities, =X currency pairs
- All 3 filter calls now pass t.underlying as second argument
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Direction (Ouvert) : t.direction n'existe pas dans trade_entry_prices → utilisait
undefined, excluait tout. Remplacé par _isBearishStr(t.strategy) comme Fermés.
Asset class (Ouvert, Fermés, Non loggés) : l'IA retournait parfois "commodities",
"currencies", "fx", "equity" au lieu des clés canoniques. Double correction :
- Frontend : _normalizeAssetClass() mappe les variantes → energy|metals|agriculture|
indices|equities|forex dans les 3 sections filtrées
- Backend database.py : _normalize_asset_class() appliqué à l'INSERT dans
trade_entry_prices et skipped_trades (nouveaux trades normalisés au stockage)
- Prompt ai_analyzer.py : suggested_trades[].asset_class contraint à l'enum explicite
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>