Fix OKX spot swap tgtCcy and show options trading USDT in header.
OKX requires quote_ccy/base_ccy for market orders; include trading_usdt in balance display and total funds. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6528,6 +6528,7 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
options_trading_usdc = None
|
||||
options_funding_usdc = None
|
||||
options_funding_usdt = None
|
||||
options_trading_usdt = None
|
||||
if (
|
||||
OKX_OPTIONS_ENABLED
|
||||
and exchange_options.apiKey
|
||||
@@ -6536,13 +6537,14 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
try:
|
||||
from lib.exchange.okx_options_lib import options_header_balances
|
||||
|
||||
options_trading_usdc, options_funding_usdc, options_funding_usdt = options_header_balances(
|
||||
options_trading_usdc, options_funding_usdc, options_funding_usdt, options_trading_usdt = options_header_balances(
|
||||
exchange_options
|
||||
)
|
||||
except Exception:
|
||||
options_trading_usdc = None
|
||||
options_funding_usdc = None
|
||||
options_funding_usdt = None
|
||||
options_trading_usdt = None
|
||||
recommended_capital = get_recommended_capital(current_capital)
|
||||
key_list = (
|
||||
conn.execute("SELECT * FROM key_monitors").fetchall() if plan.key_list else []
|
||||
@@ -6675,10 +6677,12 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
options_trading_usdc,
|
||||
options_funding_usdc,
|
||||
options_funding_usdt,
|
||||
options_trading_usdt,
|
||||
),
|
||||
options_funding_usdc=options_funding_usdc,
|
||||
options_funding_usdt=options_funding_usdt,
|
||||
options_trading_usdc=options_trading_usdc,
|
||||
options_trading_usdt=options_trading_usdt,
|
||||
trading_day=trading_day,
|
||||
daily_start_capital=DAILY_START_CAPITAL,
|
||||
current_capital=current_capital,
|
||||
@@ -6887,11 +6891,12 @@ def api_account_snapshot():
|
||||
options_trading_usdc = None
|
||||
options_funding_usdc = None
|
||||
options_funding_usdt = None
|
||||
options_trading_usdt = None
|
||||
if OKX_OPTIONS_ENABLED and exchange_options.apiKey:
|
||||
try:
|
||||
from lib.exchange.okx_options_lib import options_header_balances
|
||||
|
||||
options_trading_usdc, options_funding_usdc, options_funding_usdt = options_header_balances(
|
||||
options_trading_usdc, options_funding_usdc, options_funding_usdt, options_trading_usdt = options_header_balances(
|
||||
exchange_options,
|
||||
force=force_refresh,
|
||||
)
|
||||
@@ -6899,6 +6904,7 @@ def api_account_snapshot():
|
||||
options_trading_usdc = None
|
||||
options_funding_usdc = None
|
||||
options_funding_usdt = None
|
||||
options_trading_usdt = None
|
||||
recommended_capital = get_recommended_capital(current_capital)
|
||||
from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
|
||||
|
||||
@@ -6954,12 +6960,14 @@ def api_account_snapshot():
|
||||
"options_funding_usdc": options_funding_usdc,
|
||||
"options_funding_usdt": options_funding_usdt,
|
||||
"options_trading_usdc": options_trading_usdc,
|
||||
"options_trading_usdt": options_trading_usdt,
|
||||
"total_funds": total_funds_usdt(
|
||||
funding_usdt,
|
||||
current_capital,
|
||||
options_trading_usdc,
|
||||
options_funding_usdc,
|
||||
options_funding_usdt,
|
||||
options_trading_usdt,
|
||||
),
|
||||
"available_trading_usdt": round(available_trading_usdt, FUNDS_DECIMALS) if available_trading_usdt is not None else None,
|
||||
"unrealized_pnl": unrealized_pnl,
|
||||
|
||||
@@ -446,8 +446,8 @@ def options_header_balances(
|
||||
ex: ccxt.okx,
|
||||
*,
|
||||
force: bool = False,
|
||||
) -> tuple[float | None, float | None, float | None]:
|
||||
"""顶栏三格:交易 USDC,资金 USDC,资金 USDT(单次拉取 + 缓存)."""
|
||||
) -> tuple[float | None, float | None, float | None, float | None]:
|
||||
"""顶栏四格:交易 USDC/USDT,资金 USDC/USDT(单次拉取 + 缓存)."""
|
||||
bal = fetch_options_balances(ex, force=force)
|
||||
|
||||
def _round(v: Any) -> float | None:
|
||||
@@ -462,6 +462,7 @@ def options_header_balances(
|
||||
_round(bal.get("trading_usdc")),
|
||||
_round(bal.get("funding_usdc")),
|
||||
_round(bal.get("funding_usdt")),
|
||||
_round(bal.get("trading_usdt")),
|
||||
)
|
||||
|
||||
|
||||
@@ -1110,7 +1111,7 @@ def spot_market_swap_usdt_usdc(
|
||||
"side": "buy",
|
||||
"ordType": "market",
|
||||
"sz": str(amount),
|
||||
"tgtCcy": "quote",
|
||||
"tgtCcy": "quote_ccy",
|
||||
}
|
||||
elif d == "usdc_to_usdt":
|
||||
body = {
|
||||
@@ -1119,7 +1120,7 @@ def spot_market_swap_usdt_usdc(
|
||||
"side": "sell",
|
||||
"ordType": "market",
|
||||
"sz": str(amount),
|
||||
"tgtCcy": "base",
|
||||
"tgtCcy": "base_ccy",
|
||||
}
|
||||
else:
|
||||
return {"ok": False, "msg": "direction 须为 usdt_to_usdc 或 usdc_to_usdt"}
|
||||
|
||||
@@ -103,6 +103,7 @@ def total_funds_usdt(
|
||||
options_trading_usdc: float | None = None,
|
||||
options_funding_usdc: float | None = None,
|
||||
options_funding_usdt: float | None = None,
|
||||
options_trading_usdt: float | None = None,
|
||||
) -> float | None:
|
||||
if funding_usdt is None:
|
||||
return None
|
||||
@@ -114,6 +115,8 @@ def total_funds_usdt(
|
||||
total += float(options_funding_usdt)
|
||||
if options_trading_usdc is not None:
|
||||
total += float(options_trading_usdc)
|
||||
if options_trading_usdt is not None:
|
||||
total += float(options_trading_usdt)
|
||||
return round(total, 2)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
@@ -1168,8 +1168,9 @@ function applyAccountSnapshot(data){
|
||||
const optFunding = formatOptionsFundingLabel(data.options_funding_usdc, data.options_funding_usdt);
|
||||
if(optFunding !== "—") setFundsFieldText("options-funding-usdc", optFunding);
|
||||
}
|
||||
if(data.options_trading_usdc != null && data.options_trading_usdc !== ""){
|
||||
setFundsFieldText("options-trading-usdc", `${Number(data.options_trading_usdc).toFixed(2)} USDC`);
|
||||
if(data.options_trading_usdc != null || data.options_trading_usdt != null){
|
||||
const optTrading = formatOptionsFundingLabel(data.options_trading_usdc, data.options_trading_usdt);
|
||||
if(optTrading !== "—") setFundsFieldText("options-trading-usdc", optTrading);
|
||||
}
|
||||
if(typeof data.unrealized_pnl !== "undefined"){
|
||||
updateRealtimePnl(data.unrealized_pnl);
|
||||
|
||||
@@ -1813,8 +1813,9 @@ function applyAccountSnapshot(data){
|
||||
const optFunding = formatOptionsFundingLabel(data.options_funding_usdc, data.options_funding_usdt);
|
||||
if(optFunding !== "—") setFundsFieldText("options-funding-usdc", optFunding);
|
||||
}
|
||||
if(data.options_trading_usdc != null && data.options_trading_usdc !== ""){
|
||||
setFundsFieldText("options-trading-usdc", `${Number(data.options_trading_usdc).toFixed(2)} USDC`);
|
||||
if(data.options_trading_usdc != null || data.options_trading_usdt != null){
|
||||
const optTrading = formatOptionsFundingLabel(data.options_trading_usdc, data.options_trading_usdt);
|
||||
if(optTrading !== "—") setFundsFieldText("options-trading-usdc", optTrading);
|
||||
}
|
||||
if(typeof data.unrealized_pnl !== "undefined"){
|
||||
updateRealtimePnl(data.unrealized_pnl);
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">期权交易账户</div>
|
||||
<div class="value" id="options-trading-usdc" data-funds-field="options-trading-usdc">{% if options_trading_usdc is not none %}{{ funds_fmt(options_trading_usdc) }} USDC{% else %}—{% endif %}</div>
|
||||
<div class="value" id="options-trading-usdc" data-funds-field="options-trading-usdc">{{ options_funding_label(options_trading_usdc, options_trading_usdt) }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="stat-strip-item stat-strip-item--pnl">
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
import unittest
|
||||
|
||||
from lib.instance.instance_embed_context_lib import (
|
||||
options_funding_label,
|
||||
profit_loss_ratio_from_averages,
|
||||
profit_loss_ratio_from_trades,
|
||||
total_funds_usdt,
|
||||
@@ -27,6 +28,15 @@ 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(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(None, 10), "10.00 USDT")
|
||||
self.assertEqual(options_funding_label(None, None), "—")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"""OKX USDT/USDC 现货市价兑换参数."""
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from lib.exchange.okx_options_lib import spot_market_swap_usdt_usdc
|
||||
|
||||
|
||||
class TestOkxSpotSwap(unittest.TestCase):
|
||||
def test_usdt_to_usdc_uses_quote_ccy(self):
|
||||
ex = MagicMock()
|
||||
ex.private_post_trade_order.return_value = {
|
||||
"data": [{"sCode": "0", "ordId": "1"}],
|
||||
}
|
||||
result = spot_market_swap_usdt_usdc(ex, direction="usdt_to_usdc", amount=10)
|
||||
self.assertTrue(result["ok"])
|
||||
body = ex.private_post_trade_order.call_args[0][0]
|
||||
self.assertEqual(body["tgtCcy"], "quote_ccy")
|
||||
self.assertEqual(body["side"], "buy")
|
||||
|
||||
def test_usdc_to_usdt_uses_base_ccy(self):
|
||||
ex = MagicMock()
|
||||
ex.private_post_trade_order.return_value = {
|
||||
"data": [{"sCode": "0", "ordId": "2"}],
|
||||
}
|
||||
result = spot_market_swap_usdt_usdc(ex, direction="usdc_to_usdt", amount=5)
|
||||
self.assertTrue(result["ok"])
|
||||
body = ex.private_post_trade_order.call_args[0][0]
|
||||
self.assertEqual(body["tgtCcy"], "base_ccy")
|
||||
self.assertEqual(body["side"], "sell")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user