Add OKX env toggle to show or hide perpetual funds in the header.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -99,6 +99,8 @@ OKX_POSITION_INST_TYPE=SWAP
|
||||
EXCHANGE_DISPLAY_NAME=OKX
|
||||
# 企业微信推送里展示的账户备注
|
||||
# OKX_ACCOUNT_LABEL=
|
||||
# 顶栏是否显示永续资金账户/交易账户(热更);false 时总资金仅计期权侧
|
||||
OKX_SHOW_PERP_FUNDS=true
|
||||
|
||||
# =============================================================================
|
||||
# 期权(主账户 API,与永续子账户 OKX_API_* 分离;修改后须重启 PM2)
|
||||
|
||||
@@ -6587,6 +6587,7 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
minimal_stats_bundle,
|
||||
options_funding_label,
|
||||
profit_loss_ratio_from_trades,
|
||||
show_perp_funds_enabled,
|
||||
total_funds_usdt,
|
||||
trade_records_summary,
|
||||
)
|
||||
@@ -6738,6 +6739,7 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
|
||||
_okx_trade_mode = get_okx_trade_mode()
|
||||
_hedge_mode_on = _okx_trade_mode in ("perp_options", "options_options")
|
||||
_show_perp_funds = show_perp_funds_enabled(exchange_key="okx")
|
||||
template_ctx = dict(
|
||||
page=page,
|
||||
key=key_list,
|
||||
@@ -6749,8 +6751,8 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
rate=rate,
|
||||
profit_loss_ratio=profit_loss_ratio,
|
||||
total_funds=total_funds_usdt(
|
||||
funding_usdt,
|
||||
current_capital,
|
||||
funding_usdt if _show_perp_funds else None,
|
||||
current_capital if _show_perp_funds else None,
|
||||
options_trading_usdc,
|
||||
options_funding_usdc,
|
||||
options_funding_usdt,
|
||||
@@ -6826,6 +6828,7 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
options_funding_label=options_funding_label,
|
||||
exchange_display=EXCHANGE_DISPLAY_NAME,
|
||||
options_enabled=OKX_OPTIONS_ENABLED,
|
||||
show_perp_funds=_show_perp_funds,
|
||||
options_nav_visible=True,
|
||||
okx_trade_mode=_okx_trade_mode,
|
||||
options_open_allowed=_okx_trade_mode == "options",
|
||||
@@ -7025,7 +7028,11 @@ def api_account_snapshot():
|
||||
active_pnl_rows = conn.execute(
|
||||
"SELECT exchange_symbol, symbol, direction FROM order_monitors WHERE status='active'"
|
||||
).fetchall()
|
||||
from lib.instance.instance_embed_context_lib import header_trade_stats_for_window, total_funds_usdt
|
||||
from lib.instance.instance_embed_context_lib import (
|
||||
header_trade_stats_for_window,
|
||||
show_perp_funds_enabled,
|
||||
total_funds_usdt,
|
||||
)
|
||||
|
||||
header_trade_stats = header_trade_stats_for_window(conn, _list_window_from_request(), APP_TZ)
|
||||
conn.close()
|
||||
@@ -7076,16 +7083,18 @@ def api_account_snapshot():
|
||||
unrealized_pnl = merge_unrealized_pnl_components(unrealized_pnl, options_unrealized_pnl)
|
||||
except Exception:
|
||||
options_unrealized_pnl = None
|
||||
_show_perp_funds = show_perp_funds_enabled(exchange_key="okx")
|
||||
return jsonify({
|
||||
"funding_usdt": funding_usdt,
|
||||
"current_capital": current_capital,
|
||||
"show_perp_funds": _show_perp_funds,
|
||||
"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,
|
||||
funding_usdt if _show_perp_funds else None,
|
||||
current_capital if _show_perp_funds else None,
|
||||
options_trading_usdc,
|
||||
options_funding_usdc,
|
||||
options_funding_usdt,
|
||||
|
||||
@@ -68,6 +68,7 @@ Binance / Gate 无期权模块时,第三列最后一格不显示或显示「本
|
||||
| 持仓模式 | 双向 / 单向净持仓等(按所) | 需重启 |
|
||||
| 仓位查询类型 | 仅 OKX:如 SWAP | 需重启 |
|
||||
| 账户备注 | 企业微信推送中显示的交易所备注 | 保存即生效 |
|
||||
| 显示永续资金 | 仅 OKX:关闭后顶栏隐藏永续资金账户与交易账户,总资金仅计期权侧 | 保存即生效 |
|
||||
|
||||
**本卡片不包含**:网页登录账号密码,是否关闭登录校验,中控通信密钥.
|
||||
|
||||
|
||||
Vendored
+6
-1
@@ -89,6 +89,7 @@ HOT_RELOAD_EXACT = frozenset({
|
||||
"HEDGE_PLAN_ENABLED",
|
||||
"HEDGE_PLAN_SHOW_PERP_OPTIONS",
|
||||
"HEDGE_PLAN_SHOW_OPTIONS_OPTIONS",
|
||||
"OKX_SHOW_PERP_FUNDS",
|
||||
"OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED",
|
||||
"OKX_OPTIONS_MAX_ACTIVE_POSITIONS",
|
||||
"OKX_TRADE_MODE",
|
||||
@@ -214,7 +215,11 @@ def _field_type(key: str, value: str) -> str:
|
||||
low = (value or "").strip().lower()
|
||||
if low in ("true", "false"):
|
||||
return "bool"
|
||||
if key.endswith("_ENABLED") or key.startswith("RISK_MOOD_"):
|
||||
if key.endswith("_ENABLED") or key.startswith("RISK_MOOD_") or key in (
|
||||
"OKX_SHOW_PERP_FUNDS",
|
||||
"HEDGE_PLAN_SHOW_PERP_OPTIONS",
|
||||
"HEDGE_PLAN_SHOW_OPTIONS_OPTIONS",
|
||||
):
|
||||
return "bool"
|
||||
try:
|
||||
if "." in low:
|
||||
|
||||
Vendored
+6
@@ -27,6 +27,11 @@ _EXCHANGE_LIVE_FIELDS: dict[str, list[tuple[str, str, str]]] = {
|
||||
("OKX_POS_MODE", "持仓模式", ""),
|
||||
("OKX_POSITION_INST_TYPE", "仓位查询类型", "如 SWAP"),
|
||||
("OKX_ACCOUNT_LABEL", "账户备注", "企业微信推送中显示"),
|
||||
(
|
||||
"OKX_SHOW_PERP_FUNDS",
|
||||
"显示永续资金",
|
||||
"默认开启;关闭后顶栏隐藏永续资金账户与交易账户,总资金仅计期权侧",
|
||||
),
|
||||
],
|
||||
"binance": [
|
||||
("LIVE_TRADING_ENABLED", "开启实盘下单", "关闭时仅走本地流程,不向交易所发单"),
|
||||
@@ -236,6 +241,7 @@ _RUNTIME_ENV_DEFAULTS: dict[str, str] = {
|
||||
"TRANSFER_CCY": "USDT",
|
||||
"HEDGE_PLAN_SHOW_PERP_OPTIONS": "true",
|
||||
"HEDGE_PLAN_SHOW_OPTIONS_OPTIONS": "true",
|
||||
"OKX_SHOW_PERP_FUNDS": "true",
|
||||
"OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED": "true",
|
||||
"OKX_OPTIONS_MAX_ACTIVE_POSITIONS": "0",
|
||||
"OKX_TRADE_MODE": "options",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
@@ -10,6 +11,20 @@ EMBED_STRATEGY_PAGES = frozenset({"strategy", "strategy_trend", "strategy_roll",
|
||||
_WIN_EPS = 1e-9
|
||||
|
||||
|
||||
def env_truthy(raw: str | None, default: bool = False) -> bool:
|
||||
if raw is None or str(raw).strip() == "":
|
||||
return default
|
||||
return str(raw).strip().lower() in ("1", "true", "yes", "on")
|
||||
|
||||
|
||||
def show_perp_funds_enabled(*, exchange_key: str | None = None) -> bool:
|
||||
"""OKX:是否在顶栏显示永续资金账户/交易账户.其他所恒为 True."""
|
||||
ex = (exchange_key or "").strip().lower()
|
||||
if ex and ex != "okx":
|
||||
return True
|
||||
return env_truthy(os.getenv("OKX_SHOW_PERP_FUNDS"), default=True)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EmbedRenderPlan:
|
||||
exchange_capitals: bool
|
||||
|
||||
@@ -1149,8 +1149,23 @@ function setFundsFieldText(field, text){
|
||||
el.innerText = text;
|
||||
});
|
||||
}
|
||||
function applyPerpFundsVisibility(show){
|
||||
const on = show !== false;
|
||||
document.querySelectorAll("[data-perp-funds='1']").forEach((el) => {
|
||||
el.style.display = on ? "" : "none";
|
||||
});
|
||||
}
|
||||
function accountSnapshotFundingMissing(data){
|
||||
if(!data || typeof data !== "object") return true;
|
||||
if(data.show_perp_funds === false){
|
||||
const hasTotal = data.total_funds != null && data.total_funds !== "";
|
||||
const hasOpt =
|
||||
data.options_funding_usdc != null ||
|
||||
data.options_funding_usdt != null ||
|
||||
data.options_trading_usdc != null ||
|
||||
data.options_trading_usdt != null;
|
||||
return !hasTotal && !hasOpt;
|
||||
}
|
||||
const hasFunding = data.funding_usdt != null && data.funding_usdt !== "";
|
||||
const hasTotal = data.total_funds != null && data.total_funds !== "";
|
||||
const hasTrading = data.current_capital != null && data.current_capital !== "";
|
||||
@@ -1159,6 +1174,9 @@ function accountSnapshotFundingMissing(data){
|
||||
let accountSnapshotRetryCount = 0;
|
||||
function applyAccountSnapshot(data){
|
||||
if(!data || typeof data !== "object") return;
|
||||
if(typeof data.show_perp_funds !== "undefined"){
|
||||
applyPerpFundsVisibility(data.show_perp_funds);
|
||||
}
|
||||
if(data.funding_usdt != null && data.funding_usdt !== ""){
|
||||
setFundsFieldText("total-capital", `${Number(data.funding_usdt).toFixed(2)}U`);
|
||||
}
|
||||
|
||||
@@ -1626,8 +1626,23 @@ function setFundsFieldText(field, text){
|
||||
el.innerText = text;
|
||||
});
|
||||
}
|
||||
function applyPerpFundsVisibility(show){
|
||||
const on = show !== false;
|
||||
document.querySelectorAll("[data-perp-funds='1']").forEach((el) => {
|
||||
el.style.display = on ? "" : "none";
|
||||
});
|
||||
}
|
||||
function accountSnapshotFundingMissing(data){
|
||||
if(!data || typeof data !== "object") return true;
|
||||
if(data.show_perp_funds === false){
|
||||
const hasTotal = data.total_funds != null && data.total_funds !== "";
|
||||
const hasOpt =
|
||||
data.options_funding_usdc != null ||
|
||||
data.options_funding_usdt != null ||
|
||||
data.options_trading_usdc != null ||
|
||||
data.options_trading_usdt != null;
|
||||
return !hasTotal && !hasOpt;
|
||||
}
|
||||
const hasFunding = data.funding_usdt != null && data.funding_usdt !== "";
|
||||
const hasTotal = data.total_funds != null && data.total_funds !== "";
|
||||
const hasTrading = data.current_capital != null && data.current_capital !== "";
|
||||
@@ -1636,6 +1651,9 @@ function accountSnapshotFundingMissing(data){
|
||||
let accountSnapshotRetryCount = 0;
|
||||
function applyAccountSnapshot(data){
|
||||
if(!data || typeof data !== "object") return;
|
||||
if(typeof data.show_perp_funds !== "undefined"){
|
||||
applyPerpFundsVisibility(data.show_perp_funds);
|
||||
}
|
||||
if(data.funding_usdt != null && data.funding_usdt !== ""){
|
||||
setFundsFieldText("total-capital", `${Number(data.funding_usdt).toFixed(2)}U`);
|
||||
}
|
||||
|
||||
@@ -38,11 +38,11 @@
|
||||
{% include 'instance_header_stats.html' %}
|
||||
</div>
|
||||
<div class="instance-header-phone-strip instance-phone-only" aria-label="手机资金摘要">
|
||||
<span class="inst-phone-chip">
|
||||
<span class="inst-phone-chip"{% if not (show_perp_funds|default(true)) %} style="display:none"{% endif %} data-perp-funds="1">
|
||||
<em>交易</em>
|
||||
<b data-funds-field="current-capital">{{ funds_fmt(current_capital) }}U</b>
|
||||
</span>
|
||||
<span class="inst-phone-chip">
|
||||
<span class="inst-phone-chip"{% if not (show_perp_funds|default(true)) %} style="display:none"{% endif %} data-perp-funds="1">
|
||||
<em>资金</em>
|
||||
<b data-funds-field="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</b>
|
||||
</span>
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
<div class="label">总资金</div>
|
||||
<div class="value" id="total-funds" data-funds-field="total-funds">{% if total_funds is not none %}{{ funds_fmt(total_funds) }}U{% else %}—{% endif %}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="stat-strip-item"{% if not (show_perp_funds|default(true)) %} style="display:none"{% endif %} data-perp-funds="1">
|
||||
<div class="label">资金账户</div>
|
||||
<div class="value" id="total-capital" data-funds-field="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="stat-strip-item"{% if not (show_perp_funds|default(true)) %} style="display:none"{% endif %} data-perp-funds="1">
|
||||
<div class="label">交易账户</div>
|
||||
<div class="value" id="current-capital" data-funds-field="current-capital">{{ funds_fmt(current_capital) }}U</div>
|
||||
</div>
|
||||
|
||||
@@ -108,6 +108,10 @@ class TestEnvSchema(unittest.TestCase):
|
||||
self.assertEqual(by_key["KEY_AUTO_ORDER_ENABLED"]["label"], "关键位自动单")
|
||||
self.assertEqual(by_key["KEY_AUTO_ORDER_ENABLED"]["type"], "bool")
|
||||
self.assertIn("KEY_AUTO_MIN_PLANNED_RR", by_key)
|
||||
self.assertIn("OKX_SHOW_PERP_FUNDS", by_key)
|
||||
self.assertEqual(by_key["OKX_SHOW_PERP_FUNDS"]["label"], "显示永续资金")
|
||||
self.assertEqual(by_key["OKX_SHOW_PERP_FUNDS"]["type"], "bool")
|
||||
self.assertTrue(by_key["OKX_SHOW_PERP_FUNDS"].get("hot_reload"))
|
||||
|
||||
groups_v = [{"title": "t", "fields": [by_key["TRADE_DIRECTION"]]}]
|
||||
clean, errors = validate_env_updates(groups_v, {"TRADE_DIRECTION": "long_only"})
|
||||
@@ -117,5 +121,22 @@ class TestEnvSchema(unittest.TestCase):
|
||||
self.assertTrue(bad)
|
||||
|
||||
|
||||
class TestShowPerpFunds(unittest.TestCase):
|
||||
def test_okx_default_on(self):
|
||||
from lib.instance.instance_embed_context_lib import show_perp_funds_enabled
|
||||
|
||||
old = os.environ.pop("OKX_SHOW_PERP_FUNDS", None)
|
||||
try:
|
||||
self.assertTrue(show_perp_funds_enabled(exchange_key="okx"))
|
||||
os.environ["OKX_SHOW_PERP_FUNDS"] = "false"
|
||||
self.assertFalse(show_perp_funds_enabled(exchange_key="okx"))
|
||||
self.assertTrue(show_perp_funds_enabled(exchange_key="gate"))
|
||||
finally:
|
||||
if old is None:
|
||||
os.environ.pop("OKX_SHOW_PERP_FUNDS", None)
|
||||
else:
|
||||
os.environ["OKX_SHOW_PERP_FUNDS"] = old
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user