feat: saxo price

This commit is contained in:
OpenSquared
2026-07-23 10:07:51 +02:00
parent 502ef6bf17
commit 1ac2270271
4 changed files with 114 additions and 4 deletions

View File

@@ -1,8 +1,11 @@
import logging
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import List
from typing import List, Optional
router = APIRouter(prefix="/api/watchlist", tags=["watchlist"])
logger = logging.getLogger(__name__)
# Intentionally duplicated from market_data.py to keep this system fully decoupled
# from the other instrument-list mechanisms (market_watchlist, INSTRUMENT_MODELS,
@@ -23,19 +26,40 @@ def list_watchlist():
return get_instruments_watchlist()
def _saxo_quote(saxo_symbol: str) -> Optional[dict]:
"""Try Saxo's own chart history for price/change/volatility — avoids the unadjusted-
roll artifact continuous futures tickers (BZ=F, CL=F, GC=F...) have on yfinance.
Untested against a live account; any failure here is routine, not an error — the
caller falls back to yfinance."""
from services.database import get_saxo_catalog_by_symbol
from services.saxo_client import get_saxo_quote_with_volatility
entry = get_saxo_catalog_by_symbol(saxo_symbol)
asset_type = entry["asset_type"] if entry else "FxSpot"
try:
return get_saxo_quote_with_volatility(saxo_symbol, asset_type)
except Exception as e:
logger.info(f"[watchlist/quotes] Saxo quote failed for '{saxo_symbol}' ({asset_type}), falling back to yfinance: {e}")
return None
@router.get("/quotes")
def watchlist_quotes():
from services.database import get_instruments_watchlist
from services.data_fetcher import get_quote_with_volatility
items = []
for row in get_instruments_watchlist():
q = get_quote_with_volatility(row["ticker"]) or {}
q = None
if row.get("saxo_symbol"):
q = _saxo_quote(row["saxo_symbol"])
if q is None:
q = get_quote_with_volatility(row["ticker"]) or {}
items.append({
**row,
"price": q.get("price"),
"change_pct": q.get("change_pct"),
"volatility_pct": q.get("volatility_pct"),
"volatility_change_pct": q.get("volatility_change_pct"),
"quote_source": q.get("source", "yfinance"),
})
return {"items": items}