fix(backtest): flag a lookback the recommendation was not computed on
Selecting a different window or a comparison strategy silently made the tiles stop matching the recommendation below, which is baked into the report and cannot follow a dropdown. On load they now agree by construction; moving off that basis says so. Also: an absent production row produced no headline and no benchmark, but any passing gate finding still rendered a green "no warnings" chip — a success badge for missing data, directly beside "this report predates the portfolio monitor". Missing baseline now reads "baseline unavailable". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,8 +42,19 @@ export function BacktestPanel() {
|
|||||||
const monitor = report?.portfolio_monitor ?? null;
|
const monitor = report?.portfolio_monitor ?? null;
|
||||||
const activeStrategy =
|
const activeStrategy =
|
||||||
selectedStrategy || monitor?.production_strategy || monitor?.strategies[0]?.strategy || '';
|
selectedStrategy || monitor?.production_strategy || monitor?.strategies[0]?.strategy || '';
|
||||||
|
// Default to the window the recommendation was computed on, so the tiles and
|
||||||
|
// the recommendation never open showing different numbers. They used to: the
|
||||||
|
// backend preferred "all" while this defaulted to "3y". The 3y fallback is
|
||||||
|
// only for reports predating basis_lookback.
|
||||||
|
const basisLookback = report?.recommendation?.basis_lookback ?? null;
|
||||||
const activeLookback =
|
const activeLookback =
|
||||||
selectedLookback || (monitor?.lookbacks.some((l) => l.lookback === '3y') ? '3y' : monitor?.lookbacks[0]?.lookback) || '';
|
selectedLookback ||
|
||||||
|
(basisLookback && monitor?.lookbacks.some((l) => l.lookback === basisLookback)
|
||||||
|
? basisLookback
|
||||||
|
: monitor?.lookbacks.some((l) => l.lookback === '3y')
|
||||||
|
? '3y'
|
||||||
|
: monitor?.lookbacks[0]?.lookback) ||
|
||||||
|
'';
|
||||||
const monitorRun = useMemo(
|
const monitorRun = useMemo(
|
||||||
() =>
|
() =>
|
||||||
monitor?.runs.find((row) => row.strategy === activeStrategy && row.lookback === activeLookback) ??
|
monitor?.runs.find((row) => row.strategy === activeStrategy && row.lookback === activeLookback) ??
|
||||||
@@ -175,6 +186,9 @@ export function BacktestPanel() {
|
|||||||
activeLookback={activeLookback}
|
activeLookback={activeLookback}
|
||||||
onStrategyChange={setSelectedStrategy}
|
onStrategyChange={setSelectedStrategy}
|
||||||
onLookbackChange={setSelectedLookback}
|
onLookbackChange={setSelectedLookback}
|
||||||
|
basisLookback={basisLookback}
|
||||||
|
basisLookbackLabel={report.recommendation?.basis_lookback_label ?? null}
|
||||||
|
productionStrategy={monitor?.production_strategy ?? null}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{report.recommendation && (
|
{report.recommendation && (
|
||||||
|
|||||||
@@ -81,7 +81,16 @@ export function BacktestRecommendationCard({
|
|||||||
<div className="glass border border-blue-400/20 p-4">
|
<div className="glass border border-blue-400/20 p-4">
|
||||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||||
<p className="section-index">What this backtest recommends</p>
|
<p className="section-index">What this backtest recommends</p>
|
||||||
{warningCount > 0 ? (
|
{/* 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 ? (
|
||||||
|
<span className="rounded-full border border-white/15 bg-white/[0.05] px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-gray-400">
|
||||||
|
baseline unavailable
|
||||||
|
</span>
|
||||||
|
) : warningCount > 0 ? (
|
||||||
<span className="rounded-full border border-amber-400/40 bg-amber-400/10 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-amber-300">
|
<span className="rounded-full border border-amber-400/40 bg-amber-400/10 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-amber-300">
|
||||||
⚠ {warningCount} warning{warningCount > 1 ? 's' : ''}
|
⚠ {warningCount} warning{warningCount > 1 ? 's' : ''}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ export function PortfolioMonitorPanel({
|
|||||||
activeLookback,
|
activeLookback,
|
||||||
onStrategyChange,
|
onStrategyChange,
|
||||||
onLookbackChange,
|
onLookbackChange,
|
||||||
|
basisLookback = null,
|
||||||
|
basisLookbackLabel = null,
|
||||||
|
productionStrategy = null,
|
||||||
}: {
|
}: {
|
||||||
monitor: BacktestPortfolioMonitor | null | undefined;
|
monitor: BacktestPortfolioMonitor | null | undefined;
|
||||||
monitorRun: BacktestPortfolioMonitorRun | null | undefined;
|
monitorRun: BacktestPortfolioMonitorRun | null | undefined;
|
||||||
@@ -37,6 +40,10 @@ export function PortfolioMonitorPanel({
|
|||||||
activeLookback: string;
|
activeLookback: string;
|
||||||
onStrategyChange: (v: string) => void;
|
onStrategyChange: (v: string) => void;
|
||||||
onLookbackChange: (v: string) => void;
|
onLookbackChange: (v: string) => void;
|
||||||
|
/** The window the recommendation below was computed on. */
|
||||||
|
basisLookback?: string | null;
|
||||||
|
basisLookbackLabel?: string | null;
|
||||||
|
productionStrategy?: string | null;
|
||||||
}) {
|
}) {
|
||||||
if (!monitor || !monitorRun) {
|
if (!monitor || !monitorRun) {
|
||||||
return (
|
return (
|
||||||
@@ -90,6 +97,21 @@ export function PortfolioMonitorPanel({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* The recommendation below is baked into the report and cannot follow a
|
||||||
|
dropdown. On load the two agree by construction; say so plainly the
|
||||||
|
moment a selection moves off that basis. */}
|
||||||
|
{((basisLookback && activeLookback !== basisLookback) ||
|
||||||
|
(productionStrategy && activeStrategy !== productionStrategy)) && (
|
||||||
|
<p className="text-[11px] text-amber-300/80">
|
||||||
|
Showing{' '}
|
||||||
|
{productionStrategy && activeStrategy !== productionStrategy
|
||||||
|
? 'a comparison strategy'
|
||||||
|
: 'a different window'}
|
||||||
|
. The recommendation below is computed on the production strategy over{' '}
|
||||||
|
{basisLookbackLabel ?? basisLookback} — these tiles will not match it.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Tier 1 — what the book returned. */}
|
{/* Tier 1 — what the book returned. */}
|
||||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-5">
|
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-5">
|
||||||
<StatTile
|
<StatTile
|
||||||
|
|||||||
@@ -336,6 +336,13 @@ export interface BacktestCurvePoint {
|
|||||||
export interface BacktestRecommendation {
|
export interface BacktestRecommendation {
|
||||||
headline: string | null;
|
headline: string | null;
|
||||||
items: { topic: string; text: string }[];
|
items: { topic: string; text: string }[];
|
||||||
|
/**
|
||||||
|
* The monitor lookback every production/benchmark figure was read from. The
|
||||||
|
* page defaults its selector to this so the tiles and the recommendation
|
||||||
|
* cannot open on different windows. Absent on reports predating the field.
|
||||||
|
*/
|
||||||
|
basis_lookback?: string | null;
|
||||||
|
basis_lookback_label?: string | null;
|
||||||
note?: string;
|
note?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user