feat: system logs page + dynamic IV watchlist with auto-add from cycle

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>
This commit is contained in:
OpenSquared
2026-06-18 13:07:35 +02:00
parent 3b4e035819
commit 05a475fb04
10 changed files with 759 additions and 24 deletions

View File

@@ -116,7 +116,7 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
get_config, get_custom_patterns, save_custom_pattern,
save_pattern_scores, log_macro_regime, log_geo_alert, log_trade_entries,
add_cycle_run, update_cycle_run, save_reasoning_trace,
get_latest_portfolio_lessons,
get_latest_portfolio_lessons, log_system_event,
)
from services.data_fetcher import fetch_geo_news, get_all_quotes, get_macro_gauges, score_macro_scenarios
from services.geo_analyzer import compute_geo_risk_score
@@ -466,6 +466,28 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
log_trade_entries(run_id=scoring_run_id, scored_patterns=scored, quotes=quotes)
# Auto-add any new underlying tickers to the IV watchlist
try:
from services.database import add_watchlist_ticker, get_watchlist_tickers
from services.iv_engine import _resolve_ticker, bootstrap_iv_history
existing = set(get_watchlist_tickers())
new_proxies = set()
for sp in scored:
for trade in (sp.get("trade_rankings") or sp.get("suggested_trades") or []):
underlying = trade.get("underlying") or sp.get("underlying") or ""
if underlying:
proxy = _resolve_ticker(underlying)
if proxy not in existing:
new_proxies.add(proxy)
for proxy in new_proxies:
if add_watchlist_ticker(proxy, added_by="cycle"):
_log.info(f"[Watchlist] Auto-added new ticker: {proxy}")
log_system_event("INFO", "auto_cycle", f"Nouveau ticker ajouté à la watchlist IV: {proxy}", cycle_id=scoring_run_id, ticker=proxy)
if new_proxies:
bootstrap_iv_history(tickers=list(new_proxies), min_existing=0)
except Exception as _we:
_log.warning(f"[Watchlist] Auto-add failed: {_we}")
gauges_summary = {
k: {"value": v.get("value"), "change_pct": v.get("change_pct"), "label": v.get("label")}
for k, v in gauges.items()