feat: cockpit

This commit is contained in:
OpenSquared
2026-07-24 15:52:58 +02:00
parent 3ec0c024ad
commit 7cb81cbd65
6 changed files with 211 additions and 19 deletions

View File

@@ -213,6 +213,31 @@ export default function Dashboard() {
const lastCycle = (cycleStatusData as any)?.last_cycle ?? null
const openPositions: any[] = (openPositionsData as any) ?? []
// Trades Overview shows each position's trading-side underlying (yfinance/futures
// convention: ^GSPC, ^NDX, GC=F, HG=F, EURUSD=X...), which reads as noise next to the
// Cockpit's own friendly Watchlist names for the exact same instrument (SP500, NASDAQ,
// GOLD, COPPER, EURUSD). Rename to the Watchlist ticker when one plausibly matches —
// suffix-stripped (EURUSD=X -> EURUSD) or via a small curated root-ticker alias table —
// and only if that name is actually in the current Watchlist (not just "known"), so this
// stays tied to what's configured rather than a static list.
const UNDERLYING_ALIASES: Record<string, string> = {
'^GSPC': 'SP500', '^NDX': 'NASDAQ', '^DJI': 'DOW', '^RUT': 'RUSSELL2000',
'GC=F': 'GOLD', 'SI=F': 'SILVER', 'HG=F': 'COPPER', 'PL=F': 'PLATINUM',
'CL=F': 'CRUDE', 'BZ=F': 'BRENT', 'NG=F': 'NATGAS',
'ZW=F': 'WHEAT', 'ZC=F': 'CORN', 'ZS=F': 'SOYBEANS',
}
const underlyingDisplayName = (underlying: string): string => {
if (!underlying) return underlying
const upper = underlying.toUpperCase()
const watchlistTickers = new Set(((watchlistItems as any[]) ?? []).map(w => w.ticker.toUpperCase()))
const baseTicker = upper.replace(/(=X|=F)$/, '')
if (watchlistTickers.has(upper)) return upper
if (watchlistTickers.has(baseTicker)) return baseTicker
const alias = UNDERLYING_ALIASES[upper]
if (alias && watchlistTickers.has(alias)) return alias
return underlying
}
// Patterns from the last cycle only (filter by created_at >= cycle started_at)
const cyclePatterns = useMemo(() => {
const started = lastCycle?.started_at ?? null
@@ -1013,7 +1038,7 @@ export default function Dashboard() {
{/* ── Command Center: Intelligence & Contexte ── */}
<div className="grid grid-cols-4 gap-3">
<TradeRankList trades={openPositions} mode="recent" title="🏆 Trades Overview" linkTo="/portfolio" />
<TradeRankList trades={openPositions} mode="recent" title="🏆 Trades Overview" linkTo="/portfolio" tickerLabel={underlyingDisplayName} />
{/* Wavelets Signal — latest per-ticker signal from the watchlist scan (computed each
cycle). Single click on the card = Wavelets Simulation overview; double-click a
@@ -1035,7 +1060,10 @@ export default function Dashboard() {
{signals.slice(0, 5).map((s: any, i: number) => (
<div
key={i}
onDoubleClick={() => navigate(`/instruments/${encodeURIComponent(s.ticker)}?tab=wavelets`)}
onDoubleClick={() => quickAddInstrument.mutate(s.ticker, {
onSuccess: ({ id }) => navigate(`/instruments/${encodeURIComponent(id)}?tab=wavelets`),
onError: (e) => console.error(`[WaveletsSignal] failed to open ${s.ticker} in Instrument Analysis:`, e),
})}
title="Double-click to open in Instrument Analysis → Wavelets"
className="flex items-center gap-1.5 text-[10px] rounded px-1 -mx-1 py-0.5 cursor-pointer hover:bg-dark-700/40 transition-colors"
>