Collapse field hints into a default-folded 规则说明 on strategy and runtime settings.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+105
-41
@@ -1,4 +1,4 @@
|
|||||||
import { FormEvent, useEffect, useState } from "react";
|
import { FormEvent, ReactNode, useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
changeCredentials,
|
changeCredentials,
|
||||||
getUsername,
|
getUsername,
|
||||||
@@ -15,9 +15,38 @@ type StratSub =
|
|||||||
| "exit"
|
| "exit"
|
||||||
| "pace";
|
| "pace";
|
||||||
|
|
||||||
|
function RulesFold({
|
||||||
|
open,
|
||||||
|
onToggle,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
onToggle: () => void;
|
||||||
|
children: ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={`settings-rules ${open ? "open" : ""}`}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="settings-rules-toggle"
|
||||||
|
aria-expanded={open}
|
||||||
|
onClick={onToggle}
|
||||||
|
>
|
||||||
|
<span>规则说明</span>
|
||||||
|
<span className="settings-rules-chevron" aria-hidden>
|
||||||
|
{open ? "▾" : "▸"}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
{open ? <div className="settings-rules-body">{children}</div> : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function SettingsPage() {
|
export default function SettingsPage() {
|
||||||
const [tab, setTab] = useState<Tab>("strategy");
|
const [tab, setTab] = useState<Tab>("strategy");
|
||||||
const [stratSub, setStratSub] = useState<StratSub>("position");
|
const [stratSub, setStratSub] = useState<StratSub>("position");
|
||||||
|
const [stratRulesOpen, setStratRulesOpen] = useState(false);
|
||||||
|
const [runtimeRulesOpen, setRuntimeRulesOpen] = useState(false);
|
||||||
|
|
||||||
const [newUsername, setNewUsername] = useState(getUsername() || "admin");
|
const [newUsername, setNewUsername] = useState(getUsername() || "admin");
|
||||||
const [currentPassword, setCurrentPassword] = useState("");
|
const [currentPassword, setCurrentPassword] = useState("");
|
||||||
@@ -247,11 +276,6 @@ export default function SettingsPage() {
|
|||||||
|
|
||||||
{tab === "strategy" ? (
|
{tab === "strategy" ? (
|
||||||
<div className="card settings-card">
|
<div className="card settings-card">
|
||||||
<p className="settings-lead">
|
|
||||||
{(runtime?.mode ?? mode) === "LIVE"
|
|
||||||
? "当前 LIVE:仓位/选约/出场影响真下单;模拟资金已隐藏。"
|
|
||||||
: "当前 SIM:本地撮合;可改模拟资金与交易所行情源。"}
|
|
||||||
</p>
|
|
||||||
{stratOk ? <div className="settings-ok">{stratOk}</div> : null}
|
{stratOk ? <div className="settings-ok">{stratOk}</div> : null}
|
||||||
{err && tab === "strategy" ? <div className="err">{err}</div> : null}
|
{err && tab === "strategy" ? <div className="err">{err}</div> : null}
|
||||||
|
|
||||||
@@ -301,9 +325,6 @@ export default function SettingsPage() {
|
|||||||
币安(USDT 永续 + 欧洲期权)
|
币安(USDT 永续 + 欧洲期权)
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<p className="settings-hint">
|
|
||||||
有持仓时不可切换。切换后套用该所合约并重连行情。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
{(runtime?.mode ?? mode) !== "LIVE" ? (
|
{(runtime?.mode ?? mode) !== "LIVE" ? (
|
||||||
<div className="field">
|
<div className="field">
|
||||||
@@ -319,9 +340,6 @@ export default function SettingsPage() {
|
|||||||
setInitialEquity(Number(e.target.value))
|
setInitialEquity(Number(e.target.value))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<p className="settings-hint">
|
|
||||||
仅 SIM 显示。保存后重置权益(须无持仓)。默认 10000。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="field">
|
<div className="field">
|
||||||
@@ -405,9 +423,6 @@ export default function SettingsPage() {
|
|||||||
<option value="0">关闭(默认)</option>
|
<option value="0">关闭(默认)</option>
|
||||||
<option value="1">开启</option>
|
<option value="1">开启</option>
|
||||||
</select>
|
</select>
|
||||||
<p className="settings-hint">
|
|
||||||
关闭时不限制 |ATM − 现价|;开启后超过下方点数则不开仓。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label htmlFor="atmoff">开仓 ATM 最大偏差(点)</label>
|
<label htmlFor="atmoff">开仓 ATM 最大偏差(点)</label>
|
||||||
@@ -421,9 +436,6 @@ export default function SettingsPage() {
|
|||||||
value={maxAtmOff}
|
value={maxAtmOff}
|
||||||
onChange={(e) => setMaxAtmOff(Number(e.target.value))}
|
onChange={(e) => setMaxAtmOff(Number(e.target.value))}
|
||||||
/>
|
/>
|
||||||
<p className="settings-hint">
|
|
||||||
仅开关开启时生效。币安行权价较粗时可能长时间无开仓机会。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -491,9 +503,6 @@ export default function SettingsPage() {
|
|||||||
value={closeDevPct}
|
value={closeDevPct}
|
||||||
onChange={(e) => setCloseDevPct(Number(e.target.value))}
|
onChange={(e) => setCloseDevPct(Number(e.target.value))}
|
||||||
/>
|
/>
|
||||||
<p className="settings-hint">
|
|
||||||
主要用于 SIM 流动性闸门;LIVE 以交易所能否成交为准。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -530,9 +539,6 @@ export default function SettingsPage() {
|
|||||||
setOrderInterval(Number(e.target.value))
|
setOrderInterval(Number(e.target.value))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<p className="settings-hint">
|
|
||||||
LIVE 私有下单/查单间隔,默认 1s。范围 0.2–30。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label htmlFor="skipWe">周六日跳过开仓(上海时区)</label>
|
<label htmlFor="skipWe">周六日跳过开仓(上海时区)</label>
|
||||||
@@ -561,6 +567,53 @@ export default function SettingsPage() {
|
|||||||
</section>
|
</section>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
<RulesFold
|
||||||
|
open={stratRulesOpen}
|
||||||
|
onToggle={() => setStratRulesOpen((v) => !v)}
|
||||||
|
>
|
||||||
|
<ul className="settings-rules-list">
|
||||||
|
<li>
|
||||||
|
{(runtime?.mode ?? mode) === "LIVE"
|
||||||
|
? "当前 LIVE:仓位/选约/出场影响真下单;模拟资金已隐藏。"
|
||||||
|
: "当前 SIM:本地撮合;可改模拟资金与交易所行情源。"}
|
||||||
|
</li>
|
||||||
|
{stratSub === "position" ? (
|
||||||
|
<>
|
||||||
|
<li>交易所:有持仓时不可切换。切换后套用该所合约并重连行情。</li>
|
||||||
|
{(runtime?.mode ?? mode) !== "LIVE" ? (
|
||||||
|
<li>
|
||||||
|
模拟资金:仅 SIM 显示。保存后重置权益(须无持仓)。默认
|
||||||
|
10000。
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{stratSub === "select" ? (
|
||||||
|
<>
|
||||||
|
<li>
|
||||||
|
开仓 ATM 偏差限制:关闭时不限制 |ATM −
|
||||||
|
现价|;开启后超过下方点数则不开仓。
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
开仓 ATM 最大偏差:仅开关开启时生效。币安行权价较粗时可能长时间无开仓机会。
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{stratSub === "exit" ? (
|
||||||
|
<li>
|
||||||
|
平仓买一/标记最大偏差:主要用于 SIM
|
||||||
|
流动性闸门;LIVE 以交易所能否成交为准。
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
{stratSub === "pace" ? (
|
||||||
|
<li>
|
||||||
|
实盘下单最小间隔:LIVE 私有下单/查单间隔,默认 1s。范围
|
||||||
|
0.2–30。
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
</ul>
|
||||||
|
</RulesFold>
|
||||||
|
|
||||||
<div className="settings-actions">
|
<div className="settings-actions">
|
||||||
<button className="btn" type="submit">
|
<button className="btn" type="submit">
|
||||||
保存策略
|
保存策略
|
||||||
@@ -572,10 +625,6 @@ export default function SettingsPage() {
|
|||||||
|
|
||||||
{tab === "runtime" ? (
|
{tab === "runtime" ? (
|
||||||
<div className="card settings-card">
|
<div className="card settings-card">
|
||||||
<p className="settings-lead">
|
|
||||||
SIM = 本地撮合;LIVE = 当前所选交易所真下单(OKX / 币安均已接入)。密钥只写入服务器{" "}
|
|
||||||
<span className="mono">.env</span>,接口不回传明文。OKX 期权常需账户有 USDC;币安多为 USDT。
|
|
||||||
</p>
|
|
||||||
{runtimeOk ? <div className="settings-ok">{runtimeOk}</div> : null}
|
{runtimeOk ? <div className="settings-ok">{runtimeOk}</div> : null}
|
||||||
{err && tab === "runtime" ? <div className="err">{err}</div> : null}
|
{err && tab === "runtime" ? <div className="err">{err}</div> : null}
|
||||||
<form onSubmit={onSaveRuntime}>
|
<form onSubmit={onSaveRuntime}>
|
||||||
@@ -595,15 +644,6 @@ export default function SettingsPage() {
|
|||||||
<option value="SIM">SIM 本地模拟</option>
|
<option value="SIM">SIM 本地模拟</option>
|
||||||
<option value="LIVE">LIVE 实盘</option>
|
<option value="LIVE">LIVE 实盘</option>
|
||||||
</select>
|
</select>
|
||||||
<p className="settings-hint">
|
|
||||||
当前:{runtime?.mode || "—"} · 交易所 {runtime?.exchange || "—"} ·{" "}
|
|
||||||
{runtime?.mode === "LIVE"
|
|
||||||
? runtime.live_ready
|
|
||||||
? "LIVE 就绪"
|
|
||||||
: `未就绪(${runtime.live_ready_reason})`
|
|
||||||
: "SIM"}
|
|
||||||
。有持仓时不可切换模式。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -697,13 +737,37 @@ export default function SettingsPage() {
|
|||||||
onChange={(e) => setBnSecret(e.target.value)}
|
onChange={(e) => setBnSecret(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className="settings-hint">
|
|
||||||
留空保存=不覆盖已有密钥。交易所选币安且 LIVE 时用此密钥真下单(无
|
|
||||||
Passphrase)。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<RulesFold
|
||||||
|
open={runtimeRulesOpen}
|
||||||
|
onToggle={() => setRuntimeRulesOpen((v) => !v)}
|
||||||
|
>
|
||||||
|
<ul className="settings-rules-list">
|
||||||
|
<li>
|
||||||
|
SIM = 本地撮合;LIVE =
|
||||||
|
当前所选交易所真下单(OKX / 币安均已接入)。密钥只写入服务器{" "}
|
||||||
|
<span className="mono">.env</span>
|
||||||
|
,接口不回传明文。OKX 期权常需账户有 USDC;币安多为 USDT。
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
当前:{runtime?.mode || "—"} · 交易所{" "}
|
||||||
|
{runtime?.exchange || "—"} ·{" "}
|
||||||
|
{runtime?.mode === "LIVE"
|
||||||
|
? runtime.live_ready
|
||||||
|
? "LIVE 就绪"
|
||||||
|
: `未就绪(${runtime.live_ready_reason})`
|
||||||
|
: "SIM"}
|
||||||
|
。有持仓时不可切换模式。
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
密钥留空保存 = 不覆盖已有配置。交易所选币安且 LIVE
|
||||||
|
时用币安密钥真下单(无 Passphrase)。
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</RulesFold>
|
||||||
|
|
||||||
<div className="settings-actions">
|
<div className="settings-actions">
|
||||||
<button className="btn" type="submit" disabled={loading}>
|
<button className="btn" type="submit" disabled={loading}>
|
||||||
{loading ? "保存中…" : "保存模式与密钥"}
|
{loading ? "保存中…" : "保存模式与密钥"}
|
||||||
|
|||||||
@@ -472,6 +472,60 @@ input {
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-rules {
|
||||||
|
margin: 4px 0 12px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--input);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-rules-toggle {
|
||||||
|
appearance: none;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--muted);
|
||||||
|
padding: 10px 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-rules-toggle:hover {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-rules.open .settings-rules-toggle {
|
||||||
|
color: var(--text);
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-rules-chevron {
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-rules-body {
|
||||||
|
padding: 10px 12px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-rules-list {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 18px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-rules-list li + li {
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-actions {
|
.settings-actions {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user