From ecd8af167656343845a820bb1503186cb799f005 Mon Sep 17 00:00:00 2001 From: Justin Gu <97915@qq.com> Date: Sat, 4 Jul 2026 00:09:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(web-ui):=20=E5=8F=96=E8=A1=8C=E6=83=85?= =?UTF-8?q?=E6=95=B4=E5=90=88=20+=20=E5=B8=82=E5=9C=BA=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E8=AF=86=E5=88=AB=20+=20=E4=B8=80=E9=94=AE=E5=AF=BB=E4=BC=98?= =?UTF-8?q?=E5=85=A8=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 取消单标的/寻优页独立的「取行情」按钮,整合进「开始回测/开始寻测」 (SymbolPicker 经 defineExpose 暴露 loadBars,父组件串联取数+回测) - 取消市场手动选择(沪/深/北交所下拉),改为 market.ts 按代码段智能 识别(17 边界用例验证),代码框旁显示识别结果 - 成交价下拉精简为中文「开盘价/收盘价」,初始资金默认 100 万 - 寻优页 ParamGridPicker 切换策略自动填入预设参数网格 - 寻优页新增「一键寻优所有策略」按钮 + 全局策略排名表 (OptimizeView 复用 store.runOptimizeAll,结果区含最佳/排名/合计网格点) - types/api/store 新增 OptimizeAll 契约 + submitOptimizeAllTask - vue-tsc + vite build 通过 --- web-ui/src/api.ts | 14 ++ web-ui/src/components/ParamGridPicker.vue | 27 ++- web-ui/src/components/StocksPicker.vue | 35 ++-- web-ui/src/components/SymbolPicker.vue | 85 ++++++---- web-ui/src/market.ts | 50 ++++++ web-ui/src/stores/backtest.ts | 40 +++++ web-ui/src/types.ts | 40 ++++- web-ui/src/views/BacktestView.vue | 39 +++-- web-ui/src/views/OptimizeView.vue | 196 ++++++++++++++++++++-- web-ui/src/views/PortfolioView.vue | 12 +- 10 files changed, 449 insertions(+), 89 deletions(-) create mode 100644 web-ui/src/market.ts diff --git a/web-ui/src/api.ts b/web-ui/src/api.ts index 193b0ae..426dbde 100644 --- a/web-ui/src/api.ts +++ b/web-ui/src/api.ts @@ -7,6 +7,7 @@ import type { BacktestResult, Bar, Category, + OptimizeAllBacktestRequest, OptimizeBacktestRequest, PortfolioBacktestRequest, TaskListResponse, @@ -160,6 +161,19 @@ export async function submitOptimizeTask( return (await resp.json()) as TaskSubmitResponse } +/** 提交「一键寻优所有策略」后台任务,返回 task_id。 */ +export async function submitOptimizeAllTask( + req: OptimizeAllBacktestRequest, +): Promise { + const resp = await fetch(`${BASE}/backtest/optimize-all/run/async`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(req), + }) + if (!resp.ok) await throwError(resp) + return (await resp.json()) as TaskSubmitResponse +} + /** 查询后台任务状态(轮询用)。 */ export async function fetchTask(taskId: string): Promise { const resp = await fetch(`${BASE}/backtest/tasks/${taskId}`) diff --git a/web-ui/src/components/ParamGridPicker.vue b/web-ui/src/components/ParamGridPicker.vue index f225d0f..a110b12 100644 --- a/web-ui/src/components/ParamGridPicker.vue +++ b/web-ui/src/components/ParamGridPicker.vue @@ -50,12 +50,28 @@ function onInput(name: string, val: string) { syncOutputs() } -// 切换策略时清空选择 +// 把预设取值列表格式化为输入框文本(逗号分隔) +function presetToText(vals: Array): string { + return vals.join(', ') +} + +// 切换策略时:若有预设网格则自动勾选并填入预设取值,否则清空选择 watch( () => props.strategy?.name, () => { - selected.value = new Set() - inputs.value = {} + const preset = props.strategy?.preset_grid + if (preset && Object.keys(preset).length > 0) { + const names = Object.keys(preset) + selected.value = new Set(names) + const newInputs: Record = {} + for (const n of names) { + newInputs[n] = presetToText(preset[n]) + } + inputs.value = newInputs + } else { + selected.value = new Set() + inputs.value = {} + } syncOutputs() }, ) @@ -71,7 +87,10 @@ const gridPoints = computed(() => {