Upgrade mission-control UI with charts and live metrics API without impacting trading loop

This commit is contained in:
2026-02-26 17:57:42 -08:00
parent 94aba00967
commit dbf6bf4e73
2 changed files with 171 additions and 28 deletions

64
app.py
View File

@@ -33,6 +33,70 @@ def curate_now():
def api_account():
return JSONResponse(account_snapshot())
@app.get("/api/metrics")
def api_metrics(hours: int = 72):
db = SessionLocal()
try:
since = datetime.utcnow() - timedelta(hours=hours)
decs = db.query(BotDecision).filter(BotDecision.ts >= since).order_by(BotDecision.ts.asc()).all()
trades = db.query(TradeExecution).filter(TradeExecution.ts >= since).order_by(TradeExecution.ts.asc()).all()
# hour buckets
buckets = {}
for d in decs:
k = d.ts.strftime("%m-%d %H:00")
buckets.setdefault(k, {"buy": 0, "sell": 0, "hold": 0, "executed": 0, "failed": 0})
if d.action in ("buy", "sell", "hold"):
buckets[k][d.action] += 1
if d.status == "executed":
buckets[k]["executed"] += 1
if d.status == "failed":
buckets[k]["failed"] += 1
labels = list(buckets.keys())
buy = [buckets[k]["buy"] for k in labels]
sell = [buckets[k]["sell"] for k in labels]
hold = [buckets[k]["hold"] for k in labels]
executed = [buckets[k]["executed"] for k in labels]
failed = [buckets[k]["failed"] for k in labels]
# cumulative notional (proxy activity curve)
t_labels, t_values = [], []
c = 0.0
for t in trades:
c += float(t.notional or 0)
t_labels.append(t.ts.strftime("%m-%d %H:%M"))
t_values.append(round(c, 2))
# symbol leaderboard
lb = {}
for d in decs:
row = lb.setdefault(d.symbol, {"symbol": d.symbol, "decisions": 0, "executed": 0})
row["decisions"] += 1
if d.status == "executed":
row["executed"] += 1
leaderboard = sorted(lb.values(), key=lambda x: x["executed"], reverse=True)
return {
"ok": True,
"hours": hours,
"decisionSeries": {
"labels": labels,
"buy": buy,
"sell": sell,
"hold": hold,
"executed": executed,
"failed": failed,
},
"activitySeries": {
"labels": t_labels,
"cumulativeNotional": t_values,
},
"leaderboard": leaderboard,
}
finally:
db.close()
@app.get("/")
def home(request: Request):
db = SessionLocal()