diff --git a/frontend/src/components/backtest/MetricCards.vue b/frontend/src/components/backtest/MetricCards.vue index 6979d2c..dd2fab8 100644 --- a/frontend/src/components/backtest/MetricCards.vue +++ b/frontend/src/components/backtest/MetricCards.vue @@ -2,34 +2,40 @@ import { computed } from 'vue' interface RelativeMetrics { - total_return: number - annual_return: number - alpha: number - beta: number - sharpe_ratio: number - sortino_ratio: number - information_ratio: number - annual_volatility: number - max_drawdown: number - benchmark_return: number - benchmark_volatility: number + total_return: number | null + annual_return: number | null + alpha: number | null + beta: number | null + sharpe_ratio: number | null + sortino_ratio: number | null + information_ratio: number | null + annual_volatility: number | null + max_drawdown: number | null + benchmark_return: number | null + benchmark_volatility: number | null } const props = defineProps<{ metrics: RelativeMetrics }>() +// null/NaN 安全格式化(后端 NaN→None,退化指标如 sortino 可能为 null) +function fmt(value: number | null | undefined, digits: number, percent = false): string { + if (value == null || isNaN(value as number)) return '—' + return percent ? `${(value * 100).toFixed(digits)}%` : value.toFixed(digits) +} + // 格式化百分比(×100,保留2位小数) -function formatPercent(value: number): string { - return `${(value * 100).toFixed(2)}%` +function formatPercent(value: number | null): string { + return fmt(value, 2, true) } // 格式化小数(保留3位小数) -function formatDecimal(value: number): string { - return value.toFixed(3) +function formatDecimal(value: number | null): string { + return fmt(value, 3) } // 格式化Sharpe等比率(保留2位小数) -function formatRatio(value: number): string { - return value.toFixed(2) +function formatRatio(value: number | null): string { + return fmt(value, 2) } const metricCards = computed(() => [