summaryrefslogtreecommitdiff
path: root/frontend/src/pages/RunsPage.svelte
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/pages/RunsPage.svelte')
-rw-r--r--frontend/src/pages/RunsPage.svelte59
1 files changed, 31 insertions, 28 deletions
diff --git a/frontend/src/pages/RunsPage.svelte b/frontend/src/pages/RunsPage.svelte
index bd77892..154fd67 100644
--- a/frontend/src/pages/RunsPage.svelte
+++ b/frontend/src/pages/RunsPage.svelte
@@ -2,7 +2,7 @@
import Page from '../components/Page.svelte';
import Status from '../components/Status.svelte';
import Chart from '../components/Chart.svelte';
- import type { EChartsOption } from 'echarts';
+ import { Button } from '$lib/components/ui/button';
import { listRuns } from '../lib/client';
import { extractStatusMessage } from '../lib/state';
import { fmtDateTime, runStatusLabel, runStatusBadge, fmtPct, fmtNum, formatMoney } from '../lib/format';
@@ -40,31 +40,34 @@
else chosenIds = [...chosenIds.slice(-3), id];
}
- const compareOption: EChartsOption = $derived.by(() => {
- if (comparable.length === 0) return {};
- const series = comparable.map((r, idx) => {
- const eq = r.result?.equity ?? [];
- const base = eq[0]?.equity ?? 1;
- const colors = ['#0f766e', '#b45309', '#1d4ed8', '#8b1d8b'];
- return {
- name: `Run ${r.id.slice(0, 6)}.`,
- type: 'line' as const,
- data: eq.map((p) => p.equity / base),
- showSymbol: false,
- lineStyle: { width: 1.5 },
- itemStyle: { color: colors[idx % colors.length] }
- };
- });
- let xLen = Math.max(...comparable.map((r) => (r.result?.equity ?? []).length), 0);
- const longest = comparable.reduce((best, r) => ((r.result?.equity ?? []).length > best.length ? r.result!.equity : best), [] as { date: string }[]);
- return {
- legend: { top: 0 },
- grid: { top: 32, left: 64, right: 20, bottom: 50 },
- tooltip: { trigger: 'axis' },
- xAxis: { type: 'category', data: longest.map((p) => p.date) },
- yAxis: { type: 'value', scale: true, name: '归一净值' },
- series
- };
+ // long-format rows for the layerchart renderer: one row per index, one
+ // column per selected run, first-day-normalized equity
+ const compare = $derived.by(() => {
+ if (comparable.length === 0) return { data: [], series: [] };
+ const colors = ['#0f766e', '#b45309', '#1d4ed8', '#8b1d8b'];
+ const flat = comparable.map((r, i) => ({
+ run: r,
+ eq: r.result?.equity ?? [],
+ key: `s${i}`,
+ color: colors[i % colors.length]
+ }));
+ const len = Math.max(0, ...flat.map((f) => f.eq.length));
+ const data: { [key: string]: string | number; i: number }[] = [];
+ for (let i = 0; i < len; i++) {
+ const row: { [key: string]: string | number; i: number } = { i, date: flat[0].eq[i]?.date ?? String(i) };
+ for (const f of flat) {
+ const base = f.eq[0]?.equity ?? 1;
+ if (f.eq[i]) row[f.key] = f.eq[i].equity / base;
+ }
+ data.push(row);
+ }
+ const series = flat.map((f) => ({
+ key: f.key,
+ label: `Run ${f.run.id.slice(0, 6)}.`,
+ value: f.key,
+ color: f.color
+ }));
+ return { data, series };
});
const configDiffRows = $derived.by(() => {
@@ -133,7 +136,7 @@
<td>{r.result ? fmtPct(r.result.metrics.total_return) : '—'}</td>
<td>{r.result ? fmtPct(r.result.metrics.max_drawdown) : '—'}</td>
<td>
- <a class="btn small" href={`#/projects/${r.project_id}/results?run=${r.id}`}>查看</a>
+ <Button size="sm" variant="outline" href={`#/projects/${r.project_id}/results?run=${r.id}`}>查看</Button>
</td>
</tr>
{/each}
@@ -168,7 +171,7 @@
{#if comparable.length > 1}
<div class="card stack">
<h2>收益曲线对齐比较(首日归一)</h2>
- <Chart option={compareOption} ariaLabel="多回测对比曲线" height={340} />
+ <Chart data={compare.data} series={compare.series} ariaLabel="多回测对比曲线" height={340} />
<p class="hint">起点对齐为 1,仅叠加不同起点的曲线时只在各自区间内展示。</p>
</div>
{:else}