93 lines
2.8 KiB
Python
93 lines
2.8 KiB
Python
from typing import List, Optional
|
|
|
|
from fastapi import APIRouter, HTTPException, Query
|
|
from pydantic import BaseModel
|
|
|
|
from services import saxo_auth
|
|
from services.saxo_scheduler import get_watchlist, set_watchlist
|
|
from services.database import get_saxo_snapshots, get_saxo_snapshot_symbols
|
|
|
|
router = APIRouter(prefix="/api/saxo", tags=["saxo"])
|
|
|
|
|
|
class WatchlistRequest(BaseModel):
|
|
symbols: List[str]
|
|
|
|
|
|
@router.get("/status")
|
|
def status():
|
|
return saxo_auth.get_status()
|
|
|
|
|
|
@router.post("/disconnect")
|
|
def disconnect():
|
|
saxo_auth.disconnect()
|
|
return {"disconnected": True}
|
|
|
|
|
|
@router.get("/watchlist")
|
|
def watchlist():
|
|
return {"symbols": get_watchlist()}
|
|
|
|
|
|
@router.put("/watchlist")
|
|
def update_watchlist(req: WatchlistRequest):
|
|
set_watchlist(req.symbols)
|
|
return {"symbols": get_watchlist()}
|
|
|
|
|
|
def _validate_symbol(symbol: str) -> dict:
|
|
from services.saxo_client import resolve_instrument, SaxoNotConnected
|
|
try:
|
|
info = resolve_instrument(symbol)
|
|
return {
|
|
"symbol": symbol.upper(), "valid": True, "error": None,
|
|
"uic": info["uic"], "matched_symbol": info["symbol"],
|
|
"description": info["description"], "asset_type": info["asset_type"],
|
|
}
|
|
except SaxoNotConnected as e:
|
|
return {"symbol": symbol.upper(), "valid": False, "uic": None, "error": str(e)}
|
|
except Exception as e:
|
|
return {"symbol": symbol.upper(), "valid": False, "uic": None, "error": str(e)}
|
|
|
|
|
|
@router.get("/validate")
|
|
def validate_watchlist():
|
|
"""Checks each watchlist symbol actually resolves to a real Saxo option-root instrument."""
|
|
return [_validate_symbol(s) for s in get_watchlist()]
|
|
|
|
|
|
@router.get("/validate/{symbol}")
|
|
def validate_symbol(symbol: str):
|
|
return _validate_symbol(symbol)
|
|
|
|
|
|
@router.get("/symbols")
|
|
def symbols_with_history():
|
|
"""Distinct symbols that already have recorded snapshot history (may differ from the
|
|
current watchlist — includes ad-hoc 'snapshot now' calls and previously-tracked symbols)."""
|
|
return get_saxo_snapshot_symbols()
|
|
|
|
|
|
@router.post("/snapshot-now/{symbol}")
|
|
def snapshot_now(symbol: str):
|
|
from services.saxo_client import snapshot_options_chain, SaxoNotConnected, SaxoApiError
|
|
from services.database import save_saxo_snapshot_rows
|
|
try:
|
|
rows = snapshot_options_chain(symbol)
|
|
except SaxoNotConnected as e:
|
|
raise HTTPException(status_code=401, detail=str(e))
|
|
except (ValueError, SaxoApiError) as e:
|
|
raise HTTPException(status_code=502, detail=str(e))
|
|
save_saxo_snapshot_rows(rows)
|
|
return {"symbol": symbol.upper(), "rows_saved": len(rows)}
|
|
|
|
|
|
@router.get("/history")
|
|
def history(
|
|
symbol: Optional[str] = Query(None),
|
|
date_from: Optional[str] = Query(None),
|
|
date_to: Optional[str] = Query(None),
|
|
):
|
|
return get_saxo_snapshots(symbol, date_from, date_to)
|