From 2580b5c08092486b40dc9a3162a93efe65bc4fb5 Mon Sep 17 00:00:00 2001 From: claude_dev Date: Sat, 15 Aug 2026 22:54:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(strategy):=20=E5=AE=9E=E4=BE=8B=E5=81=9A?= =?UTF-8?q?=E5=AE=9E=E5=AE=A1=E8=AE=A1=E8=A1=A5=E6=BC=8F6=E9=A1=B9?= =?UTF-8?q?=E2=80=94=E2=80=94=E2=91=A0=E5=9B=9E=E6=B5=8B=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E9=80=8F=E4=BC=A0instance=5Fid(CTA/=E7=BB=84?= =?UTF-8?q?=E5=90=88=E4=B8=A4=E9=A1=B5=E5=8E=9F=E5=8F=AA=E9=A2=84=E5=A1=AB?= =?UTF-8?q?=E4=B8=8D=E4=BC=A0,=E5=9B=9B=E6=A0=BC=E5=9B=9E=E5=86=99?= =?UTF-8?q?=E9=93=BE=E8=B7=AF=E6=96=AD=E5=A4=B4;=E7=BB=84=E5=90=88?= =?UTF-8?q?=E9=A1=B5=E8=A1=A5=E6=A1=A3=E6=A1=88=E9=A2=84=E5=A1=ABmax=5Fpoo?= =?UTF-8?q?l/benchmark/pool)=E2=91=A1=E6=A8=A1=E6=8B=9F=E7=9B=98/=E5=AE=9E?= =?UTF-8?q?=E7=9B=98=E5=88=97=E8=A1=A8=E6=94=AF=E6=8C=81=3Finstance=3DN?= =?UTF-8?q?=E6=8C=89=E6=A1=A3=E6=A1=88=E8=BF=87=E6=BB=A4(=E7=AD=96?= =?UTF-8?q?=E7=95=A5=E5=BA=93=E8=A1=8C=E5=8A=A0=E6=A8=A1=E6=8B=9F=E5=8E=86?= =?UTF-8?q?=E5=8F=B2/=E5=AE=9E=E7=9B=98=E5=8E=86=E5=8F=B2=E6=8C=89?= =?UTF-8?q?=E9=92=AE,=E8=BF=87=E6=BB=A4=E4=B8=AD=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E6=9D=A1=E5=8F=AF=E9=80=80=E5=87=BA)=E2=91=A2live=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E8=A1=A5D1=E5=BF=AB=E7=85=A7(setting=E7=94=A8?= =?UTF-8?q?=E6=A1=A3=E6=A1=88=E5=8F=82=E6=95=B0,API=E7=9B=B4=E8=B0=83?= =?UTF-8?q?=E4=B8=8D=E7=BB=95=E8=BF=87)=E2=91=A3=E5=AE=9E=E7=9B=98?= =?UTF-8?q?=E8=B4=A6=E6=88=B7=E4=B9=9F=E6=A0=87=E5=8F=82=E6=95=B0=E6=BC=82?= =?UTF-8?q?=E7=A7=BB=E8=A7=92=E6=A0=87(D2=E5=8F=AA=E4=BA=AE=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E6=AD=A5;account=5Fparams=5Fdrifted=E5=85=BC=E5=AE=B9?= =?UTF-8?q?live=20setting=E5=AD=97=E6=AE=B5)=E2=91=A4paper=E9=A1=B5PORTFOL?= =?UTF-8?q?IO=5FLABELS=E6=8D=A2STRATEGY=5FLABELS=E5=85=B1=E4=BA=AB?= =?UTF-8?q?=E2=91=A5=E5=90=8C=E6=AD=A5=E7=94=9F=E6=95=88=E6=97=B6=E6=9C=BA?= =?UTF-8?q?=E5=AE=9E=E8=AF=81=E6=A0=87=E6=B3=A8(=E5=AE=9E=E8=B5=B0?= =?UTF-8?q?=E6=AF=8F=E6=97=A5=E7=BB=93=E7=AE=97=E9=87=8D=E8=AF=BBDB?= =?UTF-8?q?=E6=AC=A1=E6=99=9A=E7=94=9F=E6=95=88;=E5=BD=B1=E5=AD=90?= =?UTF-8?q?=E8=BF=9B=E7=A8=8B=E5=B8=B8=E9=A9=BB=E5=86=85=E5=AD=98=E9=9C=80?= =?UTF-8?q?=E9=87=8D=E5=90=AF,=E5=AE=9E=E8=AF=81live=5Fstep/runner.py?= =?UTF-8?q?=E5=9D=87=E4=BB=8EDB=E8=AF=BB);921=E7=BB=BF+build=E7=BB=BF=20[v?= =?UTF-8?q?ps]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/backtest.ts | 2 ++ frontend/src/api/portfolio.ts | 2 ++ frontend/src/views/backtest/New.vue | 1 + .../src/views/backtest/PortfolioBacktest.vue | 21 +++++++++++++++++++ frontend/src/views/live/List.vue | 13 ++++++++++-- frontend/src/views/paper/List.vue | 14 +++++++++++-- frontend/src/views/paper/New.vue | 9 ++------ frontend/src/views/strategy/List.vue | 4 +++- sanguo_api/instance_store.py | 18 ++++++++++++---- sanguo_api/routes_live.py | 3 +++ sanguo_api/routes_paper.py | 3 ++- sanguo_api/routes_strategy.py | 7 ++++++- 12 files changed, 79 insertions(+), 18 deletions(-) diff --git a/frontend/src/api/backtest.ts b/frontend/src/api/backtest.ts index 82a77f8..0feb593 100644 --- a/frontend/src/api/backtest.ts +++ b/frontend/src/api/backtest.ts @@ -11,6 +11,8 @@ export interface CtaSubmit { slippage?: number commission_rate?: number stamp_duty_rate?: number + // §12.6 可选关联档案(带了则结果回写档案四格) + instance_id?: number | null } export interface TaskStatus { diff --git a/frontend/src/api/portfolio.ts b/frontend/src/api/portfolio.ts index 213734e..753e084 100644 --- a/frontend/src/api/portfolio.ts +++ b/frontend/src/api/portfolio.ts @@ -15,6 +15,8 @@ export interface PortfolioBacktestReq { slippage?: number // K线周期(组合回放暂仅日线 d) interval?: string + // §12.6 可选关联档案(带了则结果回写档案四格) + instance_id?: number | null } export interface EquityPoint { diff --git a/frontend/src/views/backtest/New.vue b/frontend/src/views/backtest/New.vue index 3d2edc4..5cdee21 100644 --- a/frontend/src/views/backtest/New.vue +++ b/frontend/src/views/backtest/New.vue @@ -167,6 +167,7 @@ async function onSubmit(): Promise { commission_rate: Number(form.commission_rate), stamp_duty_rate: Number(form.stamp_duty_rate), slippage: Number(form.slippage), + instance_id: route.query.instance && !Array.isArray(route.query.instance) ? Number(route.query.instance) : null, }) ElMessage.success('回测已提交') void tid diff --git a/frontend/src/views/backtest/PortfolioBacktest.vue b/frontend/src/views/backtest/PortfolioBacktest.vue index 64240ab..0bf39cb 100644 --- a/frontend/src/views/backtest/PortfolioBacktest.vue +++ b/frontend/src/views/backtest/PortfolioBacktest.vue @@ -6,6 +6,7 @@ import type { EChartsCoreOption } from 'echarts' import { useChart } from '@/composables/useChart' import { darkTitle, darkTooltip, darkGrid, darkAxis } from '@/utils/echartsDark' import { getTaskParams } from '@/api/backtest' +import { apiClient } from '@/api/client' import { disableFutureDate } from '@/utils/dates' import { INTERVAL_OPTIONS } from '@/constants/intervals' import { @@ -370,6 +371,7 @@ async function onSubmit(): Promise { min_commission: Number(form.min_commission), slippage: Number(form.slippage), interval: form.interval, + instance_id: route.query.instance && !Array.isArray(route.query.instance) ? Number(route.query.instance) : null, }) ElMessage.success('回测已提交,后台运行中') router.push('/backtest/history') // 跳任务中心(历史任务页)看进度/结果 @@ -391,8 +393,27 @@ onMounted(() => { // 从任务列表「点任务ID」跳转:按该任务参数完整预填 void prefillFromTask(fromTaskId.value) } + // §12.6 从策略库档案发起:按档案参数预填(max_pool/benchmark 等) + const instQ = route.query.instance + if (instQ && !Array.isArray(instQ)) void prefillFromInstance(Number(instQ)) }) +async function prefillFromInstance(instId: number): Promise { + try { + const { data } = await apiClient.get<{ + instance: { name?: string; code_file: string; params: Record; symbol_or_pool?: string } + }>(`/strategy/instances/${instId}`) + const inst = data.instance + if (inst.code_file) form.strategy = inst.code_file.replace(/\.py$/, '') + const p = inst.params || {} + if (typeof p.max_pool === 'number') form.max_pool = p.max_pool + if (typeof p.benchmark === 'string') form.benchmark = p.benchmark + if (inst.symbol_or_pool && inst.symbol_or_pool !== '') form.pool = inst.symbol_or_pool + } catch { + /* 预填失败走默认 */ + } +} + async function prefillFromTask(tid: string): Promise { try { const tp = await getTaskParams(tid) diff --git a/frontend/src/views/live/List.vue b/frontend/src/views/live/List.vue index 408724b..e459221 100644 --- a/frontend/src/views/live/List.vue +++ b/frontend/src/views/live/List.vue @@ -1,6 +1,6 @@