Include options unrealized PnL in OKX instance header realtime PnL total.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-08 11:05:52 +08:00
parent 75a64c5d24
commit f68197b775
6 changed files with 102 additions and 2 deletions
+13
View File
@@ -615,6 +615,19 @@ def fetch_option_positions(ex: ccxt.okx) -> list[dict[str, Any]]:
return []
def fetch_options_unrealized_pnl_usdc(ex: ccxt.okx) -> float | None:
"""期权持仓未实现盈亏合计(USDC,统计口径与 USDT 1:1)。"""
total = 0.0
found = False
for pos in fetch_option_positions(ex):
upl = _safe_float(pos.get("upl"))
if upl is None:
continue
found = True
total += upl
return round(total, 4) if found else None
def estimate_usdt_to_usdc(ex: ccxt.okx, usdt_amount: float) -> dict[str, Any]:
if usdt_amount <= 0:
return {"ok": False, "msg": "兑换数量须大于 0"}
+15
View File
@@ -110,3 +110,18 @@ def resolve_instance_unrealized_pnl(
if active_rows and get_metrics_fn:
return sum_unrealized_pnl_from_metrics(active_rows, get_metrics_fn)
return None
def merge_unrealized_pnl_components(*parts: float | None) -> float | None:
"""合并永续与期权等多路未实现盈亏(任一路有值即参与合计)。"""
total = 0.0
found = False
for part in parts:
if part is None:
continue
try:
total += float(part)
found = True
except (TypeError, ValueError):
continue
return round(total, 2) if found else None
+23 -1
View File
@@ -1046,6 +1046,26 @@ function sumOrdersFloatPnl(orders){
});
return found ? total : null;
}
function combineRealtimeFloatPnl(perpTotal, optionsTotal){
let total = 0, found = false;
[perpTotal, optionsTotal].forEach(v=>{
if(v != null && !Number.isNaN(Number(v))){
total += Number(v);
found = true;
}
});
return found ? total : null;
}
function paintRealtimePnlFromSnapshot(data){
if(!data) return;
const perp = data.order_prices && data.order_prices.length
? sumOrdersFloatPnl(data.order_prices)
: null;
const combined = combineRealtimeFloatPnl(perp, data.options_unrealized_pnl);
if(combined !== null || perp !== null || data.options_unrealized_pnl != null){
paintRealtimePnl(combined);
}
}
function formatOptionsFundingLabel(usdc, usdt) {
const parts = [];
@@ -1319,7 +1339,9 @@ function refreshPriceSnapshotConditional(){
tickOrderHoldDurations();
}
if(data.order_prices && data.order_prices.length){
paintRealtimePnl(sumOrdersFloatPnl(data.order_prices));
paintRealtimePnlFromSnapshot(data);
} else if (typeof data.options_unrealized_pnl !== "undefined") {
paintRealtimePnlFromSnapshot(data);
}
}).catch(()=>{});
}
+23 -1
View File
@@ -1597,6 +1597,26 @@ function sumOrdersFloatPnl(orders){
});
return found ? total : null;
}
function combineRealtimeFloatPnl(perpTotal, optionsTotal){
let total = 0, found = false;
[perpTotal, optionsTotal].forEach(v=>{
if(v != null && !Number.isNaN(Number(v))){
total += Number(v);
found = true;
}
});
return found ? total : null;
}
function paintRealtimePnlFromSnapshot(data){
if(!data) return;
const perp = data.order_prices && data.order_prices.length
? sumOrdersFloatPnl(data.order_prices)
: null;
const combined = combineRealtimeFloatPnl(perp, data.options_unrealized_pnl);
if(combined !== null || perp !== null || data.options_unrealized_pnl != null){
paintRealtimePnl(combined);
}
}
function formatOptionsFundingLabel(usdc, usdt) {
const parts = [];
@@ -1912,7 +1932,9 @@ function refreshPriceSnapshotConditional(){
{% endif %}
}
if(data.order_prices && data.order_prices.length){
paintRealtimePnl(sumOrdersFloatPnl(data.order_prices));
paintRealtimePnlFromSnapshot(data);
} else if (typeof data.options_unrealized_pnl !== "undefined") {
paintRealtimePnlFromSnapshot(data);
}
}).catch(()=>{});
}