feat: saxo price
This commit is contained in:
@@ -187,7 +187,7 @@ def delete_history(symbol: str):
|
||||
|
||||
|
||||
class CatalogRefreshRequest(BaseModel):
|
||||
asset_types: Optional[List[str]] = None # default: CATALOG_ASSET_TYPES (FuturesOption, FxVanillaOption)
|
||||
asset_types: Optional[List[str]] = None # default: CATALOG_ASSET_TYPES (FuturesOption, FxVanillaOption, ContractFutures, FxSpot)
|
||||
|
||||
|
||||
@router.post("/catalog/refresh")
|
||||
|
||||
@@ -6387,12 +6387,15 @@ def upsert_saxo_catalog_rows(rows: List[Dict[str, Any]]):
|
||||
|
||||
|
||||
def get_saxo_catalog(asset_type: Optional[str] = None, q: Optional[str] = None, limit: int = 200) -> List[Dict[str, Any]]:
|
||||
"""asset_type accepts a single value or a comma-separated list (e.g. the Instruments
|
||||
Watchlist "Option" picker passes all 4 option AssetTypes at once)."""
|
||||
conn = get_conn()
|
||||
query = "SELECT * FROM saxo_instrument_catalog WHERE 1=1"
|
||||
params: List[Any] = []
|
||||
if asset_type:
|
||||
query += " AND asset_type=?"
|
||||
params.append(asset_type)
|
||||
types = [t.strip() for t in asset_type.split(",") if t.strip()]
|
||||
query += f" AND asset_type IN ({','.join('?' * len(types))})"
|
||||
params.extend(types)
|
||||
if q:
|
||||
query += " AND (symbol LIKE ? OR description LIKE ?)"
|
||||
params.extend([f"%{q}%", f"%{q}%"])
|
||||
|
||||
@@ -60,7 +60,17 @@ def _synthesize_quote(
|
||||
|
||||
# Bounded, stable catalogs worth fully caching in our own DB (StockOption/StockIndexOption
|
||||
# are far too large to bulk-fetch — those stay resolved on demand via Keywords search).
|
||||
CATALOG_ASSET_TYPES = ["FuturesOption", "FxVanillaOption"]
|
||||
# ContractFutures (outright futures — Brent, WTI, Gold, Copper, index e-minis) and FxSpot
|
||||
# (FX pairs) are the underlying-side equivalents of the option types above — added so the
|
||||
# Instruments Watchlist "Quote" link (services.database.set_instrument_watchlist_saxo_quote_symbol)
|
||||
# has a real catalog to search instead of only ever finding option instruments.
|
||||
CATALOG_ASSET_TYPES = ["FuturesOption", "FxVanillaOption", "ContractFutures", "FxSpot"]
|
||||
|
||||
# Which of CATALOG_ASSET_TYPES are options (Instruments Watchlist "Option" picker) vs.
|
||||
# underlyings (the "Quote" picker) — see routers/instruments_watchlist.py's saxo-*-link
|
||||
# endpoints and frontend/src/pages/Config.tsx's SaxoLinkPicker.
|
||||
OPTION_ASSET_TYPES = ["FuturesOption", "FxVanillaOption", "StockOption", "StockIndexOption"]
|
||||
UNDERLYING_ASSET_TYPES = ["ContractFutures", "FxSpot", "StockIndex"]
|
||||
|
||||
# symbol -> resolved instrument details, cheap in-process cache (roots don't change within a session)
|
||||
_root_uic_cache: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
@@ -338,9 +338,20 @@ function RiskProfilesCard() {
|
||||
// scheduler's watchlist (Config -> Saxo below) so it gets snapshotted.
|
||||
// 'quote' — which Saxo spot/futures symbol prices this instrument in the Cockpit,
|
||||
// replacing yfinance's unadjusted continuous-futures tickers.
|
||||
// Asset-type filters matching backend services.saxo_client.OPTION_ASSET_TYPES /
|
||||
// UNDERLYING_ASSET_TYPES — keeps each picker's search scoped to the catalog rows that
|
||||
// are actually relevant, instead of both searching the same undifferentiated list.
|
||||
const SAXO_LINK_KIND_META = {
|
||||
option: { label: 'Option', placeholder: 'Search Saxo option symbol…', activeCls: 'text-emerald-400 hover:text-emerald-300 bg-emerald-900/20 border-emerald-700/30' },
|
||||
quote: { label: 'Quote', placeholder: 'Search Saxo spot/futures symbol…', activeCls: 'text-sky-400 hover:text-sky-300 bg-sky-900/20 border-sky-700/30' },
|
||||
option: {
|
||||
label: 'Option', placeholder: 'Search Saxo option symbol…',
|
||||
activeCls: 'text-emerald-400 hover:text-emerald-300 bg-emerald-900/20 border-emerald-700/30',
|
||||
assetTypes: 'FuturesOption,FxVanillaOption,StockOption,StockIndexOption',
|
||||
},
|
||||
quote: {
|
||||
label: 'Quote', placeholder: 'Search Saxo spot/futures symbol…',
|
||||
activeCls: 'text-sky-400 hover:text-sky-300 bg-sky-900/20 border-sky-700/30',
|
||||
assetTypes: 'ContractFutures,FxSpot,StockIndex',
|
||||
},
|
||||
} as const
|
||||
|
||||
function SaxoLinkPicker({ ticker, kind, saxoSymbol }: { ticker: string; kind: keyof typeof SAXO_LINK_KIND_META; saxoSymbol: string | null }) {
|
||||
@@ -350,8 +361,8 @@ function SaxoLinkPicker({ ticker, kind, saxoSymbol }: { ticker: string; kind: ke
|
||||
const optionLink = useSetWatchlistSaxoOptionLink()
|
||||
const quoteLink = useSetWatchlistSaxoQuoteLink()
|
||||
const { mutate: setLink, isPending } = kind === 'option' ? optionLink : quoteLink
|
||||
const { data: catalogMatches } = useSaxoCatalog(undefined, value.length >= 2 ? value : undefined)
|
||||
const meta = SAXO_LINK_KIND_META[kind]
|
||||
const { data: catalogMatches } = useSaxoCatalog(meta.assetTypes, value.length >= 2 ? value : undefined)
|
||||
|
||||
const save = (sym?: string) => {
|
||||
const resolved = (sym ?? value).trim().toUpperCase() || null
|
||||
|
||||
Reference in New Issue
Block a user