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>
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>
- auto_cycle.py: replace _log with logger (NameError at lines 484/489)
- auto_cycle.py: normalize underlying via _normalize_ticker before _resolve_ticker
so WHEAT→ZW=F→WEAT and EUR/USD→EURUSD=X→FXE reach the IV watchlist correctly
- iv_engine.py: _resolve_ticker now strips slash-format forex (EUR/USD→EURUSD=X)
before _PROXY lookup, fixing yfinance 500/404 spam from get_atm_iv
- database.py: _fetch in log_trade_entries uses _normalize_ticker (not _normalize_yf_ticker)
so commodity aliases like WHEAT→ZW=F are applied at price-fetch time
- ai_analyzer.py: max_workers=1 for batch scorer — parallel workers both slept and
retried simultaneously after 429, causing repeated bursts; sequential fixes the pattern
- journal.py + JournalDeBord.tsx: add price_warning field (no_price_data/no_entry_price/
no_live_price) with visible ⚠ badge and amber color on affected ticker/price cells
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DB: 9 new columns on trade_entry_prices (status, closed_at, close_reason,
close_note, pnl_realized, close_price, target_pct, stop_loss_pct, signal_threshold)
via ALTER TABLE migration; close_trade(), get_closed_trades(),
update_trade_exit_params() helpers; exit_defaults config key
- Backend: PATCH /trades/{id}/close, PATCH /trades/{id}/exit-params,
GET/PUT /exit-defaults, GET /closed-trades with win-rate/avg-PnL stats;
trade-mtm now computes alert_type (target_reached|stop_loss) per trade
- Journal: new "Fermés" tab with closed trades table + stats banner (win rate,
avg PnL, total PnL, best trade); open trades show Cible/Stop progress bar +
🎯/🛑 alert badges + 1-click close modal (price, reason, note)
- Config: new "Paramètres de sortie" panel — target_pct, stop_loss_pct,
signal_reversal_mode, signal_reversal_threshold with live sliders
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hook ai_score_news_batch() into /api/geo/news so every news fetch is enriched
by GPT-4o-mini: corrected impact_score, ai_dir_energy/metals/indices, ai_resolution
(ceasefire/peace deal flag), ai_insight (1 French sentence). Gracefully no-ops
when OpenAI is not configured.
Add _compute_ai_alignment() in geo_analyzer: for each pattern compares the news
AI directional signals against the pattern's expected_move direction and produces
a -25..+25 bonus injected into similarity/relevance scores. Contra-signals
(e.g. peace deal → oil bearish while pattern expects oil spike) are flagged.
Frontend GeoRadar: PatternRelevanceCard shows AI alignment badge (green = aligned,
red = contra-signal) + base relevance diff + AI insights. NewsCard shows ai_insight,
directional arrows per asset class (⛽↑ 🥇↓) and resolution badge when expanded.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- DB: add system_logs table (level/source/cycle_id/ticker/message) and
iv_watchlist table (ticker/added_by/is_active); seed builtin 18 tickers
- DBLogHandler attached at startup — all WARNING+ logs auto-persist to DB
- log_system_event() helper for structured manual events
- New router /api/logs: GET with filters (level, source, cycle_id, ticker,
date range), GET /sources, GET /cycles for dropdowns, DELETE /clear
- iv_watchlist now read from DB instead of hardcoded constant; options_vol
watchlist/refresh/bootstrap endpoints all use get_watchlist_tickers()
- New endpoints: POST/DELETE /options-vol/watchlist-tickers/{ticker} to
add/remove tickers; adding triggers background 1-year bootstrap
- auto_cycle: after log_trade_entries(), auto-detect new underlying proxies
not yet in watchlist, add them and bootstrap their IV history
Frontend:
- New page SystemLogs (/logs): log table with level/source/cycle/ticker/date
filters, color-coded rows, expandable JSON details, auto-refresh 30s
- Options Lab: WatchlistManager section — add ticker input, chip list with
builtin/auto/manual color coding, remove button for non-builtins
- Sidebar: Logs Système nav link (ScrollText icon)
- useApi: useSystemLogs, useLogSources, useLogCycles, useClearLogs,
useWatchlistTickers, useAddWatchlistTicker, useRemoveWatchlistTicker
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- iv_engine/options_vol: when get_atm_iv() returns None (yfinance chain unavailable),
fall back to most recent iv_history row so IV Rank is always computable from
bootstrapped data; live vs history source tagged as iv_source field
- Dashboard: build mtmMap from tradeMtmData.trades (trade_entry_prices, cycle auto-log)
keyed by pattern_id; getAddedInfo() falls back to mtmMap so Entrée/Durée columns
populate automatically after each AI cycle without manual portfolio add
- OptionsLab: show '~' prefix and 'IV estimée' label when IV comes from history fallback;
fix near-invisible text-slate-700 on 'Sans historique' section header
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- JournalDeBord: trade rows now expand inline (full-width) instead of
PostmortemPanel appearing below the whole table. Click anywhere on a
row to toggle. Period selector extended to 15/30/60/90j.
- Config: added Rétention Journal (30/60/90/180j) and Seuil Maturité
(20/30/35/50%) controls, wired to the Appliquer button.
- Backend: journal_retention_days and maturity_threshold_pct read from
config table; seeded at startup with defaults 90d / 35%. get_status()
now returns both values so Config page can initialise correctly.
- cycle.py: CycleConfigRequest accepts and validates both new params.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
IV Rank was stuck at 50 for all tickers because the formula returns 50.0
when iv_max == iv_min (not enough historical snapshots). Added:
- bootstrap_iv_history(): downloads 1y of closes per ticker, computes
30d rolling realized vol (annualized), saves each day to iv_history
- POST /api/options-vol/bootstrap-history endpoint (runs in background)
- OptionsLab: auto-detect when IV Rank needs bootstrapping and show
an amber banner with one-click "Initialiser historique" button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sprint 2.1 — Pattern Reliability Score
- database.py: get_pattern_reliability() — win_rate × log(n+1) sur trades matures (≥35% horizon)
- database.py: get_all_pattern_reliability_map() pour injection rapide dans les prompts
- ai_analyzer.py: inject reliability_map dans suggest_patterns (patterns fiables mis en avant)
- auto_cycle.py: charge reliability_map avant suggestion et le passe au suggéreur
- routers/analytics.py: GET /api/analytics/reliability
- PatternEditor.tsx: ReliabilityBadge sur chaque card + usePatternReliability hook
- useApi.ts: usePatternReliability, useCalibration hooks
Sprint 2.2 — Contre-thèses & Invalidation Triggers
- database.py: migration ALTER TABLE — counter_thesis, invalidation_trigger, invalidation_probability
- database.py: save_custom_pattern() persiste les 3 nouveaux champs
- ai_analyzer.py: counter_thesis + invalidation_trigger + invalidation_probability dans le JSON schema
- auto_cycle.py: détection automatique des triggers d'invalidation contre les news (keyword match)
- routers/analytics.py: GET /api/analytics/invalidation-alerts
- PatternEditor.tsx: affichage contre-thèse dans les cards + champs dans le formulaire
- PatternEditor.tsx: affichage dans AiSuggestModal (suggestions IA)
- routers/patterns.py: PatternRequest inclut les 3 nouveaux champs
Sprint 2.3 — Calibration probabiliste & Demi-vie KB
- database.py: migration — predicted_probability sur pattern_score_history
- database.py: save_pattern_scores() stocke probability du pattern à chaque scoring run
- database.py: get_calibration_data() — Brier score + buckets de calibration par décile
- database.py: expires_at + confidence_decay_days sur knowledge_base
- database.py: decay_kb_confidence() — decay automatique + archivage à 0
- auto_cycle.py: decay_kb_confidence() appelé au début de chaque cycle (non-bloquant)
- routers/analytics.py: GET /api/analytics/calibration + POST /api/analytics/kb/decay
- frontend/src/pages/Analytics.tsx: nouvelle page — tableau fiabilité + calibration Brier
- App.tsx + Sidebar.tsx: route /analytics + entrée menu
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- knowledge.py: trade_line crash when pnl_pct is None in dict
(t.get('pnl_pct',0) returns None if key exists with None value — use 'or 0')
- database.py: cleanup_stale_running_cycles() marks any 'running' cycle
as 'error' on startup (uvicorn reload mid-cycle left status stuck)
- main.py: call cleanup_stale_running_cycles() at startup with warning log
- database.py: _normalize_ticker() converts GPT-4o exchange:symbol format
(NSE:RELIANCE → RELIANCE.NS, BSE:X → X.BO, etc.) so yfinance stops
spamming 404 errors for every MTM request
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- database.py: add delete_ai_report(), delete_reasoning_state(), delete_kb_entry()
- reasoning.py: DELETE /api/reasoning/reports/{id}
- knowledge.py: DELETE /api/knowledge/history/{id} and /entries/{id}
- useApi.ts: useDeleteAiReport, useDeleteReasoningState, useDeleteKbEntry hooks
- RapportIA.tsx: trash icon on hover in archived reports sidebar
- SuperContexte.tsx: trash icon on hover for history versions and KB entries;
both propagate onDelete through CategorySection down to KbEntry
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- knowledge.py: classify trades by maturity before building synthesis
prompt; only mature trades (≥35% elapsed) contribute to P&L stats
and conclusions; immature trades listed for transparency only
- Add 6h staleness gate on POST /synthesize (force=true to override)
- System prompt now includes hard timing rule: GPT-4o must not revise
existing conclusions because of newly-added immature trades
- useApi.ts: useSynthesizeKnowledge accepts force boolean param
- SuperContexte.tsx: shows amber notice with age when skipped + offers
"Force quand même" button; success banner uses new response shape
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>