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>
This commit is contained in:
OpenSquared
2026-06-25 21:27:21 +02:00
parent 0b1fcff49c
commit f71e3be3ff
6 changed files with 545 additions and 190 deletions

View File

@@ -95,14 +95,40 @@ def list_actions():
"description": "Scans RSS news, FRED eco surprises, MA crossovers, and institutional reports to create new market_events.",
"status": "running" if _running.get("check-market-events") else "idle",
"implemented": True,
"group": "detection",
},
{"id": "refresh-price-data", "label": "Refresh Price Data", "description": "Download fresh OHLCV for all watchlist instruments.", "status": "idle", "implemented": False},
{"id": "compute-indicators", "label": "Compute Indicators", "description": "Recalculate MA/RSI/BB/ATR for all instruments.", "status": "idle", "implemented": False},
{"id": "evaluate-impacts", "label": "Evaluate Impacts", "description": "AI-score market_events that lack impact_score.", "status": "idle", "implemented": False},
{"id": "generate-signals", "label": "Generate Signals", "description": "Compute trading signals from indicators + events.", "status": "idle", "implemented": False},
{"id": "update-regime", "label": "Update Regime", "description": "Re-classify the current macro regime.", "status": "idle", "implemented": False},
{"id": "snapshot-portfolio", "label": "Snapshot Portfolio", "description": "Store a risk snapshot (VaR, PnL, delta).", "status": "idle", "implemented": False},
{"id": "generate-report", "label": "Generate Report", "description": "Build and persist the daily cycle report.", "status": "idle", "implemented": False},
{
"id": "bootstrap-macro",
"label": "Bootstrap Macro Events",
"description": "Seed 30+ historical macro/geopolitical events (FOMC, NFP, elections…). Idempotent — skip duplicates.",
"status": "running" if _running.get("bootstrap-macro") else "idle",
"implemented": True,
"group": "bootstrap",
},
{
"id": "bootstrap-eco",
"label": "Bootstrap Eco Calendar",
"description": "Seed economic calendar events from FRED/ECB/BOE (last 12 months). Idempotent.",
"status": "running" if _running.get("bootstrap-eco") else "idle",
"implemented": True,
"group": "bootstrap",
},
{
"id": "bootstrap-categories",
"label": "Bootstrap Impact Categories",
"description": "Seed default event categories with instrument impact weights (FOMC, NFP, CPI, BOE…).",
"status": "running" if _running.get("bootstrap-categories") else "idle",
"implemented": True,
"group": "bootstrap",
},
{"id": "bootstrap-ma", "label": "Bootstrap MA Crossovers", "description": "Detect historical MA50/100/200 crossovers for all instruments.", "status": "idle", "implemented": False, "group": "bootstrap"},
{"id": "refresh-price-data", "label": "Refresh Price Data", "description": "Download fresh OHLCV for all watchlist instruments.", "status": "idle", "implemented": False, "group": "data"},
{"id": "compute-indicators", "label": "Compute Indicators", "description": "Recalculate MA/RSI/BB/ATR for all instruments.", "status": "idle", "implemented": False, "group": "data"},
{"id": "evaluate-impacts", "label": "Evaluate Impacts", "description": "AI-score market_events that lack impact_score.", "status": "idle", "implemented": False, "group": "ai"},
{"id": "generate-signals", "label": "Generate Signals", "description": "Compute trading signals from indicators + events.", "status": "idle", "implemented": False, "group": "ai"},
{"id": "update-regime", "label": "Update Regime", "description": "Re-classify the current macro regime.", "status": "idle", "implemented": False, "group": "ai"},
{"id": "snapshot-portfolio", "label": "Snapshot Portfolio", "description": "Store a risk snapshot (VaR, PnL, delta).", "status": "idle", "implemented": False, "group": "portfolio"},
{"id": "generate-report", "label": "Generate Report", "description": "Build and persist the daily cycle report.", "status": "idle", "implemented": False, "group": "portfolio"},
]
return {"actions": actions}
@@ -192,3 +218,51 @@ def check_market_events_background(
background_tasks.add_task(_run)
return {"status": "accepted", "message": "check-market-events started in background"}
# ── POST /api/actions/bootstrap-macro ────────────────────────────────────────
@router.post("/bootstrap-macro")
def run_bootstrap_macro(force: bool = Query(False)):
"""Seed macro/geopolitical historical events. Idempotent by default."""
_guard("bootstrap-macro")
try:
from services.macro_events_bootstrap import bootstrap_macro_events
result = bootstrap_macro_events(force=force)
except Exception as e:
_release("bootstrap-macro")
raise HTTPException(500, str(e))
_release("bootstrap-macro")
return {"action": "bootstrap-macro", "status": "success", **result}
# ── POST /api/actions/bootstrap-eco ──────────────────────────────────────────
@router.post("/bootstrap-eco")
def run_bootstrap_eco(force: bool = Query(False)):
"""Seed economic calendar events. Idempotent by default."""
_guard("bootstrap-eco")
try:
from services.eco_calendar_bootstrap import bootstrap_eco_events
result = bootstrap_eco_events(force=force)
except Exception as e:
_release("bootstrap-eco")
raise HTTPException(500, str(e))
_release("bootstrap-eco")
return {"action": "bootstrap-eco", "status": "success", **result}
# ── POST /api/actions/bootstrap-categories ────────────────────────────────────
@router.post("/bootstrap-categories")
def run_bootstrap_categories(force: bool = Query(False)):
"""Seed default event categories with impact weights. Idempotent."""
_guard("bootstrap-categories")
try:
from services.impact_categories_bootstrap import bootstrap_impact_categories
result = bootstrap_impact_categories(force=force)
except Exception as e:
_release("bootstrap-categories")
raise HTTPException(500, str(e))
_release("bootstrap-categories")
return {"action": "bootstrap-categories", "status": "success", **result}