修复币本位期权浮盈二次折算:中控汇总与实例统计把已折 U 的数再乘指数。
约 -6.5U 被当成 ETH 再 ×2700 显示成 -1.7万U;汇总框直接按 U 展示,stats 不再对 open_float 二次换算。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1846,32 +1846,24 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
except Exception:
|
||||
index_px = None
|
||||
stats = compute_options_stats_from_history(history, index_px=index_px)
|
||||
# sum_options_net_pnl_usdc 已按指数折 U;勿再 × 指数(否则顶栏会变成上万假亏)
|
||||
open_float = sum_options_net_pnl_usdc(cfg, ex, raw_live)
|
||||
# 币本位浮盈为币数量,折算为 U 再与已平合计
|
||||
if open_float is not None and str(stats.get("pnl_unit") or "") == "U":
|
||||
px = index_px
|
||||
if px is None or px <= 0:
|
||||
for h in history:
|
||||
try:
|
||||
px = float(h.get("idx_px") or 0)
|
||||
except (TypeError, ValueError):
|
||||
px = 0
|
||||
if px > 0:
|
||||
break
|
||||
if px and px > 0:
|
||||
open_float = round(float(open_float) * float(px), 4)
|
||||
net_realized = _safe_float(stats.get("net_realized_pnl")) or 0.0
|
||||
total_pnl = None
|
||||
if open_float is not None:
|
||||
total_pnl = round(net_realized + float(open_float), 4)
|
||||
elif stats.get("total_closed"):
|
||||
total_pnl = round(net_realized, 4)
|
||||
from lib.options.options_margin_mode_lib import is_coin_margin_mode
|
||||
|
||||
pnl_unit = "U" if is_coin_margin_mode() else str(stats.get("pnl_unit") or "USDC")
|
||||
return jsonify(
|
||||
{
|
||||
"ok": True,
|
||||
**stats,
|
||||
"open_float_pnl": open_float,
|
||||
"total_pnl": total_pnl,
|
||||
"pnl_unit": pnl_unit,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -817,7 +817,19 @@
|
||||
return `${sign}${fmt(n, 2)}U`;
|
||||
}
|
||||
|
||||
/** 中控期权浮盈汇总框:只显示换算后的 U(持仓表净盈亏仍用 fmtOptPnlText 双显). */
|
||||
/** 已是 USDT 的期权浮盈合计(upl_total_usdc),禁止再按币×指数. */
|
||||
function fmtOptFloatUsdt(v) {
|
||||
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
|
||||
const n = Number(v);
|
||||
const uAbs = Math.abs(n).toFixed(2);
|
||||
const uSign = n < 0 ? "-" : n > 0 ? "+" : "";
|
||||
return `${uSign}${uAbs}U`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 币数量 → U(仅逐仓净盈亏双显/兜底合计用).
|
||||
* 汇总框勿用:后端 upl_total 已折 U,再乘会变成上万假亏.
|
||||
*/
|
||||
function fmtOptPnlUsdtOnly(v, ccy, spotPx) {
|
||||
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
|
||||
const n = Number(v);
|
||||
@@ -3939,6 +3951,7 @@
|
||||
const bal =
|
||||
opt.balances && typeof opt.balances === "object" ? opt.balances : {};
|
||||
const pick = (a, b) => (a != null && a !== "" ? a : b);
|
||||
// upl_total_usdc 实际为已折 USDT 的合计(币本位 × 指数后)
|
||||
let upl =
|
||||
opt.upl_total_usdc != null && Number.isFinite(Number(opt.upl_total_usdc))
|
||||
? Number(opt.upl_total_usdc)
|
||||
@@ -3949,10 +3962,16 @@
|
||||
let found = false;
|
||||
pos.forEach((p) => {
|
||||
const n = optionsRowDisplayPnl(p);
|
||||
if (n != null) {
|
||||
sum += n;
|
||||
found = true;
|
||||
if (n == null || !Number.isFinite(Number(n))) return;
|
||||
const ccy = String(optPremiumCcyOf(p) || "").toUpperCase();
|
||||
let uu = Number(n);
|
||||
if (ccy === "ETH" || ccy === "BTC") {
|
||||
const px = spotPxFromOptMeta(opt, p);
|
||||
if (!Number.isFinite(px) || !(px > 0)) return;
|
||||
uu = Number(n) * px;
|
||||
}
|
||||
sum += uu;
|
||||
found = true;
|
||||
});
|
||||
if (found) upl = sum;
|
||||
}
|
||||
@@ -3987,11 +4006,10 @@
|
||||
? `${fmt(fundUsdt, 2)} <small style="font-size:12px;color:var(--muted)">U</small>`
|
||||
: "—";
|
||||
const tradeTxt = formatCoinTradingLabel(usdt, eth, btc);
|
||||
const pnlCcy = optionsPanelCcy(optMeta);
|
||||
const spotPx = spotPxFromOptMeta(optMeta);
|
||||
// 汇总 upnl 已是 USDT,勿再按 ETH/BTC×指数(否则 -6U 会变成 -1.7万U)
|
||||
const pnlTxt = upnl == null || Number.isNaN(Number(upnl))
|
||||
? "—"
|
||||
: `<span class="${pnlCls(upnl)}">${fmtOptPnlUsdtOnly(upnl, pnlCcy, spotPx)}</span>`;
|
||||
: `<span class="${pnlCls(upnl)}">${fmtOptFloatUsdt(upnl)}</span>`;
|
||||
return `<div class="${rowCls}">
|
||||
<div class="stat-box"><div class="stat-label">资金账户</div><div class="stat-value">${fundTxt}</div></div>
|
||||
<div class="stat-box"><div class="stat-label">交易账户</div><div class="stat-value">${tradeTxt}</div></div>
|
||||
@@ -4671,14 +4689,14 @@
|
||||
parts.push(`交易 ${fmt(bal.trading, 2)}U`);
|
||||
}
|
||||
if (optUpl != null && Number.isFinite(Number(optUpl))) {
|
||||
parts.push(`浮盈 ${fmtOptPnlUsdtOnly(optUpl, optCcy, spotPxFromOptMeta(opt))}`);
|
||||
parts.push(`浮盈 ${fmtOptFloatUsdt(optUpl)}`);
|
||||
}
|
||||
if (optUpl != null && Number.isFinite(Number(optUpl)) && openCount === 0) {
|
||||
// 永续空仓时主数字优先展示期权浮盈(只显示 U)
|
||||
// 永续空仓时主数字优先展示期权浮盈(已是 U,勿再按币折算)
|
||||
pnlShow = optUpl;
|
||||
pnlSuffix = "期权";
|
||||
pnlUnit = optCcy;
|
||||
pnlSpotPx = spotPxFromOptMeta(opt);
|
||||
pnlUnit = "USDC";
|
||||
pnlSpotPx = null;
|
||||
}
|
||||
}
|
||||
optLine = parts.join(" · ");
|
||||
|
||||
Reference in New Issue
Block a user