From 45c7e73db51d2a78177e0e440ca837d30e56c839 Mon Sep 17 00:00:00 2001 From: dekun Date: Tue, 25 Aug 2026 09:36:13 +0800 Subject: [PATCH] Hide coin/USDC options env fields by margin mode. Also toggle close-gate and compound subfields so the settings page only shows relevant controls. Co-authored-by: Cursor --- lib/common/static/instance_settings_prefs.js | 113 ++++++++++++++++--- lib/env/env_ui_manifest.py | 75 ++++++++++-- lib/instance/templates/embed_shell.html | 2 +- lib/instance/templates/index.html | 2 +- 4 files changed, 162 insertions(+), 30 deletions(-) diff --git a/lib/common/static/instance_settings_prefs.js b/lib/common/static/instance_settings_prefs.js index e8d15e6..362ab9f 100644 --- a/lib/common/static/instance_settings_prefs.js +++ b/lib/common/static/instance_settings_prefs.js @@ -351,7 +351,7 @@ body.appendChild(panelsWrap); body.dataset.envModeSectionIdx = String(modeSectionIdx); bindTradeModeAutoRefresh(body); - bindCompoundBudgetVisibility(body); + bindOptionsSectionVisibility(body); return body; } @@ -363,27 +363,104 @@ return input ? input.closest(".env-field-row") : null; } - function syncCompoundBudgetVisibility(body) { + function envFieldValue(body, key, fallback) { + const input = body && body.querySelector('.env-field-input[data-env-key="' + key + '"]'); + if (!input) return fallback; + return String(input.value || fallback || "").trim(); + } + + function envTruthy(v) { + return ["1", "true", "yes", "on"].indexOf(String(v || "").toLowerCase()) >= 0; + } + + function setEnvRowHidden(row, hide) { + if (!row) return; + row.hidden = !!hide; + row.style.display = hide ? "none" : ""; + } + + var COIN_ONLY_ENV_KEYS = [ + "OKX_OPTIONS_COIN_COMPOUND", + "OKX_OPTIONS_COIN_BUDGET_USDT", + "OKX_OPTIONS_COIN_MAX_USDT_ENABLED", + "OKX_OPTIONS_COIN_MAX_USDT", + "OKX_OPTIONS_COIN_SPOT_BUY_BUFFER", + "OKX_OPTIONS_CLOSE_RECYCLE_MULT_COIN", + ]; + var USDC_ONLY_ENV_KEYS = [ + "OKX_OPTIONS_TRADE_BUDGET_USDC", + "OKX_OPTIONS_COMPOUND_FULL_ENABLED", + "OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED", + "OKX_OPTIONS_COMPOUND_FULL_CAP_USDC", + "OKX_OPTIONS_CLOSE_RECYCLE_MULT", + ]; + + function syncOptionsSectionVisibility(body) { if (!body) return; - const compoundSel = body.querySelector( - '.env-field-input[data-env-key="OKX_OPTIONS_COMPOUND_FULL_ENABLED"]' - ); - const budgetRow = envFieldRowByKey(body, "OKX_OPTIONS_TRADE_BUDGET_USDC"); - if (!budgetRow) return; - const compoundOn = !compoundSel || String(compoundSel.value || "").toLowerCase() === "true"; - budgetRow.hidden = compoundOn; - budgetRow.style.display = compoundOn ? "none" : ""; + var margin = (envFieldValue(body, "OKX_OPTIONS_MARGIN_MODE", "coin") || "coin").toLowerCase(); + var isCoin = margin !== "usdc"; + var gateMode = (envFieldValue(body, "OKX_OPTIONS_CLOSE_GATE_MODE", "premium") || "premium").toLowerCase(); + var compoundOn = envTruthy(envFieldValue(body, "OKX_OPTIONS_COMPOUND_FULL_ENABLED", "true")); + var coinCompoundOn = envTruthy(envFieldValue(body, "OKX_OPTIONS_COIN_COMPOUND", "true")); + var coinMaxOn = envTruthy(envFieldValue(body, "OKX_OPTIONS_COIN_MAX_USDT_ENABLED", "false")); + var compoundCapOn = envTruthy(envFieldValue(body, "OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED", "false")); + + COIN_ONLY_ENV_KEYS.forEach(function (key) { + setEnvRowHidden(envFieldRowByKey(body, key), !isCoin); + }); + USDC_ONLY_ENV_KEYS.forEach(function (key) { + setEnvRowHidden(envFieldRowByKey(body, key), isCoin); + }); + + // 门控子项:按模式显隐(仍受本位过滤) + if (isCoin) { + setEnvRowHidden(envFieldRowByKey(body, "OKX_OPTIONS_CLOSE_RECYCLE_MULT_COIN"), gateMode !== "premium"); + } else { + setEnvRowHidden(envFieldRowByKey(body, "OKX_OPTIONS_CLOSE_RECYCLE_MULT"), gateMode !== "premium"); + } + setEnvRowHidden(envFieldRowByKey(body, "OKX_OPTIONS_CLOSE_NET_PNL_MIN_USDT"), gateMode !== "net_pnl"); + + if (!isCoin) { + setEnvRowHidden(envFieldRowByKey(body, "OKX_OPTIONS_TRADE_BUDGET_USDC"), compoundOn); + setEnvRowHidden(envFieldRowByKey(body, "OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED"), !compoundOn); + setEnvRowHidden( + envFieldRowByKey(body, "OKX_OPTIONS_COMPOUND_FULL_CAP_USDC"), + !(compoundOn && compoundCapOn) + ); + } + if (isCoin) { + setEnvRowHidden(envFieldRowByKey(body, "OKX_OPTIONS_COIN_BUDGET_USDT"), coinCompoundOn); + setEnvRowHidden(envFieldRowByKey(body, "OKX_OPTIONS_COIN_MAX_USDT"), !coinMaxOn); + } + } + + function bindOptionsSectionVisibility(body) { + if (!body) return; + syncOptionsSectionVisibility(body); + var keys = [ + "OKX_OPTIONS_MARGIN_MODE", + "OKX_OPTIONS_CLOSE_GATE_MODE", + "OKX_OPTIONS_COMPOUND_FULL_ENABLED", + "OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED", + "OKX_OPTIONS_COIN_COMPOUND", + "OKX_OPTIONS_COIN_MAX_USDT_ENABLED", + ]; + keys.forEach(function (key) { + var el = body.querySelector('.env-field-input[data-env-key="' + key + '"]'); + if (!el || el.dataset.optionsVisBound === "1") return; + el.dataset.optionsVisBound = "1"; + el.addEventListener("change", function () { + syncOptionsSectionVisibility(body); + }); + }); + } + + function syncCompoundBudgetVisibility(body) { + syncOptionsSectionVisibility(body); } function bindCompoundBudgetVisibility(body) { - if (!body) return; - syncCompoundBudgetVisibility(body); - const compoundSel = body.querySelector( - '.env-field-input[data-env-key="OKX_OPTIONS_COMPOUND_FULL_ENABLED"]' - ); - if (!compoundSel || compoundSel.dataset.compoundBudgetBound === "1") return; - compoundSel.dataset.compoundBudgetBound = "1"; - compoundSel.addEventListener("change", () => syncCompoundBudgetVisibility(body)); + bindOptionsSectionVisibility(body); } function bindTradeModeAutoRefresh(body) { diff --git a/lib/env/env_ui_manifest.py b/lib/env/env_ui_manifest.py index 57e12f5..efc994d 100644 --- a/lib/env/env_ui_manifest.py +++ b/lib/env/env_ui_manifest.py @@ -480,7 +480,7 @@ def build_env_ui_payload( _build_field(key, label, note, schema, values) for key, label, note in sec["fields"] ] - fields = _mark_compound_budget_hidden(fields) + fields = _mark_options_section_hidden(fields) groups.append({ "title": sec["title"], "fields": fields, @@ -489,18 +489,68 @@ def build_env_ui_payload( return groups -def _mark_compound_budget_hidden(fields: list[dict[str, Any]]) -> list[dict[str, Any]]: - """全仓复利开启时标记单笔预算为 hidden(供 SSR/前端隐藏;切换开关仍可再显示).""" - compound_on = True +# 币本位专属 / USDC 专属(配置页互斥隐藏) +_COIN_ONLY_ENV_KEYS = frozenset({ + "OKX_OPTIONS_COIN_COMPOUND", + "OKX_OPTIONS_COIN_BUDGET_USDT", + "OKX_OPTIONS_COIN_MAX_USDT_ENABLED", + "OKX_OPTIONS_COIN_MAX_USDT", + "OKX_OPTIONS_COIN_SPOT_BUY_BUFFER", + "OKX_OPTIONS_CLOSE_RECYCLE_MULT_COIN", +}) +_USDC_ONLY_ENV_KEYS = frozenset({ + "OKX_OPTIONS_TRADE_BUDGET_USDC", + "OKX_OPTIONS_COMPOUND_FULL_ENABLED", + "OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED", + "OKX_OPTIONS_COMPOUND_FULL_CAP_USDC", + "OKX_OPTIONS_CLOSE_RECYCLE_MULT", +}) + + +def _field_current(fields: list[dict[str, Any]], key: str, default: str = "") -> str: for f in fields: - if f.get("key") == "OKX_OPTIONS_COMPOUND_FULL_ENABLED": - compound_on = _env_truthy(str(f.get("current") or f.get("default") or "true")) - break - if not compound_on: - return fields + if f.get("key") == key: + return str(f.get("current") or f.get("default") or default).strip() + return default + + +def _mark_options_section_hidden(fields: list[dict[str, Any]]) -> list[dict[str, Any]]: + """按本位/门控模式/全仓复利标记 hidden(供 SSR/前端;切换开关仍可再显示).""" + margin = (_field_current(fields, "OKX_OPTIONS_MARGIN_MODE", "coin") or "coin").lower() + is_coin = margin != "usdc" + gate_mode = (_field_current(fields, "OKX_OPTIONS_CLOSE_GATE_MODE", "premium") or "premium").lower() + compound_on = _env_truthy(_field_current(fields, "OKX_OPTIONS_COMPOUND_FULL_ENABLED", "true")) + coin_compound_on = _env_truthy(_field_current(fields, "OKX_OPTIONS_COIN_COMPOUND", "true")) + coin_max_on = _env_truthy(_field_current(fields, "OKX_OPTIONS_COIN_MAX_USDT_ENABLED", "false")) + compound_cap_on = _env_truthy( + _field_current(fields, "OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED", "false") + ) + out: list[dict[str, Any]] = [] for f in fields: - if f.get("key") == "OKX_OPTIONS_TRADE_BUDGET_USDC": + key = str(f.get("key") or "") + hide = False + if is_coin and key in _USDC_ONLY_ENV_KEYS: + hide = True + elif (not is_coin) and key in _COIN_ONLY_ENV_KEYS: + hide = True + elif key == "OKX_OPTIONS_CLOSE_RECYCLE_MULT" and gate_mode != "premium": + hide = True + elif key == "OKX_OPTIONS_CLOSE_RECYCLE_MULT_COIN" and gate_mode != "premium": + hide = True + elif key == "OKX_OPTIONS_CLOSE_NET_PNL_MIN_USDT" and gate_mode != "net_pnl": + hide = True + elif key == "OKX_OPTIONS_TRADE_BUDGET_USDC" and compound_on: + hide = True + elif key == "OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED" and not compound_on: + hide = True + elif key == "OKX_OPTIONS_COMPOUND_FULL_CAP_USDC" and not (compound_on and compound_cap_on): + hide = True + elif key == "OKX_OPTIONS_COIN_BUDGET_USDT" and coin_compound_on: + hide = True + elif key == "OKX_OPTIONS_COIN_MAX_USDT" and not coin_max_on: + hide = True + if hide: item = dict(f) item["hidden"] = True out.append(item) @@ -509,6 +559,11 @@ def _mark_compound_budget_hidden(fields: list[dict[str, Any]]) -> list[dict[str, return out +def _mark_compound_budget_hidden(fields: list[dict[str, Any]]) -> list[dict[str, Any]]: + """兼容旧调用名.""" + return _mark_options_section_hidden(fields) + + def filter_updates_for_ui(exchange_key: str, updates: dict[str, str]) -> dict[str, str]: allowed = ui_allowed_keys(exchange_key) return {k: v for k, v in (updates or {}).items() if k in allowed} diff --git a/lib/instance/templates/embed_shell.html b/lib/instance/templates/embed_shell.html index 5c729ca..efb7378 100644 --- a/lib/instance/templates/embed_shell.html +++ b/lib/instance/templates/embed_shell.html @@ -155,7 +155,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj - + diff --git a/lib/instance/templates/index.html b/lib/instance/templates/index.html index cf396d1..2e64eb5 100644 --- a/lib/instance/templates/index.html +++ b/lib/instance/templates/index.html @@ -2097,6 +2097,6 @@ document.addEventListener("DOMContentLoaded", function () { }); {% endif %} - + \ No newline at end of file