币本位顶栏资金显示USDT+ETH,单笔期权本位改为下拉选择

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-20 12:38:39 +08:00
parent 1314349fe5
commit a84554e613
12 changed files with 271 additions and 46 deletions
+4
View File
@@ -166,6 +166,10 @@ SELECT_OPTIONS: dict[str, tuple[tuple[str, str], ...]] = {
("perp_options", "永期对冲"),
("options_options", "期期对冲"),
),
"OKX_OPTIONS_MARGIN_MODE": (
("usdc", "USDC(USDⓈ权利金)"),
("coin", "币本位(USDT买币桥)"),
),
"HEDGE_PLAN_OPTION_PRIMARY": (
("true", "以期权为主"),
("false", "保险模式"),
+1 -1
View File
@@ -146,7 +146,7 @@ _OPTIONS_SECTION: dict[str, Any] = {
(
"OKX_OPTIONS_MARGIN_MODE",
"单笔期权本位",
"usdc=USDⓈ权利金(现状);coin=币本位+USDT买币桥。有持仓/半成品桥时勿切换;改后需重启",
"usdc=USDⓈ权利金;coin=币本位+USDT买币桥。有持仓/半成品桥时勿切换;改后需重启",
),
(
"OKX_OPTIONS_TRADE_BUDGET_USDC",
+61 -12
View File
@@ -509,8 +509,8 @@ def fetch_account_balances_by_type(
ex: ccxt.okx,
account_type: str,
) -> tuple[dict[str, float | None], dict[str, float | None]]:
out: dict[str, float | None] = {"USDT": None, "USDC": None, "USDG": None}
avail: dict[str, float | None] = {"USDT": None, "USDC": None, "USDG": None}
out: dict[str, float | None] = {"USDT": None, "USDC": None, "USDG": None, "ETH": None, "BTC": None}
avail: dict[str, float | None] = {"USDT": None, "USDC": None, "USDG": None, "ETH": None, "BTC": None}
try:
bal = ex.fetch_balance(params={"type": account_type})
for c in out:
@@ -525,8 +525,8 @@ def fetch_funding_balances_via_asset_api(
ex: ccxt.okx,
) -> tuple[dict[str, float | None], dict[str, float | None]]:
"""OKX 资金账户余额(GET /api/v5/asset/balances),比 ccxt fetch_balance 更准确."""
out: dict[str, float | None] = {"USDT": None, "USDC": None, "USDG": None}
avail: dict[str, float | None] = {"USDT": None, "USDC": None, "USDG": None}
out: dict[str, float | None] = {"USDT": None, "USDC": None, "USDG": None, "ETH": None, "BTC": None}
avail: dict[str, float | None] = {"USDT": None, "USDC": None, "USDG": None, "ETH": None, "BTC": None}
try:
resp = ex.private_get_asset_balances({})
for row in (resp or {}).get("data") or []:
@@ -620,13 +620,21 @@ def fetch_options_balances(
"funding_usdt": funding.get("USDT"),
"funding_usdc": funding.get("USDC"),
"funding_usdg": funding.get("USDG"),
"funding_eth": funding.get("ETH"),
"funding_btc": funding.get("BTC"),
"funding_usdt_avail": funding_avail.get("USDT"),
"funding_usdc_avail": funding_avail.get("USDC"),
"funding_eth_avail": funding_avail.get("ETH"),
"funding_btc_avail": funding_avail.get("BTC"),
"trading_usdt": trading.get("USDT"),
"trading_usdc": trading.get("USDC"),
"trading_usdg": trading.get("USDG"),
"trading_eth": trading.get("ETH"),
"trading_btc": trading.get("BTC"),
"trading_usdt_avail": trading_avail.get("USDT"),
"trading_usdc_avail": trading_avail.get("USDC"),
"trading_eth_avail": trading_avail.get("ETH"),
"trading_btc_avail": trading_avail.get("BTC"),
}
_OPTIONS_BALANCE_CACHE["updated_at"] = now
_OPTIONS_BALANCE_CACHE["data"] = result
@@ -642,22 +650,63 @@ def options_header_balances(
返回:(trading_usdc, funding_usdc, funding_usdt, trading_usdt)
"""
pack = options_header_balance_pack(ex, force=force)
return (
pack.get("trading_usdc"),
pack.get("funding_usdc"),
pack.get("funding_usdt"),
pack.get("trading_usdt"),
)
def options_header_balance_pack(
ex: ccxt.okx,
*,
force: bool = False,
) -> dict[str, Any]:
"""顶栏/快照用期权资金包(含币本位 ETH/BTC)."""
import os
bal = fetch_options_balances(ex, force=force)
def _round(v: Any) -> float | None:
def _round(v: Any, nd: int = 2) -> float | None:
if v is None:
return None
try:
return round(float(v), 2)
return round(float(v), nd)
except (TypeError, ValueError):
return None
return (
_round(bal.get("trading_usdc")),
_round(bal.get("funding_usdc")),
_round(bal.get("funding_usdt")),
_round(bal.get("trading_usdt")),
)
def _round_coin(v: Any) -> float | None:
if v is None:
return None
try:
return round(float(v), 8)
except (TypeError, ValueError):
return None
try:
from lib.options.options_margin_mode_lib import normalize_options_margin_mode
margin_mode = normalize_options_margin_mode()
except Exception:
margin_mode = "usdc"
underly = (os.getenv("OKX_OPTIONS_DEFAULT_UNDERLY") or "ETH").strip().upper() or "ETH"
coin_key = "btc" if underly == "BTC" else "eth"
return {
"trading_usdc": _round(bal.get("trading_usdc")),
"funding_usdc": _round(bal.get("funding_usdc")),
"funding_usdt": _round(bal.get("funding_usdt")),
"trading_usdt": _round(bal.get("trading_usdt")),
"funding_eth": _round_coin(bal.get("funding_eth")),
"trading_eth": _round_coin(bal.get("trading_eth")),
"funding_btc": _round_coin(bal.get("funding_btc")),
"trading_btc": _round_coin(bal.get("trading_btc")),
"options_margin_mode": margin_mode,
"options_underly": underly,
"funding_coin": _round_coin(bal.get(f"funding_{coin_key}")),
"trading_coin": _round_coin(bal.get(f"trading_{coin_key}")),
}
def fetch_index_price(ex: ccxt.okx, uly: str) -> float | None:
+31 -2
View File
@@ -103,9 +103,38 @@ def profit_loss_ratio_from_trades(trades: list[dict[str, Any]] | None) -> float
def options_funding_label(
funding_usdc: float | None,
funding_usdt: float | None = None,
funding_eth: float | None = None,
margin_mode: str | None = None,
underly: str = "ETH",
) -> str:
"""期权侧顶栏仅展示 USDC(USDT 归永续资金/交易账户).funding_usdt 参数保留兼容,忽略."""
_ = funding_usdt
"""期权侧顶栏文案.
USDC 模式: USDC.
币本位:USDT + 标的币(ETH/BTC).
"""
try:
from lib.options.options_margin_mode_lib import normalize_options_margin_mode
mode = normalize_options_margin_mode(margin_mode)
except Exception:
mode = str(margin_mode or "usdc").strip().lower() or "usdc"
if mode == "coin":
parts: list[str] = []
if funding_usdt is not None:
try:
parts.append(f"{float(funding_usdt):.2f} USDT")
except (TypeError, ValueError):
pass
coin = funding_eth
ccy = (underly or "ETH").strip().upper() or "ETH"
if coin is not None:
try:
n = float(coin)
txt = f"{n:.6f}".rstrip("0").rstrip(".")
parts.append(f"{txt or '0'} {ccy}")
except (TypeError, ValueError):
pass
return " / ".join(parts) if parts else ""
if funding_usdc is None:
return ""
try:
+33 -6
View File
@@ -1136,8 +1136,23 @@ function paintRealtimePnlFromSnapshot(data){
}
}
function formatOptionsFundingLabel(usdc, usdt) {
// 期权侧顶栏仅 USDC;usdt 参数忽略(USDT 在永续资金/交易账户)
function formatOptionsFundingLabel(usdc, usdt, eth, marginMode, underly) {
const mode = String(marginMode || "usdc").toLowerCase();
if (mode === "coin") {
const parts = [];
if (usdt !== null && usdt !== undefined && usdt !== "") {
const n = Number(usdt);
if (!Number.isNaN(n)) parts.push(`${n.toFixed(2)} USDT`);
}
if (eth !== null && eth !== undefined && eth !== "") {
const n = Number(eth);
if (!Number.isNaN(n)) {
const txt = String(n.toFixed(6)).replace(/\.?0+$/, "");
parts.push(`${txt || "0"} ${String(underly || "ETH").toUpperCase()}`);
}
}
return parts.length ? parts.join(" / ") : "—";
}
if (usdc === null || usdc === undefined || usdc === "") return "—";
const n = Number(usdc);
if (Number.isNaN(n)) return "—";
@@ -1187,12 +1202,24 @@ function applyAccountSnapshot(data){
if(data.current_capital != null && data.current_capital !== "" && !Number.isNaN(Number(data.current_capital))){
setFundsFieldText("current-capital", `${Number(data.current_capital).toFixed(2)}U`);
}
if(data.options_funding_usdc != null || data.options_funding_usdt != null){
const optFunding = formatOptionsFundingLabel(data.options_funding_usdc, data.options_funding_usdt);
if(data.options_funding_usdc != null || data.options_funding_usdt != null || data.options_funding_eth != null){
const optFunding = formatOptionsFundingLabel(
data.options_funding_usdc,
data.options_funding_usdt,
data.options_funding_eth,
data.options_margin_mode,
data.options_underly
);
setFundsFieldText("options-funding-usdc", optFunding);
}
if(data.options_trading_usdc != null || data.options_trading_usdt != null){
const optTrading = formatOptionsFundingLabel(data.options_trading_usdc, data.options_trading_usdt);
if(data.options_trading_usdc != null || data.options_trading_usdt != null || data.options_trading_eth != null){
const optTrading = formatOptionsFundingLabel(
data.options_trading_usdc,
data.options_trading_usdt,
data.options_trading_eth,
data.options_margin_mode,
data.options_underly
);
setFundsFieldText("options-trading-usdc", optTrading);
}
if(typeof data.unrealized_pnl !== "undefined"){
+33 -6
View File
@@ -1617,8 +1617,23 @@ function paintRealtimePnlFromSnapshot(data){
}
}
function formatOptionsFundingLabel(usdc, usdt) {
// 期权侧顶栏仅 USDC;usdt 参数忽略(USDT 在永续资金/交易账户)
function formatOptionsFundingLabel(usdc, usdt, eth, marginMode, underly) {
const mode = String(marginMode || "usdc").toLowerCase();
if (mode === "coin") {
const parts = [];
if (usdt != null && usdt !== "") {
const n = Number(usdt);
if (!Number.isNaN(n)) parts.push(`${n.toFixed(2)} USDT`);
}
if (eth != null && eth !== "") {
const n = Number(eth);
if (!Number.isNaN(n)) {
const txt = String(n.toFixed(6)).replace(/\.?0+$/, "");
parts.push(`${txt || "0"} ${String(underly || "ETH").toUpperCase()}`);
}
}
return parts.length ? parts.join(" / ") : "—";
}
if(usdc == null || usdc === "") return "—";
const n = Number(usdc);
if(Number.isNaN(n)) return "—";
@@ -1668,12 +1683,24 @@ function applyAccountSnapshot(data){
if(data.current_capital != null && data.current_capital !== "" && !Number.isNaN(Number(data.current_capital))){
setFundsFieldText("current-capital", `${Number(data.current_capital).toFixed(2)}U`);
}
if(data.options_funding_usdc != null || data.options_funding_usdt != null){
const optFunding = formatOptionsFundingLabel(data.options_funding_usdc, data.options_funding_usdt);
if(data.options_funding_usdc != null || data.options_funding_usdt != null || data.options_funding_eth != null){
const optFunding = formatOptionsFundingLabel(
data.options_funding_usdc,
data.options_funding_usdt,
data.options_funding_eth,
data.options_margin_mode,
data.options_underly
);
setFundsFieldText("options-funding-usdc", optFunding);
}
if(data.options_trading_usdc != null || data.options_trading_usdt != null){
const optTrading = formatOptionsFundingLabel(data.options_trading_usdc, data.options_trading_usdt);
if(data.options_trading_usdc != null || data.options_trading_usdt != null || data.options_trading_eth != null){
const optTrading = formatOptionsFundingLabel(
data.options_trading_usdc,
data.options_trading_usdt,
data.options_trading_eth,
data.options_margin_mode,
data.options_underly
);
setFundsFieldText("options-trading-usdc", optTrading);
}
if(typeof data.unrealized_pnl !== "undefined"){
@@ -35,11 +35,11 @@
{% if options_enabled %}
<div class="stat-strip-item">
<div class="label">期权资金账户</div>
<div class="value" id="options-funding-usdc" data-funds-field="options-funding-usdc">{{ options_funding_label(options_funding_usdc) }}</div>
<div class="value" id="options-funding-usdc" data-funds-field="options-funding-usdc">{{ options_funding_label(options_funding_usdc, options_funding_usdt, options_funding_eth, options_margin_mode, options_underly|default('ETH')) }}</div>
</div>
<div class="stat-strip-item">
<div class="label">期权交易账户</div>
<div class="value" id="options-trading-usdc" data-funds-field="options-trading-usdc">{{ options_funding_label(options_trading_usdc) }}</div>
<div class="value" id="options-trading-usdc" data-funds-field="options-trading-usdc">{{ options_funding_label(options_trading_usdc, options_trading_usdt, options_trading_eth, options_margin_mode, options_underly|default('ETH')) }}</div>
</div>
{% endif %}
<div class="stat-strip-item stat-strip-item--pnl">
+2
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import os
from typing import Any
@@ -156,6 +157,7 @@ def build_options_hub_snapshot(cfg: dict[str, Any]) -> dict[str, Any]:
"max_active_positions": options_max_active_positions(),
"options_margin_mode": margin_mode,
"options_margin_mode_label": "币本位" if margin_mode == "coin" else "USDC",
"options_underly": (os.getenv("OKX_OPTIONS_DEFAULT_UNDERLY") or "ETH").strip().upper() or "ETH",
"coin_budget": coin_budget,
"bridge_status": bridge_status,
"open_bridges": open_bridges,