/** * BacktestResultDashboard — 첨부 이미지 스타일 백테스팅 결과 대시보드 */ import React, { useMemo } from 'react'; import type { BacktestAnalysis, BacktestSignal } from '../../utils/backendApi'; import { buildEquityFromSignals, type TradeHistoryRow } from '../../utils/backtestEquity'; import BacktestAssetChart from './BacktestAssetChart'; export interface BacktestResultDashboardProps { analysis: BacktestAnalysis | null; signals: BacktestSignal[]; strategyName: string; symbol: string; timeframe: string; barCount: number; createdAt?: string; } const pct = (v: number, dec = 1) => isFinite(v) && v !== 0 ? `${v >= 0 ? '+' : ''}${(v * 100).toFixed(dec)}%` : '0.0%'; const pctAbs = (v: number, dec = 0) => isFinite(v) ? `${(v * 100).toFixed(dec)}%` : '–'; const num = (v: number, dec = 2) => isFinite(v) ? v.toFixed(dec) : '–'; function fmtDateTime(ts: number): string { const d = new Date(ts * 1000); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`; } function SectionTitle({ title, sub }: { title: string; sub?: string }) { return (

{title}

{sub &&

{sub}

}
); } export function BacktestResultDashboard({ analysis, signals, strategyName: _strategyName, symbol, timeframe: _timeframe, barCount: _barCount, createdAt: _createdAt, }: BacktestResultDashboardProps) { const a = analysis; const { curve, markers, trades } = useMemo(() => { if (!a) return { curve: [], markers: [], trades: [] }; return buildEquityFromSignals(signals, a.initialCapital, symbol.startsWith('KRW-') ? symbol : `KRW-${symbol.replace(/^KRW-/, '')}`); }, [a, signals, symbol]); if (!a) { return (
📊

결과 데이터가 없습니다.

); } const profitFactor = a.profitLossRatio > 0 ? a.profitLossRatio : ( a.grossLoss !== 0 ? Math.abs(a.grossProfit / a.grossLoss) : 0 ); return (
총 수익률 (ROI)
= 0 ? 'up' : 'down'}`}> {pct(a.totalReturnPct)}

초기 자본 대비 최종 수익

최대 낙폭 (MDD)
{pct(a.maxDrawdownPct)}

기간 중 최대 손실폭

샤프 비율 (Sharpe Ratio)
= 1 ? 'up' : a.sharpeRatio >= 0 ? 'neutral' : 'down'}`}>{num(a.sharpeRatio)}

위험 대비 초과 수익

승률 및 Profit Factor
{pctAbs(a.winRate)}
{num(profitFactor)}
{trades.length === 0 ? ( ) : trades.map((t: TradeHistoryRow) => ( ))}
거래 일시 종목 유형 체결가 수량 손익 (%)
거래 내역이 없습니다.
{fmtDateTime(t.time)} {t.symbol.startsWith('KRW-') ? t.symbol : `KRW-${t.symbol}`} {t.side === 'buy' ? '매수' : '매도'} {Math.round(t.price).toLocaleString()} {t.quantity.toFixed(4)} = 0 ? 'up' : 'down') : ''}> {t.pnlPct != null ? pct(t.pnlPct) : '–'}
); } export default BacktestResultDashboard;