fix: allow repeated platform checks and unify Alpha metric formatting
Deploy production / deploy (push) Successful in 54s

This commit is contained in:
yuxuanhui
2026-09-12 22:42:37 +08:00
parent c18960946b
commit f3eb239e1a
8 changed files with 196 additions and 17 deletions
+12
View File
@@ -0,0 +1,12 @@
/** Format the five headline Alpha metrics for display; stored/filter values stay raw.
* Unsupported fields return undefined so callers can retain their existing format.
*/
export function formatAlphaMetric(
key: string,
value: unknown,
): string | undefined {
const percent = key === "turnover" || key === "returns" || key === "drawdown";
if (!percent && key !== "sharpe" && key !== "fitness") return undefined;
if (typeof value !== "number" || !Number.isFinite(value)) return "—";
return percent ? `${(value * 100).toFixed(2)}%` : value.toFixed(2);
}
+5 -1
View File
@@ -1,4 +1,5 @@
import { useEffect, useRef, useState } from "react";
import { formatAlphaMetric } from "../alphaMetrics";
import {
Banner,
Button,
@@ -464,7 +465,10 @@ function MetricTable({
<h3>{title}</h3>
{entries.length ? (
<DetailFieldGrid
data={entries.map(([key, value]) => ({ key, value }))}
data={entries.map(([key, value]) => ({
key,
value: formatAlphaMetric(key, value) ?? value,
}))}
/>
) : (
<p className="muted">未提供</p>
+26 -14
View File
@@ -34,6 +34,7 @@ export function SubmissionPanel({
const [busy, setBusy] = useState("");
const [error, setError] = useState("");
const dirty = useRef(false);
const submittedJob = useRef<string | null>(null);
const mounted = useRef(true);
useEffect(() => {
mounted.current = true;
@@ -47,6 +48,16 @@ export function SubmissionPanel({
.then((value) => {
if (!active) return;
setData(value);
// Rebase only our completed writeback and its exact resulting snapshot.
// A different expression/settings/description must still require review.
if (
value.job?.id === submittedJob.current &&
value.job?.status === "completed" &&
value.job.checkpoint.review_snapshot === value.snapshot
) {
setSnapshot(value.snapshot);
submittedJob.current = null;
}
if (!dirty.current) {
setDraft(value.descriptions);
setSnapshot(value.snapshot);
@@ -89,6 +100,7 @@ export function SubmissionPanel({
if (!mounted.current) return;
// Keep the reviewed draft visible while the durable task runs.
dirty.current = true;
submittedJob.current = job.id;
setData((current) => (current ? { ...current, job } : current));
onTask();
} catch (e) {
@@ -105,14 +117,14 @@ export function SubmissionPanel({
<div className="detail-section">
<h3>Description 与提交检查</h3>
<p className="muted">
AI 一次生成完整 Description,可在下方统一修改。写回并检查会更新 BRAIN 的
AI 一次生成完整 Description,可在下方统一修改。平台检查会更新 BRAIN 的
Description,随后获取平台提交检查结果,不会正式提交 Alpha。
</p>
{error && <Banner type="danger" description={error} />}
{!data.can_check && (
<Banner
type="info"
description="写回并检查需要待提交 Alpha。有本地比较基准时,需取得有效、完整且低于阈值的自相关结果;没有本地基准时可直接继续平台检查。"
description="平台检查需要待提交 Alpha。有本地比较基准时,需取得有效、完整且低于阈值的自相关结果;没有本地基准时可直接继续平台检查。"
/>
)}
{!data.can_generate && (
@@ -121,6 +133,17 @@ export function SubmissionPanel({
</p>
)}
<div className="inline-actions">
<Button
disabled={!!busy || running}
onClick={() => {
dirty.current = false;
setDraft(data.descriptions);
setSnapshot(data.snapshot);
setError("");
}}
>
载入最新描述
</Button>
<Button
onClick={() => void generate()}
loading={busy === "generate"}
@@ -181,18 +204,7 @@ export function SubmissionPanel({
disabled={!!busy || running || conflict || !data.can_check}
onClick={() => void check()}
>
写回 Description 并检查
</Button>
<Button
disabled={!!busy || running}
onClick={() => {
dirty.current = false;
setDraft(data.descriptions);
setSnapshot(data.snapshot);
setError("");
}}
>
载入最新描述
平台检查
</Button>
</div>
{data.job && (
+4 -1
View File
@@ -27,6 +27,7 @@ import {
IconRefresh,
} from "@douyinfe/semi-icons";
import "./AlphaPage.css";
import { formatAlphaMetric } from "../alphaMetrics";
import type { ColumnProps } from "@douyinfe/semi-ui-19/lib/es/table/interface";
import {
api,
@@ -84,8 +85,10 @@ function metricColor(key: string, value: unknown): string | undefined {
return "var(--semi-color-danger)";
}
/** Convert Margin only for display; filtering and sorting still use raw values. */
/** Format metrics only for display; filtering and sorting still use raw values. */
function formatMetric(key: string, value: unknown): string {
const headline = formatAlphaMetric(key, value);
if (headline !== undefined) return headline;
if (key !== "margin") return formatNumber(value, 3);
return typeof value === "number" && Number.isFinite(value)
? `${formatNumber(value * 10000, 2)}bps`
+1
View File
@@ -138,6 +138,7 @@ export type Job = {
alpha_ids?: string[];
};
checkpoint: {
review_snapshot?: string;
date?: string;
dataset_id?: string;
datasets_completed?: number;