suggest_patterns_from_market_context() was missing the specialist desk
block that score_patterns_with_context() already received. All 7 desks
(forex, metals, agri, energy, indices, crypto, bonds) with their
fundamentals, macro sensitivity, and upcoming reports are now injected
so the AI can generate targeted patterns per desk rather than generic ones.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On restart the scheduler was counting interval_hours from now, ignoring
when the last cycle actually ran. It now reads last_run_at (in-memory or
DB) and deducts elapsed time so a restart doesn't silently push the next
fire by a full interval.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: category filter now ORs signal column so multi-category reports (category='multi') appear when filtering by Forex/Energy/Metals/Indices — fixes blank results
- Frontend: category pills expanded to match desk taxonomy (agri, crypto, bonds, indices added; equities kept for compatibility)
- Frontend: banner pointing to /specialist-desks clarifies the split between auto-fetched reports and manual desk report scheduling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- handleSave now shows a red toast "Lancez d'abord un backtest" instead of
silently returning when activeRun is null (was completely invisible to user)
- All toasts now color-coded: green (emerald) for success, red for errors
- list_runs now includes context_snapshot so market data table shows when
loading a historical run from the right panel
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>
Fix: history runs now always show results — create synthetic selected preset
from run data when no matching preset found (was broken for custom events).
Also force mode='events' and reset matchResults on history load.
Discover tab: new "Discover" panel in left sidebar (AI knowledge search).
- GPT-4o generates 6 matching events from a free-text query (date, assets, hint)
- Confidence score + category badge per event
- Click → pre-fills experiment form exactly like a preset → ready to Run
- Backend: POST /api/pattern-lab/discover (DiscoverRequest, sorted by confidence)
- Frontend: useDiscoverEvents hook + DiscoveredEvent type + Discover UI with
Enter-to-search, spinner, empty states
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: PATCH /api/patterns/custom/{id} — partial update, only provided fields changed
- useApi.ts: usePatchPattern mutation hook
- PatternCard: pencil icon (non-builtin only) → edit mode with inline inputs for name, description, direction (select), category, and #regime; ✓/✗ buttons to save or cancel; card border highlights blue while editing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- GET /api/market/validate?symbol= — validates ticker against yfinance,
returns {valid, name, price} or {valid: false, reason: 'helpful message'}
- Added SLV, USO, WEAT, CORN, TUR to ETFs WATCHLIST category
Frontend:
- validateTicker() async helper exported from useApi.ts
- InstrumentPicker (PatternLab): custom ticker field now validates before selecting
Shows spinner while checking, red error message if not found on yfinance
- InstrumentLens (PatternExplorer): same validation on Go button + Enter key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New 'etfs' asset class in WATCHLIST: 18 key ETFs used in Pattern Lab presets
(SPY, QQQ, IWM, TLT, IEF, HYG, GLD, EEM, FXI, EWG, EWJ, EWU, EWZ,
XLF, SMH, KWEB, UUP, BIL)
- Added 'etfs' tab between Indices and Equities in Markets page
- Extended AssetClass union type to include 'etfs'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend: DELETE /api/patterns/purge-all — removes all custom + backtested patterns
(source != 'builtin'), leaves Pattern Lab run history intact.
Frontend Config > Data Management:
- New PurgeButton for Pattern Library (custom_patterns table)
- Updated note: Trade Ideas are computed live from the Pattern Library (no separate table),
so purging patterns resets trade idea generation on next cycle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend (pattern_lab.py):
- Replace binary HIT/MISS with FULL / PARTIAL / MISS scoring
FULL: right direction AND ≥ 50% of expected move
PARTIAL: right direction AND ≥ 15% of expected move (was always MISS before)
MISS: wrong direction or negligible move
- Add direction_correct, direction_ratio, hit_type fields to all outcomes
- Add Black-Scholes ATM options P&L simulation (_bs_price, _ncdf, _sigma_for)
Normalised to S₀=K=100, per-asset-class vol heuristic (FX 8%, indices 16%, crypto 65%)
Supports: long call/put, straddle, strangle, call spread, put spread
- estimated_options_pnl_pct shows what the strategy would have returned
Frontend (PatternLab.tsx):
- OutcomeRow component: FULL HIT (green) / PARTIAL (amber) / MISS (red)
- Shows direction tick/cross + ratio % of target achieved
- Shows estimated options P&L with DollarSign icon
- Hit rate header shows full hits + partial count separately
- Card border: emerald = full, amber = partial, red = miss
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- instruments.ts: 90 IB-options-tradable instruments in 12 categories
(US Indices, Europe, Asia, EM, Sectors, Forex, Bonds, Metals, Energy,
Agriculture, Crypto, Volatility) — EUR/CHF, Cotton, etc. all included
- PatternExplorer: replace text input in Instrument Lens with categorised
grid picker (category pill filters + search + custom ticker fallback)
- PatternLab: add Instrument Scan tab alongside Event Presets
- Pick any instrument from the shared categorised picker
- Set period (start/end date) + horizon per pattern
- AI scans the full period: identifies 4-6 key pattern instances each with
their own entry date, expected move, strategy
- 'Evaluate outcomes' fetches actual price at T+horizon per pattern
- 'Save pattern' promotes any instance to the Pattern Library
- backend/services/pattern_lab.py: run_instrument_scan() + evaluate_instrument_outcomes()
(per-pattern analysis_date vs shared date in event mode)
- backend/routers/pattern_lab.py: POST /instrument-scan + POST /evaluate-instrument/{id}
- useApi.ts: useInstrumentScan + useEvaluateInstrumentScan hooks
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>
Complete French→English translation across all frontend pages and backend
services — every label, button, header, empty state, toast, and nav item
is now in English. Build verified clean (tsc + vite). No i18n library
added; direct string replacement throughout.
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: _scheduler_loop now distinguishes weekday (interval_hours)
from weekend (weekend_cycle_times UTC slots or sleep until Monday);
_parse_weekend_times() and _next_weekend_slot() helpers;
get_status() exposes weekend_cycle_enabled + weekend_cycle_times
- cycle.py: CycleConfigRequest adds weekend_cycle_enabled + weekend_cycle_times;
update_cycle_config validates HH:MM format and persists to config DB
- Config.tsx: weekend scheduling section with enable toggle + time picker
(06:00/08:00/12:00/18:00/22:00/00:00 UTC presets, multi-select);
weekendEnabled + weekendTimes state synced from cycle status
Default: enabled with 08:00 + 22:00 UTC (covers news scan + Globex open Sunday)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- portfolio_context.py: add _safe_float() helper (converts NaN/Inf → None);
use .squeeze().dropna() on yfinance closes before computing moves;
guard division by checking closes.iloc[-2] != 0
- cycle.py: add _sanitize_floats() recursive sanitizer applied to the full
snapshot before FastAPI serializes it — catches any remaining NaN from
iv_rank, technical indicators, or other sources
Fixes 500 on GET /api/cycle/contexts/{run_id} when yfinance returns NaN
weekend data for portfolio positions.
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>
Phase 4 — Price Discovery Status (la pièce maîtresse) :
- price_discovery.py (nouveau) : capture_price_snapshots() sauve les prix des tickers
liés à chaque news scorée (energy→BZ=F/NG=F, metals→GC=F/HG=F, indices→^GSPC/IWM)
- compute_absorptions() mesure combien du mouvement attendu s'est déjà produit
(status: not_yet_priced <30% / partially_priced 30-80% / fully_priced >80%)
- build_price_discovery_block() → bloc prompt avec opportunités classées
- database.py : table news_price_snapshots + save/get/purge fonctions
- auto_cycle.py : capture après ai_score_news_batch, compute avant suggestions,
block injecté dans suggestion + scoring prompts + context snapshot
- ai_analyzer.py : param price_discovery_block dans suggest + score
Phase 5 — Replay historique :
- cycle.py : POST /api/cycle/contexts/{run_id}/replay — recharge le snapshot historique
et relance suggest_patterns_from_market_context avec le contexte original
- useApi.ts : hook useReplayCycle
- SystemLogs.tsx : bouton "Rejouer ce cycle" dans onglet Contexte IA avec champ
notes, résultats inline (liste des patterns générés), section price_discovery
ouverte par défaut en rouge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 2 — Données macro FRED :
- fred_fetcher.py (nouveau) : 7 séries FRED (CPI, NFP, UNRATE, FEDFUNDS, GDP, ICSA,
spread 10Y-2Y) avec détection direction bullish/bearish et block prompt formaté
- ai_analyzer.py : param fred_block dans suggest + score, injecté dans les deux prompts
- auto_cycle.py : fetch FRED non-bloquant avant la suggestion
Context log — Snapshot du contexte complet :
- database.py : table cycle_context_snapshots + save/get/list fonctions
- auto_cycle.py : sauvegarde le snapshot (meta, news partitionnées, FRED, tech, IV, quotes)
- cycle.py : GET /api/cycle/contexts + GET /api/cycle/contexts/{run_id}
- useApi.ts : hooks useCycleContextSnapshots + useCycleContextSnapshot
- SystemLogs.tsx : onglet "Contexte IA" avec liste de cycles et visualiseur JSON
par section (cycle_meta, macro, news, FRED, tech) avec accordéon
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- database.py: get_last_completed_cycle_ts() pour mesurer le delta entre cycles
- auto_cycle.py: calcul delta_minutes + cycle_meta dict transmis aux fonctions IA
- ai_analyzer.py: apply_news_decay() (halflife par catégorie), partition_news_by_age()
(3 buckets: inter_cycle / recent_24h / older), _build_temporal_news_block() pour
le prompt suggestion; cycle_meta injecté aussi dans score_patterns_with_context()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dashboard: P&L card séparé en deux colonnes (Ouvertes/Réalisées) pour Simulé et Portfolio
- Dashboard: closed trades P&L locked from pnl_realized, ne fluctue plus après fermeture
- Journal Ouvert: filtres ticker/stratégie + classe d'actif + direction (haussier/baissier)
- Journal Fermés: mêmes filtres + filtre P&L (gagnants/perdants) + bouton supprimer par ligne
- Journal Non loggés: filtres ticker + classe d'actif + raison de skip
- Backend: DELETE /api/journal/trades/{id} + delete_trade() dans database.py
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- get_pnl_snapshot(id) : détail complet d'un snapshot avec trades parsés
- diff_pnl_snapshots(a, b) : diff positions entre deux snapshots (nouvelles /
fermées / évolution PnL par position + delta portfolio)
- GET /api/var/pnl/snapshots/{id} : détail snapshot
- GET /api/var/pnl/diff?a=&b= : calcul du diff
Frontend PositionHistory.tsx :
- Timeline scrollable des snapshots avec sparkline PnL
- Clic snapshot → détail des positions à ce moment (prix entrée, prix actuel,
PnL %, PnL €, régime macro)
- Boutons A/B par snapshot → sélection de deux points à comparer
- Vue diff A→B : nouvelles positions, fermées, évolution PnL par trade,
delta portfolio (capital, PnL %, PnL €)
- Route /position-history + nav sidebar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: get_status() now resolves scoring_run_id by querying
trade_entry_prices within the cycle time window, fixing the mismatch
between cycle run_id and the id actually written to trades
- Dashboard: Trades du cycle filters by scoring_run_id (no stale fallback)
- Dashboard: Pattern du cycle shows only patterns added in last cycle
(created_at >= started_at), renamed from Top Patterns
- Dashboard: Dernier Cycle now shows 4 stats (patterns/scorés/loggés/fermés)
+ IA commentary snippet
- Dashboard: P&L simulated mode bottom half shows open/closed/capital/profit
- Dashboard: Régime Macro shows top 4 scenario score bars
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dashboard: insert 2 rows of 4 mini-cards between top row and trade ideas
- Row 1: PnL Simulé, Risque Simulé, Dernier Cycle, Régime Macro
- Row 2: Super Contexte, Signaux Géo, Meilleur Pattern, Patterns Actifs
- All cards link to underlying pages via react-router Link
Journal: add 'Non loggés' tab exposing trades suggested by cycle
but skipped because no risk profile was matched
- New skipped_trades table (auto-created on backend restart)
- log_trade_entries() persists each skip with score/gain/asset_class
- GET /api/journal/skipped-trades + useSkippedTrades hook
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- portfolio_risk.py: add _infer_asset_class() with ticker→asset_class map
covering energy/metals/agri/indices/forex/rates futures, ETFs, forex pairs,
exchange prefixes (NSE:). Fallback applied when JOIN finds no match (orphaned
pattern_id after re-seed). Fixes "unknown 100%" shown in screenshot.
- RiskDashboard.tsx: add Portefeuille Réel / Simulé toggle at top.
New SimRiskPanel component with KPI row + concentration bars + conflict cards
+ AI recommendations — all visible inline in Risk Dashboard.
Red badge on Simulé tab when danger alerts exist.
- JournalDeBord.tsx: remove standalone Risque Sim. tab (moved to Risk Dashboard).
Replace with a red banner in summary cards when conflicts are detected,
pointing user to Risk Dashboard → Simulé.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>