summaryrefslogtreecommitdiff
path: root/frontend/src/pages/ProjectBacktestTab.svelte
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/pages/ProjectBacktestTab.svelte')
-rw-r--r--frontend/src/pages/ProjectBacktestTab.svelte56
1 files changed, 36 insertions, 20 deletions
diff --git a/frontend/src/pages/ProjectBacktestTab.svelte b/frontend/src/pages/ProjectBacktestTab.svelte
index b640189..7d5b172 100644
--- a/frontend/src/pages/ProjectBacktestTab.svelte
+++ b/frontend/src/pages/ProjectBacktestTab.svelte
@@ -11,11 +11,13 @@
runStatusBadge
} from '../lib/format';
import { summarizeCoverage } from '../lib/coverage';
- import type { Run, Dataset } from '../lib/types';
+ import { go } from '../lib/router';
+import type { Run, Dataset } from '../lib/types';
import { Button } from '$lib/components/ui/button';
import { Alert } from '$lib/components/ui/alert';
import { Badge } from '$lib/components/ui/badge';
import * as Card from '$lib/components/ui/card';
+import * as Textarea from '$lib/components/ui/textarea';
import * as Table from '$lib/components/ui/table';
import * as Select from '$lib/components/ui/select';
import { Input } from '$lib/components/ui/input';
@@ -224,15 +226,26 @@
const canRerun = $derived(latest?.status === 'succeeded' || latest?.status === 'failed' || latest?.status === 'cancelled');
</script>
-<div class="flex flex-col gap-3">
- <Card.Root size="sm" class="p-4 flex flex-col gap-3">
- <h2 class="text-lg font-semibold">发起回测</h2>
- <Status busy={busyDatasets} error={cfgErr} busyText="检查就绪的数据集…" empty={null} />
+<div class="flex flex-col gap-4 md:gap-6">
+ <Card.Root size="sm" class="p-4 flex flex-col gap-4">
+ <div class="flex justify-between items-center gap-4">
+ <h2 class="text-lg font-semibold m-0">发起回测</h2>
+ <Button class="w-fit" onclick={startRun} disabled={!datasetId}>开始回测</Button>
+ </div>
+ {#if busyDatasets}
+ <Status busy skeletonRows={3} busyText="检查就绪的数据集…" empty={null} />
+ {:else if cfgErr}
+ <Status busy={false} error={cfgErr} empty={null}/>
+ {/if}
{#if !busyDatasets && datasetsReady.length === 0 && !cfgErr}
- <Status busy={false} empty="没有就绪的数据集" emptyHint="先在「数据」标签创建并等待数据集状态变为【就绪】,再回到这里发起回测" />
+ <Status busy={false} empty="没有就绪的数据集" emptyHint="先在「数据」标签创建并等待数据集状态变为【就绪】,再回到这里发起回测">
+ {#snippet emptyCta()}
+ <Button onclick={() => go(`/projects/${projectId}/data`)}>去接入数据</Button>
+ {/snippet}
+ </Status>
{/if}
{#if datasetsReady.length > 0}
- <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<div>
<Label for="bt-dataset">数据集</Label>
<Select.Root
@@ -261,7 +274,7 @@
<Label for="bt-capital">起始资金</Label>
<Input id="bt-capital" class="tabular-nums" type="number" bind:value={capital} min="1" step="1000" />
</div>
- <div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
+ <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<Label for="bt-commission">佣金率</Label>
<Input id="bt-commission" class="tabular-nums" type="number" bind:value={commission} min="0" max="0.1" step="0.0001" />
@@ -277,7 +290,7 @@
</div>
<div class="sm:col-span-2 lg:col-span-3">
<Label for="bt-params">策略参数(JSON,可选)</Label>
- <textarea id="bt-params" class="flex w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50" rows="1" bind:value={paramsText} placeholder="输入 JSON 参数,例如 fast/slow 周期"></textarea>
+ <Textarea.Root id="bt-params" class="w-full" rows={1} bind:value={paramsText} placeholder="输入 JSON 参数,例如 fast/slow 周期" />
</div>
</div>
{#if needAck}
@@ -288,28 +301,31 @@
<ul class="list-disc pl-5 mt-1">
{#each datasetWarnMsgs as m, i (i)}<li>{m}</li>{/each}
</ul>
- <label class="inline-flex items-center gap-2 mt-1">
+ <label class="inline-flex items-center gap-4 mt-1">
<input type="checkbox" bind:checked={acknowledged} />
我已知晓并用当前数据继续
</label>
</div>
</Alert>
{/if}
- <div class="flex items-center gap-2 flex-wrap">
- <Button onclick={startRun} disabled={!datasetId}>开始回测</Button>
+ <div class="flex items-center gap-4 flex-wrap">
{#if selectedDataset}
- <span class="text-sm text-muted-foreground">本次回测与该数据集、当前策略快照一同记录,可按原始条件重跑</span>
+ <span class="text-xs text-muted-foreground">本次回测与该数据集、当前策略快照一同记录,可按原始条件重跑</span>
{/if}
</div>
- <Status busy={false} error={cfgErr} empty={actionMsg} />
+ {#if cfgErr}
+ <Status busy={false} error={cfgErr} empty={null} />
+ {:else if actionMsg}
+ <Status busy={false} empty={actionMsg} />
+ {/if}
{/if}
</Card.Root>
- <Card.Root size="sm" class="p-4 flex flex-col gap-3">
- <div class="flex flex-wrap justify-between items-center gap-2">
- <h2 class="text-lg font-semibold">最近一次回测</h2>
+ <Card.Root size="sm" class="p-4 flex flex-col gap-4">
+ <div class="flex flex-wrap justify-between items-center gap-4">
+ <h2 class="text-lg font-semibold m-0">最近一次回测</h2>
{#if latest}
- <span class="flex items-center gap-2">
+ <span class="flex items-center gap-4">
<Badge variant="outline">Run 分支 {latest.version_id?.slice(0,8) ?? '—'}</Badge>
<Badge variant={runStatusBadge[latest.status] === 'ok' ? 'secondary' : runStatusBadge[latest.status] === 'warn' ? 'outline' : 'destructive'}>{runStatusLabel[latest.status]}</Badge>
</span>
@@ -349,7 +365,7 @@
{#if latest.status === 'succeeded' && latest.result}
<Button variant="outline" href={`#/projects/${projectId}/results?run=${latest.id}`} class="w-fit">查看完整结果与图表</Button>
{/if}
- <div class="flex items-center gap-2 flex-wrap">
+ <div class="flex items-center gap-4 flex-wrap">
{#if canCancel}
<Button variant="destructive" onclick={onCancel} disabled={cancelBusy}>
{cancelBusy ? '取消中…' : '取消回测'}
@@ -366,7 +382,7 @@
{/if}
</Card.Root>
- <div class="flex flex-col gap-3">
+ <div class="flex flex-col gap-4">
<h2 class="text-lg font-semibold">回测历史</h2>
<Status busy={historyBusy} error={historyErr} busyText="载入历史…" empty={null} />
{#if history && history.length > 0}