83 lines
2.6 KiB
Python
83 lines
2.6 KiB
Python
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)
|