币本位顶栏资金显示USDT+ETH,单笔期权本位改为下拉选择
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6626,22 +6626,36 @@ def render_main_page(page="trade", embed_mode=None):
|
|||||||
options_funding_usdc = None
|
options_funding_usdc = None
|
||||||
options_funding_usdt = None
|
options_funding_usdt = None
|
||||||
options_trading_usdt = None
|
options_trading_usdt = None
|
||||||
|
options_funding_eth = None
|
||||||
|
options_trading_eth = None
|
||||||
|
options_margin_mode = "usdc"
|
||||||
|
options_underly = "ETH"
|
||||||
if (
|
if (
|
||||||
OKX_OPTIONS_ENABLED
|
OKX_OPTIONS_ENABLED
|
||||||
and exchange_options.apiKey
|
and exchange_options.apiKey
|
||||||
and embed_mode != "fragment"
|
and embed_mode != "fragment"
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
from lib.exchange.okx_options_lib import options_header_balances
|
from lib.exchange.okx_options_lib import options_header_balance_pack
|
||||||
|
|
||||||
options_trading_usdc, options_funding_usdc, options_funding_usdt, options_trading_usdt = options_header_balances(
|
_op = options_header_balance_pack(exchange_options)
|
||||||
exchange_options
|
options_trading_usdc = _op.get("trading_usdc")
|
||||||
)
|
options_funding_usdc = _op.get("funding_usdc")
|
||||||
|
options_funding_usdt = _op.get("funding_usdt")
|
||||||
|
options_trading_usdt = _op.get("trading_usdt")
|
||||||
|
options_funding_eth = _op.get("funding_coin")
|
||||||
|
options_trading_eth = _op.get("trading_coin")
|
||||||
|
options_margin_mode = _op.get("options_margin_mode") or "usdc"
|
||||||
|
options_underly = _op.get("options_underly") or "ETH"
|
||||||
except Exception:
|
except Exception:
|
||||||
options_trading_usdc = None
|
options_trading_usdc = None
|
||||||
options_funding_usdc = None
|
options_funding_usdc = None
|
||||||
options_funding_usdt = None
|
options_funding_usdt = None
|
||||||
options_trading_usdt = None
|
options_trading_usdt = None
|
||||||
|
options_funding_eth = None
|
||||||
|
options_trading_eth = None
|
||||||
|
options_margin_mode = "usdc"
|
||||||
|
options_underly = "ETH"
|
||||||
recommended_capital = get_recommended_capital(current_capital)
|
recommended_capital = get_recommended_capital(current_capital)
|
||||||
key_list = (
|
key_list = (
|
||||||
conn.execute("SELECT * FROM key_monitors").fetchall() if plan.key_list else []
|
conn.execute("SELECT * FROM key_monitors").fetchall() if plan.key_list else []
|
||||||
@@ -6793,6 +6807,10 @@ def render_main_page(page="trade", embed_mode=None):
|
|||||||
options_funding_usdt=options_funding_usdt,
|
options_funding_usdt=options_funding_usdt,
|
||||||
options_trading_usdc=options_trading_usdc,
|
options_trading_usdc=options_trading_usdc,
|
||||||
options_trading_usdt=options_trading_usdt,
|
options_trading_usdt=options_trading_usdt,
|
||||||
|
options_funding_eth=options_funding_eth,
|
||||||
|
options_trading_eth=options_trading_eth,
|
||||||
|
options_margin_mode=options_margin_mode,
|
||||||
|
options_underly=options_underly,
|
||||||
trading_day=trading_day,
|
trading_day=trading_day,
|
||||||
daily_start_capital=DAILY_START_CAPITAL,
|
daily_start_capital=DAILY_START_CAPITAL,
|
||||||
current_capital=current_capital,
|
current_capital=current_capital,
|
||||||
@@ -7050,19 +7068,32 @@ def api_account_snapshot():
|
|||||||
options_funding_usdc = None
|
options_funding_usdc = None
|
||||||
options_funding_usdt = None
|
options_funding_usdt = None
|
||||||
options_trading_usdt = None
|
options_trading_usdt = None
|
||||||
|
options_funding_eth = None
|
||||||
|
options_trading_eth = None
|
||||||
|
options_margin_mode = "usdc"
|
||||||
|
options_underly = "ETH"
|
||||||
if OKX_OPTIONS_ENABLED and exchange_options.apiKey:
|
if OKX_OPTIONS_ENABLED and exchange_options.apiKey:
|
||||||
try:
|
try:
|
||||||
from lib.exchange.okx_options_lib import options_header_balances
|
from lib.exchange.okx_options_lib import options_header_balance_pack
|
||||||
|
|
||||||
options_trading_usdc, options_funding_usdc, options_funding_usdt, options_trading_usdt = options_header_balances(
|
_op = options_header_balance_pack(exchange_options, force=force_refresh)
|
||||||
exchange_options,
|
options_trading_usdc = _op.get("trading_usdc")
|
||||||
force=force_refresh,
|
options_funding_usdc = _op.get("funding_usdc")
|
||||||
)
|
options_funding_usdt = _op.get("funding_usdt")
|
||||||
|
options_trading_usdt = _op.get("trading_usdt")
|
||||||
|
options_funding_eth = _op.get("funding_coin")
|
||||||
|
options_trading_eth = _op.get("trading_coin")
|
||||||
|
options_margin_mode = _op.get("options_margin_mode") or "usdc"
|
||||||
|
options_underly = _op.get("options_underly") or "ETH"
|
||||||
except Exception:
|
except Exception:
|
||||||
options_trading_usdc = None
|
options_trading_usdc = None
|
||||||
options_funding_usdc = None
|
options_funding_usdc = None
|
||||||
options_funding_usdt = None
|
options_funding_usdt = None
|
||||||
options_trading_usdt = None
|
options_trading_usdt = None
|
||||||
|
options_funding_eth = None
|
||||||
|
options_trading_eth = None
|
||||||
|
options_margin_mode = "usdc"
|
||||||
|
options_underly = "ETH"
|
||||||
recommended_capital = get_recommended_capital(current_capital)
|
recommended_capital = get_recommended_capital(current_capital)
|
||||||
from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
|
from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
|
||||||
|
|
||||||
@@ -7145,6 +7176,10 @@ def api_account_snapshot():
|
|||||||
"options_funding_usdt": options_funding_usdt,
|
"options_funding_usdt": options_funding_usdt,
|
||||||
"options_trading_usdc": options_trading_usdc,
|
"options_trading_usdc": options_trading_usdc,
|
||||||
"options_trading_usdt": options_trading_usdt,
|
"options_trading_usdt": options_trading_usdt,
|
||||||
|
"options_funding_eth": options_funding_eth,
|
||||||
|
"options_trading_eth": options_trading_eth,
|
||||||
|
"options_margin_mode": options_margin_mode,
|
||||||
|
"options_underly": options_underly,
|
||||||
"total_funds": total_funds_usdt(
|
"total_funds": total_funds_usdt(
|
||||||
funding_usdt if _show_perp_funds else None,
|
funding_usdt if _show_perp_funds else None,
|
||||||
current_capital if _show_perp_funds else None,
|
current_capital if _show_perp_funds else None,
|
||||||
|
|||||||
Vendored
+4
@@ -166,6 +166,10 @@ SELECT_OPTIONS: dict[str, tuple[tuple[str, str], ...]] = {
|
|||||||
("perp_options", "永期对冲"),
|
("perp_options", "永期对冲"),
|
||||||
("options_options", "期期对冲"),
|
("options_options", "期期对冲"),
|
||||||
),
|
),
|
||||||
|
"OKX_OPTIONS_MARGIN_MODE": (
|
||||||
|
("usdc", "USDC(USDⓈ权利金)"),
|
||||||
|
("coin", "币本位(USDT买币桥)"),
|
||||||
|
),
|
||||||
"HEDGE_PLAN_OPTION_PRIMARY": (
|
"HEDGE_PLAN_OPTION_PRIMARY": (
|
||||||
("true", "以期权为主"),
|
("true", "以期权为主"),
|
||||||
("false", "保险模式"),
|
("false", "保险模式"),
|
||||||
|
|||||||
Vendored
+1
-1
@@ -146,7 +146,7 @@ _OPTIONS_SECTION: dict[str, Any] = {
|
|||||||
(
|
(
|
||||||
"OKX_OPTIONS_MARGIN_MODE",
|
"OKX_OPTIONS_MARGIN_MODE",
|
||||||
"单笔期权本位",
|
"单笔期权本位",
|
||||||
"usdc=USDⓈ权利金(现状);coin=币本位+USDT买币桥。有持仓/半成品桥时勿切换;改后需重启",
|
"usdc=USDⓈ权利金;coin=币本位+USDT买币桥。有持仓/半成品桥时勿切换;改后需重启",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"OKX_OPTIONS_TRADE_BUDGET_USDC",
|
"OKX_OPTIONS_TRADE_BUDGET_USDC",
|
||||||
|
|||||||
@@ -509,8 +509,8 @@ def fetch_account_balances_by_type(
|
|||||||
ex: ccxt.okx,
|
ex: ccxt.okx,
|
||||||
account_type: str,
|
account_type: str,
|
||||||
) -> tuple[dict[str, float | None], dict[str, float | None]]:
|
) -> tuple[dict[str, float | None], dict[str, float | None]]:
|
||||||
out: 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}
|
avail: dict[str, float | None] = {"USDT": None, "USDC": None, "USDG": None, "ETH": None, "BTC": None}
|
||||||
try:
|
try:
|
||||||
bal = ex.fetch_balance(params={"type": account_type})
|
bal = ex.fetch_balance(params={"type": account_type})
|
||||||
for c in out:
|
for c in out:
|
||||||
@@ -525,8 +525,8 @@ def fetch_funding_balances_via_asset_api(
|
|||||||
ex: ccxt.okx,
|
ex: ccxt.okx,
|
||||||
) -> tuple[dict[str, float | None], dict[str, float | None]]:
|
) -> tuple[dict[str, float | None], dict[str, float | None]]:
|
||||||
"""OKX 资金账户余额(GET /api/v5/asset/balances),比 ccxt fetch_balance 更准确."""
|
"""OKX 资金账户余额(GET /api/v5/asset/balances),比 ccxt fetch_balance 更准确."""
|
||||||
out: 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}
|
avail: dict[str, float | None] = {"USDT": None, "USDC": None, "USDG": None, "ETH": None, "BTC": None}
|
||||||
try:
|
try:
|
||||||
resp = ex.private_get_asset_balances({})
|
resp = ex.private_get_asset_balances({})
|
||||||
for row in (resp or {}).get("data") or []:
|
for row in (resp or {}).get("data") or []:
|
||||||
@@ -620,13 +620,21 @@ def fetch_options_balances(
|
|||||||
"funding_usdt": funding.get("USDT"),
|
"funding_usdt": funding.get("USDT"),
|
||||||
"funding_usdc": funding.get("USDC"),
|
"funding_usdc": funding.get("USDC"),
|
||||||
"funding_usdg": funding.get("USDG"),
|
"funding_usdg": funding.get("USDG"),
|
||||||
|
"funding_eth": funding.get("ETH"),
|
||||||
|
"funding_btc": funding.get("BTC"),
|
||||||
"funding_usdt_avail": funding_avail.get("USDT"),
|
"funding_usdt_avail": funding_avail.get("USDT"),
|
||||||
"funding_usdc_avail": funding_avail.get("USDC"),
|
"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_usdt": trading.get("USDT"),
|
||||||
"trading_usdc": trading.get("USDC"),
|
"trading_usdc": trading.get("USDC"),
|
||||||
"trading_usdg": trading.get("USDG"),
|
"trading_usdg": trading.get("USDG"),
|
||||||
|
"trading_eth": trading.get("ETH"),
|
||||||
|
"trading_btc": trading.get("BTC"),
|
||||||
"trading_usdt_avail": trading_avail.get("USDT"),
|
"trading_usdt_avail": trading_avail.get("USDT"),
|
||||||
"trading_usdc_avail": trading_avail.get("USDC"),
|
"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["updated_at"] = now
|
||||||
_OPTIONS_BALANCE_CACHE["data"] = result
|
_OPTIONS_BALANCE_CACHE["data"] = result
|
||||||
@@ -642,22 +650,63 @@ def options_header_balances(
|
|||||||
|
|
||||||
返回:(trading_usdc, funding_usdc, funding_usdt, trading_usdt)
|
返回:(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)
|
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:
|
if v is None:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
return round(float(v), 2)
|
return round(float(v), nd)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return (
|
def _round_coin(v: Any) -> float | None:
|
||||||
_round(bal.get("trading_usdc")),
|
if v is None:
|
||||||
_round(bal.get("funding_usdc")),
|
return None
|
||||||
_round(bal.get("funding_usdt")),
|
try:
|
||||||
_round(bal.get("trading_usdt")),
|
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:
|
def fetch_index_price(ex: ccxt.okx, uly: str) -> float | None:
|
||||||
|
|||||||
@@ -103,9 +103,38 @@ def profit_loss_ratio_from_trades(trades: list[dict[str, Any]] | None) -> float
|
|||||||
def options_funding_label(
|
def options_funding_label(
|
||||||
funding_usdc: float | None,
|
funding_usdc: float | None,
|
||||||
funding_usdt: float | None = None,
|
funding_usdt: float | None = None,
|
||||||
|
funding_eth: float | None = None,
|
||||||
|
margin_mode: str | None = None,
|
||||||
|
underly: str = "ETH",
|
||||||
) -> str:
|
) -> 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:
|
if funding_usdc is None:
|
||||||
return "—"
|
return "—"
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1136,8 +1136,23 @@ function paintRealtimePnlFromSnapshot(data){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatOptionsFundingLabel(usdc, usdt) {
|
function formatOptionsFundingLabel(usdc, usdt, eth, marginMode, underly) {
|
||||||
// 期权侧顶栏仅 USDC;usdt 参数忽略(USDT 在永续资金/交易账户)
|
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 "—";
|
if (usdc === null || usdc === undefined || usdc === "") return "—";
|
||||||
const n = Number(usdc);
|
const n = Number(usdc);
|
||||||
if (Number.isNaN(n)) return "—";
|
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))){
|
if(data.current_capital != null && data.current_capital !== "" && !Number.isNaN(Number(data.current_capital))){
|
||||||
setFundsFieldText("current-capital", `${Number(data.current_capital).toFixed(2)}U`);
|
setFundsFieldText("current-capital", `${Number(data.current_capital).toFixed(2)}U`);
|
||||||
}
|
}
|
||||||
if(data.options_funding_usdc != null || data.options_funding_usdt != null){
|
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);
|
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);
|
setFundsFieldText("options-funding-usdc", optFunding);
|
||||||
}
|
}
|
||||||
if(data.options_trading_usdc != null || data.options_trading_usdt != null){
|
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);
|
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);
|
setFundsFieldText("options-trading-usdc", optTrading);
|
||||||
}
|
}
|
||||||
if(typeof data.unrealized_pnl !== "undefined"){
|
if(typeof data.unrealized_pnl !== "undefined"){
|
||||||
|
|||||||
@@ -1617,8 +1617,23 @@ function paintRealtimePnlFromSnapshot(data){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatOptionsFundingLabel(usdc, usdt) {
|
function formatOptionsFundingLabel(usdc, usdt, eth, marginMode, underly) {
|
||||||
// 期权侧顶栏仅 USDC;usdt 参数忽略(USDT 在永续资金/交易账户)
|
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 "—";
|
if(usdc == null || usdc === "") return "—";
|
||||||
const n = Number(usdc);
|
const n = Number(usdc);
|
||||||
if(Number.isNaN(n)) return "—";
|
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))){
|
if(data.current_capital != null && data.current_capital !== "" && !Number.isNaN(Number(data.current_capital))){
|
||||||
setFundsFieldText("current-capital", `${Number(data.current_capital).toFixed(2)}U`);
|
setFundsFieldText("current-capital", `${Number(data.current_capital).toFixed(2)}U`);
|
||||||
}
|
}
|
||||||
if(data.options_funding_usdc != null || data.options_funding_usdt != null){
|
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);
|
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);
|
setFundsFieldText("options-funding-usdc", optFunding);
|
||||||
}
|
}
|
||||||
if(data.options_trading_usdc != null || data.options_trading_usdt != null){
|
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);
|
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);
|
setFundsFieldText("options-trading-usdc", optTrading);
|
||||||
}
|
}
|
||||||
if(typeof data.unrealized_pnl !== "undefined"){
|
if(typeof data.unrealized_pnl !== "undefined"){
|
||||||
|
|||||||
@@ -35,11 +35,11 @@
|
|||||||
{% if options_enabled %}
|
{% if options_enabled %}
|
||||||
<div class="stat-strip-item">
|
<div class="stat-strip-item">
|
||||||
<div class="label">期权资金账户</div>
|
<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>
|
||||||
<div class="stat-strip-item">
|
<div class="stat-strip-item">
|
||||||
<div class="label">期权交易账户</div>
|
<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>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="stat-strip-item stat-strip-item--pnl">
|
<div class="stat-strip-item stat-strip-item--pnl">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
from typing import Any
|
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(),
|
"max_active_positions": options_max_active_positions(),
|
||||||
"options_margin_mode": margin_mode,
|
"options_margin_mode": margin_mode,
|
||||||
"options_margin_mode_label": "币本位" if margin_mode == "coin" else "USDC",
|
"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,
|
"coin_budget": coin_budget,
|
||||||
"bridge_status": bridge_status,
|
"bridge_status": bridge_status,
|
||||||
"open_bridges": open_bridges,
|
"open_bridges": open_bridges,
|
||||||
|
|||||||
@@ -3882,27 +3882,59 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderStatRow(funding, trading, upnl, kind) {
|
function renderStatRow(funding, trading, upnl, kind, optMeta) {
|
||||||
if (!showAccountPnlPref()) return "";
|
if (!showAccountPnlPref()) return "";
|
||||||
const isOpt = kind === "options";
|
const isOpt = kind === "options";
|
||||||
const fundLabel = isOpt ? "期权资金账户" : "资金账户";
|
const fundLabel = isOpt ? "期权资金账户" : "资金账户";
|
||||||
const tradeLabel = isOpt ? "期权交易账户" : "交易账户";
|
const tradeLabel = isOpt ? "期权交易账户" : "交易账户";
|
||||||
const pnlLabel = isOpt ? "期权浮盈" : "浮盈合计";
|
const pnlLabel = isOpt ? "期权浮盈" : "浮盈合计";
|
||||||
const rowCls = isOpt ? "stat-row stat-row-options" : "stat-row";
|
const rowCls = isOpt ? "stat-row stat-row-options" : "stat-row";
|
||||||
|
let fundTxt = `${fmt(funding, 2)} <small style="font-size:12px;color:var(--muted)">U</small>`;
|
||||||
|
let tradeTxt = `${fmt(trading, 2)} <small style="font-size:12px;color:var(--muted)">U</small>`;
|
||||||
|
if (isOpt && optMeta && (optMeta.options_margin_mode === "coin" || optMeta.margin_mode === "coin")) {
|
||||||
|
const underly = String(optMeta.options_underly || "ETH").toUpperCase();
|
||||||
|
fundTxt = formatCoinOptFunds(optMeta, "funding", underly);
|
||||||
|
tradeTxt = formatCoinOptFunds(optMeta, "trading", underly);
|
||||||
|
}
|
||||||
return `<div class="${rowCls}">
|
return `<div class="${rowCls}">
|
||||||
<div class="stat-box"><div class="stat-label">${fundLabel}</div><div class="stat-value">${fmt(funding, 2)} <small style="font-size:12px;color:var(--muted)">U</small></div></div>
|
<div class="stat-box"><div class="stat-label">${fundLabel}</div><div class="stat-value">${fundTxt}</div></div>
|
||||||
<div class="stat-box"><div class="stat-label">${tradeLabel}</div><div class="stat-value">${fmt(trading, 2)} <small style="font-size:12px;color:var(--muted)">U</small></div></div>
|
<div class="stat-box"><div class="stat-label">${tradeLabel}</div><div class="stat-value">${tradeTxt}</div></div>
|
||||||
<div class="stat-box"><div class="stat-label">${pnlLabel}</div><div class="stat-value ${pnlCls(upnl)}">${fmt(upnl, 2)}</div></div>
|
<div class="stat-box"><div class="stat-label">${pnlLabel}</div><div class="stat-value ${pnlCls(upnl)}">${fmt(upnl, 2)}</div></div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatCoinOptFunds(opt, side, underly) {
|
||||||
|
const bal = (opt && opt.balances) || {};
|
||||||
|
const usdt = side === "funding"
|
||||||
|
? (bal.funding_usdt != null ? bal.funding_usdt : opt.funding_usdt)
|
||||||
|
: (bal.trading_usdt != null ? bal.trading_usdt : opt.trading_usdt);
|
||||||
|
let coin = side === "funding"
|
||||||
|
? (bal.funding_eth != null ? bal.funding_eth : bal.funding_btc)
|
||||||
|
: (bal.trading_eth != null ? bal.trading_eth : bal.trading_btc);
|
||||||
|
if (underly === "BTC" && side === "funding" && bal.funding_btc != null) coin = bal.funding_btc;
|
||||||
|
if (underly === "BTC" && side === "trading" && bal.trading_btc != null) coin = bal.trading_btc;
|
||||||
|
const parts = [];
|
||||||
|
if (usdt != null && usdt !== "") {
|
||||||
|
const n = Number(usdt);
|
||||||
|
if (!Number.isNaN(n)) parts.push(`${n.toFixed(2)} USDT`);
|
||||||
|
}
|
||||||
|
if (coin != null && coin !== "") {
|
||||||
|
const n = Number(coin);
|
||||||
|
if (!Number.isNaN(n)) {
|
||||||
|
const txt = String(n.toFixed(6)).replace(/\.?0+$/, "");
|
||||||
|
parts.push(`${txt || "0"} ${underly}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parts.length ? parts.join(" / ") : "—";
|
||||||
|
}
|
||||||
|
|
||||||
function renderAccountStatRow(row, ag) {
|
function renderAccountStatRow(row, ag) {
|
||||||
return renderStatRow(row.funding_usdt, row.trading_usdt, ag.total_unrealized_pnl);
|
return renderStatRow(row.funding_usdt, row.trading_usdt, ag.total_unrealized_pnl);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderOptionsAccountStatRow(opt) {
|
function renderOptionsAccountStatRow(opt) {
|
||||||
const bal = optionsBalanceFields(opt);
|
const bal = optionsBalanceFields(opt);
|
||||||
return renderStatRow(bal.funding, bal.trading, bal.upl, "options");
|
return renderStatRow(bal.funding, bal.trading, bal.upl, "options", opt || {});
|
||||||
}
|
}
|
||||||
|
|
||||||
function shortOptionsInst(instId) {
|
function shortOptionsInst(instId) {
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ class TestEnvSchema(unittest.TestCase):
|
|||||||
"OKX_POS_MODE",
|
"OKX_POS_MODE",
|
||||||
"POSITION_SIZING_MODE",
|
"POSITION_SIZING_MODE",
|
||||||
"TRADE_DIRECTION",
|
"TRADE_DIRECTION",
|
||||||
|
"OKX_OPTIONS_MARGIN_MODE",
|
||||||
):
|
):
|
||||||
self.assertIn(key, SELECT_OPTIONS)
|
self.assertIn(key, SELECT_OPTIONS)
|
||||||
|
|
||||||
@@ -101,9 +102,19 @@ class TestEnvSchema(unittest.TestCase):
|
|||||||
self.skipTest("missing okx .env.example")
|
self.skipTest("missing okx .env.example")
|
||||||
groups = build_env_ui_payload("okx", example, env_path if os.path.isfile(env_path) else example)
|
groups = build_env_ui_payload("okx", example, env_path if os.path.isfile(env_path) else example)
|
||||||
by_key = {f["key"]: f for g in groups for f in g["fields"]}
|
by_key = {f["key"]: f for g in groups for f in g["fields"]}
|
||||||
for key in ("OKX_TD_MODE", "OKX_POS_MODE", "POSITION_SIZING_MODE", "TRADE_DIRECTION"):
|
for key in (
|
||||||
|
"OKX_TD_MODE",
|
||||||
|
"OKX_POS_MODE",
|
||||||
|
"POSITION_SIZING_MODE",
|
||||||
|
"TRADE_DIRECTION",
|
||||||
|
"OKX_OPTIONS_MARGIN_MODE",
|
||||||
|
):
|
||||||
self.assertEqual(by_key[key]["type"], "select")
|
self.assertEqual(by_key[key]["type"], "select")
|
||||||
self.assertTrue(by_key[key]["options"])
|
self.assertTrue(by_key[key]["options"])
|
||||||
|
self.assertEqual(
|
||||||
|
{o["value"] for o in by_key["OKX_OPTIONS_MARGIN_MODE"]["options"]},
|
||||||
|
{"usdc", "coin"},
|
||||||
|
)
|
||||||
self.assertIn("KEY_AUTO_ORDER_ENABLED", by_key)
|
self.assertIn("KEY_AUTO_ORDER_ENABLED", by_key)
|
||||||
self.assertEqual(by_key["KEY_AUTO_ORDER_ENABLED"]["label"], "关键位自动单")
|
self.assertEqual(by_key["KEY_AUTO_ORDER_ENABLED"]["label"], "关键位自动单")
|
||||||
self.assertEqual(by_key["KEY_AUTO_ORDER_ENABLED"]["type"], "bool")
|
self.assertEqual(by_key["KEY_AUTO_ORDER_ENABLED"]["type"], "bool")
|
||||||
@@ -146,6 +157,10 @@ class TestShowPerpFunds(unittest.TestCase):
|
|||||||
self.assertEqual(options_funding_label(12.5, 99.0), "12.50 USDC")
|
self.assertEqual(options_funding_label(12.5, 99.0), "12.50 USDC")
|
||||||
self.assertEqual(options_funding_label(0.0, 50.0), "0.00 USDC")
|
self.assertEqual(options_funding_label(0.0, 50.0), "0.00 USDC")
|
||||||
self.assertEqual(options_funding_label(None, 10.0), "—")
|
self.assertEqual(options_funding_label(None, 10.0), "—")
|
||||||
|
self.assertEqual(
|
||||||
|
options_funding_label(1.0, 20.0, 0.01, "coin", "ETH"),
|
||||||
|
"20.00 USDT / 0.01 ETH",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -27,17 +27,22 @@ class TestHeaderStatsLib(unittest.TestCase):
|
|||||||
|
|
||||||
def test_total_funds_usdt(self):
|
def test_total_funds_usdt(self):
|
||||||
self.assertEqual(total_funds_usdt(100.5, 59.27), 159.77)
|
self.assertEqual(total_funds_usdt(100.5, 59.27), 159.77)
|
||||||
self.assertIsNone(total_funds_usdt(None, 10))
|
self.assertEqual(total_funds_usdt(None, 10), 10.0)
|
||||||
|
self.assertIsNone(total_funds_usdt(None, None))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
total_funds_usdt(100, 50, options_trading_usdc=0.2, options_trading_usdt=10),
|
total_funds_usdt(100, 50, options_trading_usdc=0.2, options_trading_usdt=10),
|
||||||
160.2,
|
160.2,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_options_funding_label(self):
|
def test_options_funding_label(self):
|
||||||
self.assertEqual(options_funding_label(1.5, 10), "1.50 USDC · 10.00 USDT")
|
self.assertEqual(options_funding_label(1.5, 10), "1.50 USDC")
|
||||||
self.assertEqual(options_funding_label(10.19, 0), "10.19 USDC")
|
self.assertEqual(options_funding_label(10.19, 0), "10.19 USDC")
|
||||||
self.assertEqual(options_funding_label(None, 10), "10.00 USDT")
|
self.assertEqual(options_funding_label(None, 10), "—")
|
||||||
self.assertEqual(options_funding_label(None, None), "—")
|
self.assertEqual(options_funding_label(None, None), "—")
|
||||||
|
self.assertEqual(
|
||||||
|
options_funding_label(None, 12.5, 0.004321, "coin", "ETH"),
|
||||||
|
"12.50 USDT / 0.004321 ETH",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user