Add always-apply rule: deploy after edits; chat-only when user says 先聊.
Also split strategy settings into subtabs and hide sim equity in LIVE. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
---
|
||||
description: 改完必推送部署;用户说「先聊」则只讨论不改代码
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# 交付与对话模式(硬指令)
|
||||
|
||||
## 先聊模式(最高优先)
|
||||
|
||||
用户消息含 **「先聊」**(或明确说只讨论、不要改代码、先别动手)时:
|
||||
|
||||
- **禁止**改代码、写文件、提交、推送、部署
|
||||
- 只做讨论、方案对比、审计结论、问答
|
||||
- 用户之后明确说「改吧 / 实现 / 动手」再进入改代码
|
||||
|
||||
## 改完必推送 + 部署
|
||||
|
||||
在**本仓库**完成一轮实际代码修改后(用户未说「先聊」、且不是纯问答),必须收尾:
|
||||
|
||||
1. **提交**:有改动则 `git add` 相关文件并 `git commit`(勿提交 `.env`、密钥、本地临时脚本如 `scripts/_ssh_*`、`_t.py`)
|
||||
2. **推送**:`git push origin HEAD`(或当前跟踪分支)
|
||||
3. **部署**:用本机脚本更新服务器(git pull + 构建 + pm2),例如:
|
||||
|
||||
```powershell
|
||||
$env:PYTHONIOENCODING='utf-8'
|
||||
# DEPLOY_PASS 须已在环境中配置;勿把密码写进规则或提交到仓库
|
||||
& .\.venv\Scripts\python.exe .\scripts\deploy_remote.py
|
||||
```
|
||||
|
||||
- 服务器:`47.236.184.99`,目录 `/opt/eth_hedge_sim`,进程 `eth-hedge-api`
|
||||
- 部署成功后简要回报:commit、push、health 是否 OK
|
||||
- 若用户明确说「先别部署 / 只提交不推 / 只推不部署」,则按用户当次要求覆盖本条
|
||||
|
||||
## 例外(可不推送部署)
|
||||
|
||||
- 「先聊」或只读审计、解释、方案讨论
|
||||
- 仅改本地未跟踪的临时探测脚本且用户未要求交付
|
||||
- 推送/部署失败时说明原因与已完成步骤,不要假装成功
|
||||
+331
-262
@@ -9,9 +9,15 @@ import {
|
||||
} from "../api/client";
|
||||
|
||||
type Tab = "strategy" | "runtime" | "account";
|
||||
type StratSub =
|
||||
| "position"
|
||||
| "select"
|
||||
| "exit"
|
||||
| "pace";
|
||||
|
||||
export default function SettingsPage() {
|
||||
const [tab, setTab] = useState<Tab>("strategy");
|
||||
const [stratSub, setStratSub] = useState<StratSub>("position");
|
||||
|
||||
const [newUsername, setNewUsername] = useState(getUsername() || "admin");
|
||||
const [currentPassword, setCurrentPassword] = useState("");
|
||||
@@ -120,31 +126,38 @@ export default function SettingsPage() {
|
||||
e.preventDefault();
|
||||
setStratOk("");
|
||||
setErr("");
|
||||
const isLive = (runtime?.mode ?? mode) === "LIVE";
|
||||
try {
|
||||
const body: Record<string, unknown> = {
|
||||
fee_rate: fee,
|
||||
exit_mode: exitMode,
|
||||
net_profit_target: netTarget,
|
||||
premium_exit_multiple: premMult,
|
||||
rest_seconds: rest,
|
||||
live_order_interval_sec: orderInterval,
|
||||
skip_weekends: skipWeekends,
|
||||
leverage,
|
||||
min_option_hours: minHours,
|
||||
min_option_leverage: minOptLev,
|
||||
atm_open_offset_enabled: atmOffOn,
|
||||
max_atm_open_offset: maxAtmOff,
|
||||
close_bid_mark_max_pct: closeDevPct,
|
||||
perp_qty_eth: perpQty,
|
||||
option_qty_eth: optQty,
|
||||
exchange,
|
||||
};
|
||||
// LIVE 不改模拟资金,避免误重置本地账本
|
||||
if (!isLive) {
|
||||
body.initial_equity = initialEquity;
|
||||
}
|
||||
await apiFetch("/api/settings/strategy", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({
|
||||
fee_rate: fee,
|
||||
exit_mode: exitMode,
|
||||
net_profit_target: netTarget,
|
||||
premium_exit_multiple: premMult,
|
||||
rest_seconds: rest,
|
||||
live_order_interval_sec: orderInterval,
|
||||
skip_weekends: skipWeekends,
|
||||
leverage,
|
||||
min_option_hours: minHours,
|
||||
min_option_leverage: minOptLev,
|
||||
atm_open_offset_enabled: atmOffOn,
|
||||
max_atm_open_offset: maxAtmOff,
|
||||
close_bid_mark_max_pct: closeDevPct,
|
||||
perp_qty_eth: perpQty,
|
||||
option_qty_eth: optQty,
|
||||
initial_equity: initialEquity,
|
||||
exchange,
|
||||
}),
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
setStratOk(
|
||||
"策略参数已保存(切换交易所/改模拟资金需无持仓;切换后会重连行情)",
|
||||
isLive
|
||||
? "策略参数已保存(实盘模式不改模拟资金)"
|
||||
: "策略参数已保存(切换交易所/改模拟资金需无持仓;切换后会重连行情)",
|
||||
);
|
||||
} catch (ex) {
|
||||
setErr(ex instanceof Error ? ex.message : String(ex));
|
||||
@@ -206,7 +219,10 @@ export default function SettingsPage() {
|
||||
<button
|
||||
type="button"
|
||||
className={tab === "strategy" ? "tab active" : "tab"}
|
||||
onClick={() => setTab("strategy")}
|
||||
onClick={() => {
|
||||
setTab("strategy");
|
||||
loadRuntime();
|
||||
}}
|
||||
>
|
||||
策略设置
|
||||
</button>
|
||||
@@ -232,265 +248,318 @@ export default function SettingsPage() {
|
||||
{tab === "strategy" ? (
|
||||
<div className="card settings-card">
|
||||
<p className="settings-lead">
|
||||
无开仓时间窗;可切换 OKX / 币安行情(SIM 本地撮合);周六日可跳过开仓;出场按净盈利或到期全平。
|
||||
{(runtime?.mode ?? mode) === "LIVE"
|
||||
? "当前 LIVE:仓位/选约/出场影响真下单;模拟资金已隐藏。"
|
||||
: "当前 SIM:本地撮合;可改模拟资金与交易所行情源。"}
|
||||
</p>
|
||||
{stratOk ? <div className="settings-ok">{stratOk}</div> : null}
|
||||
{err && tab === "strategy" ? <div className="err">{err}</div> : null}
|
||||
|
||||
<div className="settings-subtabs" role="tablist" aria-label="策略子分类">
|
||||
{(
|
||||
[
|
||||
["position", "仓位"],
|
||||
["select", "选约"],
|
||||
["exit", "出场"],
|
||||
["pace", "节奏"],
|
||||
] as const
|
||||
).map(([id, label]) => (
|
||||
<button
|
||||
key={id}
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={stratSub === id}
|
||||
className={
|
||||
stratSub === id ? "settings-subtab active" : "settings-subtab"
|
||||
}
|
||||
onClick={() => setStratSub(id)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<form onSubmit={onSaveStrategy}>
|
||||
<section className="settings-section">
|
||||
<h3>交易所</h3>
|
||||
<div className="settings-fields">
|
||||
<div className="field">
|
||||
<label htmlFor="exch">行情交易所(SIM 只读)</label>
|
||||
<select
|
||||
id="exch"
|
||||
className="mono"
|
||||
value={exchange}
|
||||
onChange={(e) =>
|
||||
setExchange(e.target.value === "binance" ? "binance" : "okx")
|
||||
}
|
||||
>
|
||||
<option value="okx">OKX(USDC 期权族)</option>
|
||||
<option value="binance">币安(USDT 永续 + 欧洲期权)</option>
|
||||
</select>
|
||||
<p className="settings-hint">
|
||||
有持仓时不可切换。切换后自动套用该所合约并重连公共行情。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="settings-section">
|
||||
<h3>资金</h3>
|
||||
<div className="settings-fields">
|
||||
<div className="field">
|
||||
<label htmlFor="equity">模拟资金(USDT)</label>
|
||||
<input
|
||||
id="equity"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="100"
|
||||
min="1000"
|
||||
value={initialEquity}
|
||||
onChange={(e) => setInitialEquity(Number(e.target.value))}
|
||||
/>
|
||||
<p className="settings-hint">
|
||||
保存后将权益重置为该金额(须无持仓)。默认 10000。
|
||||
</p>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="lev">永续杠杆倍数</label>
|
||||
<input
|
||||
id="lev"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
value={leverage}
|
||||
onChange={(e) => setLeverage(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="perp">永续 ETH 数量</label>
|
||||
<input
|
||||
id="perp"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0.01"
|
||||
value={perpQty}
|
||||
onChange={(e) => setPerpQty(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="opt">期权 ETH 数量</label>
|
||||
<input
|
||||
id="opt"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0.01"
|
||||
value={optQty}
|
||||
onChange={(e) => setOptQty(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="settings-section">
|
||||
<h3>选约</h3>
|
||||
<div className="settings-fields">
|
||||
<div className="field">
|
||||
<label htmlFor="hours">期权最小剩余小时</label>
|
||||
<input
|
||||
id="hours"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
value={minHours}
|
||||
onChange={(e) => setMinHours(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="olev">期权杠杆下限(现价÷卖一权利金)</label>
|
||||
<input
|
||||
id="olev"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
value={minOptLev}
|
||||
onChange={(e) => setMinOptLev(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="atmoffOn">开仓 ATM 偏差限制</label>
|
||||
<select
|
||||
id="atmoffOn"
|
||||
className="mono"
|
||||
value={atmOffOn ? "1" : "0"}
|
||||
onChange={(e) => setAtmOffOn(e.target.value === "1")}
|
||||
>
|
||||
<option value="0">关闭(默认)</option>
|
||||
<option value="1">开启</option>
|
||||
</select>
|
||||
<p className="settings-hint">
|
||||
关闭时不限制 |ATM − 现价|;开启后超过下方点数则不开仓。
|
||||
</p>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="atmoff">开仓 ATM 最大偏差(点)</label>
|
||||
<input
|
||||
id="atmoff"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.5"
|
||||
min="0"
|
||||
disabled={!atmOffOn}
|
||||
value={maxAtmOff}
|
||||
onChange={(e) => setMaxAtmOff(Number(e.target.value))}
|
||||
/>
|
||||
<p className="settings-hint">
|
||||
仅在上方开关开启时生效。默认 3;币安行权价步进较粗时可能长时间无开仓机会。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="settings-section">
|
||||
<h3>出场与风控</h3>
|
||||
<div className="settings-fields">
|
||||
<div className="field">
|
||||
<label htmlFor="exitMode">出场规则</label>
|
||||
<select
|
||||
id="exitMode"
|
||||
className="mono"
|
||||
value={exitMode}
|
||||
onChange={(e) =>
|
||||
setExitMode(
|
||||
e.target.value === "premium_multiple"
|
||||
? "premium_multiple"
|
||||
: "fixed_usdt",
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="fixed_usdt">固定净盈利(USDT)</option>
|
||||
<option value="premium_multiple">权利金倍数</option>
|
||||
</select>
|
||||
</div>
|
||||
{exitMode === "fixed_usdt" ? (
|
||||
{stratSub === "position" ? (
|
||||
<section className="settings-section">
|
||||
<h3>交易所与仓位</h3>
|
||||
<div className="settings-fields">
|
||||
<div className="field">
|
||||
<label htmlFor="netTarget">净盈利出场目标(USDT)</label>
|
||||
<label htmlFor="exch">交易所</label>
|
||||
<select
|
||||
id="exch"
|
||||
className="mono"
|
||||
value={exchange}
|
||||
onChange={(e) =>
|
||||
setExchange(
|
||||
e.target.value === "binance" ? "binance" : "okx",
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="okx">OKX(USDC 期权族)</option>
|
||||
<option value="binance">
|
||||
币安(USDT 永续 + 欧洲期权)
|
||||
</option>
|
||||
</select>
|
||||
<p className="settings-hint">
|
||||
有持仓时不可切换。切换后套用该所合约并重连行情。
|
||||
</p>
|
||||
</div>
|
||||
{(runtime?.mode ?? mode) !== "LIVE" ? (
|
||||
<div className="field">
|
||||
<label htmlFor="equity">模拟资金(USDT)</label>
|
||||
<input
|
||||
id="equity"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="100"
|
||||
min="1000"
|
||||
value={initialEquity}
|
||||
onChange={(e) =>
|
||||
setInitialEquity(Number(e.target.value))
|
||||
}
|
||||
/>
|
||||
<p className="settings-hint">
|
||||
仅 SIM 显示。保存后重置权益(须无持仓)。默认 10000。
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="field">
|
||||
<label htmlFor="lev">永续杠杆倍数</label>
|
||||
<input
|
||||
id="netTarget"
|
||||
id="lev"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
value={leverage}
|
||||
onChange={(e) => setLeverage(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="perp">永续 ETH 数量</label>
|
||||
<input
|
||||
id="perp"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0.01"
|
||||
value={perpQty}
|
||||
onChange={(e) => setPerpQty(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="opt">期权 ETH 数量</label>
|
||||
<input
|
||||
id="opt"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0.01"
|
||||
value={optQty}
|
||||
onChange={(e) => setOptQty(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{stratSub === "select" ? (
|
||||
<section className="settings-section">
|
||||
<h3>选约条件</h3>
|
||||
<div className="settings-fields">
|
||||
<div className="field">
|
||||
<label htmlFor="hours">期权最小剩余小时</label>
|
||||
<input
|
||||
id="hours"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
value={minHours}
|
||||
onChange={(e) => setMinHours(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="olev">
|
||||
期权杠杆下限(现价÷卖一权利金)
|
||||
</label>
|
||||
<input
|
||||
id="olev"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
value={minOptLev}
|
||||
onChange={(e) => setMinOptLev(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="atmoffOn">开仓 ATM 偏差限制</label>
|
||||
<select
|
||||
id="atmoffOn"
|
||||
className="mono"
|
||||
value={atmOffOn ? "1" : "0"}
|
||||
onChange={(e) => setAtmOffOn(e.target.value === "1")}
|
||||
>
|
||||
<option value="0">关闭(默认)</option>
|
||||
<option value="1">开启</option>
|
||||
</select>
|
||||
<p className="settings-hint">
|
||||
关闭时不限制 |ATM − 现价|;开启后超过下方点数则不开仓。
|
||||
</p>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="atmoff">开仓 ATM 最大偏差(点)</label>
|
||||
<input
|
||||
id="atmoff"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.5"
|
||||
min="0"
|
||||
disabled={!atmOffOn}
|
||||
value={maxAtmOff}
|
||||
onChange={(e) => setMaxAtmOff(Number(e.target.value))}
|
||||
/>
|
||||
<p className="settings-hint">
|
||||
仅开关开启时生效。币安行权价较粗时可能长时间无开仓机会。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{stratSub === "exit" ? (
|
||||
<section className="settings-section">
|
||||
<h3>出场与平仓</h3>
|
||||
<div className="settings-fields">
|
||||
<div className="field">
|
||||
<label htmlFor="exitMode">出场规则</label>
|
||||
<select
|
||||
id="exitMode"
|
||||
className="mono"
|
||||
value={exitMode}
|
||||
onChange={(e) =>
|
||||
setExitMode(
|
||||
e.target.value === "premium_multiple"
|
||||
? "premium_multiple"
|
||||
: "fixed_usdt",
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="fixed_usdt">固定净盈利(USDT)</option>
|
||||
<option value="premium_multiple">权利金倍数</option>
|
||||
</select>
|
||||
</div>
|
||||
{exitMode === "fixed_usdt" ? (
|
||||
<div className="field">
|
||||
<label htmlFor="netTarget">净盈利出场目标(USDT)</label>
|
||||
<input
|
||||
id="netTarget"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.1"
|
||||
min="0.1"
|
||||
value={netTarget}
|
||||
onChange={(e) => setNetTarget(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="field">
|
||||
<label htmlFor="premMult">
|
||||
权利金倍数(1 = 一倍权利金)
|
||||
</label>
|
||||
<input
|
||||
id="premMult"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.1"
|
||||
min="0.1"
|
||||
value={premMult}
|
||||
onChange={(e) => setPremMult(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="field">
|
||||
<label htmlFor="closeDev">平仓买一/标记最大偏差(%)</label>
|
||||
<input
|
||||
id="closeDev"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
value={closeDevPct}
|
||||
onChange={(e) => setCloseDevPct(Number(e.target.value))}
|
||||
/>
|
||||
<p className="settings-hint">
|
||||
主要用于 SIM 流动性闸门;LIVE 以交易所能否成交为准。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{stratSub === "pace" ? (
|
||||
<section className="settings-section">
|
||||
<h3>节奏与费用</h3>
|
||||
<div className="settings-fields">
|
||||
<div className="field">
|
||||
<label htmlFor="rest">轮间休息秒数</label>
|
||||
<input
|
||||
id="rest"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="1"
|
||||
value={rest}
|
||||
onChange={(e) => setRest(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="orderInterval">
|
||||
实盘下单最小间隔(秒)
|
||||
</label>
|
||||
<input
|
||||
id="orderInterval"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.1"
|
||||
min="0.1"
|
||||
value={netTarget}
|
||||
onChange={(e) => setNetTarget(Number(e.target.value))}
|
||||
min="0.2"
|
||||
max="30"
|
||||
value={orderInterval}
|
||||
onChange={(e) =>
|
||||
setOrderInterval(Number(e.target.value))
|
||||
}
|
||||
/>
|
||||
<p className="settings-hint">
|
||||
LIVE 私有下单/查单间隔,默认 1s。范围 0.2–30。
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="field">
|
||||
<label htmlFor="premMult">权利金倍数(1 = 一倍权利金)</label>
|
||||
<label htmlFor="skipWe">周六日跳过开仓(上海时区)</label>
|
||||
<select
|
||||
id="skipWe"
|
||||
className="mono"
|
||||
value={skipWeekends ? "1" : "0"}
|
||||
onChange={(e) => setSkipWeekends(e.target.value === "1")}
|
||||
>
|
||||
<option value="1">开启(推荐)</option>
|
||||
<option value="0">关闭</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="fee">手续费率</label>
|
||||
<input
|
||||
id="premMult"
|
||||
id="fee"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.1"
|
||||
min="0.1"
|
||||
value={premMult}
|
||||
onChange={(e) => setPremMult(Number(e.target.value))}
|
||||
step="0.0001"
|
||||
value={fee}
|
||||
onChange={(e) => setFee(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="field">
|
||||
<label htmlFor="closeDev">平仓买一/标记最大偏差(%)</label>
|
||||
<input
|
||||
id="closeDev"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
value={closeDevPct}
|
||||
onChange={(e) => setCloseDevPct(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="rest">轮间休息秒数</label>
|
||||
<input
|
||||
id="rest"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="1"
|
||||
value={rest}
|
||||
onChange={(e) => setRest(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="orderInterval">实盘下单最小间隔(秒)</label>
|
||||
<input
|
||||
id="orderInterval"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.1"
|
||||
min="0.2"
|
||||
max="30"
|
||||
value={orderInterval}
|
||||
onChange={(e) => setOrderInterval(Number(e.target.value))}
|
||||
/>
|
||||
<p className="settings-hint">
|
||||
LIVE 私有下单/查单间隔,默认 1s(非高频建议 ≥1)。范围
|
||||
0.2–30。保存后立即生效。
|
||||
</p>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="skipWe">周六日跳过开仓(上海时区)</label>
|
||||
<select
|
||||
id="skipWe"
|
||||
className="mono"
|
||||
value={skipWeekends ? "1" : "0"}
|
||||
onChange={(e) => setSkipWeekends(e.target.value === "1")}
|
||||
>
|
||||
<option value="1">开启(推荐)</option>
|
||||
<option value="0">关闭</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="fee">手续费率</label>
|
||||
<input
|
||||
id="fee"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.0001"
|
||||
value={fee}
|
||||
onChange={(e) => setFee(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
<div className="settings-actions">
|
||||
<button className="btn" type="submit">
|
||||
|
||||
@@ -399,6 +399,38 @@ input {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.settings-subtabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin: 0 0 18px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.settings-subtab {
|
||||
appearance: none;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--input);
|
||||
color: var(--muted);
|
||||
border-radius: 6px;
|
||||
padding: 6px 12px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.settings-subtab:hover {
|
||||
color: var(--text);
|
||||
border-color: #2a3441;
|
||||
}
|
||||
|
||||
.settings-subtab.active {
|
||||
color: #111;
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.settings-ok {
|
||||
color: var(--up);
|
||||
margin-bottom: 12px;
|
||||
|
||||
Reference in New Issue
Block a user