Show cancellable pending option orders beside the order form.
Fetch live OKX option hangs on the right after submit, with refresh and one-click cancel. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3639,6 +3639,101 @@ html[data-theme="light"] .options-strike-table--t .opt-strike-row-atm td {
|
|||||||
.opt-order-panel-inner {
|
.opt-order-panel-inner {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
.opt-order-layout {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
.opt-order-main {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.opt-order-pending {
|
||||||
|
flex: 0 0 280px;
|
||||||
|
max-width: 320px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(158, 192, 255, 0.2);
|
||||||
|
background: rgba(0, 0, 0, 0.18);
|
||||||
|
}
|
||||||
|
.opt-order-pending-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.opt-order-pending-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: #9ec0ff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.opt-order-pending-head .btn-secondary {
|
||||||
|
font-size: 0.68rem;
|
||||||
|
padding: 2px 8px;
|
||||||
|
}
|
||||||
|
.opt-pending-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
max-height: 220px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.opt-pending-empty {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
}
|
||||||
|
.opt-pending-item {
|
||||||
|
padding: 8px 9px;
|
||||||
|
border-radius: 7px;
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
}
|
||||||
|
.opt-pending-item-top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.opt-pending-side {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.opt-pending-side.is-buy { color: #3dd68c; }
|
||||||
|
.opt-pending-side.is-sell { color: #ff6b7a; }
|
||||||
|
.opt-pending-inst {
|
||||||
|
font-size: 0.68rem;
|
||||||
|
color: #c5d0ee;
|
||||||
|
word-break: break-all;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.opt-pending-meta {
|
||||||
|
font-size: 0.68rem;
|
||||||
|
color: #8892b0;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
.opt-pending-item .opt-pending-cancel {
|
||||||
|
font-size: 0.68rem;
|
||||||
|
padding: 2px 8px;
|
||||||
|
}
|
||||||
|
html[data-theme="light"] .opt-order-pending {
|
||||||
|
background: rgba(0, 0, 0, 0.03);
|
||||||
|
border-color: rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
html[data-theme="light"] .opt-pending-item {
|
||||||
|
background: #fff;
|
||||||
|
border-color: rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.opt-order-layout {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.opt-order-pending {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
.opt-order-panel-inner .opt-order-title {
|
.opt-order-panel-inner .opt-order-title {
|
||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
|
|||||||
@@ -27,7 +27,9 @@
|
|||||||
let lastGoodPositionsAt = 0;
|
let lastGoodPositionsAt = 0;
|
||||||
let positionsRefreshSeq = 0;
|
let positionsRefreshSeq = 0;
|
||||||
let refreshAllTimer = null;
|
let refreshAllTimer = null;
|
||||||
|
let pendingRefreshTimer = null;
|
||||||
const POSITIONS_STALE_MS = 45000;
|
const POSITIONS_STALE_MS = 45000;
|
||||||
|
const PENDING_POLL_MS = 8000;
|
||||||
|
|
||||||
function fmt(v, d) {
|
function fmt(v, d) {
|
||||||
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
|
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
|
||||||
@@ -68,6 +70,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parkOrderPanel() {
|
function parkOrderPanel() {
|
||||||
|
stopPendingOrdersPoll();
|
||||||
const panel = orderPanel();
|
const panel = orderPanel();
|
||||||
const host = orderPanelHost();
|
const host = orderPanelHost();
|
||||||
const inline = document.querySelector(".opt-order-inline-row");
|
const inline = document.querySelector(".opt-order-inline-row");
|
||||||
@@ -116,6 +119,99 @@
|
|||||||
panel.style.display = "";
|
panel.style.display = "";
|
||||||
orderPanelHost().hidden = true;
|
orderPanelHost().hidden = true;
|
||||||
tr.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
tr.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
||||||
|
refreshPendingOrders();
|
||||||
|
startPendingOrdersPoll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function paintPendingOrders(orders) {
|
||||||
|
const host = document.getElementById("opt-pending-list");
|
||||||
|
if (!host) return;
|
||||||
|
const rows = Array.isArray(orders) ? orders : [];
|
||||||
|
if (!rows.length) {
|
||||||
|
host.innerHTML = '<div class="muted opt-pending-empty">暂无未成交委托</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
host.innerHTML = rows.map(function (o) {
|
||||||
|
const side = String(o.side || "").toLowerCase();
|
||||||
|
const sideCls = side === "buy" ? "is-buy" : side === "sell" ? "is-sell" : "";
|
||||||
|
const remain = (o.sz != null && o.fill_sz != null) ? Math.max(0, Number(o.sz) - Number(o.fill_sz)) : o.sz;
|
||||||
|
const pxTxt = o.px != null ? fmtOptionPx(o.px, null) : "—";
|
||||||
|
return (
|
||||||
|
'<div class="opt-pending-item" data-ord="' + (o.ord_id || "") + '" data-inst="' + (o.inst_id || "") + '">' +
|
||||||
|
'<div class="opt-pending-item-top">' +
|
||||||
|
'<span class="opt-pending-side ' + sideCls + '">' + (o.side_label || side || "—") + "</span>" +
|
||||||
|
'<button type="button" class="btn-secondary opt-pending-cancel" data-ord="' + (o.ord_id || "") +
|
||||||
|
'" data-inst="' + (o.inst_id || "") + '">撤销</button>' +
|
||||||
|
"</div>" +
|
||||||
|
'<div class="opt-pending-inst" title="' + (o.inst_id || "") + '">' + (o.inst_id || "—") + "</div>" +
|
||||||
|
'<div class="opt-pending-meta">价 ' + pxTxt +
|
||||||
|
" · 张数 " + (o.sz != null ? o.sz : "—") +
|
||||||
|
(o.fill_sz != null && Number(o.fill_sz) > 0 ? " · 已成 " + o.fill_sz : "") +
|
||||||
|
(remain != null && o.fill_sz != null && Number(o.fill_sz) > 0 ? " · 剩余 " + remain : "") +
|
||||||
|
"</div></div>"
|
||||||
|
);
|
||||||
|
}).join("");
|
||||||
|
host.querySelectorAll(".opt-pending-cancel").forEach(function (btn) {
|
||||||
|
btn.addEventListener("click", function () {
|
||||||
|
cancelPendingOrder(btn.getAttribute("data-inst"), btn.getAttribute("data-ord"), btn);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshPendingOrders() {
|
||||||
|
const host = document.getElementById("opt-pending-list");
|
||||||
|
if (!host) return;
|
||||||
|
try {
|
||||||
|
const d = await apiJson("/api/options/orders/pending");
|
||||||
|
if (!d.ok) {
|
||||||
|
host.innerHTML = '<div class="muted opt-pending-empty">' + (d.msg || "获取委托失败") + "</div>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
paintPendingOrders(d.orders || []);
|
||||||
|
} catch (e) {
|
||||||
|
host.innerHTML = '<div class="muted opt-pending-empty">获取委托失败</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startPendingOrdersPoll() {
|
||||||
|
stopPendingOrdersPoll();
|
||||||
|
pendingRefreshTimer = setInterval(function () {
|
||||||
|
const panel = orderPanel();
|
||||||
|
if (!panel || panel.style.display === "none") {
|
||||||
|
stopPendingOrdersPoll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
refreshPendingOrders();
|
||||||
|
}, PENDING_POLL_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopPendingOrdersPoll() {
|
||||||
|
if (pendingRefreshTimer) {
|
||||||
|
clearInterval(pendingRefreshTimer);
|
||||||
|
pendingRefreshTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cancelPendingOrder(inst, ordId, btn) {
|
||||||
|
if (!inst || !ordId) return;
|
||||||
|
if (!confirm("撤销该委托?\n合约: " + inst + "\n订单: " + ordId)) return;
|
||||||
|
if (btn) btn.disabled = true;
|
||||||
|
try {
|
||||||
|
const d = await apiJson("/api/options/orders/cancel", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ inst_id: inst, ord_id: ordId }),
|
||||||
|
});
|
||||||
|
if (!d.ok) {
|
||||||
|
alert(d.msg || "撤销失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await refreshPendingOrders();
|
||||||
|
refreshAllPositions();
|
||||||
|
if (typeof refreshAccountSnapshot === "function") refreshAccountSnapshot();
|
||||||
|
} finally {
|
||||||
|
if (btn) btn.disabled = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function currentSizeMode() {
|
function currentSizeMode() {
|
||||||
@@ -852,9 +948,11 @@
|
|||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
const msgEl = document.getElementById("opt-order-msg");
|
const msgEl = document.getElementById("opt-order-msg");
|
||||||
msgEl.textContent = d.ok ? "下单已提交,可在 OKX 委托中查看" : (d.msg || "失败");
|
msgEl.textContent = d.ok ? "下单已提交,右侧可查看/撤销未成交委托" : (d.msg || "失败");
|
||||||
msgEl.classList.toggle("opt-error", !d.ok);
|
msgEl.classList.toggle("opt-error", !d.ok);
|
||||||
if (d.ok) {
|
if (d.ok) {
|
||||||
|
refreshPendingOrders();
|
||||||
|
startPendingOrdersPoll();
|
||||||
refreshAllPositions();
|
refreshAllPositions();
|
||||||
if (typeof refreshAccountSnapshot === "function") refreshAccountSnapshot();
|
if (typeof refreshAccountSnapshot === "function") refreshAccountSnapshot();
|
||||||
} else {
|
} else {
|
||||||
@@ -1667,6 +1765,12 @@
|
|||||||
document.getElementById("opt-load-chain").addEventListener("click", loadChain);
|
document.getElementById("opt-load-chain").addEventListener("click", loadChain);
|
||||||
document.getElementById("opt-refresh-positions").addEventListener("click", refreshAllPositions);
|
document.getElementById("opt-refresh-positions").addEventListener("click", refreshAllPositions);
|
||||||
document.getElementById("opt-open-btn").addEventListener("click", openPosition);
|
document.getElementById("opt-open-btn").addEventListener("click", openPosition);
|
||||||
|
const pendingRefreshBtn = document.getElementById("opt-pending-refresh");
|
||||||
|
if (pendingRefreshBtn) {
|
||||||
|
pendingRefreshBtn.addEventListener("click", function () {
|
||||||
|
refreshPendingOrders();
|
||||||
|
});
|
||||||
|
}
|
||||||
bindOptionsPosTabs();
|
bindOptionsPosTabs();
|
||||||
|
|
||||||
document.querySelectorAll('input[name="opt-size-mode"]').forEach(function (r) {
|
document.querySelectorAll('input[name="opt-size-mode"]').forEach(function (r) {
|
||||||
|
|||||||
@@ -766,6 +766,66 @@ def quote_option_contract(ex: ccxt.okx, inst_id: str) -> dict[str, Any]:
|
|||||||
return {"ok": False, "msg": str(e)}
|
return {"ok": False, "msg": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_option_pending_orders(ex: ccxt.okx, inst_id: str | None = None) -> list[dict[str, Any]]:
|
||||||
|
"""未成交期权委托(限价挂单)."""
|
||||||
|
params: dict[str, Any] = {"instType": "OPTION"}
|
||||||
|
inst = (inst_id or "").strip()
|
||||||
|
if inst:
|
||||||
|
params["instId"] = inst
|
||||||
|
try:
|
||||||
|
rows = ex.private_get_trade_orders_pending(params).get("data") or []
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
out: list[dict[str, Any]] = []
|
||||||
|
for o in rows:
|
||||||
|
if not isinstance(o, dict):
|
||||||
|
continue
|
||||||
|
oid = str(o.get("ordId") or "").strip()
|
||||||
|
iid = str(o.get("instId") or "").strip()
|
||||||
|
if not oid or not iid:
|
||||||
|
continue
|
||||||
|
side = str(o.get("side") or "").lower()
|
||||||
|
px = _safe_float(o.get("px"))
|
||||||
|
sz = _safe_float(o.get("sz"))
|
||||||
|
fill_sz = _safe_float(o.get("fillSz")) or 0.0
|
||||||
|
acc_fill = _safe_float(o.get("accFillSz"))
|
||||||
|
if acc_fill is not None:
|
||||||
|
fill_sz = acc_fill
|
||||||
|
out.append(
|
||||||
|
{
|
||||||
|
"ord_id": oid,
|
||||||
|
"inst_id": iid,
|
||||||
|
"side": side,
|
||||||
|
"side_label": "买入" if side == "buy" else ("卖出" if side == "sell" else side or "—"),
|
||||||
|
"px": px,
|
||||||
|
"sz": int(sz) if sz is not None else None,
|
||||||
|
"fill_sz": int(fill_sz) if fill_sz is not None else 0,
|
||||||
|
"state": str(o.get("state") or ""),
|
||||||
|
"ord_type": str(o.get("ordType") or ""),
|
||||||
|
"c_time": o.get("cTime"),
|
||||||
|
"u_time": o.get("uTime"),
|
||||||
|
"reduce_only": str(o.get("reduceOnly") or "").lower() in ("true", "1", "yes"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
out.sort(key=lambda x: int(float(x.get("c_time") or 0)), reverse=True)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def cancel_option_order(ex: ccxt.okx, *, inst_id: str, ord_id: str) -> dict[str, Any]:
|
||||||
|
inst_id = (inst_id or "").strip()
|
||||||
|
ord_id = (ord_id or "").strip()
|
||||||
|
if not inst_id or not ord_id:
|
||||||
|
return {"ok": False, "msg": "缺少 inst_id 或 ord_id"}
|
||||||
|
try:
|
||||||
|
resp = ex.private_post_trade_cancel_order({"instId": inst_id, "ordId": ord_id})
|
||||||
|
data = (resp or {}).get("data") or []
|
||||||
|
if data and str(data[0].get("sCode")) == "0":
|
||||||
|
return {"ok": True, "data": data[0], "raw": resp}
|
||||||
|
return {"ok": False, "msg": _okx_trade_error_message(resp=resp), "raw": resp}
|
||||||
|
except Exception as e:
|
||||||
|
return {"ok": False, "msg": _okx_trade_error_message(e)}
|
||||||
|
|
||||||
|
|
||||||
def place_option_limit_order(
|
def place_option_limit_order(
|
||||||
ex: ccxt.okx,
|
ex: ccxt.okx,
|
||||||
*,
|
*,
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ def _build_cfg(app_module: Any) -> dict[str, Any]:
|
|||||||
fetch_options_balances,
|
fetch_options_balances,
|
||||||
format_position_row,
|
format_position_row,
|
||||||
options_api_ready,
|
options_api_ready,
|
||||||
|
cancel_option_order,
|
||||||
|
fetch_option_pending_orders,
|
||||||
place_option_limit_order,
|
place_option_limit_order,
|
||||||
place_option_market_order,
|
place_option_market_order,
|
||||||
quote_option_contract,
|
quote_option_contract,
|
||||||
@@ -119,6 +121,8 @@ def _build_cfg(app_module: Any) -> dict[str, Any]:
|
|||||||
"fetch_option_book_depth": fetch_option_book_depth,
|
"fetch_option_book_depth": fetch_option_book_depth,
|
||||||
"place_option_limit_order": place_option_limit_order,
|
"place_option_limit_order": place_option_limit_order,
|
||||||
"place_option_market_order": place_option_market_order,
|
"place_option_market_order": place_option_market_order,
|
||||||
|
"fetch_option_pending_orders": fetch_option_pending_orders,
|
||||||
|
"cancel_option_order": cancel_option_order,
|
||||||
"fetch_option_positions": fetch_option_positions,
|
"fetch_option_positions": fetch_option_positions,
|
||||||
"fetch_options_balances": fetch_options_balances,
|
"fetch_options_balances": fetch_options_balances,
|
||||||
"format_position_row": format_position_row,
|
"format_position_row": format_position_row,
|
||||||
@@ -531,6 +535,61 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@app.route("/api/options/orders/pending")
|
||||||
|
@lr
|
||||||
|
def api_options_orders_pending():
|
||||||
|
ex, err = _require_options_ex(cfg)
|
||||||
|
if ex is None:
|
||||||
|
return jsonify({"ok": False, "msg": err})
|
||||||
|
inst_id = (request.args.get("inst_id") or "").strip() or None
|
||||||
|
try:
|
||||||
|
orders = cfg["fetch_option_pending_orders"](ex, inst_id)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"ok": False, "msg": f"获取委托失败: {e}"})
|
||||||
|
return jsonify({"ok": True, "orders": orders, "count": len(orders)})
|
||||||
|
|
||||||
|
@app.route("/api/options/orders/cancel", methods=["POST"])
|
||||||
|
@lr
|
||||||
|
def api_options_orders_cancel():
|
||||||
|
ex, err = _require_options_ex(cfg)
|
||||||
|
if ex is None:
|
||||||
|
return jsonify({"ok": False, "msg": err})
|
||||||
|
data = request.get_json(silent=True) or {}
|
||||||
|
inst_id = (data.get("inst_id") or "").strip()
|
||||||
|
ord_id = (data.get("ord_id") or "").strip()
|
||||||
|
if not inst_id or not ord_id:
|
||||||
|
return jsonify({"ok": False, "msg": "缺少 inst_id 或 ord_id"})
|
||||||
|
out = cfg["cancel_option_order"](ex, inst_id=inst_id, ord_id=ord_id)
|
||||||
|
if out.get("ok"):
|
||||||
|
from lib.exchange.okx_options_lib import invalidate_option_positions_cache
|
||||||
|
|
||||||
|
invalidate_option_positions_cache()
|
||||||
|
# 本地未成交开仓记录标记取消,避免假 open
|
||||||
|
try:
|
||||||
|
conn = cfg["get_db"]()
|
||||||
|
try:
|
||||||
|
init_options_tables(conn)
|
||||||
|
conn.execute(
|
||||||
|
"""
|
||||||
|
UPDATE options_trades
|
||||||
|
SET status = 'cancelled',
|
||||||
|
signal_note = CASE
|
||||||
|
WHEN signal_note IS NULL OR TRIM(signal_note) = '' THEN '委托撤销'
|
||||||
|
ELSE signal_note
|
||||||
|
END,
|
||||||
|
closed_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE inst_id = ? AND exchange_ord_id = ? AND status = 'open'
|
||||||
|
""",
|
||||||
|
(inst_id, ord_id),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
_sync_options_trades(cfg, force=True)
|
||||||
|
return jsonify(out), (200 if out.get("ok") else 400)
|
||||||
|
|
||||||
@app.route("/api/options/positions")
|
@app.route("/api/options/positions")
|
||||||
@lr
|
@lr
|
||||||
def api_options_positions():
|
def api_options_positions():
|
||||||
|
|||||||
@@ -66,39 +66,52 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="opt-order-panel-host" class="opt-order-panel-host" hidden aria-hidden="true">
|
<div id="opt-order-panel-host" class="opt-order-panel-host" hidden aria-hidden="true">
|
||||||
<div id="opt-order-panel" class="opt-order-panel-inner" style="display:none">
|
<div id="opt-order-panel" class="opt-order-panel-inner" style="display:none">
|
||||||
<h3 class="opt-order-title">下单</h3>
|
<div class="opt-order-layout">
|
||||||
<div id="opt-order-inst" class="options-order-inst"></div>
|
<div class="opt-order-main">
|
||||||
<div class="options-order-grid">
|
<h3 class="opt-order-title">下单</h3>
|
||||||
<div><span class="k">卖一/张</span><span id="opt-order-ask" class="v">—</span></div>
|
<div id="opt-order-inst" class="options-order-inst"></div>
|
||||||
<div><span class="k">买一/张</span><span id="opt-order-bid" class="v">—</span></div>
|
<div class="options-order-grid">
|
||||||
<div><span class="k">张数</span><span id="opt-order-sheets" class="v">—</span></div>
|
<div><span class="k">卖一/张</span><span id="opt-order-ask" class="v">—</span></div>
|
||||||
<div><span class="k" id="opt-order-eth-label">ETH 数量</span><span id="opt-order-eth" class="v">—</span></div>
|
<div><span class="k">买一/张</span><span id="opt-order-bid" class="v">—</span></div>
|
||||||
<div><span class="k">预估权利金</span><span id="opt-order-premium" class="v">—</span></div>
|
<div><span class="k">张数</span><span id="opt-order-sheets" class="v">—</span></div>
|
||||||
<div><span class="k">合约杠杆</span><span id="opt-order-leverage" class="v" title="名义价值÷权利金,测算用">—</span></div>
|
<div><span class="k" id="opt-order-eth-label">ETH 数量</span><span id="opt-order-eth" class="v">—</span></div>
|
||||||
<div><span class="k">到期平衡</span><span id="opt-order-expiry-be" class="v">—</span></div>
|
<div><span class="k">预估权利金</span><span id="opt-order-premium" class="v">—</span></div>
|
||||||
<div><span class="k">距平衡</span><span id="opt-order-dist-be" class="v">—</span></div>
|
<div><span class="k">合约杠杆</span><span id="opt-order-leverage" class="v" title="名义价值÷权利金,测算用">—</span></div>
|
||||||
|
<div><span class="k">到期平衡</span><span id="opt-order-expiry-be" class="v">—</span></div>
|
||||||
|
<div><span class="k">距平衡</span><span id="opt-order-dist-be" class="v">—</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="options-estimate-row">
|
||||||
|
<label class="opt-est-label" for="opt-target-idx">目标位(指数)</label>
|
||||||
|
<input type="number" id="opt-target-idx" class="opt-target-idx" step="0.1" min="0" placeholder="达价限价平仓">
|
||||||
|
<span class="k">预计价值</span>
|
||||||
|
<span id="opt-est-value" class="v">—</span>
|
||||||
|
<span class="k">盈利</span>
|
||||||
|
<span id="opt-est-profit" class="v">—</span>
|
||||||
|
<span class="k">目标杠杆</span>
|
||||||
|
<span id="opt-est-leverage" class="v" title="目标位名义价值÷权利金">—</span>
|
||||||
|
<span class="muted opt-est-note">目标价=监控指数;到位后按买一限价平仓;无止损,到期即止损</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-row options-order-mode-row">
|
||||||
|
<label><input type="radio" name="opt-size-mode" value="sheets" checked> 指定张数</label>
|
||||||
|
<input type="number" id="opt-sheets-amount" min="1" step="1" value="1" placeholder="张数">
|
||||||
|
<label><input type="radio" name="opt-size-mode" value="budget_full"> 按可用余额打满</label>
|
||||||
|
<label><input type="radio" name="opt-size-mode" value="eth_amount"> 指定币数量</label>
|
||||||
|
<input type="number" id="opt-eth-amount" min="0.01" step="0.01" placeholder="如 0.5" style="display:none">
|
||||||
|
<input type="text" id="opt-signal-note" placeholder="备注(关键位说明)">
|
||||||
|
<button type="button" class="btn-primary" id="opt-open-btn">限价买入 @ 卖一</button>
|
||||||
|
</div>
|
||||||
|
<div id="opt-order-msg" class="muted"></div>
|
||||||
|
</div>
|
||||||
|
<aside class="opt-order-pending" aria-label="未成交委托">
|
||||||
|
<div class="opt-order-pending-head">
|
||||||
|
<h4 class="opt-order-pending-title">委托</h4>
|
||||||
|
<button type="button" class="btn-secondary" id="opt-pending-refresh">刷新</button>
|
||||||
|
</div>
|
||||||
|
<div id="opt-pending-list" class="opt-pending-list">
|
||||||
|
<div class="muted opt-pending-empty">暂无未成交委托</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
<div class="options-estimate-row">
|
|
||||||
<label class="opt-est-label" for="opt-target-idx">目标位(指数)</label>
|
|
||||||
<input type="number" id="opt-target-idx" class="opt-target-idx" step="0.1" min="0" placeholder="达价限价平仓">
|
|
||||||
<span class="k">预计价值</span>
|
|
||||||
<span id="opt-est-value" class="v">—</span>
|
|
||||||
<span class="k">盈利</span>
|
|
||||||
<span id="opt-est-profit" class="v">—</span>
|
|
||||||
<span class="k">目标杠杆</span>
|
|
||||||
<span id="opt-est-leverage" class="v" title="目标位名义价值÷权利金">—</span>
|
|
||||||
<span class="muted opt-est-note">目标价=监控指数;到位后按买一限价平仓;无止损,到期即止损</span>
|
|
||||||
</div>
|
|
||||||
<div class="form-row options-order-mode-row">
|
|
||||||
<label><input type="radio" name="opt-size-mode" value="sheets" checked> 指定张数</label>
|
|
||||||
<input type="number" id="opt-sheets-amount" min="1" step="1" value="1" placeholder="张数">
|
|
||||||
<label><input type="radio" name="opt-size-mode" value="budget_full"> 按可用余额打满</label>
|
|
||||||
<label><input type="radio" name="opt-size-mode" value="eth_amount"> 指定币数量</label>
|
|
||||||
<input type="number" id="opt-eth-amount" min="0.01" step="0.01" placeholder="如 0.5" style="display:none">
|
|
||||||
<input type="text" id="opt-signal-note" placeholder="备注(关键位说明)">
|
|
||||||
<button type="button" class="btn-primary" id="opt-open-btn">限价买入 @ 卖一</button>
|
|
||||||
</div>
|
|
||||||
<div id="opt-order-msg" class="muted"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user