import { useMemo, useState } from 'react'; import { Link } from 'react-router-dom'; import { useActivation } from '../hooks/useActivation'; import { useRankings } from '../hooks/useScores'; import { useTrades } from '../hooks/useTrades'; import { useWatchlist } from '../hooks/useWatchlist'; import { usePaperTrades } from '../hooks/usePaperTrades'; import { useTickerNames } from '../hooks/useTickers'; import { Callout } from '../components/ui/Callout'; import { Section } from '../components/ui/Section'; import { OpenTradesPanel } from '../components/dashboard/OpenTradesPanel'; import { PerfChart } from '../components/dashboard/PerfChart'; import { PriceRail, RadarChart, radarAxesFromDimensions } from '../components/charts/horizon'; import type { RadarAxis } from '../components/charts/horizon'; import { SkeletonCard, SkeletonTable } from '../components/ui/Skeleton'; import { tradePnl } from '../lib/paperTrade'; import { qualifiesSetup, disqualifyReason, activationSummary, primaryTargetProbability, } from '../lib/qualification'; import type { TradeSetup } from '../lib/types'; function fmtR(value: number | null): string { if (value === null) return '—'; return `${value > 0 ? '+' : ''}${value.toFixed(2)}R`; } function rColor(value: number | null): string { if (value === null) return 'text-gray-400'; if (value > 0) return 'text-emerald-300'; if (value < 0) return 'text-red-300'; return 'text-gray-300'; } function money(value: number): string { const sign = value >= 0 ? '+' : '−'; return `${sign}$${Math.abs(value).toFixed(2)}`; } function Metric({ label, value, sub, valueClass = 'text-gray-100' }: { label: string; value: string; sub?: string; valueClass?: string; }) { return (
{label}
{value}
{sub &&{sub}
}reward / risk
{setup.rr_ratio.toFixed(1)} : 1
target probability
{Math.round(prob)} %
{today}
{qualifiedCount} qualified of {liveCount} live setups · {exposure.count} open position{exposure.count === 1 ? '' : 's'} {exposure.rPriced > 0 && ( <> {' · '} {fmtR(exposure.unrealR)} unrealized > )}
)}Watchlist empty — open any ticker and tap ☆ to add it.
) )} {/* Setup in focus — full width; select a radar row below to swap it */}qualified fingerprints · hover a corner for scores
None clear the gate today — the closest candidates are below.
)} {/* Below the gate, collapsed by default when there are qualified setups */} {radar.below.length > 0 && ( <> {showBelow && (