Add depth-based option close flow.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -185,6 +185,34 @@
|
||||
return price + "/" + size;
|
||||
}
|
||||
|
||||
function fmtBidDepth(levels) {
|
||||
const bids = (levels || []).slice(0, 5);
|
||||
if (!bids.length) return "—";
|
||||
return bids.map(function (x, idx) {
|
||||
return "买" + (idx + 1) + " " + fmtPxSz(x.px, x.sz);
|
||||
}).join(" · ");
|
||||
}
|
||||
|
||||
function fmtClosePreview(preview) {
|
||||
if (!preview || preview.total_received == null) return "—";
|
||||
let text = fmt(preview.total_received, 4) + " USDC";
|
||||
if (preview.covered_sheets != null) {
|
||||
text += " · 覆盖 " + preview.covered_sheets + "张";
|
||||
}
|
||||
if (preview.uncovered_sheets > 0) {
|
||||
text += " · 缺 " + preview.uncovered_sheets + "张";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function fmtPreviewLevels(preview) {
|
||||
const levels = (preview && preview.levels) || [];
|
||||
if (!levels.length) return "暂无可用买盘深度";
|
||||
return levels.map(function (x) {
|
||||
return "买" + x.level + " " + fmt(x.px, 4) + " × " + x.sheets + "张 ≈ " + fmt(x.received, 4) + " USDC";
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
function pnlCls(v) {
|
||||
if (v === null || v === undefined || Number.isNaN(Number(v))) return "";
|
||||
const n = Number(v);
|
||||
@@ -456,12 +484,15 @@
|
||||
const sideCls = (p.opt_type || "").toUpperCase() === "P" ? "pos-side-short" : "pos-side-long";
|
||||
const expMs = p.exp_time_ms != null ? p.exp_time_ms : p.exp_time;
|
||||
const expAttr = expMs != null && expMs !== "" ? String(expMs) : "";
|
||||
const closePreview = p.close_preview || {};
|
||||
const closePreviewCls = pnlCls(closePreview.estimated_pnl);
|
||||
const closeSheets = p.avail_pos != null && Number(p.avail_pos) > 0 ? p.avail_pos : p.pos;
|
||||
return (
|
||||
'<div class="pos-card-head">' +
|
||||
'<div class="pos-card-symbol"><strong>' + (p.inst_id || "") + '</strong>' +
|
||||
'<span class="pos-side-badge ' + sideCls + '">' + optTypeLabel(p.opt_type) + "</span></div>" +
|
||||
'<div class="pos-head-actions">' +
|
||||
'<button type="button" class="btn-primary opt-close-btn" data-inst="' + p.inst_id + '">限价平仓</button>' +
|
||||
'<button type="button" class="btn-primary opt-close-btn" data-inst="' + p.inst_id + '" data-sheets="' + closeSheets + '">多档平仓</button>' +
|
||||
"</div></div>" +
|
||||
'<div class="pos-meta">' +
|
||||
'<span class="pos-meta-item">行权价: ' + fmt(p.strike, 0) + "</span>" +
|
||||
@@ -475,6 +506,8 @@
|
||||
'<div class="pos-cell"><span class="pos-label">开仓均价</span><span class="pos-value">' + fmt(p.avg_px, 4) + "</span></div>" +
|
||||
'<div class="pos-cell"><span class="pos-label">标记价</span><span class="pos-value">' + fmt(p.mark_px, 4) + "</span></div>" +
|
||||
'<div class="pos-cell"><span class="pos-label">指数价</span><span class="pos-value">' + fmt(p.idx_px, 0) + "</span></div>" +
|
||||
'<div class="pos-cell"><span class="pos-label">买盘深度</span><span class="pos-value">' + fmtBidDepth(p.bid_depth) + "</span></div>" +
|
||||
'<div class="pos-cell"><span class="pos-label">按买盘收回</span><span class="pos-value ' + closePreviewCls + '">' + fmtClosePreview(closePreview) + "</span></div>" +
|
||||
'<div class="pos-cell"><span class="pos-label">到期平衡</span><span class="pos-value">' + fmt(p.expiry_be_px, 0) + "</span></div>" +
|
||||
'<div class="pos-cell"><span class="pos-label">平掉回本</span><span class="pos-value">' + fmt(p.close_be_px, 0) + "</span></div>" +
|
||||
'<div class="pos-cell"><span class="pos-label">浮盈亏</span><span class="pos-value ' + uplCls + '">' + fmt(p.upl, 2) + "</span></div>" +
|
||||
@@ -562,26 +595,42 @@
|
||||
}
|
||||
|
||||
async function closePosition(inst, btn) {
|
||||
const q = await apiJson("/api/options/quote?inst_id=" + encodeURIComponent(inst) + "&mode=sheets&sheets=1");
|
||||
const sheets = btn && btn.getAttribute("data-sheets") ? parseInt(btn.getAttribute("data-sheets"), 10) : null;
|
||||
let url = "/api/options/quote?inst_id=" + encodeURIComponent(inst) + "&mode=close_preview";
|
||||
if (sheets && sheets > 0) url += "&sheets=" + encodeURIComponent(sheets);
|
||||
const q = await apiJson(url);
|
||||
if (!q.ok) {
|
||||
alert(q.msg || "获取买一价失败");
|
||||
return;
|
||||
}
|
||||
const bid = q.bid;
|
||||
if (bid == null || bid <= 0) {
|
||||
alert("暂无买一价,请稍后在 OKX App 平仓或等盘口恢复");
|
||||
const preview = q.close_preview || {};
|
||||
if (!preview.covered_sheets || preview.covered_sheets <= 0) {
|
||||
alert("暂无可用买盘深度,请稍后在 OKX App 平仓或等盘口恢复");
|
||||
return;
|
||||
}
|
||||
if (!confirm("限价卖出 @ 买一 " + fmt(bid, 4) + "(每 1 ETH/BTC)?\n合约:" + inst)) return;
|
||||
const msg = [
|
||||
"按最多5档买盘拆分限价卖出?",
|
||||
"合约: " + inst,
|
||||
"预计收回: " + fmtClosePreview(preview),
|
||||
preview.estimated_pnl != null ? "预估盈亏: " + fmt(preview.estimated_pnl, 4) + " USDC" : "",
|
||||
"",
|
||||
fmtPreviewLevels(preview),
|
||||
preview.uncovered_sheets > 0 ? "\n注意: 当前买盘不足,预计仍剩 " + preview.uncovered_sheets + " 张未覆盖。" : ""
|
||||
].filter(function (x) { return x !== ""; }).join("\n");
|
||||
if (!confirm(msg)) return;
|
||||
if (btn) btn.disabled = true;
|
||||
try {
|
||||
const r = await apiJson("/api/options/close", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ inst_id: inst }),
|
||||
body: JSON.stringify({ inst_id: inst, mode: "depth_split", sheets: sheets }),
|
||||
});
|
||||
if (r.ok) {
|
||||
alert("平仓单已提交" + (r.bid != null ? " @ " + fmt(r.bid, 4) : ""));
|
||||
let okMsg = "平仓单已提交 " + (r.submitted_sheets || 0) + " 张";
|
||||
if (r.premium_received != null) okMsg += "\n预估收回: " + fmt(r.premium_received, 4) + " USDC";
|
||||
if (r.remaining_sheets > 0) okMsg += "\n剩余: " + r.remaining_sheets + " 张";
|
||||
if (r.stopped_reason) okMsg += "\n停止原因: " + r.stopped_reason;
|
||||
alert(okMsg);
|
||||
} else {
|
||||
alert(r.msg || "平仓失败");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user