feat: add durable WorldQuant backtests with UI and AI confirmation
This commit is contained in:
+67
-6
@@ -15,6 +15,7 @@ import type { Account, Job } from "./types";
|
||||
import { AccountPage } from "./pages/AccountPage";
|
||||
import { AlphaPage } from "./pages/AlphaPage";
|
||||
import { JobPanel } from "./components/JobPanel";
|
||||
import { BacktestPage } from "./backtests/BacktestPage";
|
||||
import { ChatPanel } from "./ai/ChatPanel";
|
||||
import type { PageContext, UIAction } from "./ai/types";
|
||||
|
||||
@@ -23,8 +24,18 @@ export default function App() {
|
||||
const [account, setAccount] = useState<Account | null>(null);
|
||||
const [jobs, setJobs] = useState<Job[]>([]);
|
||||
const [page, setPage] = useState(
|
||||
location.hash === "#account" ? "account" : "alphas",
|
||||
location.hash === "#backtests"
|
||||
? "backtests"
|
||||
: location.hash === "#account"
|
||||
? "account"
|
||||
: "alphas",
|
||||
);
|
||||
const [visitedBacktests, setVisitedBacktests] = useState(
|
||||
page === "backtests",
|
||||
);
|
||||
useEffect(() => {
|
||||
if (page === "backtests") setVisitedBacktests(true);
|
||||
}, [page]);
|
||||
const [showJobs, setShowJobs] = useState(false);
|
||||
const [refreshKey, setRefreshKey] = useState(0);
|
||||
const [pollError, setPollError] = useState("");
|
||||
@@ -34,6 +45,9 @@ export default function App() {
|
||||
const [alphaContext, setAlphaContext] = useState<PageContext>({
|
||||
page: "alphas",
|
||||
});
|
||||
const [backtestContext, setBacktestContext] = useState<PageContext>({
|
||||
page: "backtests",
|
||||
});
|
||||
const [aiAction, setAIAction] = useState<UIAction | null>(null);
|
||||
const chatOffset = viewport >= 1440 && chatOpen ? chatWidth : 0;
|
||||
const focusBusiness = useCallback(() => {
|
||||
@@ -84,7 +98,13 @@ export default function App() {
|
||||
};
|
||||
window.addEventListener("session-expired", expired);
|
||||
const hash = () =>
|
||||
setPage(location.hash === "#account" ? "account" : "alphas");
|
||||
setPage(
|
||||
location.hash === "#backtests"
|
||||
? "backtests"
|
||||
: location.hash === "#account"
|
||||
? "account"
|
||||
: "alphas",
|
||||
);
|
||||
window.addEventListener("hashchange", hash);
|
||||
return () => {
|
||||
window.removeEventListener("session-expired", expired);
|
||||
@@ -175,6 +195,14 @@ export default function App() {
|
||||
>
|
||||
个人信息
|
||||
</button>
|
||||
<button
|
||||
aria-label="回测研究"
|
||||
className={`nav-item ${page === "backtests" ? "active" : ""}`}
|
||||
aria-current={page === "backtests" ? "page" : undefined}
|
||||
onClick={() => changePage("backtests")}
|
||||
>
|
||||
回测研究
|
||||
</button>
|
||||
<div className="sidebar-bottom">
|
||||
<div className="connection-line">
|
||||
<i
|
||||
@@ -234,7 +262,7 @@ export default function App() {
|
||||
</div>
|
||||
</header>
|
||||
<main
|
||||
className={`page-content ${page === "alphas" ? "bounded-page" : "account-page"}`}
|
||||
className={`page-content ${page !== "account" ? "bounded-page" : "account-page"}`}
|
||||
>
|
||||
{pollError && (
|
||||
<Banner
|
||||
@@ -262,10 +290,32 @@ export default function App() {
|
||||
}
|
||||
chatOffset={chatOffset}
|
||||
onContext={setAlphaContext}
|
||||
action={aiAction}
|
||||
action={
|
||||
aiAction?.type === "open_alpha" ||
|
||||
aiAction?.type === "apply_filters"
|
||||
? aiAction
|
||||
: null
|
||||
}
|
||||
onOverlay={focusBusiness}
|
||||
/>
|
||||
</div>
|
||||
<div className="backtest-page-view" hidden={page !== "backtests"}>
|
||||
{visitedBacktests && (
|
||||
<BacktestPage
|
||||
active={page === "backtests"}
|
||||
suspended={showJobs || (viewport < 1440 && chatOpen)}
|
||||
chatOffset={chatOffset}
|
||||
timezone={account?.timezone}
|
||||
action={aiAction}
|
||||
onContext={setBacktestContext}
|
||||
onAction={(action) => {
|
||||
focusBusiness();
|
||||
changePage("alphas");
|
||||
setAIAction(action);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<JobPanel
|
||||
@@ -304,7 +354,13 @@ export default function App() {
|
||||
width={chatWidth}
|
||||
onWidth={setChatWidth}
|
||||
onClose={() => setChatOpen(false)}
|
||||
context={page === "alphas" ? alphaContext : { page: "account" }}
|
||||
context={
|
||||
page === "alphas"
|
||||
? alphaContext
|
||||
: page === "backtests"
|
||||
? backtestContext
|
||||
: { page: "account" }
|
||||
}
|
||||
timezone={account?.timezone}
|
||||
onSettings={() => {
|
||||
focusBusiness();
|
||||
@@ -318,7 +374,12 @@ export default function App() {
|
||||
onChanged={actionDone}
|
||||
onAction={(action) => {
|
||||
focusBusiness();
|
||||
changePage("alphas");
|
||||
changePage(
|
||||
action.type === "open_backtest" ||
|
||||
action.type === "open_backtest_preview"
|
||||
? "backtests"
|
||||
: "alphas",
|
||||
);
|
||||
setAIAction(action);
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user