(function (global) { function fmtPnl(v) { if (v == null || v === "") return "—"; const n = Number(v); if (!Number.isFinite(n)) return "—"; return (n > 0 ? "+" : "") + n.toFixed(2) + " U"; } function pnlClass(v) { const n = Number(v); if (!Number.isFinite(n) || n === 0) return "pnl-flat"; return n > 0 ? "pnl-up" : "pnl-down"; } function fmtPnlHtml(v) { return '' + escapeHtml(fmtPnl(v)) + ""; } function escapeHtml(s) { return String(s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function positionSummary(brief, instanceId, online) { const list = brief || []; if (!list.length) return '暂无持仓'; const items = list.slice(0, 8).map(function (p) { const id = p.inst_id || ""; const sheets = escapeHtml(String(p.sheets != null ? p.sheets : "—")); const invalid = !!(p.bid_invalid || (p.bid_px == null && (p.bid_sz == null || p.bid_sz === ""))); const reason = (p.bid_invalid_reason || "买一无效,禁止平仓").toString(); let closeBtn = ""; if (online && id) { if (invalid) { closeBtn = ''; } else { closeBtn = ''; } } return ( "
  • " + escapeHtml(id) + " · " + sheets + "张 · " + fmtPnlHtml(p.pnl_usdt) + "" + closeBtn + "
  • " ); }); const more = list.length > 8 ? '
  • …共 ' + list.length + " 笔
  • " : ""; return ""; } function fmtBidPx(v) { if (v == null || v === "") return "—"; const n = Number(v); if (!Number.isFinite(n)) return "—"; let s = n.toFixed(4).replace(/\.?0+$/, ""); return s || "0"; } function isLiquidityWeak(p) { if (!p) return false; if (p.liquidity_ok === false || p.bid_invalid) return true; const uncovered = Number(p.uncovered_sheets); return Number.isFinite(uncovered) && uncovered > 0; } function bidLiquidityLine(p) { if (p.bid_invalid || (p.bid_px == null && (p.bid_sz == null || p.bid_sz === ""))) { const reason = (p.bid_invalid_reason || "无买一/残档").toString(); const short = reason.length > 18 ? reason.slice(0, 18) + "…" : reason; return { text: short, weak: true }; } const px = fmtBidPx(p.bid_px); const sz = p.bid_sz != null ? String(p.bid_sz) : "—"; const uncovered = Number(p.uncovered_sheets); let text = px + " / " + sz + "张"; const weak = isLiquidityWeak(p); if (Number.isFinite(uncovered) && uncovered > 0) { text += "(差" + uncovered + ")"; } return { text: text, weak: weak }; } function bidLiquiditySummary(brief) { const list = brief || []; if (!list.length) return '—'; const items = list.slice(0, 8).map(function (p) { const line = bidLiquidityLine(p); const cls = line.weak ? "liq-warn" : "liq-ok"; return '
  • ' + escapeHtml(line.text) + "
  • "; }); const more = list.length > 8 ? '
  • …共 ' + list.length + " 笔
  • " : ""; const anyWeak = list.some(isLiquidityWeak); const wrapCls = anyWeak ? "pos-brief liq-brief liq-brief--warn" : "pos-brief liq-brief"; return '"; } function renderRow(row) { const snap = row.snapshot || {}; const inst = snap.instance || {}; const pnl = snap.pnl && snap.pnl.unrealized_usdt != null ? snap.pnl.unrealized_usdt : snap.upl_total_usdc; const brief = snap.positions_brief || snap.positions || []; const online = !!row.online; const status = online ? '在线' : '离线'; const err = row.error ? '
    错误: ' + escapeHtml(row.error) + "
    " : ""; return ( "" + "" + escapeHtml(row.name || "") + "" + err + "" + "" + status + "" + "" + escapeHtml(inst.mode || "—") + "" + "" + fmtPnlHtml(pnl) + "" + "" + positionSummary(brief, row.id, online) + "" + "" + bidLiquiditySummary(brief) + "" + '' + escapeHtml(row.pulled_at || "—") + "" + '
    ' + '内嵌打开' + '' + "
    " + "" ); } async function requestClose(btn) { const instanceId = decodeURIComponent(btn.getAttribute("data-close-inst") || ""); const optId = decodeURIComponent(btn.getAttribute("data-close-opt") || ""); if (!instanceId || !optId) return; if ( !confirm( "确认向该客户端下发买一限价全平?\n\n合约: " + optId + "\n\n无效买一将被拒绝;下单在客户端本地执行。" ) ) { return; } const oldText = btn.textContent; btn.disabled = true; btn.textContent = "平仓中…"; try { const rr = await fetch("/api/instances/" + encodeURIComponent(instanceId) + "/options/close", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, body: JSON.stringify({ inst_id: optId }), }); let dd = {}; try { dd = await rr.json(); } catch (e) { dd = {}; } if (dd && dd.ok) { let msg = dd.msg || "已提交买一全平"; if (dd.submitted_sheets != null) msg += " · " + dd.submitted_sheets + " 张"; if (dd.fully_closed) msg += " · 已全平"; alert(msg); await refresh(); } else { alert((dd && dd.msg) || "平仓失败"); btn.disabled = false; btn.textContent = oldText; } } catch (e) { alert((e && e.message) || "请求失败"); btn.disabled = false; btn.textContent = oldText; } } async function refresh() { const status = document.getElementById("overview-status"); const grid = document.getElementById("overview-grid"); if (!grid) return; if (status) status.textContent = "刷新中…"; try { const r = await fetch("/api/overview"); const d = await r.json(); if (!d.ok) throw new Error(d.msg || "失败"); const list = d.instances || []; if (!list.length) { grid.innerHTML = '

    暂无启用实例,请先到「实例」添加.

    '; } else { grid.innerHTML = '
    ' + "" + "" + "" + list.map(renderRow).join("") + "
    名称状态模式浮盈持仓买一/流动性拉取时间操作
    "; grid.querySelectorAll("[data-open-tab]").forEach(function (btn) { btn.addEventListener("click", async function () { const id = btn.getAttribute("data-open-tab"); const rr = await fetch("/api/instances/" + id + "/open_url"); const dd = await rr.json(); if (dd.ok && dd.url) window.open(dd.url, "_blank", "noopener"); else alert(dd.msg || "无法打开"); }); }); grid.querySelectorAll("[data-close-opt]").forEach(function (btn) { btn.addEventListener("click", function () { requestClose(btn); }); }); } if (status) status.textContent = "已更新 · 共 " + list.length + " 台"; } catch (e) { if (status) status.textContent = e.message || "加载失败"; } } function boot() { const btn = document.getElementById("btn-refresh"); if (btn) btn.addEventListener("click", refresh); refresh(); setInterval(refresh, 10000); } global.HubOverview = { boot: boot, refresh: refresh }; })(window);