feat: add scoped alpha sync and local self-correlation
This commit is contained in:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user