From 10508db11ddd9438e502e1a9d30b87e7d0cc323c Mon Sep 17 00:00:00 2001 From: claude_dev Date: Sat, 11 Jul 2026 23:31:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(backtest):=20=E7=BB=93=E6=9E=9C=E9=A1=B54?= =?UTF-8?q?=E4=B8=AA=E6=98=BE=E7=A4=BAbug=E2=80=94=E6=88=90=E4=BA=A4?= =?UTF-8?q?=E6=9E=9A=E4=B8=BE/=E6=97=B6=E9=97=B4/=E6=B5=AE=E7=82=B9?= =?UTF-8?q?=E7=B2=BE=E5=BA=A6/=E6=97=A5=E5=BF=97=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 交易详情方向/开平显原始枚举: Direction.LONG/Offset.OPEN → 买/卖、开仓/平仓/平今/平昨 (TradesTable 加 fmtDir/fmtOff, 方向带涨跌色) 2. 交易详情时间 2024-06-25T00:00:00+08:00 → 2024-06-25 (日内带时分则保留) 3. 每日收益浮点精度 0.6533000000054017 → 0.65 (toFixed2, 涨跌色), 日期去 00:00:00 4. 日志tab硬编码"暂无日志数据": 前端接 getLog(/task/:id/log) + 后端 cta_engine 加 stdout tee 把引擎 load/run/stats 输出落盘到 {task_id}.log (worker 进程内隔离) → 新回测日志 tab 显示真实引擎输出(2117字) 验证: 交易 买/开仓/2024-06-25, 收益 0.15, 日志 2117字真实内容 --- frontend/src/components/TradesTable.vue | 59 ++++++++++++++++++++++--- frontend/src/views/backtest/Result.vue | 35 +++++++++++++-- sanguo_backtest/cta_engine.py | 52 ++++++++++++++++++---- 3 files changed, 128 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/TradesTable.vue b/frontend/src/components/TradesTable.vue index 7d4c0c7..a432931 100644 --- a/frontend/src/components/TradesTable.vue +++ b/frontend/src/components/TradesTable.vue @@ -2,15 +2,62 @@ import type { Trade } from '@/api/backtest' defineProps<{ trades: Trade[] }>() + +// vnpy 枚举 repr → 中文(兼容已是中文的情况) +function fmtDir(d: string): string { + if (!d) return '' + const s = String(d) + if (s.includes('LONG') || s === '多') return '买' + if (s.includes('SHORT') || s === '空') return '卖' + if (s.includes('NET')) return '净' + return s +} +function fmtOff(o: string): string { + if (!o) return '' + const s = String(o) + if (s.includes('CLOSETODAY')) return '平今' + if (s.includes('CLOSEFIRST')) return '平昨' + if (s.includes('CLOSE')) return '平仓' + if (s.includes('OPEN')) return '开仓' + return s +} +// 2024-06-25T00:00:00+08:00 → 2024-06-25(日内带时分则保留) +function fmtDate(dt: string): string { + if (!dt) return '' + const s = String(dt).slice(0, 19).replace('T', ' ') + return s.endsWith(' 00:00:00') ? s.slice(0, 10) : s +} +function dirClass(d: string): string { + const v = fmtDir(d) + return v === '买' ? 'up' : v === '卖' ? 'down' : '' +} + + diff --git a/frontend/src/views/backtest/Result.vue b/frontend/src/views/backtest/Result.vue index d967163..7e0b573 100644 --- a/frontend/src/views/backtest/Result.vue +++ b/frontend/src/views/backtest/Result.vue @@ -3,7 +3,7 @@ import { ref, computed, onMounted } from 'vue' import { useRoute } from 'vue-router' import { getResult, getEquityCurve, getDailyPnl, getTrades, getKline, - getRelativeMetrics, getBenchmarkCurve, getRiskSeries, + getRelativeMetrics, getBenchmarkCurve, getRiskSeries, getLog, type EquityPoint, type PnlPoint, type Trade, type KlineBar, type RelativeMetrics, type BenchmarkCurveData, type RiskSeriesData, } from '@/api/backtest' @@ -42,6 +42,7 @@ const equity = ref([]) const pnl = ref([]) const trades = ref([]) const kline = ref([]) +const logText = ref('') // statEntries is no longer used in the new layout but kept for potential future use // const statEntries = computed(() => @@ -135,10 +136,32 @@ onMounted(async () => { kline.value = [] } } + try { + logText.value = await getLog(taskId) + } catch { + logText.value = '' + } } finally { loading.value = false } }) + +// 每日收益格式化:浮点精度 → 2 位;日期去 00:00:00 +function fmtPnl(v: number | string): string { + const n = typeof v === 'number' ? v : parseFloat(v) + if (!Number.isFinite(n)) return String(v) + return n.toFixed(2) +} +function fmtPnlClass(v: number | string): string { + const n = typeof v === 'number' ? v : parseFloat(v) + if (!Number.isFinite(n) || n === 0) return '' + return n > 0 ? 'up' : 'down' +} +function fmtDate(d: string): string { + if (!d) return '' + const s = String(d).slice(0, 19).replace('T', ' ') + return s.endsWith(' 00:00:00') ? s.slice(0, 10) : s +}