diff --git a/frontend/src/pages/backtest/StrategyWalkForward.tsx b/frontend/src/pages/backtest/StrategyWalkForward.tsx index cb9a205..c154bcf 100644 --- a/frontend/src/pages/backtest/StrategyWalkForward.tsx +++ b/frontend/src/pages/backtest/StrategyWalkForward.tsx @@ -36,11 +36,17 @@ function Stat({ label, value, hint, color }: { label: string; value: string; hin ) } +/** 把 NaN/Infinity 归一为 null (走 ?! '—' 的既有降级路径), 防止 toFixed 渲染出 "NaN"。 */ +function fin(v: number | null | undefined): number | null { + return v != null && Number.isFinite(v) ? v : null +} + /** OOS 拼接净值曲线 (逐折复利) — walk-forward 核心产出的极简 SVG 折线。 */ function OosEquityChart({ curve }: { curve: { fold: number; date: string; value: number }[] }) { - if (!curve.length) return null + // 过滤非有限值: 零成交折等边界可能产出 NaN/Infinity, 混入会让坐标全 NaN → SVG 空白。 + const vals = curve.map(p => p.value).filter(v => Number.isFinite(v)) + if (!vals.length) return null const W = 600, H = 120, pad = 8 - const vals = curve.map(p => p.value) const lo = Math.min(1, ...vals), hi = Math.max(1, ...vals) const span = hi - lo || 1 // 起点补一个 value=1 基准, 让曲线从 1.0 起步 @@ -112,6 +118,12 @@ export function StrategyWalkForward() { const result = task?.result const progress = task?.progress const summary = result?.summary + // 汇总里的可空数值先归一: 零成交折等边界可能产出 NaN/Infinity, + // 直接 toFixed 会渲染出 "NaN"; 这里把非有限值转 null, 下游统一走 '—' 降级。 + const compounded = fin(summary?.compounded_oos_return) + const degradation = fin(summary?.degradation) + const avgIs = fin(summary?.avg_is_objective) + const avgOos = fin(summary?.avg_oos_objective) return (