from fastapi import FastAPI, Request from fastapi.responses import JSONResponse from fastapi.templating import Jinja2Templates from sqlalchemy import func from datetime import datetime, timedelta from db import init_db, SessionLocal, BotDecision, TradeExecution, CuratedInsight from bot import start_scheduler, run_cycle, curate_cycle from services import account_snapshot app = FastAPI(title="alpaca-llm-bot-v1") templates = Jinja2Templates(directory="templates") @app.on_event("startup") def startup(): init_db() start_scheduler() @app.get("/health") def health(): return {"ok": True} @app.post("/run-now") def run_now(): run_cycle() return {"ok": True, "ran": True} @app.post("/curate-now") def curate_now(): curate_cycle() return {"ok": True, "curated": True} @app.get("/api/account") def api_account(): return JSONResponse(account_snapshot()) @app.get("/") def home(request: Request): db = SessionLocal() try: since = datetime.utcnow() - timedelta(hours=24) decisions = db.query(BotDecision).order_by(BotDecision.ts.desc()).limit(120).all() trades = db.query(TradeExecution).order_by(TradeExecution.ts.desc()).limit(120).all() insights = db.query(CuratedInsight).order_by(CuratedInsight.ts.desc()).limit(30).all() stats = { "decisions": db.query(func.count(BotDecision.id)).filter(BotDecision.ts >= since).scalar() or 0, "trades": db.query(func.count(TradeExecution.id)).filter(TradeExecution.ts >= since).scalar() or 0, "executed": db.query(func.count(BotDecision.id)).filter(BotDecision.ts >= since, BotDecision.status == "executed").scalar() or 0, "failed": db.query(func.count(BotDecision.id)).filter(BotDecision.ts >= since, BotDecision.status == "failed").scalar() or 0, "insights": db.query(func.count(CuratedInsight.id)).filter(CuratedInsight.ts >= since).scalar() or 0, } return templates.TemplateResponse("index.html", {"request": request, "decisions": decisions, "trades": trades, "insights": insights, "stats": stats}) finally: db.close()