diff --git a/static/hub.css b/static/hub.css index 3956181..ba0a6eb 100644 --- a/static/hub.css +++ b/static/hub.css @@ -382,6 +382,15 @@ input.readonly { .pnl-up { color: var(--ok); font-weight: 600; } .pnl-down { color: var(--bad); font-weight: 600; } .pnl-flat { color: var(--muted); } +.liq-ok { color: var(--text); } +.liq-warn { + color: #fbbf24; + font-weight: 600; +} +.liq-brief--warn { + border-left: 2px solid rgba(251, 191, 36, 0.7); + padding-left: 8px; +} @media (max-width: 800px) { .desk, diff --git a/static/hub.js b/static/hub.js index 175143e..16b10fa 100644 --- a/static/hub.js +++ b/static/hub.js @@ -39,6 +39,55 @@ 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 || {}; @@ -73,6 +122,9 @@ "" + positionSummary(brief) + "" + + "" + + bidLiquiditySummary(brief) + + "" + '' + escapeHtml(row.pulled_at || "—") + "" + @@ -105,7 +157,7 @@ grid.innerHTML = '
    ' + "" + - "" + + "" + "" + list.map(renderRow).join("") + "
    名称状态模式浮盈持仓拉取时间操作名称状态模式浮盈持仓买一/流动性拉取时间操作
    ";