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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user