feat: saxo

This commit is contained in:
OpenSquared
2026-07-18 22:20:33 +02:00
parent b0b7f9bfda
commit 0d3a6a6fad
5 changed files with 93 additions and 1 deletions

View File

@@ -39,6 +39,45 @@ def update_watchlist(req: WatchlistRequest):
return {"symbols": get_watchlist()}
class ExpandWatchlistRequest(BaseModel):
keyword: str
@router.post("/watchlist/expand")
def expand_watchlist(req: ExpandWatchlistRequest):
"""Adds every catalog entry matching a keyword (ex. 'EURUSD' -> every weekly/monthly
FuturesOption root, not just one) — avoids hand-picking each maturity's exact ticker."""
matches = get_saxo_catalog(None, req.keyword, limit=200)
if not matches:
raise HTTPException(status_code=404, detail=f"Aucune entrée du catalogue ne correspond à '{req.keyword}' — rafraîchissez le catalogue d'abord si besoin.")
current = get_watchlist()
added = [m["symbol"] for m in matches if m["symbol"] not in current]
set_watchlist(current + added)
return {"symbols": get_watchlist(), "added": added}
@router.get("/debug-chain/{symbol}")
def debug_chain(symbol: str):
"""Raw, unparsed options-chain snapshot for one symbol — diagnostic endpoint only.
Also written to System Logs (details field) so it can be read from the Logs page
instead of requiring direct URL/API access."""
from services.saxo_client import debug_chain_raw, SaxoNotConnected, SaxoApiError
from services.database import log_system_event
try:
result = debug_chain_raw(symbol)
log_system_event(
level="INFO", source="saxo_debug",
message=f"Raw options chain snapshot for {symbol.upper()} (voir details)",
ticker=symbol, details=result,
)
return result
except SaxoNotConnected as e:
raise HTTPException(status_code=401, detail=str(e))
except SaxoApiError as e:
log_system_event(level="ERROR", source="saxo_debug", message=f"debug-chain failed for {symbol.upper()}: {e}", ticker=symbol)
raise HTTPException(status_code=502, detail=str(e))
def _validate_symbol(symbol: str) -> dict:
from services.saxo_client import resolve_instrument, SaxoNotConnected
try:

View File

@@ -242,6 +242,15 @@ def _snapshot_via_subscription(uic: int, asset_type: str = "StockOption") -> Dic
return snapshot
def debug_chain_raw(symbol: str) -> Dict[str, Any]:
"""Returns the raw (unparsed) options-chain subscription response for one symbol —
diagnostic only, used to pin down exactly where Mid/Greeks/Spot live in Saxo's real
payload instead of guessing field names blind."""
instrument = resolve_instrument(symbol)
subscription = _snapshot_via_subscription(instrument["uic"], asset_type=instrument["asset_type"] or "StockOption")
return {"instrument": instrument, "raw": subscription}
def snapshot_options_chain(symbol: str, target_days: int = 30) -> List[Dict[str, Any]]:
"""
Returns normalized rows ready for services/database.save_saxo_snapshot_rows: