Show live risk-sizing estimate on plan page instead of stale manual qty.
When 以损定仓 is on, state() overlays next-open k/qty/exit from market preview. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -128,6 +128,32 @@ class StrategyEngine:
|
|||||||
risk_option_unit = self.ledger.get_setting_float("risk_option_unit", 2.0)
|
risk_option_unit = self.ledger.get_setting_float("risk_option_unit", 2.0)
|
||||||
risk_exit_unit = self.ledger.get_setting_float("risk_exit_unit", 15.0)
|
risk_exit_unit = self.ledger.get_setting_float("risk_exit_unit", 15.0)
|
||||||
risk_last_k = self.ledger.get_setting_float("risk_last_k", 0.0)
|
risk_last_k = self.ledger.get_setting_float("risk_last_k", 0.0)
|
||||||
|
risk_preview: dict[str, Any] | None = None
|
||||||
|
# 以损定仓:监控页展示「下一次开仓」实时估算,避免仍显示上次手填/过期名义
|
||||||
|
if sizing_mode == "risk_based":
|
||||||
|
try:
|
||||||
|
from .risk_sizing import preview_risk_sizing
|
||||||
|
|
||||||
|
risk_preview = preview_risk_sizing(self.db)
|
||||||
|
if risk_preview.get("ok"):
|
||||||
|
if risk_preview.get("perp_qty_eth") is not None:
|
||||||
|
perp_qty = float(risk_preview["perp_qty_eth"])
|
||||||
|
if risk_preview.get("option_qty_eth") is not None:
|
||||||
|
opt_qty = float(risk_preview["option_qty_eth"])
|
||||||
|
if risk_preview.get("net_profit_target") is not None:
|
||||||
|
net_target = float(risk_preview["net_profit_target"])
|
||||||
|
exit_amt = net_target
|
||||||
|
if risk_preview.get("k") is not None:
|
||||||
|
risk_last_k = float(risk_preview["k"])
|
||||||
|
if risk_preview.get("perp_unit") is not None:
|
||||||
|
risk_perp_unit = float(risk_preview["perp_unit"])
|
||||||
|
if risk_preview.get("option_unit") is not None:
|
||||||
|
risk_option_unit = float(risk_preview["option_unit"])
|
||||||
|
if risk_preview.get("exit_unit") is not None:
|
||||||
|
risk_exit_unit = float(risk_preview["exit_unit"])
|
||||||
|
except Exception:
|
||||||
|
logger.exception("risk sizing preview for state() failed")
|
||||||
|
risk_preview = {"ok": False, "detail": "以损定仓预览失败"}
|
||||||
rest_until = row["rest_until_ms"]
|
rest_until = row["rest_until_ms"]
|
||||||
rest_left = 0
|
rest_left = 0
|
||||||
if rest_until:
|
if rest_until:
|
||||||
@@ -172,6 +198,7 @@ class StrategyEngine:
|
|||||||
"risk_option_unit": risk_option_unit,
|
"risk_option_unit": risk_option_unit,
|
||||||
"risk_exit_unit": risk_exit_unit,
|
"risk_exit_unit": risk_exit_unit,
|
||||||
"risk_last_k": risk_last_k if risk_last_k > 0 else None,
|
"risk_last_k": risk_last_k if risk_last_k > 0 else None,
|
||||||
|
"risk_sizing_preview": risk_preview,
|
||||||
"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,
|
||||||
|
|||||||
@@ -145,14 +145,23 @@ export default function PlanPage() {
|
|||||||
const pos = plan?.position;
|
const pos = plan?.position;
|
||||||
const open = !!pos?.has_position;
|
const open = !!pos?.has_position;
|
||||||
const exitMode = plan?.exit_mode ?? "fixed_usdt";
|
const exitMode = plan?.exit_mode ?? "fixed_usdt";
|
||||||
const exitTarget = plan?.exit_target_usdt ?? plan?.net_profit_target ?? 15;
|
const riskBased =
|
||||||
|
plan?.risk_based === true || plan?.sizing_mode === "risk_based";
|
||||||
|
const riskLiveOk = !riskBased || plan?.risk_sizing_preview?.ok === true;
|
||||||
|
const displayPerpQty = riskLiveOk ? (plan?.perp_qty_eth ?? 1) : null;
|
||||||
|
const displayOptQty = riskLiveOk ? (plan?.option_qty_eth ?? 2) : null;
|
||||||
|
const exitTarget = riskLiveOk
|
||||||
|
? (plan?.exit_target_usdt ?? plan?.net_profit_target ?? 15)
|
||||||
|
: null;
|
||||||
const netPnl = pos?.net_pnl ?? 0;
|
const netPnl = pos?.net_pnl ?? 0;
|
||||||
const movePct = pos?.move_pct ?? 0;
|
const movePct = pos?.move_pct ?? 0;
|
||||||
const exitRuleLabel =
|
const exitRuleLabel =
|
||||||
exitMode === "premium_multiple"
|
exitMode === "premium_multiple"
|
||||||
? `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}`
|
? `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}`
|
||||||
: plan?.risk_based || plan?.sizing_mode === "risk_based"
|
: plan?.risk_based || plan?.sizing_mode === "risk_based"
|
||||||
|
? exitTarget != null
|
||||||
? `以损定仓 · 固定 ${fmt(exitTarget)} U(基数${fmt(plan?.risk_exit_unit ?? 15)})`
|
? `以损定仓 · 固定 ${fmt(exitTarget)} U(基数${fmt(plan?.risk_exit_unit ?? 15)})`
|
||||||
|
: `以损定仓 · 待估算(基数${fmt(plan?.risk_exit_unit ?? 15)})`
|
||||||
: `固定 ${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?.fixed_direction_enabled
|
const atmRule = plan?.fixed_direction_enabled
|
||||||
@@ -206,7 +215,9 @@ export default function PlanPage() {
|
|||||||
<span className={`plan-mobile-pnl-num mono ${pnlClass(open ? netPnl : null)}`}>
|
<span className={`plan-mobile-pnl-num mono ${pnlClass(open ? netPnl : null)}`}>
|
||||||
{open ? fmt(netPnl) : "—"}
|
{open ? fmt(netPnl) : "—"}
|
||||||
</span>
|
</span>
|
||||||
<span className="plan-mobile-sub mono">目标 {fmt(exitTarget)} U</span>
|
<span className="plan-mobile-sub mono">
|
||||||
|
目标 {exitTarget != null ? `${fmt(exitTarget)} U` : "—"}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="plan-mobile-pnl-block">
|
<div className="plan-mobile-pnl-block">
|
||||||
<span className="plan-mobile-label">到期</span>
|
<span className="plan-mobile-label">到期</span>
|
||||||
@@ -237,7 +248,11 @@ export default function PlanPage() {
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span className="plan-rules-k">开仓</span>
|
<span className="plan-rules-k">开仓</span>
|
||||||
永续 {fmt(plan?.perp_qty_eth ?? 1, 2)} ETH / {fmt(plan?.leverage, 0)}x
|
永续{" "}
|
||||||
|
{displayPerpQty != null
|
||||||
|
? `${fmt(displayPerpQty, 2)} ETH`
|
||||||
|
: "—"}{" "}
|
||||||
|
/ {fmt(plan?.leverage, 0)}x
|
||||||
· 期权 {fmt(plan?.option_qty_eth ?? 2, 2)} ETH 名义 · 剩余≥
|
· 期权 {fmt(plan?.option_qty_eth ?? 2, 2)} ETH 名义 · 剩余≥
|
||||||
{fmt(plan?.min_option_hours, 0)}h · 期权杠杆≥
|
{fmt(plan?.min_option_hours, 0)}h · 期权杠杆≥
|
||||||
{fmt(plan?.min_option_leverage, 0)}x · {atmRule}
|
{fmt(plan?.min_option_leverage, 0)}x · {atmRule}
|
||||||
@@ -354,11 +369,27 @@ export default function PlanPage() {
|
|||||||
{plan?.risk_last_k
|
{plan?.risk_last_k
|
||||||
? ` · k=${fmt(plan.risk_last_k, 1)}`
|
? ` · k=${fmt(plan.risk_last_k, 1)}`
|
||||||
: ""}
|
: ""}
|
||||||
|
{plan?.risk_sizing_preview?.budget != null
|
||||||
|
? ` · 预算${fmt(plan.risk_sizing_preview.budget, 2)}U`
|
||||||
|
: ""}
|
||||||
|
{plan?.risk_sizing_preview?.max_loss != null
|
||||||
|
? ` · 估亏${fmt(plan.risk_sizing_preview.max_loss, 2)}U`
|
||||||
|
: ""}
|
||||||
|
{plan?.risk_sizing_preview?.ok === false
|
||||||
|
? ` · ${String(plan.risk_sizing_preview.detail || "预览失败")}`
|
||||||
|
: ""}
|
||||||
{" · "}
|
{" · "}
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
永续{fmt(plan?.perp_qty_eth ?? 1, 2)}ETH/期权
|
永续
|
||||||
{fmt(plan?.option_qty_eth ?? 2, 2)}ETH · 剩余≥
|
{displayPerpQty != null
|
||||||
|
? `${fmt(displayPerpQty, 2)}ETH`
|
||||||
|
: "—"}
|
||||||
|
/期权
|
||||||
|
{displayOptQty != null
|
||||||
|
? `${fmt(displayOptQty, 2)}ETH`
|
||||||
|
: "—"}{" "}
|
||||||
|
· 剩余≥
|
||||||
{fmt(plan?.min_option_hours, 0)}h · 期权杠杆≥
|
{fmt(plan?.min_option_hours, 0)}h · 期权杠杆≥
|
||||||
{fmt(plan?.min_option_leverage, 0)}x
|
{fmt(plan?.min_option_leverage, 0)}x
|
||||||
{plan?.fixed_direction_enabled
|
{plan?.fixed_direction_enabled
|
||||||
@@ -451,7 +482,11 @@ export default function PlanPage() {
|
|||||||
{open ? fmt(netPnl) : "—"}
|
{open ? fmt(netPnl) : "—"}
|
||||||
</span>
|
</span>
|
||||||
{" / "}
|
{" / "}
|
||||||
{open || exitMode === "fixed_usdt" ? fmt(exitTarget) : "—"}
|
{open || exitMode === "fixed_usdt"
|
||||||
|
? exitTarget != null
|
||||||
|
? fmt(exitTarget)
|
||||||
|
: "—"
|
||||||
|
: "—"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="kv">
|
<div className="kv">
|
||||||
|
|||||||
Reference in New Issue
Block a user