feat: cockpit

This commit is contained in:
OpenSquared
2026-07-24 10:33:35 +02:00
parent 02a7cc2bd6
commit 2e5326f8bf
2 changed files with 29 additions and 3 deletions

View File

@@ -146,7 +146,15 @@ export const useAddWatchlistInstrument = () => {
export const useInstrumentCatalogIds = () =>
useQuery({
queryKey: ['instrument-analysis-catalog'],
queryFn: () => api.get('/instruments/').then(r => new Set((r.data ?? []).map((i: any) => String(i.id).toUpperCase()))),
// No trailing slash: the backend route is registered at "/api/instruments" exactly
// (router.get("")) — a trailing slash still resolves via FastAPI's redirect_slashes,
// but calling the exact path avoids depending on that 307 round-trip surviving the
// nginx proxy_pass unchanged.
queryFn: () => api.get('/instruments').then(r => {
const ids = new Set<string>((r.data ?? []).map((i: any) => String(i.id).toUpperCase()))
console.debug('[useInstrumentCatalogIds] loaded', ids.size, 'catalog ids:', Array.from(ids))
return ids
}),
staleTime: 10 * 60_000,
})

View File

@@ -1,4 +1,4 @@
import { useMemo, useState, useRef, useLayoutEffect } from 'react'
import { useMemo, useState, useRef, useLayoutEffect, useEffect } from 'react'
import { useQuery } from '@tanstack/react-query'
import {
useGeoRiskScore, useAllQuotes,
@@ -132,7 +132,7 @@ export default function Dashboard() {
const { data: geoNews } = useGeoNews()
const { data: watchlistItems } = useInstrumentsWatchlist()
const { data: watchlistQuotesData } = useInstrumentsWatchlistQuotes()
const { data: instrumentCatalogIds } = useInstrumentCatalogIds()
const { data: instrumentCatalogIds, isError: instrumentCatalogError, error: instrumentCatalogErrorObj } = useInstrumentCatalogIds()
const [ecoImpactFilter, setEcoImpactFilter] = useState<Set<string>>(new Set(['high', 'medium', 'low']))
const [ecoCurrencyFilter, setEcoCurrencyFilter] = useState<Set<string>>(new Set(ECO_CURRENCY_FILTERS))
const [ecoShowNextWeek, setEcoShowNextWeek] = useState(false)
@@ -145,6 +145,24 @@ export default function Dashboard() {
const { data: waveletSignalsData } = useWaveletWatchlistSignals()
const { data: saxoIvWatchlistData } = useSaxoIvWatchlist()
// Diagnostic for the Watchlist card's "open in Instrument Analysis" arrow/double-click:
// logs whether the catalog query succeeded and how each watchlist ticker resolves
// against it, so a missing arrow can be traced to "catalog query failed" vs. "ticker
// genuinely isn't registered in instruments.json" without needing a debugger.
useEffect(() => {
if (instrumentCatalogError) {
console.error('[Watchlist] instrument catalog query failed:', instrumentCatalogErrorObj)
return
}
if (!instrumentCatalogIds || !watchlistQuotesData) return
const items: any[] = (watchlistQuotesData as any)?.items ?? []
console.debug('[Watchlist] catalog ids:', Array.from(instrumentCatalogIds as Set<string>))
console.debug('[Watchlist] ticker -> catalogId resolution:', items.map((it: any) => ({
ticker: it.ticker,
resolved: resolveCatalogId(it.ticker, instrumentCatalogIds as Set<string>) ?? null,
})))
}, [instrumentCatalogIds, instrumentCatalogError, instrumentCatalogErrorObj, watchlistQuotesData])
// Historical PnL curve (realized, from closed portfolio positions) + latest VaR snapshot
const { data: pnlHistoryData } = usePnlHistory()
const { data: latestVarData } = useQuery({