fix(hub): live market PnL and Gate drag SL place
Parse Gate unrealised_pnl in agent; refresh hub market floating PnL from board and trends; clamp Gate TP/SL triggers before place. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -907,14 +907,78 @@
|
||||
if (upnl == null || !Number.isFinite(Number(upnl))) return { text: "—", cls: "" };
|
||||
const n = Number(upnl);
|
||||
let text = (n >= 0 ? "+" : "") + n.toFixed(2) + "U";
|
||||
const margin = ctx.plan_margin;
|
||||
const notional = ctx.notional_usdt;
|
||||
if (notional != null && Number(notional) > 1e-8) {
|
||||
if (margin != null && Number(margin) > 1e-8) {
|
||||
const pct = (n / Number(margin)) * 100;
|
||||
text += " (" + (pct >= 0 ? "+" : "") + pct.toFixed(2) + "%)";
|
||||
} else if (notional != null && Number(notional) > 1e-8) {
|
||||
const pct = (n / Math.abs(Number(notional))) * 100;
|
||||
text += " (" + (pct >= 0 ? "+" : "") + pct.toFixed(2) + "%)";
|
||||
}
|
||||
return { text: text, cls: n > 0 ? "pnl-up" : n < 0 ? "pnl-down" : "" };
|
||||
}
|
||||
|
||||
function findTrendFloatingPnl(row, sym, side) {
|
||||
const hm = row.hub_monitor;
|
||||
if (!hm || !Array.isArray(hm.trends)) return null;
|
||||
for (let i = 0; i < hm.trends.length; i++) {
|
||||
const t = hm.trends[i];
|
||||
const ts = normalizeMarketSymbol(t.exchange_symbol || t.symbol || "");
|
||||
if (ts !== sym) continue;
|
||||
if ((t.direction || "").toLowerCase() !== side) continue;
|
||||
const fp = t.floating_pnl;
|
||||
if (fp != null && Number.isFinite(Number(fp))) return Number(fp);
|
||||
if (t.plan_margin_capital != null && Number(t.plan_margin_capital) > 0) {
|
||||
/* 保留 plan_margin 供百分比 */
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findTrendPlanMargin(row, sym, side) {
|
||||
const hm = row.hub_monitor;
|
||||
if (!hm || !Array.isArray(hm.trends)) return null;
|
||||
for (let i = 0; i < hm.trends.length; i++) {
|
||||
const t = hm.trends[i];
|
||||
const ts = normalizeMarketSymbol(t.exchange_symbol || t.symbol || "");
|
||||
if (ts !== sym) continue;
|
||||
if ((t.direction || "").toLowerCase() !== side) continue;
|
||||
const m = t.plan_margin_capital;
|
||||
if (m != null && Number.isFinite(Number(m)) && Number(m) > 0) return Number(m);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function syncPosTpslFromAgentPosition(p) {
|
||||
if (!posContext || !p) return;
|
||||
const et = p.exchange_tpsl;
|
||||
if (et && typeof et === "object") {
|
||||
if (et.sl && et.sl.trigger_price != null) {
|
||||
posContext.stop_loss = Number(et.sl.trigger_price);
|
||||
}
|
||||
if (et.tp && et.tp.trigger_price != null) {
|
||||
posContext.take_profit = Number(et.tp.trigger_price);
|
||||
posContext.tp_monitored = false;
|
||||
}
|
||||
}
|
||||
const cond = Array.isArray(p.conditional_orders) ? p.conditional_orders : [];
|
||||
for (let i = 0; i < cond.length; i++) {
|
||||
const o = cond[i];
|
||||
const lbl = String(o.label || "");
|
||||
const px =
|
||||
o.trigger_price != null && Number.isFinite(Number(o.trigger_price))
|
||||
? Number(o.trigger_price)
|
||||
: null;
|
||||
if (px == null) continue;
|
||||
if (/^止损/.test(lbl)) posContext.stop_loss = px;
|
||||
else if (/^止盈/.test(lbl) && !/止盈止损/.test(lbl)) {
|
||||
posContext.take_profit = px;
|
||||
posContext.tp_monitored = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function paintPosPnl(ctx) {
|
||||
if (!elPosPnl) return;
|
||||
const p = formatPosPnlText(ctx);
|
||||
@@ -949,23 +1013,46 @@
|
||||
const row = rows[i];
|
||||
const ex = row.exchange || {};
|
||||
if (ex.id !== posContext.exchange_id) continue;
|
||||
const planMargin = findTrendPlanMargin(row, sym, side);
|
||||
if (planMargin != null) posContext.plan_margin = planMargin;
|
||||
const positions = (row.agent && row.agent.positions) || [];
|
||||
for (let j = 0; j < positions.length; j++) {
|
||||
const p = positions[j];
|
||||
if ((p.side || "").toLowerCase() !== side) continue;
|
||||
if (normalizeMarketSymbol(p.symbol || "") !== sym) continue;
|
||||
if (p.unrealized_pnl != null && Number.isFinite(Number(p.unrealized_pnl))) {
|
||||
posContext.unrealized_pnl = Number(p.unrealized_pnl);
|
||||
if (p.notional_usdt != null && Number.isFinite(Number(p.notional_usdt))) {
|
||||
posContext.notional_usdt = Number(p.notional_usdt);
|
||||
}
|
||||
paintPosPnl(posContext);
|
||||
try {
|
||||
sessionStorage.setItem(HUB_MARKET_POS_CTX_KEY, JSON.stringify(posContext));
|
||||
} catch (_) {}
|
||||
let upnl =
|
||||
p.unrealized_pnl != null && Number.isFinite(Number(p.unrealized_pnl))
|
||||
? Number(p.unrealized_pnl)
|
||||
: null;
|
||||
if (upnl == null) upnl = findTrendFloatingPnl(row, sym, side);
|
||||
if (upnl == null) return;
|
||||
posContext.unrealized_pnl = upnl;
|
||||
if (p.notional_usdt != null && Number.isFinite(Number(p.notional_usdt))) {
|
||||
posContext.notional_usdt = Number(p.notional_usdt);
|
||||
}
|
||||
syncPosTpslFromAgentPosition(p);
|
||||
if (elPosSl && posContext.stop_loss != null) {
|
||||
elPosSl.textContent = fmtPrice(posContext.stop_loss);
|
||||
}
|
||||
if (elPosTp && posContext.take_profit != null && !posContext.tp_monitored) {
|
||||
elPosTp.textContent = fmtPrice(posContext.take_profit);
|
||||
}
|
||||
paintPosPnl(posContext);
|
||||
updatePositionLines();
|
||||
try {
|
||||
sessionStorage.setItem(HUB_MARKET_POS_CTX_KEY, JSON.stringify(posContext));
|
||||
} catch (_) {}
|
||||
return;
|
||||
}
|
||||
const trendUpnl = findTrendFloatingPnl(row, sym, side);
|
||||
if (trendUpnl != null) {
|
||||
posContext.unrealized_pnl = trendUpnl;
|
||||
paintPosPnl(posContext);
|
||||
try {
|
||||
sessionStorage.setItem(HUB_MARKET_POS_CTX_KEY, JSON.stringify(posContext));
|
||||
} catch (_) {}
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
@@ -2012,6 +2099,7 @@
|
||||
applyPriceAutoScale();
|
||||
updateVisibleRangeMarkers();
|
||||
syncPosContextForView(exKey, sym);
|
||||
if (posContext) refreshPosPnlFromBoard();
|
||||
showLatestOhlcv();
|
||||
try {
|
||||
updateIndicators();
|
||||
|
||||
Reference in New Issue
Block a user