Files
signal-platform/frontend/src/pages/SignalsPage.tsx
T
dennisthiessenandClaude Opus 5 13a984a84d refactor(signals): split the Track Record tab and cut the backtest page down
One tab stacked three things that all called themselves a track record:
realized paper P&L, setup-outcome grading under the rejected take-profit model,
and the backtest portfolio simulation. Split into Setups | Paper Trades |
Backtest, one subject each. `track` stays the Paper Trades slug so the legacy
/performance redirect keeps working. The grading diagnostic and its Evaluate /
Reset controls go with Backtest, not Paper Trades — reset_track_record deletes
trade_setups, not paper trades.

BacktestPanel 439 -> 175 lines. Its run settings alone were 106 lines of
hand-rolled sr-only radio cards for two binary choices; they are now two
Dropdowns and a button on one wrapping row, with the per-option prose moved into
the existing explainer. The amber warnings survive as a conditional slot, so a
non-default choice still announces itself but the common path is silent.

The recommendation printed eight findings at equal weight, burying the verdict
in tuning detail. `topic` now splits them: production, benchmark and robustness
stay inline, gate/exit/cutoff collapse behind a disclosure, and any WARNING or
LAGS item is promoted out of the collapsed group regardless of topic. No topic
chips — every backend string already self-prefixes, so a chip would render
"GATE | Gate: ...".

Portfolio metrics are now two tiers: five headline tiles for what the book
returned, then a smaller labelled row for how good that return was (Sortino,
Calmar (MAR), Gain/Pain, Profit Factor $, EV/trade). Reports cached before those
metrics existed hide the second row rather than showing a half-populated line of
dashes.

Extracted EquityCurveChart, PortfolioMonitorPanel and BacktestRecommendationCard,
plus a StatTile primitive and shared formatters for the duplication in the files
this touched. DashboardPage and OpenTradesPanel deliberately keep their own
copies — migrating them is separate scope.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:09:23 +02:00

63 lines
2.1 KiB
TypeScript

import type { ReactNode } from 'react';
import { useSearchParams } from 'react-router-dom';
import { PageHeader } from '../components/ui/PageHeader';
import { Tabs } from '../components/ui/Tabs';
import { SetupsPanel } from '../components/signals/SetupsPanel';
import { MyTradesPanel } from '../components/signals/MyTradesPanel';
import { BacktestPanel } from '../components/signals/BacktestPanel';
import { EvaluationPanel } from '../components/signals/EvaluationPanel';
const tabs = ['Setups', 'Paper Trades', 'Backtest'] as const;
type Tab = (typeof tabs)[number];
// `track` stays the Paper Trades slug: App.tsx redirects the legacy /performance
// route to ?tab=track, and that is where realized results live.
const SLUG_TO_TAB: Record<string, Tab> = {
track: 'Paper Trades',
backtest: 'Backtest',
};
const TAB_TO_SLUG: Record<Tab, string> = {
Setups: '',
'Paper Trades': 'track',
Backtest: 'backtest',
};
const SUBTITLE: Record<Tab, string> = {
Setups: 'Detected trade setups from the latest scan',
'Paper Trades': 'What the strategy actually delivered on trades you took',
Backtest: 'Whether the promoted strategy is worth trading, replayed over history',
};
export default function SignalsPage() {
const [searchParams, setSearchParams] = useSearchParams();
const activeTab: Tab = SLUG_TO_TAB[searchParams.get('tab') ?? ''] ?? 'Setups';
const setTab = (tab: Tab) => {
const slug = TAB_TO_SLUG[tab];
setSearchParams(slug ? { tab: slug } : {}, { replace: true });
};
const body: Record<Tab, ReactNode> = {
Setups: <SetupsPanel />,
'Paper Trades': <MyTradesPanel />,
// The backtest and the diagnostic that checks it against live outcomes.
Backtest: (
<div className="space-y-6">
<BacktestPanel />
<EvaluationPanel />
</div>
),
};
return (
<div className="space-y-6 animate-slide-up">
<PageHeader title="Signals" subtitle={SUBTITLE[activeTab]} />
<Tabs tabs={tabs} active={activeTab} onChange={setTab} />
<div className="animate-fade-in" key={activeTab}>
{body[activeTab]}
</div>
</div>
);
}