diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py index d1b33a7..b034de4 100644 --- a/crypto_monitor_okx/app.py +++ b/crypto_monitor_okx/app.py @@ -6626,22 +6626,36 @@ def render_main_page(page="trade", embed_mode=None): options_funding_usdc = None options_funding_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 and embed_mode != "fragment" ): 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( - exchange_options - ) + _op = options_header_balance_pack(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: options_trading_usdc = None options_funding_usdc = None options_funding_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) key_list = ( 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_trading_usdc=options_trading_usdc, 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, daily_start_capital=DAILY_START_CAPITAL, current_capital=current_capital, @@ -7050,19 +7068,32 @@ def api_account_snapshot(): options_funding_usdc = None options_funding_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: 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( - exchange_options, - force=force_refresh, - ) + _op = options_header_balance_pack(exchange_options, force=force_refresh) + 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: options_trading_usdc = None options_funding_usdc = None options_funding_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) 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_trading_usdc": options_trading_usdc, "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( funding_usdt if _show_perp_funds else None, current_capital if _show_perp_funds else None, diff --git a/lib/env/env_schema.py b/lib/env/env_schema.py index 9d4451d..b62525a 100644 --- a/lib/env/env_schema.py +++ b/lib/env/env_schema.py @@ -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", "保险模式"), diff --git a/lib/env/env_ui_manifest.py b/lib/env/env_ui_manifest.py index afba271..ff09200 100644 --- a/lib/env/env_ui_manifest.py +++ b/lib/env/env_ui_manifest.py @@ -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", diff --git a/lib/exchange/okx_options_lib.py b/lib/exchange/okx_options_lib.py index 8ad7ff7..ea33ba0 100644 --- a/lib/exchange/okx_options_lib.py +++ b/lib/exchange/okx_options_lib.py @@ -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: diff --git a/lib/instance/instance_embed_context_lib.py b/lib/instance/instance_embed_context_lib.py index a9c5507..22704da 100644 --- a/lib/instance/instance_embed_context_lib.py +++ b/lib/instance/instance_embed_context_lib.py @@ -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: diff --git a/lib/instance/templates/embed_boot_scripts.html b/lib/instance/templates/embed_boot_scripts.html index 96cb39f..a289fc5 100644 --- a/lib/instance/templates/embed_boot_scripts.html +++ b/lib/instance/templates/embed_boot_scripts.html @@ -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"){ diff --git a/lib/instance/templates/index.html b/lib/instance/templates/index.html index 3354750..74564c1 100644 --- a/lib/instance/templates/index.html +++ b/lib/instance/templates/index.html @@ -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"){ diff --git a/lib/instance/templates/instance_header_stats.html b/lib/instance/templates/instance_header_stats.html index 6c5083b..15d18d7 100644 --- a/lib/instance/templates/instance_header_stats.html +++ b/lib/instance/templates/instance_header_stats.html @@ -35,11 +35,11 @@ {% if options_enabled %}
期权资金账户
-
{{ options_funding_label(options_funding_usdc) }}
+
{{ options_funding_label(options_funding_usdc, options_funding_usdt, options_funding_eth, options_margin_mode, options_underly|default('ETH')) }}
期权交易账户
-
{{ options_funding_label(options_trading_usdc) }}
+
{{ options_funding_label(options_trading_usdc, options_trading_usdt, options_trading_eth, options_margin_mode, options_underly|default('ETH')) }}
{% endif %}
diff --git a/lib/options/options_hub_lib.py b/lib/options/options_hub_lib.py index 973739b..4e0cdd8 100644 --- a/lib/options/options_hub_lib.py +++ b/lib/options/options_hub_lib.py @@ -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, diff --git a/manual_trading_hub/static/app.js b/manual_trading_hub/static/app.js index 755a13f..610a817 100644 --- a/manual_trading_hub/static/app.js +++ b/manual_trading_hub/static/app.js @@ -3882,27 +3882,59 @@ }; } - function renderStatRow(funding, trading, upnl, kind) { + function renderStatRow(funding, trading, upnl, kind, optMeta) { if (!showAccountPnlPref()) return ""; const isOpt = kind === "options"; const fundLabel = isOpt ? "期权资金账户" : "资金账户"; const tradeLabel = isOpt ? "期权交易账户" : "交易账户"; const pnlLabel = isOpt ? "期权浮盈" : "浮盈合计"; const rowCls = isOpt ? "stat-row stat-row-options" : "stat-row"; + let fundTxt = `${fmt(funding, 2)} U`; + let tradeTxt = `${fmt(trading, 2)} U`; + 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 `
-
${fundLabel}
${fmt(funding, 2)} U
-
${tradeLabel}
${fmt(trading, 2)} U
+
${fundLabel}
${fundTxt}
+
${tradeLabel}
${tradeTxt}
${pnlLabel}
${fmt(upnl, 2)}
`; } + 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) { return renderStatRow(row.funding_usdt, row.trading_usdt, ag.total_unrealized_pnl); } function renderOptionsAccountStatRow(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) { diff --git a/tests/test_instance_display_env_settings.py b/tests/test_instance_display_env_settings.py index 361bc02..ec68a9e 100644 --- a/tests/test_instance_display_env_settings.py +++ b/tests/test_instance_display_env_settings.py @@ -91,6 +91,7 @@ class TestEnvSchema(unittest.TestCase): "OKX_POS_MODE", "POSITION_SIZING_MODE", "TRADE_DIRECTION", + "OKX_OPTIONS_MARGIN_MODE", ): self.assertIn(key, SELECT_OPTIONS) @@ -101,9 +102,19 @@ class TestEnvSchema(unittest.TestCase): self.skipTest("missing okx .env.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"]} - 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.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.assertEqual(by_key["KEY_AUTO_ORDER_ENABLED"]["label"], "关键位自动单") 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(0.0, 50.0), "0.00 USDC") 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__": diff --git a/tests/test_instance_header_stats_lib.py b/tests/test_instance_header_stats_lib.py index 6b7db32..c5a3c46 100644 --- a/tests/test_instance_header_stats_lib.py +++ b/tests/test_instance_header_stats_lib.py @@ -27,17 +27,22 @@ class TestHeaderStatsLib(unittest.TestCase): def test_total_funds_usdt(self): 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( total_funds_usdt(100, 50, options_trading_usdc=0.2, options_trading_usdt=10), 160.2, ) 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(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, 12.5, 0.004321, "coin", "ETH"), + "12.50 USDT / 0.004321 ETH", + ) if __name__ == "__main__":