from __future__ import annotations import asyncio from pathlib import Path from typing import Any from fastapi import FastAPI, HTTPException, Query, WebSocket, WebSocketDisconnect from fastapi.responses import FileResponse from market import create_provider, load_market_config from market.service import build_dashboard, build_realtime_info app = FastAPI(title="AITrading Simple Frontend API", version="1.0.0") BASE_DIR = Path(__file__).resolve().parent WEB_INDEX = BASE_DIR / "web" / "index.html" @app.get("/") def index() -> FileResponse: if not WEB_INDEX.exists(): raise HTTPException(status_code=500, detail="frontend page not found") return FileResponse(WEB_INDEX) @app.get("/api/stock") def stock_dashboard( query: str = Query(..., description="stock code or name"), days: int = Query(120, ge=30, le=365), sma_fast: int = Query(5, ge=2, le=60), sma_slow: int = Query(20, ge=3, le=120), ) -> dict[str, Any]: try: provider = create_provider(load_market_config()) return build_dashboard( provider=provider, query=query, days=days, sma_fast=sma_fast, sma_slow=sma_slow, ) except RuntimeError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc @app.get("/api/stock/realtime") def stock_realtime( query: str = Query(..., description="stock code or name"), ) -> dict[str, Any]: try: provider = create_provider(load_market_config()) return build_realtime_info(provider=provider, query=query) except RuntimeError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc @app.websocket("/ws/stock/realtime") async def stock_realtime_ws(websocket: WebSocket) -> None: await websocket.accept() query = (websocket.query_params.get("query") or "").strip() if not query: await websocket.send_json({"error": "query is required"}) await websocket.close(code=1008) return try: while True: provider = create_provider(load_market_config()) payload = build_realtime_info(provider=provider, query=query) await websocket.send_json(payload) # WebSocket channel keeps the connection alive, front-end no longer polls via HTTP. await asyncio.sleep(2) except WebSocketDisconnect: return except Exception as exc: # noqa: BLE001 await websocket.send_json({"error": str(exc)}) await websocket.close(code=1011) if __name__ == "__main__": import uvicorn uvicorn.run("web_app:app", host="0.0.0.0", port=8000, reload=False)