import { Disclosure } from '../ui/Disclosure'; import type { BacktestRecommendation } from '../../lib/types'; /** * The verdict, ahead of the tuning detail. * * Two problems this solves. All eight findings used to render as equal-weight * bullets, so "does this strategy work" sat in the same register as "which * cutoff scored best". And the headline — which is a *description of the * config*, not a verdict — was the loudest thing on the card while every actual * finding was small grey text. * * So: findings first, each split into a label and its detail; the config * description demoted to a footer where it belongs. */ const PRIMARY_TOPICS = new Set(['production', 'benchmark', 'robustness']); /** * Mirrors how the backend phrases a bad result — `_build_recommendation` emits * "Robustness WARNING: …" and "Book vs SPY: LAGS …". There is deliberately no * `severity` field on the payload; if that changes, this is the one place to fix. */ function isWarning(text: string): boolean { return text.includes('WARNING') || text.includes('LAGS'); } /** * Every backend string self-prefixes ("Gate: keep the R:R floor…"), so the * prefix IS the label — no need for a chip that would just repeat it, and no * need to reword anything server-side. Split on the first colon; if a string * ever stops carrying one, it renders whole as detail. */ function splitLabel(text: string): { label: string | null; detail: string } { const at = text.indexOf(': '); if (at === -1 || at > 48) return { label: null, detail: text }; return { label: text.slice(0, at), detail: text.slice(at + 2) }; } function Finding({ text, primary }: { text: string; primary: boolean }) { const warn = isWarning(text); const { label, detail } = splitLabel(text); return (
  • {label && ( {label} )} {detail}
  • ); } export function BacktestRecommendationCard({ recommendation, }: { recommendation: BacktestRecommendation; }) { const items = recommendation.items; if (items.length === 0) return null; // A warning is always visible, whatever its topic — burying "the edge // disappears without the top 5% of winners" behind a disclosure would defeat // the point of surfacing it at all. const primary = items.filter((i) => PRIMARY_TOPICS.has(i.topic) || isWarning(i.text)); const secondary = items.filter((i) => !PRIMARY_TOPICS.has(i.topic) && !isWarning(i.text)); const warningCount = items.filter((i) => isWarning(i.text)).length; return (

    What this backtest recommends

    {/* No headline means the backend found no production monitor row, so nothing here describes the production book. Zero keyword warnings is then absence of data, not a clean bill of health — a green chip beside "this report predates the portfolio monitor" would be a success badge for missing data. */} {!recommendation.headline ? ( baseline unavailable ) : warningCount > 0 ? ( ⚠ {warningCount} warning{warningCount > 1 ? 's' : ''} ) : ( no warnings )}
    {primary.length > 0 && ( )} {/* The config description, demoted: it says what the strategy IS, which is context for the findings above rather than a finding itself. */} {recommendation.headline && (

    Configuration under test

    {recommendation.headline}

    )} {recommendation.note && (

    {recommendation.note}

    )}
    {/* Outside the card body on purpose: Disclosure renders its own glass-sm panel, so nesting it inside the bordered card double-frames it. */} {secondary.length > 0 && ( )}
    ); }