持仓区增加账户内划转,买一平仓规则并入左侧开平仓说明

持仓卡片底部可折叠划转面板(资金/交易 USDT/USDC);移除持仓区重复平仓规则,统一到左侧开平仓规则说明。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-23 08:11:19 +08:00
parent fe5bb923d7
commit aa14688a7e
3 changed files with 264 additions and 18 deletions
+62
View File
@@ -5297,6 +5297,68 @@ html[data-theme="light"] .opt-source-badge--oo {
border-color: #ffb347;
color: #ffb347;
}
.options-page-wrap .opt-pos-transfer {
margin-top: 10px;
padding: 10px 12px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
}
.options-page-wrap .opt-pos-transfer-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
cursor: pointer;
list-style: none;
font-weight: 600;
font-size: 0.88rem;
margin: 0 0 8px;
}
.options-page-wrap .opt-pos-transfer-head::-webkit-details-marker {
display: none;
}
.options-page-wrap .opt-pos-transfer[open] .opt-pos-transfer-closed-hint {
display: none;
}
.options-page-wrap .opt-pos-transfer:not([open]) .opt-pos-transfer-open-hint {
display: none;
}
.options-page-wrap .opt-pos-transfer:not([open]) .opt-pos-transfer-body {
display: none;
}
.options-page-wrap .opt-pos-transfer:not([open]) .opt-pos-transfer-head {
margin-bottom: 0;
}
.options-page-wrap .opt-pos-transfer-body .options-settings-subtitle {
margin-bottom: 6px;
}
.options-page-wrap .opt-pos-transfer-form {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 0;
}
.options-page-wrap .opt-pos-transfer-form select,
.options-page-wrap .opt-pos-transfer-form input[type="number"] {
font-size: 0.74rem;
}
.options-page-wrap .opt-pos-transfer-form input[type="number"] {
width: 96px;
max-width: 30vw;
}
.options-page-wrap .opt-pos-xfer-msg {
margin-top: 6px;
font-size: 0.76rem;
min-height: 1.1em;
}
.options-page-wrap .opt-pos-xfer-msg.opt-error {
color: #ff7b72;
}
.options-page-wrap .opt-pos-xfer-msg.opt-success {
color: #3dd68c;
}
.opt-pos-card {
margin-bottom: 10px;
}
+159
View File
@@ -373,6 +373,164 @@
}
}
function setXferMsg(text, isErr) {
const el = document.getElementById("opt-pos-xfer-msg");
if (!el) return;
el.textContent = text || "";
el.classList.toggle("opt-error", !!isErr);
el.classList.toggle("opt-success", !!text && !isErr);
}
function xferRoundAvail(v) {
const n = Number(v);
if (!Number.isFinite(n) || n <= 0) return null;
return Math.round(n * 100) / 100;
}
function xferPickBalance(bal, account, ccy) {
if (!bal) return null;
const acct = account === "trading" ? "trading" : "funding";
const c = String(ccy || "").toLowerCase();
const availKey = acct + "_" + c + "_avail";
const totalKey = acct + "_" + c;
return xferRoundAvail(bal[availKey] != null ? bal[availKey] : bal[totalKey]);
}
function xferAccountLabel(acct) {
return acct === "trading" ? "交易账户" : "资金账户";
}
function setPosXferBusy(busy) {
["opt-pos-xfer-btn", "opt-pos-xfer-all-btn"].forEach(function (id) {
const btn = document.getElementById(id);
if (!btn) return;
if (busy) {
if (!btn.dataset.origText) btn.dataset.origText = btn.textContent;
btn.disabled = true;
} else {
btn.disabled = false;
if (btn.dataset.origText) {
btn.textContent = btn.dataset.origText;
delete btn.dataset.origText;
}
}
});
const input = document.getElementById("opt-pos-xfer-amount");
if (input) input.disabled = !!busy;
}
async function submitPosTransfer(amount) {
const ccyEl = document.getElementById("opt-pos-xfer-ccy");
const fromEl = document.getElementById("opt-pos-xfer-from");
const toEl = document.getElementById("opt-pos-xfer-to");
if (!ccyEl || !fromEl || !toEl) return { ok: false };
setPosXferBusy(true);
setXferMsg("划转中…", false);
try {
const r = await apiJson("/api/options/transfer", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
ccy: ccyEl.value,
from: fromEl.value,
to: toEl.value,
amount: amount,
}),
});
if (r.ok) {
setXferMsg("划转成功", false);
const amtEl = document.getElementById("opt-pos-xfer-amount");
if (amtEl) amtEl.value = "";
refreshAllPositions();
if (typeof refreshAccountSnapshot === "function") refreshAccountSnapshot({ force: true });
await refreshBridgeSellUi(true);
} else {
setXferMsg("划转失败:" + (r.msg || "未知错误"), true);
}
return r;
} catch (e) {
setXferMsg("划转失败:" + (e.message || "网络错误"), true);
return { ok: false };
} finally {
setPosXferBusy(false);
}
}
function bindPosTransferControls() {
const xferBtn = document.getElementById("opt-pos-xfer-btn");
const xferAllBtn = document.getElementById("opt-pos-xfer-all-btn");
const amtEl = document.getElementById("opt-pos-xfer-amount");
if (!xferBtn && !xferAllBtn) return;
function hardenXferAmountInput() {
if (!amtEl) return;
function wipe() {
const v = String(amtEl.value || "").trim();
if (/^[a-z][a-z0-9._-]{1,31}$/i.test(v)) amtEl.value = "";
}
wipe();
amtEl.setAttribute("readonly", "readonly");
amtEl.addEventListener("focus", function () {
amtEl.removeAttribute("readonly");
});
amtEl.addEventListener("blur", function () {
if (!amtEl.value) amtEl.setAttribute("readonly", "readonly");
});
setTimeout(wipe, 200);
setTimeout(wipe, 800);
}
hardenXferAmountInput();
if (xferBtn) {
xferBtn.addEventListener("click", async function () {
const amount = parseFloat((amtEl || {}).value);
if (!amount || amount <= 0) {
setXferMsg("请输入有效数量", true);
return;
}
await submitPosTransfer(amount);
});
}
if (xferAllBtn) {
xferAllBtn.addEventListener(
"click",
function () {
if (amtEl) amtEl.removeAttribute("readonly");
},
true
);
xferAllBtn.addEventListener("click", async function () {
try {
const ccyEl = document.getElementById("opt-pos-xfer-ccy");
const fromEl = document.getElementById("opt-pos-xfer-from");
const toEl = document.getElementById("opt-pos-xfer-to");
const bal = await refreshBridgeSellUi(true);
const ccy = ccyEl ? ccyEl.value : "USDT";
const from = fromEl ? fromEl.value : "funding";
const to = toEl ? toEl.value : "trading";
const amount = xferPickBalance(bal, from, ccy);
if (!amount) {
setXferMsg("划出账户可用余额不足", true);
return;
}
const msg =
"确认全部划转?\n\n" +
"币种:" + ccy + "\n" +
"划出:" + xferAccountLabel(from) + "\n" +
"划入:" + xferAccountLabel(to) + "\n" +
"金额:" + amount.toFixed(2) + " " + ccy + "\n\n" +
"将划转该账户全部可用余额。";
if (!confirm(msg)) return;
if (amtEl) amtEl.value = String(amount);
await submitPosTransfer(amount);
} catch (e) {
setXferMsg("划转失败:" + (e.message || "余额拉取失败"), true);
}
});
}
}
async function retrySellBackCoin() {
const uly = String(state.underlying || "ETH").toUpperCase();
const d = await refreshBridgeSellUi(true);
@@ -2860,6 +3018,7 @@
});
}
bindOptionsPosTabs();
bindPosTransferControls();
hardenOrderAutofill();
(function bindProfitExitOpenControls() {
+43 -18
View File
@@ -18,9 +18,9 @@
<div class="card options-order-card"{% if options_open_allowed is defined and not options_open_allowed %} style="opacity:.72"{% endif %}>
<h2>期权下单{% if options_open_allowed is defined and not options_open_allowed %} <small class="muted">(对冲模式已禁用开仓)</small>{% endif %}</h2>
<details class="opt-close-rule opt-open-rule">
<summary>开仓规则说明</summary>
<summary>仓规则说明</summary>
<div class="opt-close-rule-body">
<p>报价单位为每 1 ETH/BTC;1 张 = 0.01。默认选中<strong>最近一期</strong>到期,可手动改。</p>
<p><strong>开仓</strong> · 报价单位为每 1 ETH/BTC;1 张 = 0.01。默认选中<strong>最近一期</strong>到期,可手动改。</p>
<ul>
<li><strong>列表</strong>含卖一/买一;<strong>T 型</strong>仅卖一(买方开仓),中间为跨式双买测算。</li>
<li>环境配置「链上仅显示有卖一」开启时,隐藏无真实卖一或深度不足 1 张的合约(估算价 <strong>~</strong> 亦不显示)。</li>
@@ -30,7 +30,15 @@
<li>「全仓复利」用期权交易户<strong>全部可用</strong>×缓冲开仓(不受单笔预算限制);可选开启全仓上限;该模式下仅允许同时 1 笔持仓。</li>
<li><strong>币本位</strong>(env <code>OKX_OPTIONS_MARGIN_MODE=coin</code>):按交易户 USDT×缓冲买满 ETH/BTC 再开满期权;平仓后自动卖回 USDT;对冲仍仅 USDC。有仓勿切换本位。</li>
<li><strong>翻倍出场</strong>:开仓时可勾选;1倍=盈利等于权利金,买一可回收达标后限价平;持仓卡可改倍数或关闭。</li>
<li>平仓仅买一限价,详见说明文档。</li>
</ul>
<p><strong>平仓(买一)</strong> · 平仓前重新读盘口并校验有效流动性;市价平仓已禁用。</p>
<ul>
<li>本轮只锁<strong>买一</strong>:张数 = min(持仓, 买一深度),限价 = 当场买一。</li>
<li>买一不够时只平能吃掉的部分,剩余等下次再点「买一平仓」。</li>
<li>手动平仓只验有效买一(非残档);目标触达后才平,2×权利金只是门控(到 2× 本身不会自动平)。</li>
<li><strong>翻倍出场</strong>:开启后可自选倍数(默认1);1倍=盈利等于权利金,买一可回收达标即限价平;可随时关闭。</li>
<li>全程 <code>reduceOnly</code> 限价卖,不吃买二及以下、不走市价。</li>
<li>币本位平仓后自动卖回 USDT;失败可点持仓区「重试卖回」按交易户全部可用量市价卖出。</li>
</ul>
<p><a href="/options/guide" target="_blank" rel="noopener">打开《期权开平仓与监控说明》</a></p>
</div>
@@ -207,20 +215,6 @@
<div class="pos-empty" id="opt-pos-empty">暂无持仓</div>
<div id="opt-pos-cards"></div>
</div>
<details class="opt-close-rule">
<summary>买一平仓规则说明</summary>
<div class="opt-close-rule-body">
<p>平仓前重新读盘口并校验有效流动性;市价平仓已禁用。</p>
<ul>
<li>本轮只锁<strong>买一</strong>:张数 = min(持仓, 买一深度),限价 = 当场买一。</li>
<li>买一不够时只平能吃掉的部分,剩余等下次再点「买一平仓」。</li>
<li>手动平仓只验有效买一(非残档);目标触达后才平,2×权利金只是门控(到 2× 本身不会自动平)。</li>
<li><strong>翻倍出场</strong>:开启后可自选倍数(默认1);1倍=盈利等于权利金,买一可回收达标即限价平;可随时关闭。</li>
<li>全程 <code>reduceOnly</code> 限价卖,不吃买二及以下、不走市价。</li>
</ul>
<p><a href="/options/guide" target="_blank" rel="noopener">打开《期权开平仓与监控说明》</a></p>
</div>
</details>
</div>
<div class="options-pos-pane" data-opt-pos-pane="pending" role="tabpanel" aria-labelledby="opt-pos-tab-pending" hidden>
<div class="opt-pos-pending-pane">
@@ -351,8 +345,39 @@
</div>
</div>
</div>
<details class="opt-pos-transfer" id="opt-pos-transfer" open>
<summary class="opt-pos-transfer-head">
<span class="opt-pos-transfer-title">划转</span>
<span class="opt-pos-transfer-open-hint muted">收起</span>
<span class="opt-pos-transfer-closed-hint muted">展开</span>
</summary>
<div class="opt-pos-transfer-body">
<div class="options-settings-subtitle">账户内划转</div>
<div class="form-row opt-pos-transfer-form" autocomplete="off">
<input type="text" name="username" autocomplete="username" tabindex="-1" aria-hidden="true"
style="position:absolute;left:-9999px;width:1px;height:1px;opacity:0" value="">
<select id="opt-pos-xfer-ccy" aria-label="币种" autocomplete="off">
<option value="USDT" selected>USDT</option>
<option value="USDC">USDC</option>
</select>
<select id="opt-pos-xfer-from" aria-label="划出账户">
<option value="funding" selected>from: 资金</option>
<option value="trading">from: 交易</option>
</select>
<select id="opt-pos-xfer-to" aria-label="划入账户">
<option value="trading" selected>to: 交易</option>
<option value="funding">to: 资金</option>
</select>
<input type="number" id="opt-pos-xfer-amount" name="cm_opt_pos_xfer_amt" min="0.01" step="0.01" placeholder="数量"
autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-bwignore="true" data-form-type="other" readonly>
<button type="button" class="btn-secondary btn-sm" id="opt-pos-xfer-all-btn">全部划转</button>
<button type="button" class="btn-primary btn-sm" id="opt-pos-xfer-btn">划转</button>
</div>
<div id="opt-pos-xfer-msg" class="muted opt-pos-xfer-msg"></div>
</div>
</details>
</div>
</div>
</div>
<script src="/static/options_expiry_countdown.js?v=1"></script>
<script src="/static/options_panel.js?v=67"></script>
<script src="/static/options_panel.js?v=68"></script>