import { useMutation, useQueryClient } from '@tanstack/react-query'; import { usePerformance } from '../../hooks/usePerformance'; import { useBacktestReport } from '../../hooks/useMarketRegime'; import { triggerJob, resetTrackRecord } from '../../api/admin'; import { Button } from '../ui/Button'; import { Disclosure } from '../ui/Disclosure'; import { useToast } from '../ui/Toast'; import { BacktestPanel } from './BacktestPanel'; import { MyTradesPanel } from './MyTradesPanel'; // Need at least this many matured setups before the pipeline check means anything; // below it the live sample is too noisy to compare. const MIN_MATURED = 20; // Live expectancy this far (in R) below the backtest counts as drift, not noise. const DRIFT_TOLERANCE_R = 0.2; type PipelineStatus = 'building' | 'tracking' | 'drift' | 'no-backtest'; 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-400'; if (value < 0) return 'text-red-400'; return 'text-gray-300'; } function StatusChip({ status }: { status: PipelineStatus }) { const styles: Record = { tracking: { cls: 'border-emerald-500/30 bg-emerald-500/15 text-emerald-300', label: '✓ in sync' }, drift: { cls: 'border-amber-500/30 bg-amber-500/15 text-amber-300', label: '⚠ drift' }, building: { cls: 'border-white/10 bg-white/[0.05] text-gray-400', label: 'building' }, 'no-backtest': { cls: 'border-white/10 bg-white/[0.05] text-gray-400', label: 'no backtest' }, }; const s = styles[status]; return {s.label}; } export function TrackRecordPanel() { const queryClient = useQueryClient(); const toast = useToast(); // Setup-outcome pipeline check: does the live outcome evaluator reproduce the // backtest's target/stop grading? Both sides use the SAME target/stop/expired // model — this is a plumbing/QA signal (no look-ahead, config or data drift), // NOT validation of the ATR-trail production strategy shown in the monitor. const { data: perf } = usePerformance({ qualified_only: true }); const { data: report } = useBacktestReport(); const liveAvgR = perf?.overall.avg_r ?? null; const liveN = perf?.overall.total ?? 0; const btAvgR = report?.overall_qualified.avg_r ?? null; let status: PipelineStatus = 'building'; if (liveAvgR != null && liveN >= MIN_MATURED) { status = btAvgR == null ? 'no-backtest' : liveAvgR >= btAvgR - DRIFT_TOLERANCE_R ? 'tracking' : 'drift'; } const statusNote: Record = { building: `Fewer than ~${MIN_MATURED} matured setups so far — too few to compare.`, 'no-backtest': 'Run the backtest to get a target/stop baseline to check against.', tracking: "Live setup outcomes are resolving in line with the backtest's target/stop model — the outcome-evaluation pipeline shows no look-ahead, config or data drift. (Checks the setup-grading pipeline, not the ATR-trail production book above.)", drift: "Live setup outcomes are running materially below the backtest's target/stop model — small-sample noise, a regime shift, or a live/backtest pipeline gap. Worth a look.", }; const evaluateMutation = useMutation({ mutationFn: () => triggerJob('outcome_evaluator'), onSuccess: () => { toast.addToast('success', 'Outcome evaluation triggered. Stats will refresh shortly.'); setTimeout(() => queryClient.invalidateQueries({ queryKey: ['performance'] }), 3000); }, onError: () => { toast.addToast('error', 'Failed to trigger outcome evaluation'); }, }); const resetMutation = useMutation({ mutationFn: () => resetTrackRecord(), onSuccess: (data) => { toast.addToast('success', `Track record reset — ${data.trade_setups} setups cleared. Run the scanner to rebuild.`); queryClient.invalidateQueries({ queryKey: ['performance'] }); queryClient.invalidateQueries({ queryKey: ['trades'] }); }, onError: () => { toast.addToast('error', 'Failed to reset track record'); }, }); const onReset = () => { if ( window.confirm( 'Reset the track record? This permanently deletes ALL trade setups and their outcomes. ' + 'Live setups will regenerate on the next R:R scan. This cannot be undone.', ) ) { resetMutation.mutate(); } }; return (
{/* Your real, realized results come first; the strategy simulation follows. */}

Diagnostic only — not production P&L.{' '} Grades gate-level touch vs stop (the rejected take-profit model). Production exits are initial stop / ATR trail / max hold — see paper trades and the portfolio monitor above. Target before stop = win, stop first = loss (same-bar both = loss), neither in 30 trading days = expired at 0R. Only matured windows count. Scores{' '} all setups as a control group; runs nightly.

{/* Diagnostic, not strategy validation: live target/stop outcomes vs the backtest's target/stop model. */}
Gate barrier pipeline check Live {fmtR(liveAvgR)} Backtest {fmtR(btAvgR)} {liveN} matured{perf ? ` · ${perf.maturing} maturing` : ''} · not ATR-trail book

{statusNote[status]}

); }