feat(frontend): 结果页组件—MetricCards指标卡+5图(基准/Alpha/Beta/波动率/回撤)+API封装
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<script setup lang="ts">
|
||||
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
|
||||
}
|
||||
|
||||
const props = defineProps<{ metrics: RelativeMetrics }>()
|
||||
|
||||
// 格式化百分比(×100,保留2位小数)
|
||||
function formatPercent(value: number): string {
|
||||
return `${(value * 100).toFixed(2)}%`
|
||||
}
|
||||
|
||||
// 格式化小数(保留3位小数)
|
||||
function formatDecimal(value: number): string {
|
||||
return value.toFixed(3)
|
||||
}
|
||||
|
||||
// 格式化Sharpe等比率(保留2位小数)
|
||||
function formatRatio(value: number): string {
|
||||
return value.toFixed(2)
|
||||
}
|
||||
|
||||
const metricCards = computed(() => [
|
||||
{ label: '总收益率', value: formatPercent(props.metrics.total_return) },
|
||||
{ label: '年化收益率', value: formatPercent(props.metrics.annual_return) },
|
||||
{ label: 'Alpha', value: formatPercent(props.metrics.alpha) },
|
||||
{ label: 'Beta', value: formatDecimal(props.metrics.beta) },
|
||||
{ label: 'Sharpe比率', value: formatRatio(props.metrics.sharpe_ratio) },
|
||||
{ label: 'Sortino比率', value: formatRatio(props.metrics.sortino_ratio) },
|
||||
{ label: '信息比率', value: formatRatio(props.metrics.information_ratio) },
|
||||
{ label: '年化波动率', value: formatPercent(props.metrics.annual_volatility) },
|
||||
{ label: '最大回撤', value: formatPercent(props.metrics.max_drawdown) },
|
||||
{ label: '基准收益率', value: formatPercent(props.metrics.benchmark_return) },
|
||||
{ label: '基准波动率', value: formatPercent(props.metrics.benchmark_volatility) },
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="metric-cards">
|
||||
<div v-for="card in metricCards" :key="card.label" class="metric-card">
|
||||
<div class="metric-label">{{ card.label }}</div>
|
||||
<div class="metric-value">{{ card.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.metric-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
background: #161b22;
|
||||
border: 1px solid #30363d;
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
color: #8b949e;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
color: #e6edf3;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user