feat: unify scope settings and catalog table interactions
Deploy production / deploy (push) Successful in 54s
Deploy production / deploy (push) Successful in 54s
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
import { SimulationSettingsEditor } from "../settings/SimulationSettingsEditor";
|
||||
import { useSettingsOptions } from "../settings/useSettingsOptions";
|
||||
import { initialSettings } from "../backtests/types";
|
||||
import {
|
||||
ResearchDataInput,
|
||||
researchSelection,
|
||||
@@ -40,21 +43,9 @@ export function FlowLaunchForm({
|
||||
const [seed, setSeed] = useState(0);
|
||||
const [confirmation, setConfirmation] = useState<FlowLaunch | null>(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [settings, setSettings] = useState<SimulationSettings>({
|
||||
instrumentType: "EQUITY",
|
||||
region: "USA",
|
||||
universe: "TOP3000",
|
||||
delay: 1,
|
||||
decay: 0,
|
||||
neutralization: "INDUSTRY",
|
||||
truncation: 0.08,
|
||||
pasteurization: "ON",
|
||||
unitHandling: "VERIFY",
|
||||
nanHandling: "OFF",
|
||||
language: "FASTEXPR",
|
||||
visualization: false,
|
||||
maxTrade: "OFF",
|
||||
});
|
||||
const [settings, setSettings] = useState<SimulationSettings>(initialSettings);
|
||||
const options = useSettingsOptions();
|
||||
const [settingsValid, setSettingsValid] = useState(false);
|
||||
useEffect(() => {
|
||||
const c = new AbortController();
|
||||
Promise.all([
|
||||
@@ -169,47 +160,17 @@ export function FlowLaunchForm({
|
||||
onChange={(v) => setTemplate(v ? String(v) : undefined)}
|
||||
/>
|
||||
</label>
|
||||
<p>
|
||||
{settings.region} / {settings.universe} / D{settings.delay} · REGULAR /
|
||||
FASTEXPR / EQUITY
|
||||
</p>
|
||||
<div className="research-form-grid">
|
||||
<label>
|
||||
中性化
|
||||
<Input
|
||||
aria-label="自动研究中性化"
|
||||
value={settings.neutralization}
|
||||
onChange={(neutralization) =>
|
||||
setSettings({ ...settings, neutralization })
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Decay
|
||||
<InputNumber
|
||||
aria-label="自动研究 Decay"
|
||||
min={0}
|
||||
value={settings.decay}
|
||||
onChange={(v) => {
|
||||
if (typeof v === "number") setSettings({ ...settings, decay: v });
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Truncation
|
||||
<InputNumber
|
||||
aria-label="自动研究 Truncation"
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.01}
|
||||
value={settings.truncation}
|
||||
onChange={(v) => {
|
||||
if (typeof v === "number")
|
||||
setSettings({ ...settings, truncation: v });
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<SimulationSettingsEditor
|
||||
value={settings}
|
||||
onChange={setSettings}
|
||||
{...options}
|
||||
onRefresh={() => void options.refresh()}
|
||||
fixedScopes={inputs
|
||||
.filter((r) => ids.includes(r.id))
|
||||
.map((r) => r.scope)}
|
||||
disabled={busy}
|
||||
onValidityChange={setSettingsValid}
|
||||
/>
|
||||
<h4>本次授权预算</h4>
|
||||
<div className="research-form-grid">
|
||||
{(
|
||||
@@ -263,7 +224,13 @@ export function FlowLaunchForm({
|
||||
/>
|
||||
<Button
|
||||
theme="solid"
|
||||
disabled={!ids.length || !hypothesis.trim() || !name.trim()}
|
||||
disabled={
|
||||
!ids.length ||
|
||||
!hypothesis.trim() ||
|
||||
!name.trim() ||
|
||||
!settingsValid ||
|
||||
busy
|
||||
}
|
||||
onClick={preview}
|
||||
>
|
||||
核对并启动研究
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { SimulationSettingsEditor } from "../settings/SimulationSettingsEditor";
|
||||
import { useSettingsOptions } from "../settings/useSettingsOptions";
|
||||
import {
|
||||
ResearchDataInput,
|
||||
researchSelection,
|
||||
@@ -67,7 +69,8 @@ export function ResearchWorkspace({
|
||||
const [parent, setParent] = useState("");
|
||||
const [method, setMethod] = useState("structure");
|
||||
const [settings, setSettings] = useState<SimulationSettings>(initialSettings);
|
||||
const [neutralizations, setNeutralizations] = useState<string[]>([]);
|
||||
const options = useSettingsOptions(active && detailOpen);
|
||||
const [settingsValid, setSettingsValid] = useState(false);
|
||||
const [mode, setMode] = useState("all");
|
||||
const [limit, setLimit] = useState(100);
|
||||
const [seed, setSeed] = useState(0);
|
||||
@@ -188,28 +191,6 @@ export function ResearchWorkspace({
|
||||
setMethod("structure");
|
||||
}
|
||||
}, [active, action]);
|
||||
async function refreshSettings() {
|
||||
const data = await post<{
|
||||
content: {
|
||||
items: {
|
||||
region: string;
|
||||
universe: string;
|
||||
delay: number;
|
||||
neutralizations: string[];
|
||||
}[];
|
||||
};
|
||||
}>("/catalog/setting-options/refresh");
|
||||
const options = data.content.items
|
||||
.filter(
|
||||
(row) =>
|
||||
row.region === settings.region &&
|
||||
row.universe === settings.universe &&
|
||||
row.delay === settings.delay,
|
||||
)
|
||||
.flatMap((row) => row.neutralizations);
|
||||
setNeutralizations([...new Set(options)]);
|
||||
Toast.success("合法设置已同步");
|
||||
}
|
||||
async function save() {
|
||||
const next = asset
|
||||
? await api<Asset>(`/research/assets/${asset.id}`, {
|
||||
@@ -759,88 +740,24 @@ export function ResearchWorkspace({
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="research-section-heading">
|
||||
<h4>模拟设置</h4>
|
||||
<Button
|
||||
disabled={!inputIds.length || !!busy}
|
||||
onClick={() => void task("同步合法设置", refreshSettings)}
|
||||
>
|
||||
同步合法设置
|
||||
</Button>
|
||||
</div>
|
||||
<p>
|
||||
{settings.region || "未选择市场"} /{" "}
|
||||
{settings.universe || "未选择股票池"} / D{settings.delay}
|
||||
(来自固定输入)
|
||||
</p>
|
||||
<div className="research-form-grid">
|
||||
<label>
|
||||
中性化
|
||||
<Input
|
||||
list={`${page}-neutralizations`}
|
||||
aria-label="中性化"
|
||||
value={settings.neutralization}
|
||||
onChange={(neutralization) =>
|
||||
setSettings({ ...settings, neutralization })
|
||||
}
|
||||
/>
|
||||
<datalist id={`${page}-neutralizations`}>
|
||||
{neutralizations.map((value) => (
|
||||
<option key={value} value={value} />
|
||||
))}
|
||||
</datalist>
|
||||
</label>
|
||||
<label>
|
||||
Decay
|
||||
<InputNumber
|
||||
aria-label="Decay"
|
||||
min={0}
|
||||
value={settings.decay}
|
||||
onChange={(decay) =>
|
||||
setSettings({ ...settings, decay: Number(decay) })
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Truncation
|
||||
<InputNumber
|
||||
aria-label="Truncation"
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.01}
|
||||
value={settings.truncation}
|
||||
onChange={(truncation) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
truncation: Number(truncation),
|
||||
})
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="research-form-grid">
|
||||
{(["pasteurization", "nanHandling", "maxTrade"] as const).map(
|
||||
(key) => (
|
||||
<label key={key}>
|
||||
{key}
|
||||
<ResearchSelect
|
||||
label={key}
|
||||
value={settings[key]}
|
||||
optionList={["ON", "OFF"].map((value) => ({
|
||||
value,
|
||||
label: value,
|
||||
}))}
|
||||
onChange={(value) =>
|
||||
setSettings({ ...settings, [key]: value })
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
<SimulationSettingsEditor
|
||||
value={settings}
|
||||
onChange={setSettings}
|
||||
{...options}
|
||||
onRefresh={() => void options.refresh()}
|
||||
fixedScopes={selectedInputs.map((r) => r.scope)}
|
||||
disabled={!!busy}
|
||||
onValidityChange={setSettingsValid}
|
||||
/>
|
||||
<Button
|
||||
theme="solid"
|
||||
disabled={dirty || !inputIds.length || !hypothesis || !!busy}
|
||||
disabled={
|
||||
dirty ||
|
||||
!inputIds.length ||
|
||||
!hypothesis ||
|
||||
!!busy ||
|
||||
!settingsValid
|
||||
}
|
||||
onClick={() => void task("生成候选", expand)}
|
||||
>
|
||||
保存候选研究记录
|
||||
|
||||
Reference in New Issue
Block a user