feat(hub): add macro calendar for pre-release risk alerts

Manual FOMC/CPI/employment entries in settings drive ±1h monitor banners without touching exchange instances.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-18 11:52:30 +08:00
parent 3d29b4f9d9
commit e470c5952f
7 changed files with 932 additions and 3 deletions
+80 -1
View File
@@ -60,6 +60,16 @@ from hub_symbol_archive_lib import (
update_review_quote,
upsert_trade_overlay,
)
from hub_macro_calendar_lib import (
MACRO_EVENT_LABELS,
MACRO_EVENT_TYPES,
create_event as create_macro_event,
delete_event as delete_macro_event,
init_db as init_macro_calendar_db,
list_active_alerts,
list_events as list_macro_events,
update_event as update_macro_event,
)
from env_load import load_hub_dotenv
load_hub_dotenv()
@@ -2188,6 +2198,75 @@ def api_archive_quote_delete(quote_id: int):
return {"ok": True, "id": int(quote_id)}
class MacroEventBody(BaseModel):
event_type: str = ""
event_at: str = ""
note: str = ""
@app.get("/api/macro-calendar/meta")
def api_macro_calendar_meta():
init_macro_calendar_db()
return {
"ok": True,
"event_types": [
{"id": k, "label": MACRO_EVENT_LABELS[k]} for k in MACRO_EVENT_TYPES
],
"window_before_minutes": 60,
"window_after_minutes": 60,
"timezone": "Asia/Shanghai",
}
@app.get("/api/macro-calendar/events")
def api_macro_calendar_events():
init_macro_calendar_db()
rows = list_macro_events()
return {"ok": True, "events": rows, "count": len(rows)}
@app.get("/api/macro-calendar/active")
def api_macro_calendar_active():
init_macro_calendar_db()
alerts = list_active_alerts()
return {"ok": True, "alerts": alerts, "count": len(alerts)}
@app.post("/api/macro-calendar/events")
def api_macro_calendar_create(body: MacroEventBody = Body(...)):
init_macro_calendar_db()
try:
row = create_macro_event(body.event_type, body.event_at, note=body.note)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e)) from e
return {"ok": True, "event": row}
@app.patch("/api/macro-calendar/events/{event_id}")
def api_macro_calendar_update(event_id: int, body: MacroEventBody = Body(...)):
init_macro_calendar_db()
try:
row = update_macro_event(
int(event_id),
event_type=body.event_type or None,
event_at=body.event_at or None,
note=body.note,
)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e)) from e
if not row:
raise HTTPException(status_code=404, detail="记录不存在")
return {"ok": True, "event": row}
@app.delete("/api/macro-calendar/events/{event_id}")
def api_macro_calendar_delete(event_id: int):
init_macro_calendar_db()
if not delete_macro_event(int(event_id)):
raise HTTPException(status_code=404, detail="记录不存在")
return {"ok": True, "id": int(event_id)}
@app.get("/api/archive/detail")
def api_archive_detail(exchange_key: str = "", symbol: str = ""):
ex_k = (exchange_key or "").strip().lower()
@@ -2307,7 +2386,7 @@ def api_ping():
"service": "manual-trading-hub",
"build": HUB_BUILD,
"trade_ui": False,
"features": ["monitor", "settings", "auth", "board_sse", "dashboard_sse", "archive", "dashboard", "funds"],
"features": ["monitor", "settings", "auth", "board_sse", "dashboard_sse", "archive", "dashboard", "funds", "macro_calendar"],
"board_poll_interval_sec": HUB_BOARD_POLL_INTERVAL,
"board_version": board_store.version,
"board_aggregating": board_store.aggregating,