import React, { useEffect, useMemo, useRef, useState } from 'react'; import { ResponsiveContainer, LineChart, Line, XAxis, YAxis, Tooltip, CartesianGrid, ReferenceLine } from 'recharts'; import type { BacktestResult } from '../types'; import { buildInsightCards, toEquitySeries } from '../utils'; interface BacktestResultsDashboardProps { result: BacktestResult; } const money = (value: number): string => { const amount = Number(value || 0); return amount.toLocaleString(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 2 }); }; const pct = (value: number): string => `${Number(value || 0).toFixed(2)}%`; export const BacktestResultsDashboard: React.FC = ({ result }) => { const equitySeries = useMemo(() => toEquitySeries(result), [result]); const [cursorIndex, setCursorIndex] = useState(0); const [isPlaying, setIsPlaying] = useState(false); const [speed, setSpeed] = useState<'1x' | '5x' | '20x' | 'instant'>('1x'); const tradeRowRefs = useRef>({}); const insightCards = useMemo(() => buildInsightCards(result), [result]); const orderedTrades = useMemo( () => [...result.trades].sort((a, b) => a.entryTimestamp - b.entryTimestamp), [result.trades] ); useEffect(() => { setCursorIndex(0); setIsPlaying(false); setSpeed('1x'); }, [result]); useEffect(() => { if (!isPlaying) return; if (!equitySeries.length) return; if (speed === 'instant') { setCursorIndex(equitySeries.length - 1); setIsPlaying(false); return; } const tickMs = speed === '1x' ? 700 : speed === '5x' ? 200 : 80; const timer = window.setInterval(() => { setCursorIndex((prev) => { const atEnd = prev >= equitySeries.length - 1; if (atEnd) { setIsPlaying(false); return prev; } return prev + 1; }); }, tickMs); return () => window.clearInterval(timer); }, [isPlaying, speed, equitySeries.length]); const cursorPoint = equitySeries[Math.min(cursorIndex, Math.max(0, equitySeries.length - 1))]; const cursorTimestamp = Number(cursorPoint?.timestamp ?? result.window.fromTimestamp); const activeTrade = useMemo(() => { if (!orderedTrades.length) return null; return orderedTrades.find((trade) => ( cursorTimestamp >= trade.entryTimestamp && cursorTimestamp <= trade.exitTimestamp )) || null; }, [cursorTimestamp, orderedTrades]); useEffect(() => { if (!activeTrade) return; const row = tradeRowRefs.current[activeTrade.id]; if (row && typeof row.scrollIntoView === 'function') { row.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); } }, [activeTrade]); const replayInsight = useMemo(() => { const topBlocked = Object.entries(result.diagnostics.blockedRuleCounts || {}) .sort((a, b) => b[1] - a[1])[0]; if (activeTrade) { return `Active trade ${activeTrade.symbol} (${activeTrade.side}). Entry: ${activeTrade.entryReason}. Exit trigger: ${activeTrade.exitReason}.`; } const nextTrade = orderedTrades.find((trade) => trade.entryTimestamp > cursorTimestamp); if (nextTrade) { if (topBlocked) { return `No trade - rule failed (${topBlocked[0]}). Next trade starts at ${new Date(nextTrade.entryTimestamp).toLocaleString()}.`; } return `No trade - rule failed or no valid signal. Next trade starts at ${new Date(nextTrade.entryTimestamp).toLocaleString()}.`; } if (topBlocked) { return `No trade - rule failed (${topBlocked[0]}). Replay window ended.`; } return 'No trade - rule failed or no qualifying setup during this replay segment.'; }, [activeTrade, cursorTimestamp, orderedTrades, result.diagnostics.blockedRuleCounts]); const isAtEnd = cursorIndex >= Math.max(0, equitySeries.length - 1); return (

Replay Controls (UI-Only)

Replay window: {result.window.fromIso.slice(0, 10)}{' -> '}{result.window.toIso.slice(0, 10)} ({result.window.timezone})
Cursor: {new Date(cursorTimestamp).toLocaleString()}
Insight: {replayInsight}

Replay shows a historical simulation. No real or paper trades are placed.

Execution Assumptions

Fill Model: {result.assumptions.fillModel}
Slippage: {result.assumptions.slippageModel}
Fees: {result.assumptions.feeModel}
Latency: {result.assumptions.latencyModel}
Intra-candle: {result.assumptions.intraCandleModel}
Trigger Resolution: {result.assumptions.triggerResolution}
Warm-up Enforced: {result.assumptions.warmupEnforced ? 'Yes' : 'No'}
Deterministic Replay: {result.assumptions.deterministicReplay ? 'Yes' : 'No'}
Replay Window: {result.assumptions.replayWindow}
Window End Policy: {result.assumptions.endOfWindowPolicy}

{result.assumptions.disclaimer}

Warm-up Start

{new Date(result.warmup.startTimestamp).toLocaleString()}

Warm-up End

{new Date(result.warmup.endTimestamp).toLocaleString()}

Candles Required

15m {result.warmup.candlesRequired['15m']} | 1h {result.warmup.candlesRequired['1h']} | 4h {result.warmup.candlesRequired['4h']}

Signals Ignored

{result.warmup.signalsIgnored}

Net PnL

{money(result.summary.netPnlUsd)}

Win Rate

{pct(result.summary.winRate)}

Max Drawdown

{pct(result.summary.maxDrawdownPct)}

Sharpe

{result.summary.sharpe.toFixed(3)}

Total Trades

{result.summary.totalTrades}

Equity Curve

new Date(Number(value)).toLocaleDateString()} tick={{ fontSize: 10, fill: 'var(--bl-text-faint)' }} /> money(Number(value))} labelFormatter={(label) => new Date(Number(label)).toLocaleString()} />

Drawdown Curve

new Date(Number(value)).toLocaleDateString()} tick={{ fontSize: 10, fill: 'var(--bl-text-faint)' }} /> pct(Number(value))} labelFormatter={(label) => new Date(Number(label)).toLocaleString()} />
{insightCards.map((card) => (

{card.title}

{card.value}

{card.detail}

))}
{result.openPositionsAtEnd.length > 0 && (

Open At End

{result.openPositionsAtEnd.map((position) => (
{position.symbol} {position.side}
Entry {new Date(position.entryTimestamp).toLocaleString()}
Size {position.size}
Unrealized {money(position.unrealizedPnlUsd)} ({pct(position.unrealizedPnlPct)})
{position.status}
))}
)}

Trades

{result.trades.map((trade) => ( { tradeRowRefs.current[trade.id] = element; }} data-trade-id={trade.id} className={`${activeTrade?.id === trade.id ? 'bg-cyan-500/15' : 'hover:bg-white/5'}`} > ))} {result.trades.length === 0 && ( )}
Symbol Side Entry Exit PnL Reasoning
{trade.symbol} {trade.side} {new Date(trade.entryTimestamp).toLocaleString()} {new Date(trade.exitTimestamp).toLocaleString()} = 0 ? 'text-emerald-400' : 'text-rose-400'}`}> {money(trade.pnlUsd)} ({pct(trade.pnlPct)})
{trade.entryReason}
Exit: {trade.exitReason}
No trades were executed during this replay window.
); };