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";
|
} from "../api/client";
|
||||||
|
|
||||||
type Tab = "strategy" | "runtime" | "account";
|
type Tab = "strategy" | "runtime" | "account";
|
||||||
|
type StratSub =
|
||||||
|
| "position"
|
||||||
|
| "select"
|
||||||
|
| "exit"
|
||||||
|
| "pace";
|
||||||
|
|
||||||
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 [newUsername, setNewUsername] = useState(getUsername() || "admin");
|
const [newUsername, setNewUsername] = useState(getUsername() || "admin");
|
||||||
const [currentPassword, setCurrentPassword] = useState("");
|
const [currentPassword, setCurrentPassword] = useState("");
|
||||||
@@ -120,31 +126,38 @@ export default function SettingsPage() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setStratOk("");
|
setStratOk("");
|
||||||
setErr("");
|
setErr("");
|
||||||
|
const isLive = (runtime?.mode ?? mode) === "LIVE";
|
||||||
try {
|
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", {
|
await apiFetch("/api/settings/strategy", {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(body),
|
||||||
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,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
setStratOk(
|
setStratOk(
|
||||||
"策略参数已保存(切换交易所/改模拟资金需无持仓;切换后会重连行情)",
|
isLive
|
||||||
|
? "策略参数已保存(实盘模式不改模拟资金)"
|
||||||
|
: "策略参数已保存(切换交易所/改模拟资金需无持仓;切换后会重连行情)",
|
||||||
);
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
setErr(ex instanceof Error ? ex.message : String(ex));
|
setErr(ex instanceof Error ? ex.message : String(ex));
|
||||||
@@ -206,7 +219,10 @@ export default function SettingsPage() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={tab === "strategy" ? "tab active" : "tab"}
|
className={tab === "strategy" ? "tab active" : "tab"}
|
||||||
onClick={() => setTab("strategy")}
|
onClick={() => {
|
||||||
|
setTab("strategy");
|
||||||
|
loadRuntime();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
策略设置
|
策略设置
|
||||||
</button>
|
</button>
|
||||||
@@ -232,265 +248,318 @@ export default function SettingsPage() {
|
|||||||
{tab === "strategy" ? (
|
{tab === "strategy" ? (
|
||||||
<div className="card settings-card">
|
<div className="card settings-card">
|
||||||
<p className="settings-lead">
|
<p className="settings-lead">
|
||||||
无开仓时间窗;可切换 OKX / 币安行情(SIM 本地撮合);周六日可跳过开仓;出场按净盈利或到期全平。
|
{(runtime?.mode ?? mode) === "LIVE"
|
||||||
|
? "当前 LIVE:仓位/选约/出场影响真下单;模拟资金已隐藏。"
|
||||||
|
: "当前 SIM:本地撮合;可改模拟资金与交易所行情源。"}
|
||||||
</p>
|
</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}
|
||||||
|
|
||||||
|
<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}>
|
<form onSubmit={onSaveStrategy}>
|
||||||
<section className="settings-section">
|
{stratSub === "position" ? (
|
||||||
<h3>交易所</h3>
|
<section className="settings-section">
|
||||||
<div className="settings-fields">
|
<h3>交易所与仓位</h3>
|
||||||
<div className="field">
|
<div className="settings-fields">
|
||||||
<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" ? (
|
|
||||||
<div className="field">
|
<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
|
<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"
|
className="mono"
|
||||||
type="number"
|
type="number"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
min="0.1"
|
min="0.2"
|
||||||
value={netTarget}
|
max="30"
|
||||||
onChange={(e) => setNetTarget(Number(e.target.value))}
|
value={orderInterval}
|
||||||
|
onChange={(e) =>
|
||||||
|
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="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
|
<input
|
||||||
id="premMult"
|
id="fee"
|
||||||
className="mono"
|
className="mono"
|
||||||
type="number"
|
type="number"
|
||||||
step="0.1"
|
step="0.0001"
|
||||||
min="0.1"
|
value={fee}
|
||||||
value={premMult}
|
onChange={(e) => setFee(Number(e.target.value))}
|
||||||
onChange={(e) => setPremMult(Number(e.target.value))}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
||||||
<div className="field">
|
</section>
|
||||||
<label htmlFor="rest">轮间休息秒数</label>
|
) : null}
|
||||||
<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>
|
|
||||||
|
|
||||||
<div className="settings-actions">
|
<div className="settings-actions">
|
||||||
<button className="btn" type="submit">
|
<button className="btn" type="submit">
|
||||||
|
|||||||
@@ -399,6 +399,38 @@ input {
|
|||||||
font-size: 13px;
|
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 {
|
.settings-ok {
|
||||||
color: var(--up);
|
color: var(--up);
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
|||||||
Reference in New Issue
Block a user