release: v1.17.14 — Web UI 数据评级系统(S/A/B/C/D 五档,三入口覆盖)

纯前端 TypeScript 实现,零后端改动。回测/组合/寻优三处结果顶部
显示评级徽章,让普通人 1 秒判断「适不适合经常参与」。

评级不看收益率(避免被近期大涨误导),只看风险调整后的持有体验:
卡玛/最大回撤/胜率/利润因子/夏普/波动率六维加权 + 一票否决。
京东方「收益 126% 但胜率 35%」案例 = D 档(核心验证点)。

长线低频策略(6年6笔)不会被冤枉:交易<10笔时只降权胜率/利润因子
维度,不否决整个评级(修复用户实测反馈)。

- 新增 web-ui/src/grading/ 核心模块(engine/thresholds/combinedMetrics/index)
- 新增 GradeBadge.vue + GradeDetails.vue 组件
- 接入 BacktestView/PortfolioView/OptimizeView(寻优排名表加评级列)
- 15 个自检测试(node:test + rolldown 打包),覆盖京东方 D / 长线 B 等场景
This commit is contained in:
Justin Gu
2026-07-05 01:21:18 +08:00
parent 66c49d1a8d
commit cf843cebad
16 changed files with 1667 additions and 2 deletions
+14
View File
@@ -7,12 +7,14 @@ import { computed, nextTick, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import EquityChart from '../components/EquityChart.vue'
import GradeDetails from '../components/GradeDetails.vue'
import KlineChart from '../components/KlineChart.vue'
import MetricTable from '../components/MetricTable.vue'
import StrategyPicker from '../components/StrategyPicker.vue'
import SymbolPicker from '../components/SymbolPicker.vue'
import TradeTable from '../components/TradeTable.vue'
import { formatError, saveStrategy } from '../api'
import { gradePerformance } from '../grading'
import type { Category, ExecutionMode } from '../types'
import { useBacktestStore } from '../stores/backtest'
@@ -111,6 +113,13 @@ const strategyLabel = computed(
() => store.strategies.find((s) => s.name === strategy.value)?.label ?? strategy.value,
)
// 评级:基于完整 Performance,6 维度评分 + 一票否决。
// total_return 不直接计入评分(只通过卡玛/夏普间接体现),
// 体现「哪怕近期收益率高,长期风险大也该低评」的产品诉求。
const grade = computed(() =>
store.result ? gradePerformance(store.result.performance) : null,
)
// 当前股票完整代码(市场:6位),从 SymbolPicker 同步来的 code 是纯数字,
// 需要带上市场前缀。复用 SymbolPicker 内部已经算好的前缀更稳妥——这里简单按
// 交易所规则推断(6 位代码:6/9 开头 SH,其余 SZ;8/4 开头 BJ)。
@@ -260,6 +269,11 @@ async function onSave() {
<EquityChart :equity="store.result.equity_curve" />
</section>
<section v-if="grade" class="report-section">
<h3>评级</h3>
<GradeDetails :result="grade" expanded />
</section>
<section class="report-section">
<h3>绩效指标</h3>
<MetricTable :perf="store.result.performance" />