feat: saxo price
This commit is contained in:
@@ -49,8 +49,8 @@ def watchlist_quotes():
|
||||
items = []
|
||||
for row in get_instruments_watchlist():
|
||||
q = None
|
||||
if row.get("saxo_symbol"):
|
||||
q = _saxo_quote(row["saxo_symbol"])
|
||||
if row.get("saxo_quote_symbol"):
|
||||
q = _saxo_quote(row["saxo_quote_symbol"])
|
||||
if q is None:
|
||||
q = get_quote_with_volatility(row["ticker"]) or {}
|
||||
items.append({
|
||||
@@ -110,13 +110,27 @@ class SaxoLinkBody(BaseModel):
|
||||
saxo_symbol: str | None = None
|
||||
|
||||
|
||||
@router.put("/{ticker}/saxo-link")
|
||||
def set_saxo_link(ticker: str, body: SaxoLinkBody):
|
||||
"""Link this tracked instrument to a Saxo watchlist symbol (or pass null to unlink)
|
||||
so Options Lab's Saxo section shows broker data for it. Adds the symbol to
|
||||
services.saxo_scheduler's watchlist automatically if it wasn't already there."""
|
||||
from services.database import set_instrument_watchlist_saxo_symbol
|
||||
ok = set_instrument_watchlist_saxo_symbol(ticker, body.saxo_symbol)
|
||||
@router.put("/{ticker}/saxo-option-link")
|
||||
def set_saxo_option_link(ticker: str, body: SaxoLinkBody):
|
||||
"""Link this tracked instrument to the Saxo symbol whose OPTIONS CHAIN Options Lab
|
||||
should analyze for it (or pass null to unlink) — e.g. CL=F -> MCL:XCME. Adds the
|
||||
symbol to services.saxo_scheduler's watchlist automatically if it wasn't already
|
||||
there, so that chain starts getting snapshotted."""
|
||||
from services.database import set_instrument_watchlist_saxo_option_symbol
|
||||
ok = set_instrument_watchlist_saxo_option_symbol(ticker, body.saxo_symbol)
|
||||
if not ok:
|
||||
raise HTTPException(404, f"'{ticker}' is not in the instruments watchlist")
|
||||
return {"ticker": ticker.strip().upper(), "saxo_symbol": (body.saxo_symbol or "").strip().upper() or None}
|
||||
return {"ticker": ticker.strip().upper(), "saxo_option_symbol": (body.saxo_symbol or "").strip().upper() or None}
|
||||
|
||||
|
||||
@router.put("/{ticker}/saxo-quote-link")
|
||||
def set_saxo_quote_link(ticker: str, body: SaxoLinkBody):
|
||||
"""Link this tracked instrument to the Saxo symbol used to price it in the Cockpit
|
||||
(or pass null to unlink) — an accurate broker spot/futures feed instead of
|
||||
yfinance's unadjusted continuous-futures tickers. Independent of the options link
|
||||
above; often a different Saxo instrument."""
|
||||
from services.database import set_instrument_watchlist_saxo_quote_symbol
|
||||
ok = set_instrument_watchlist_saxo_quote_symbol(ticker, body.saxo_symbol)
|
||||
if not ok:
|
||||
raise HTTPException(404, f"'{ticker}' is not in the instruments watchlist")
|
||||
return {"ticker": ticker.strip().upper(), "saxo_quote_symbol": (body.saxo_symbol or "").strip().upper() or None}
|
||||
|
||||
@@ -183,10 +183,21 @@ def init_db():
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
added_at TEXT DEFAULT (datetime('now'))
|
||||
)""",
|
||||
# Optional link to a Saxo watchlist symbol (services.saxo_scheduler) — lets Options
|
||||
# Lab's Saxo section show only broker data for instruments the user actually tracks
|
||||
# here, instead of an independently-managed Saxo symbol list.
|
||||
# Two DISTINCT, independent Saxo links per tracked instrument — often different
|
||||
# Saxo instruments, not one shared symbol:
|
||||
# saxo_option_symbol — which Saxo options chain Options Lab should analyze for
|
||||
# this underlying (e.g. CL=F -> MCL:XCME, a micro futures option chain).
|
||||
# Feeds services.saxo_scheduler's watchlist so that chain gets snapshotted.
|
||||
# saxo_quote_symbol — which Saxo spot/futures instrument to price this
|
||||
# instrument FROM in the Cockpit, instead of yfinance's unadjusted continuous-
|
||||
# futures tickers (BZ=F, CL=F... see project_saxo_quote_source memory).
|
||||
# `saxo_symbol` (original single field, 2026-07-21) is superseded by
|
||||
# saxo_option_symbol below — kept as a column (not dropped) with its data copied
|
||||
# forward, so links set before the split aren't lost.
|
||||
"ALTER TABLE instruments_watchlist ADD COLUMN saxo_symbol TEXT",
|
||||
"ALTER TABLE instruments_watchlist ADD COLUMN saxo_option_symbol TEXT",
|
||||
"ALTER TABLE instruments_watchlist ADD COLUMN saxo_quote_symbol TEXT",
|
||||
"UPDATE instruments_watchlist SET saxo_option_symbol = saxo_symbol WHERE saxo_option_symbol IS NULL AND saxo_symbol IS NOT NULL",
|
||||
# Wavelets — saved optimization/simulation runs (ported from InstrumentSimulator's
|
||||
# WaveletOptimizationRun: form/results are free-form JSON blobs, not modeled relationally)
|
||||
"""CREATE TABLE IF NOT EXISTS wavelet_simulations (
|
||||
@@ -3274,19 +3285,22 @@ def remove_market_custom_ticker(ticker: str) -> bool:
|
||||
def get_instruments_watchlist() -> List[Dict]:
|
||||
conn = get_conn()
|
||||
rows = conn.execute(
|
||||
"SELECT ticker, name, asset_class, sort_order, added_at, saxo_symbol "
|
||||
"SELECT ticker, name, asset_class, sort_order, added_at, saxo_option_symbol, saxo_quote_symbol "
|
||||
"FROM instruments_watchlist ORDER BY sort_order ASC, added_at ASC"
|
||||
).fetchall()
|
||||
conn.close()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
def set_instrument_watchlist_saxo_symbol(ticker: str, saxo_symbol: Optional[str]) -> bool:
|
||||
"""Link (or unlink, if saxo_symbol is None/empty) a tracked instrument to a Saxo
|
||||
watchlist symbol. Linking also adds that symbol to services.saxo_scheduler's own
|
||||
watchlist if it isn't there yet, so it actually starts getting snapshotted. Unlinking
|
||||
does NOT remove it from the Saxo watchlist — it may still be wanted directly, or by
|
||||
another linked instrument; remove it from Config -> Saxo if it's truly no longer needed."""
|
||||
def set_instrument_watchlist_saxo_option_symbol(ticker: str, saxo_symbol: Optional[str]) -> bool:
|
||||
"""Link (or unlink, if saxo_symbol is None/empty) a tracked instrument to the Saxo
|
||||
symbol whose OPTIONS CHAIN Options Lab should analyze for it — e.g. CL=F -> MCL:XCME
|
||||
(a micro futures option chain), often NOT the same Saxo instrument as
|
||||
saxo_quote_symbol (the underlying's own spot/futures price feed). Linking also adds
|
||||
the symbol to services.saxo_scheduler's own watchlist if it isn't there yet, so its
|
||||
option chain actually starts getting snapshotted. Unlinking does NOT remove it from
|
||||
the Saxo watchlist — it may still be wanted directly, or by another linked
|
||||
instrument; remove it from Config -> Saxo if it's truly no longer needed."""
|
||||
from services.saxo_scheduler import get_watchlist, set_watchlist
|
||||
|
||||
ticker = ticker.upper()
|
||||
@@ -3297,7 +3311,7 @@ def set_instrument_watchlist_saxo_symbol(ticker: str, saxo_symbol: Optional[str]
|
||||
if row is None:
|
||||
conn.close()
|
||||
return False
|
||||
conn.execute("UPDATE instruments_watchlist SET saxo_symbol = ? WHERE ticker = ?", (saxo_symbol, ticker))
|
||||
conn.execute("UPDATE instruments_watchlist SET saxo_option_symbol = ? WHERE ticker = ?", (saxo_symbol, ticker))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
@@ -3308,6 +3322,25 @@ def set_instrument_watchlist_saxo_symbol(ticker: str, saxo_symbol: Optional[str]
|
||||
return True
|
||||
|
||||
|
||||
def set_instrument_watchlist_saxo_quote_symbol(ticker: str, saxo_symbol: Optional[str]) -> bool:
|
||||
"""Link (or unlink) a tracked instrument to the Saxo symbol used to price it in the
|
||||
Cockpit (services.saxo_client.get_saxo_quote_with_volatility), instead of yfinance's
|
||||
unadjusted continuous-futures tickers. No scheduler sync needed — chart bars are
|
||||
pulled live from Saxo per request, nothing to pre-accumulate."""
|
||||
ticker = ticker.upper()
|
||||
saxo_symbol = (saxo_symbol or "").strip().upper() or None
|
||||
|
||||
conn = get_conn()
|
||||
row = conn.execute("SELECT ticker FROM instruments_watchlist WHERE ticker = ?", (ticker,)).fetchone()
|
||||
if row is None:
|
||||
conn.close()
|
||||
return False
|
||||
conn.execute("UPDATE instruments_watchlist SET saxo_quote_symbol = ? WHERE ticker = ?", (saxo_symbol, ticker))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def add_instrument_watchlist(ticker: str, name: str = "", asset_class: str = "unknown") -> bool:
|
||||
conn = get_conn()
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user