Hide manual trade by default, block open while running, auto-refresh auth token.
Also fix flat-side reconcile to check both long and short residuals; document in 更新说明. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -15,7 +15,7 @@ export default function LoginPage() {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await login(username.trim(), password);
|
||||
setSession(res.token, res.username);
|
||||
setSession(res.token, res.username, res.expires_in);
|
||||
nav("/plan", { replace: true });
|
||||
} catch (ex) {
|
||||
setErr(ex instanceof Error ? ex.message : String(ex));
|
||||
|
||||
+35
-22
@@ -144,11 +144,11 @@ export default function PlanPage() {
|
||||
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginBottom: 12 }}>
|
||||
<button
|
||||
className="btn"
|
||||
className={plan?.running ? "btn btn-running" : "btn"}
|
||||
type="button"
|
||||
disabled={
|
||||
!!busy ||
|
||||
plan?.running ||
|
||||
!!plan?.running ||
|
||||
(plan?.mode === "LIVE" && plan.live_ready === false)
|
||||
}
|
||||
title={
|
||||
@@ -158,7 +158,7 @@ export default function PlanPage() {
|
||||
}
|
||||
onClick={() => act("/api/plan/start", "start")}
|
||||
>
|
||||
启动策略
|
||||
{plan?.running ? "启动中" : "启动策略"}
|
||||
</button>
|
||||
<button
|
||||
className="btn ghost"
|
||||
@@ -168,26 +168,39 @@ export default function PlanPage() {
|
||||
>
|
||||
暂停
|
||||
</button>
|
||||
{plan?.show_manual_trade_buttons ? (
|
||||
<>
|
||||
<button
|
||||
className="btn ghost"
|
||||
type="button"
|
||||
disabled={
|
||||
!!busy ||
|
||||
!!plan?.running ||
|
||||
(plan?.mode === "LIVE" && plan.live_ready === false)
|
||||
}
|
||||
title={
|
||||
plan?.running
|
||||
? "策略自动运行中,禁止手动开仓"
|
||||
: plan?.mode === "LIVE" && plan.live_ready === false
|
||||
? plan.live_ready_reason || "LIVE 未就绪"
|
||||
: undefined
|
||||
}
|
||||
onClick={() => act("/api/sim/open-group", "open")}
|
||||
>
|
||||
手动开一组
|
||||
</button>
|
||||
<button
|
||||
className="btn ghost"
|
||||
type="button"
|
||||
disabled={!!busy}
|
||||
onClick={() => act("/api/sim/close-group", "close")}
|
||||
>
|
||||
手动全平
|
||||
</button>
|
||||
</>
|
||||
) : null}
|
||||
<button
|
||||
className="btn ghost"
|
||||
type="button"
|
||||
disabled={
|
||||
!!busy || (plan?.mode === "LIVE" && plan.live_ready === false)
|
||||
}
|
||||
onClick={() => act("/api/sim/open-group", "open")}
|
||||
>
|
||||
手动开一组
|
||||
</button>
|
||||
<button
|
||||
className="btn ghost"
|
||||
type="button"
|
||||
disabled={!!busy}
|
||||
onClick={() => act("/api/sim/close-group", "close")}
|
||||
>
|
||||
手动全平
|
||||
</button>
|
||||
<button
|
||||
className="btn ghost"
|
||||
className="btn danger"
|
||||
type="button"
|
||||
disabled={!!busy}
|
||||
onClick={() => act("/api/plan/emergency-close", "emg")}
|
||||
|
||||
@@ -73,6 +73,7 @@ export default function SettingsPage() {
|
||||
const [closeDevPct, setCloseDevPct] = useState(30);
|
||||
const [perpQty, setPerpQty] = useState(1);
|
||||
const [optQty, setOptQty] = useState(2);
|
||||
const [showManualTrade, setShowManualTrade] = useState(false);
|
||||
const [initialEquity, setInitialEquity] = useState(10000);
|
||||
const [exchange, setExchange] = useState<"okx" | "binance">("okx");
|
||||
const [stratOk, setStratOk] = useState("");
|
||||
@@ -113,6 +114,7 @@ export default function SettingsPage() {
|
||||
setCloseDevPct(s.close_bid_mark_max_pct ?? 30);
|
||||
setPerpQty(s.perp_qty_eth ?? 1);
|
||||
setOptQty(s.option_qty_eth ?? 2);
|
||||
setShowManualTrade(s.show_manual_trade_buttons === true);
|
||||
setInitialEquity(s.initial_equity ?? 10000);
|
||||
setExchange(s.exchange === "binance" ? "binance" : "okx");
|
||||
})
|
||||
@@ -139,7 +141,7 @@ export default function SettingsPage() {
|
||||
new_username: newUsername.trim(),
|
||||
new_password: newPassword,
|
||||
});
|
||||
setSession(res.token, res.username);
|
||||
setSession(res.token, res.username, res.expires_in);
|
||||
setCurrentPassword("");
|
||||
setNewPassword("");
|
||||
setConfirmPassword("");
|
||||
@@ -173,6 +175,7 @@ export default function SettingsPage() {
|
||||
close_bid_mark_max_pct: closeDevPct,
|
||||
perp_qty_eth: perpQty,
|
||||
option_qty_eth: optQty,
|
||||
show_manual_trade_buttons: showManualTrade,
|
||||
exchange,
|
||||
};
|
||||
// LIVE 不改模拟资金,避免误重置本地账本
|
||||
@@ -564,6 +567,20 @@ export default function SettingsPage() {
|
||||
onChange={(e) => setFee(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="showManual">显示手动开仓</label>
|
||||
<select
|
||||
id="showManual"
|
||||
className="mono"
|
||||
value={showManualTrade ? "1" : "0"}
|
||||
onChange={(e) =>
|
||||
setShowManualTrade(e.target.value === "1")
|
||||
}
|
||||
>
|
||||
<option value="0">不显示(默认)</option>
|
||||
<option value="1">显示「手动开一组 / 手动全平」</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
@@ -607,10 +624,16 @@ export default function SettingsPage() {
|
||||
</li>
|
||||
) : null}
|
||||
{stratSub === "pace" ? (
|
||||
<li>
|
||||
实盘下单最小间隔:LIVE 私有下单/查单间隔,默认 1s。范围
|
||||
0.2–30。
|
||||
</li>
|
||||
<>
|
||||
<li>
|
||||
实盘下单最小间隔:LIVE 私有下单/查单间隔,默认 1s。范围
|
||||
0.2–30。
|
||||
</li>
|
||||
<li>
|
||||
显示手动开仓:默认关闭。开启后计划页才出现「手动开一组 /
|
||||
手动全平」。策略自动运行中禁止手动开仓(须先暂停);紧急全平始终可用。
|
||||
</li>
|
||||
</>
|
||||
) : null}
|
||||
</ul>
|
||||
</RulesFold>
|
||||
|
||||
Reference in New Issue
Block a user