Show strategy rules in fold; larger nav; notify only when cannot open.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -93,6 +93,8 @@ class StrategyEngine:
|
|||||||
max_atm_off = self.ledger.get_setting_float(
|
max_atm_off = self.ledger.get_setting_float(
|
||||||
"max_atm_open_offset", s.max_atm_open_offset
|
"max_atm_open_offset", s.max_atm_open_offset
|
||||||
)
|
)
|
||||||
|
perp_qty = self.ledger.get_setting_float("perp_qty_eth", s.perp_qty_eth)
|
||||||
|
opt_qty = self.ledger.get_setting_float("option_qty_eth", s.option_qty_eth)
|
||||||
rest_until = row["rest_until_ms"]
|
rest_until = row["rest_until_ms"]
|
||||||
rest_left = 0
|
rest_left = 0
|
||||||
if rest_until:
|
if rest_until:
|
||||||
@@ -128,6 +130,8 @@ class StrategyEngine:
|
|||||||
"premium_exit_multiple": prem_mult,
|
"premium_exit_multiple": prem_mult,
|
||||||
"exit_target_usdt": exit_amt,
|
"exit_target_usdt": exit_amt,
|
||||||
"leverage": leverage,
|
"leverage": leverage,
|
||||||
|
"perp_qty_eth": perp_qty,
|
||||||
|
"option_qty_eth": opt_qty,
|
||||||
"min_option_hours": min_hours,
|
"min_option_hours": min_hours,
|
||||||
"min_option_leverage": min_opt_lev,
|
"min_option_leverage": min_opt_lev,
|
||||||
"atm_open_offset_enabled": atm_off_on,
|
"atm_open_offset_enabled": atm_off_on,
|
||||||
|
|||||||
@@ -171,13 +171,16 @@ def assess_open_capacity(db: Database | None = None) -> dict[str, Any]:
|
|||||||
|
|
||||||
|
|
||||||
def maybe_notify_funds_short(cap: dict[str, Any] | None = None) -> None:
|
def maybe_notify_funds_short(cap: dict[str, Any] | None = None) -> None:
|
||||||
"""交易账户资金不足时企业微信推送:不足期间只推一次。"""
|
"""仅在「不能开」时推送一次;能开绝不通知。"""
|
||||||
global _notified_while_short
|
global _notified_while_short
|
||||||
cap = cap or assess_open_capacity()
|
cap = cap or assess_open_capacity()
|
||||||
short = cap.get("perp_can_open") is False or cap.get("option_can_open") is False
|
# 能开 / 未知:不通知;资金恢复则重置,便于下次不足再提醒一次
|
||||||
if not short:
|
if cap.get("funds_ok") is True:
|
||||||
_notified_while_short = False
|
_notified_while_short = False
|
||||||
return
|
return
|
||||||
|
cannot = cap.get("perp_can_open") is False or cap.get("option_can_open") is False
|
||||||
|
if not cannot:
|
||||||
|
return
|
||||||
if _notified_while_short:
|
if _notified_while_short:
|
||||||
return
|
return
|
||||||
_notified_while_short = True
|
_notified_while_short = True
|
||||||
|
|||||||
@@ -5,6 +5,16 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-07-29 — 规则说明展示策略数值;导航加大;不可开才通知
|
||||||
|
|
||||||
|
### 变更
|
||||||
|
|
||||||
|
1. 能开不通知;不能开才企业微信提醒一次(恢复后再次不足才再推)。
|
||||||
|
2. 导航字号加大;规则说明正文字号缩小。
|
||||||
|
3. 规则说明按当前策略设置展示开仓/平仓/节奏参数。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-07-29 — 可开判定只看交易账户;不足只通知一次;去重标题
|
## 2026-07-29 — 可开判定只看交易账户;不足只通知一次;去重标题
|
||||||
|
|
||||||
### 变更
|
### 变更
|
||||||
|
|||||||
@@ -228,6 +228,8 @@ export type PlanState = {
|
|||||||
premium_exit_multiple: number;
|
premium_exit_multiple: number;
|
||||||
exit_target_usdt: number;
|
exit_target_usdt: number;
|
||||||
leverage: number;
|
leverage: number;
|
||||||
|
perp_qty_eth?: number;
|
||||||
|
option_qty_eth?: number;
|
||||||
min_option_hours: number;
|
min_option_hours: number;
|
||||||
min_option_leverage: number;
|
min_option_leverage: number;
|
||||||
atm_open_offset_enabled?: boolean;
|
atm_open_offset_enabled?: boolean;
|
||||||
|
|||||||
@@ -149,6 +149,13 @@ export default function PlanPage() {
|
|||||||
? `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}`
|
? `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}`
|
||||||
: `固定 ${fmt(plan?.net_profit_target ?? 15)} U`;
|
: `固定 ${fmt(plan?.net_profit_target ?? 15)} U`;
|
||||||
const phaseLabel = PHASE_ZH[plan?.phase || ""] || plan?.phase || "—";
|
const phaseLabel = PHASE_ZH[plan?.phase || ""] || plan?.phase || "—";
|
||||||
|
const atmRule = plan?.atm_open_offset_enabled
|
||||||
|
? `ATM偏差≤${fmt(plan?.max_atm_open_offset ?? 3, 0)}`
|
||||||
|
: "ATM偏差关";
|
||||||
|
const exitDetail =
|
||||||
|
exitMode === "premium_multiple"
|
||||||
|
? `净盈利≥初始权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}`
|
||||||
|
: `净盈利≥${fmt(plan?.net_profit_target ?? 15)} U`;
|
||||||
const expiryMs =
|
const expiryMs =
|
||||||
pos?.expiry_ms ?? expiryMsFromYmd(pos?.expiry_ymd) ?? null;
|
pos?.expiry_ms ?? expiryMsFromYmd(pos?.expiry_ymd) ?? null;
|
||||||
const countdown =
|
const countdown =
|
||||||
@@ -213,10 +220,28 @@ export default function PlanPage() {
|
|||||||
<div className="plan-rules-fold">
|
<div className="plan-rules-fold">
|
||||||
<details>
|
<details>
|
||||||
<summary>规则说明</summary>
|
<summary>规则说明</summary>
|
||||||
<p className="meta plan-rules-body">
|
<div className="plan-rules-body">
|
||||||
行情 {String(plan?.exchange || "okx").toUpperCase()} · ETH-USDT-SWAP ·
|
<p>
|
||||||
目标平仓(双腿/远虚只平永续) · 未达标则到期结算
|
<span className="plan-rules-k">行情</span>
|
||||||
|
{String(plan?.exchange || "okx").toUpperCase()} · ETH-USDT-SWAP
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="plan-rules-k">开仓</span>
|
||||||
|
永续 {fmt(plan?.perp_qty_eth ?? 1, 2)} ETH / {fmt(plan?.leverage, 0)}x
|
||||||
|
· 期权 {fmt(plan?.option_qty_eth ?? 2, 2)} ETH 名义 · 剩余≥
|
||||||
|
{fmt(plan?.min_option_hours, 0)}h · 期权杠杆≥
|
||||||
|
{fmt(plan?.min_option_leverage, 0)}x · {atmRule}
|
||||||
|
{plan?.skip_weekends ? " · 周末跳过开仓" : ""}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="plan-rules-k">平仓</span>
|
||||||
|
{exitDetail} → 双腿全平;期权远虚则只平永续、期权到期结算;未达标拖到期
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="plan-rules-k">节奏</span>
|
||||||
|
组间休息 {plan?.rest_seconds ?? "—"}s · 可开判定看交易账户
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -149,13 +149,14 @@ input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.top-nav a {
|
.top-nav a {
|
||||||
padding: 6px 10px;
|
padding: 8px 14px;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: var(--bg-elev);
|
background: var(--bg-elev);
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-size: 13px;
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +308,7 @@ input {
|
|||||||
.plan-rules-fold summary {
|
.plan-rules-fold summary {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,7 +318,26 @@ input {
|
|||||||
|
|
||||||
.plan-rules-body {
|
.plan-rules-body {
|
||||||
margin: 8px 0 0;
|
margin: 8px 0 0;
|
||||||
line-height: 1.45;
|
font-size: 11px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-rules-body p {
|
||||||
|
margin: 0 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-rules-body p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-rules-k {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 2.2em;
|
||||||
|
margin-right: 6px;
|
||||||
|
color: var(--text);
|
||||||
|
font-weight: 600;
|
||||||
|
opacity: 0.85;
|
||||||
}
|
}
|
||||||
|
|
||||||
.open-cap-row {
|
.open-cap-row {
|
||||||
@@ -485,7 +505,7 @@ input {
|
|||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 13px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user