Simplify top setups table
Deploy / lint (push) Successful in 7s
Deploy / test (push) Successful in 1m17s
Deploy / deploy (push) Successful in 37s

This commit is contained in:
2026-07-04 13:57:42 +02:00
parent 65335cf1f3
commit 61156684ff
+25 -16
View File
@@ -13,7 +13,6 @@ import { OpenTradesPanel } from '../components/dashboard/OpenTradesPanel';
import { SkeletonCard, SkeletonTable } from '../components/ui/Skeleton'; import { SkeletonCard, SkeletonTable } from '../components/ui/Skeleton';
import { formatPrice } from '../lib/format'; import { formatPrice } from '../lib/format';
import { tradePnl } from '../lib/paperTrade'; import { tradePnl } from '../lib/paperTrade';
import { recommendationActionLabel } from '../lib/recommendation';
import { qualifiesSetup, activationSummary, primaryTargetProbability } from '../lib/qualification'; import { qualifiesSetup, activationSummary, primaryTargetProbability } from '../lib/qualification';
import type { TradeSetup } from '../lib/types'; import type { TradeSetup } from '../lib/types';
@@ -60,6 +59,19 @@ function DirectionTag({ direction }: { direction: string }) {
); );
} }
function convictionLabel(action: TradeSetup['recommended_action']): string {
if (!action || action === 'NEUTRAL') return '—';
if (action.endsWith('_HIGH')) return 'High';
if (action.endsWith('_MODERATE')) return 'Moderate';
return '—';
}
function convictionClass(action: TradeSetup['recommended_action']): string {
if (action?.endsWith('_HIGH')) return 'text-emerald-300';
if (action?.endsWith('_MODERATE')) return 'text-amber-300';
return 'text-gray-400';
}
export default function DashboardPage() { export default function DashboardPage() {
const trades = useTrades(); const trades = useTrades();
const watchlist = useWatchlist(); const watchlist = useWatchlist();
@@ -200,9 +212,9 @@ export default function DashboardPage() {
<div className="xl:col-span-3"> <div className="xl:col-span-3">
<Section <Section
title="Top Setups" title="Top Setups"
hint="qualified by residual momentum, ranked by production score" hint="current qualified signals"
> >
{trades.isLoading && <SkeletonTable rows={5} cols={5} />} {trades.isLoading && <SkeletonTable rows={5} cols={7} />}
{trades.isError && <Callout variant="error">Failed to load setups</Callout>} {trades.isError && <Callout variant="error">Failed to load setups</Callout>}
{trades.data && topSetups.length === 0 && ( {trades.data && topSetups.length === 0 && (
<Callout variant="empty">No qualified actionable setups right now.</Callout> <Callout variant="empty">No qualified actionable setups right now.</Callout>
@@ -214,17 +226,17 @@ export default function DashboardPage() {
<tr className="border-b border-white/[0.06] text-left text-xs uppercase tracking-wider text-gray-500"> <tr className="border-b border-white/[0.06] text-left text-xs uppercase tracking-wider text-gray-500">
<th className="px-4 py-3">Ticker</th> <th className="px-4 py-3">Ticker</th>
<th className="px-4 py-3">Dir</th> <th className="px-4 py-3">Dir</th>
<th className="px-4 py-3 text-right">Entry</th> <th className="px-4 py-3 text-right">Current / Target</th>
<th className="px-4 py-3 text-right">R:R</th> <th className="px-4 py-3 text-right">R:R</th>
<th className="px-4 py-3 text-right">Target&nbsp;Prob</th> <th className="px-4 py-3 text-right">Target&nbsp;Prob</th>
<th className="px-4 py-3 text-right">Prod. Rank</th>
<th className="px-4 py-3 text-right">Residual</th> <th className="px-4 py-3 text-right">Residual</th>
<th className="hidden px-4 py-3 md:table-cell">Action</th> <th className="hidden px-4 py-3 md:table-cell">Conviction</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{topSetups.map((setup, i) => { {topSetups.map((setup, i) => {
const isTopPick = i === 0; const isTopPick = i === 0;
const displayedPrice = setup.current_price ?? setup.entry_price;
return ( return (
<tr <tr
key={setup.id} key={setup.id}
@@ -250,7 +262,10 @@ export default function DashboardPage() {
)} )}
</td> </td>
<td className="px-4 py-3"><DirectionTag direction={setup.direction} /></td> <td className="px-4 py-3"><DirectionTag direction={setup.direction} /></td>
<td className="num px-4 py-3 text-right text-gray-200">{formatPrice(setup.entry_price)}</td> <td className="num px-4 py-3 text-right">
<div className="text-gray-200">{formatPrice(displayedPrice)}</div>
<div className="text-[11px] text-gray-500">target {formatPrice(setup.target)}</div>
</td>
<td className="num px-4 py-3 text-right text-gray-200">{setup.rr_ratio.toFixed(1)}:1</td> <td className="num px-4 py-3 text-right text-gray-200">{setup.rr_ratio.toFixed(1)}:1</td>
<td className="num px-4 py-3 text-right text-gray-200"> <td className="num px-4 py-3 text-right text-gray-200">
{(() => { {(() => {
@@ -258,24 +273,18 @@ export default function DashboardPage() {
return p != null ? `${Math.round(p)}%` : '—'; return p != null ? `${Math.round(p)}%` : '—';
})()} })()}
</td> </td>
<td className="num px-4 py-3 text-right font-semibold text-gray-200">
{setup.strategy_rank != null ? `${Math.round(setup.strategy_rank)}%ile` : '—'}
</td>
<td className="num px-4 py-3 text-right text-gray-400"> <td className="num px-4 py-3 text-right text-gray-400">
{setup.momentum_percentile != null ? `${Math.round(setup.momentum_percentile)}%ile` : '—'} {setup.momentum_percentile != null ? `${Math.round(setup.momentum_percentile)}%ile` : '—'}
</td> </td>
<td className="hidden px-4 py-3 text-xs text-gray-400 md:table-cell"> <td className={`hidden px-4 py-3 text-xs font-semibold md:table-cell ${convictionClass(setup.recommended_action)}`}>
{recommendationActionLabel(setup.recommended_action)} {convictionLabel(setup.recommended_action)}
</td> </td>
</tr> </tr>
); );
})} })}
</tbody> </tbody>
</table> </table>
<div className="flex items-center justify-between border-t border-white/[0.04] px-4 py-2.5"> <div className="flex justify-end border-t border-white/[0.04] px-4 py-2.5">
<span className="text-[11px] text-gray-500">
Production rank = 80% residual momentum + 20% realized volatility; residual still gates qualification.
</span>
<Link to="/signals" className="text-xs font-medium text-blue-300 hover:text-blue-200 transition-colors"> <Link to="/signals" className="text-xs font-medium text-blue-300 hover:text-blue-200 transition-colors">
All setups All setups
</Link> </Link>