From ab7a2309ad75545bbcd124a5c0f5235bcb4d2eec Mon Sep 17 00:00:00 2001 From: dekun Date: Wed, 14 Oct 2026 19:31:14 +0800 Subject: [PATCH] Fold chain/account/PnL hints into rules; hide dust sell-coin. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the three noisy help lines into 规则说明, and show 手动卖回币 only when trading-coin balance meets the exchange min size. Co-authored-by: Cursor --- lib/common/static/hedge_plan.js | 57 ++++++++++++++---- lib/hedge_plan/hedge_plan_register.py | 60 ++++++++++++++++++- .../templates/hedge_plan_panel.html | 11 ++-- 3 files changed, 110 insertions(+), 18 deletions(-) diff --git a/lib/common/static/hedge_plan.js b/lib/common/static/hedge_plan.js index 442a215..4f21d0c 100644 --- a/lib/common/static/hedge_plan.js +++ b/lib/common/static/hedge_plan.js @@ -1097,7 +1097,7 @@ "" + "" + '
' + - '组合净盈亏(扣开平手续费·永续盘口均价)' + + '组合净盈亏' + '' + @@ -1577,18 +1577,55 @@ function setSellCoinMsg(text, kind) { const el = $("hp-sell-coin-msg"); if (!el) return; + const show = !!(text || "").trim(); + el.hidden = !show; el.textContent = text || ""; el.classList.toggle("is-err", kind === "err"); el.classList.toggle("is-ok", kind === "ok"); el.classList.toggle("muted", !kind); } + function setSellCoinVisible(visible, meta) { + const btn = $("hp-sell-coin-btn"); + if (!btn) return; + const on = !!visible; + btn.hidden = !on; + btn.disabled = !on; + if (on && meta) { + const uly = String(meta.underlying || state.underlying || "ETH").toUpperCase(); + const avail = meta.available != null ? Number(meta.available) : null; + const minSz = meta.min_sz != null ? Number(meta.min_sz) : null; + let tip = "市价卖出交易账户全部可用 " + uly + " 换回 USDT"; + if (avail != null && !Number.isNaN(avail)) tip += " · 可用 " + avail; + if (minSz != null && !Number.isNaN(minSz)) tip += " · 最小 " + minSz; + btn.title = tip; + } + if (!on) setSellCoinMsg("", null); + } + + async function refreshSellCoinVisibility() { + const uly = String(state.underlying || "ETH").toUpperCase(); + try { + const d = await apiJson( + "/api/hedge-plan/sell-coin-status?underlying=" + encodeURIComponent(uly) + ); + if (!d || !d.ok) { + setSellCoinVisible(false); + return; + } + setSellCoinVisible(!!d.sellable, d); + } catch (_) { + setSellCoinVisible(false); + } + } + async function submitSellCoin() { const uly = String(state.underlying || "ETH").toUpperCase(); + const btn = $("hp-sell-coin-btn"); + if (!btn || btn.hidden) return; if (!window.confirm("确认市价卖出交易账户全部可用 " + uly + " 换回 USDT?")) return; setSellCoinMsg("卖回中…", null); - const btn = $("hp-sell-coin-btn"); - if (btn) btn.disabled = true; + btn.disabled = true; try { const d = await apiJson("/api/hedge-plan/sell-coin", { method: "POST", @@ -1597,6 +1634,7 @@ }); if (!d.ok) { setSellCoinMsg(d.msg || "卖回失败", "err"); + await refreshSellCoinVisibility(); return; } const sold = @@ -1606,10 +1644,10 @@ setSellCoinMsg(sold, "ok"); await loadChain(); void loadLivePositions(); + await refreshSellCoinVisibility(); } catch (e) { setSellCoinMsg(e.message || "卖回失败", "err"); - } finally { - if (btn) btn.disabled = false; + await refreshSellCoinVisibility(); } } @@ -2059,6 +2097,7 @@ $("hp-profit-rr").value = "2"; } } + void refreshSellCoinVisibility(); } function currentExp(selectId) { @@ -2186,14 +2225,6 @@ } tbody.appendChild(tr); }); - if (isOptionPrimary() && !state.poStrikeExpandAll && state.moneyFilter === "all") { - const hintTr = document.createElement("tr"); - hintTr.innerHTML = - '默认显示约 5 档(平值 + 实值2 + 虚值2) · 勾选「展开全部」查看该到期更多行权价'; - tbody.appendChild(hintTr); - } tbody.querySelectorAll(".hp-pick").forEach(function (btn) { btn.addEventListener("click", function () { const inst = btn.getAttribute("data-inst"); diff --git a/lib/hedge_plan/hedge_plan_register.py b/lib/hedge_plan/hedge_plan_register.py index bc5aba2..0cf362e 100644 --- a/lib/hedge_plan/hedge_plan_register.py +++ b/lib/hedge_plan/hedge_plan_register.py @@ -1281,10 +1281,47 @@ def register_hedge_plan_routes(app: Flask, cfg: dict[str, Any]) -> None: pass return jsonify(result) + @app.route("/api/hedge-plan/sell-coin-status") + @lr + def api_hedge_sell_coin_status(): + """是否显示「手动卖回币」:交易户有币且 ≥ 现货最小卖出量(粉尘隐藏).""" + if not _hedge_enabled(): + return jsonify({"ok": False, "sellable": False, "msg": "对冲模块未启用"}) + ex = cfg.get("exchange_options") or cfg.get("exchange") + if ex is None: + return jsonify({"ok": False, "sellable": False, "msg": "交易所未配置"}) + underlying = str( + request.args.get("underlying") or cfg.get("default_underly") or "ETH" + ).strip().upper() + if underlying not in ("ETH", "BTC"): + return jsonify({"ok": False, "sellable": False, "msg": "标的仅支持 ETH/BTC"}) + from lib.options.options_spot_bridge_lib import ( + fetch_trading_coin_available, + is_sellable_spot_coin, + spot_min_sell_sz, + ) + + avail = fetch_trading_coin_available(ex, underlying) + try: + avail_f = float(avail) if avail is not None else 0.0 + except (TypeError, ValueError): + avail_f = 0.0 + min_sz = float(spot_min_sell_sz(ex, underlying)) + sellable = is_sellable_spot_coin(ex, underlying, avail_f) + return jsonify( + { + "ok": True, + "underlying": underlying, + "available": avail_f, + "min_sz": min_sz, + "sellable": bool(sellable), + } + ) + @app.route("/api/hedge-plan/sell-coin", methods=["POST"]) @lr def api_hedge_sell_coin(): - """手动卖回币:市价卖出交易账户全部可用标的币换 USDT.""" + """手动卖回币:市价卖出交易账户全部可用标的币换 USDT(粉尘拒绝).""" if not _hedge_enabled(): return jsonify({"ok": False, "msg": "对冲模块未启用"}), 400 ex = cfg.get("exchange_options") or cfg.get("exchange") @@ -1294,6 +1331,27 @@ def register_hedge_plan_routes(app: Flask, cfg: dict[str, Any]) -> None: underlying = str(data.get("underlying") or cfg.get("default_underly") or "ETH").strip().upper() if underlying not in ("ETH", "BTC"): return jsonify({"ok": False, "msg": "标的仅支持 ETH/BTC"}), 400 + from lib.options.options_spot_bridge_lib import ( + fetch_trading_coin_available, + is_sellable_spot_coin, + spot_min_sell_sz, + ) + + avail = fetch_trading_coin_available(ex, underlying) + try: + avail_f = float(avail) if avail is not None else 0.0 + except (TypeError, ValueError): + avail_f = 0.0 + if not is_sellable_spot_coin(ex, underlying, avail_f): + min_sz = spot_min_sell_sz(ex, underlying) + return jsonify( + { + "ok": False, + "msg": f"无足够 {underlying} 可卖(可用 {avail_f:g} · 最小 {min_sz:g})", + "available": avail_f, + "min_sz": min_sz, + } + ), 400 conn = cfg["get_db"]() try: from lib.options.options_db import init_options_tables diff --git a/lib/hedge_plan/templates/hedge_plan_panel.html b/lib/hedge_plan/templates/hedge_plan_panel.html index 55eaffd..e5df9d8 100644 --- a/lib/hedge_plan/templates/hedge_plan_panel.html +++ b/lib/hedge_plan/templates/hedge_plan_panel.html @@ -50,6 +50,9 @@

手动下单:开仓按卖一限价(非交易所市价)。先开期权(按张数买币并+5%滑点),再开永续。永续杠杆取 env:BTC=BTC_LEVERAGE/ETH=ALT_LEVERAGE。

出场:盈利金额(扣开平双边手续费后净利)或目标位点数二选一。平仓先期权(须买一流动性)再永续,再把币全部卖回 USDT。一账户同时仅一笔对冲仓。

开仓校验:已选合约 · 张数≥1 · 出场条件已填 · 门禁「开仓:可」· 账户有足够交易 USDT。

+

期权链:默认显示约 5 档(平值 + 实值2 + 虚值2);勾选「展开全部」查看该到期更多行权价。

+

账户:期权币本位 · 交易/资金计 USDT · 永续全仓 / 期权逐仓(余额见下方划转)。

+

组合净盈亏:扣开平双边手续费;永续按平仓方向盘口均价估值。

买 Call + 永续空 · 点「选择」弹窗下单。

@@ -100,7 +103,7 @@ -
+
资金 — @@ -234,11 +237,11 @@

持仓

- +
-
+
@@ -494,4 +497,4 @@
- +