feat: add scoped alpha sync and local self-correlation

This commit is contained in:
yuxuanhui
2026-09-08 10:40:49 +08:00
parent 404a4d8a04
commit d2ccd94721
28 changed files with 1656 additions and 69 deletions
+100 -7
View File
@@ -11,6 +11,7 @@ import {
Pagination,
Select,
Table,
Tabs,
Tag,
TextArea,
Toast,
@@ -18,6 +19,7 @@ import {
import type { ColumnProps } from "@douyinfe/semi-ui-19/lib/es/table/interface";
import {
api,
correlationLabels,
formatNumber,
formatTime,
patch,
@@ -26,8 +28,15 @@ import {
stateLabels,
stateOptions,
} from "../api";
import type { Account, Alpha, AlphaPage as Page, Facets } from "../types";
import type {
Account,
Alpha,
AlphaPage as Page,
Facets,
Submission,
} from "../types";
import { AlphaDetail } from "../components/AlphaDetail";
import { AlphaSyncDialog } from "../components/AlphaSyncDialog";
import type { PageContext, UIAction } from "../ai/types";
const metricLabels = {
@@ -46,6 +55,7 @@ const initialColumns = [
"sharpe",
"fitness",
"turnover",
"correlation",
"research",
];
const columnLabels: Record<string, string> = {
@@ -63,6 +73,8 @@ const columnLabels: Record<string, string> = {
tags: "本地标签",
language: "语言",
created: "创建时间",
submitted: "提交时间",
correlation: "本地自相关",
};
export function AlphaPage({
@@ -99,6 +111,8 @@ export function AlphaPage({
const [facets, setFacets] = useState<Facets>({});
const [draft, setDraft] = useState<Record<string, string>>({});
const [filters, setFilters] = useState<Record<string, string>>({});
const [submission, setSubmission] = useState<Submission>("UNSUBMITTED");
const [syncingScope, setSyncingScope] = useState<Submission | null>(null);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(25);
const [sort, setSort] = useState("date_created");
@@ -157,12 +171,13 @@ export function AlphaPage({
const params = useMemo(
() => ({
...filters,
submission,
sort,
direction,
limit: pageSize,
offset: (page - 1) * pageSize,
}),
[filters, sort, direction, pageSize, page],
[filters, submission, sort, direction, pageSize, page],
);
const query = queryString(params);
useEffect(() => {
@@ -190,6 +205,7 @@ export function AlphaPage({
direction: nextDirection,
limit: _limit,
offset: _offset,
submission: nextSubmission,
...values
} = action.filters;
const next = Object.fromEntries(
@@ -201,6 +217,8 @@ export function AlphaPage({
setFilters(next);
setPage(1);
setSelected([]);
if (nextSubmission === "SUBMITTED" || nextSubmission === "UNSUBMITTED")
setSubmission(nextSubmission);
if (nextSort) setSort(String(nextSort));
if (nextDirection) setDirection(String(nextDirection));
}
@@ -247,10 +265,22 @@ export function AlphaPage({
setFilters({});
setPage(1);
}
function changeSubmission(value: string) {
setSubmission(value as Submission);
setDraft({});
setFilters({});
setPage(1);
setSelected([]);
setSort(value === "SUBMITTED" ? "date_submitted" : "date_created");
}
async function newTask(kind: string, ids: string[] = []) {
setBusy(kind);
try {
await post("/sync-jobs", { kind, alpha_ids: ids });
await post("/sync-jobs", {
kind,
alpha_ids: ids,
...(kind === "full_sync" ? { submission: "SUBMITTED" } : {}),
});
setImporting(false);
setIdText("");
onTask();
@@ -389,6 +419,31 @@ export function AlphaPage({
),
},
{ key: "language", title: "语言", width: 125, dataIndex: "language" },
{
key: "correlation",
title: "本地自相关",
width: 160,
render: (_, row) => {
const value = row!.local_correlation;
return value ? (
<span
title={`计算于 ${formatTime(value.calculated_at, account?.timezone)}`}
>
{value.stale ? "待重算" : correlationLabels[value.status]}
{value.max_correlation !== null &&
` · ${formatNumber(value.max_correlation, 3)}`}
</span>
) : (
<span className="muted">未检测</span>
);
},
},
{
key: "submitted",
title: "提交时间",
width: 165,
render: (_, row) => formatTime(row!.date_submitted, account?.timezone),
},
{
key: "created",
title: "创建时间",
@@ -419,12 +474,23 @@ export function AlphaPage({
</Button>
<Button
theme="solid"
loading={busy === "full_sync"}
disabled={!connected}
onClick={() => void newTask("full_sync")}
onClick={() => {
setSyncingScope(submission);
onOverlay();
}}
>
同步平台数据
按天同步
</Button>
{submission === "SUBMITTED" && (
<Button
loading={busy === "full_sync"}
disabled={!connected}
onClick={() => void newTask("full_sync")}
>
全量同步已提交
</Button>
)}
</div>
</div>
<div className="library-stats">
@@ -467,6 +533,15 @@ export function AlphaPage({
)}
{error && <Banner type="danger" description={error} />}
<section className="library-panel" ref={tablePanel}>
<Tabs
className="alpha-submission-tabs"
activeKey={submission}
onChange={changeSubmission}
tabList={[
{ itemKey: "UNSUBMITTED", tab: "待提交" },
{ itemKey: "SUBMITTED", tab: "已提交" },
]}
/>
<form
className="filter-panel"
onSubmit={(e) => {
@@ -619,7 +694,9 @@ export function AlphaPage({
</form>
<div className="table-toolbar">
<div>
<strong>全部 Alpha</strong>
<strong>
{submission === "UNSUBMITTED" ? "待提交" : "已提交"} Alpha
</strong>
<span className="count-pill">{data.total.toLocaleString()}</span>
{selected.length > 0 && (
<span className="selection-text">
@@ -630,6 +707,14 @@ export function AlphaPage({
<div className="inline-actions">
{selected.length > 0 && (
<>
<Button
type="tertiary"
size="small"
loading={busy === "self_correlation"}
onClick={() => void newTask("self_correlation", selected)}
>
检测自相关
</Button>
<Button
type="tertiary"
size="small"
@@ -844,6 +929,14 @@ export function AlphaPage({
/>
</label>
</Modal>
{syncingScope && (
<AlphaSyncDialog
submission={syncingScope}
suspended={overlaySuspended}
onClose={() => setSyncingScope(null)}
onTask={onTask}
/>
)}
<AlphaDetail
suspended={overlaySuspended}
chatOffset={chatOffset}