diff --git a/crypto_monitor_okx/.env.example b/crypto_monitor_okx/.env.example
index 0fbe210..53a63ba 100644
--- a/crypto_monitor_okx/.env.example
+++ b/crypto_monitor_okx/.env.example
@@ -115,6 +115,10 @@ OKX_OPTIONS_ENABLED=false
OKX_OPTIONS_ACCOUNT_LABEL=账户·期权
OKX_OPTIONS_TRADE_BUDGET_USDC=10
OKX_OPTIONS_BUDGET_BUFFER=0.95
+# 全仓复利:开关控制下单页是否出现该模式;上限开关另控封顶额度
+OKX_OPTIONS_COMPOUND_FULL_ENABLED=true
+OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED=false
+OKX_OPTIONS_COMPOUND_FULL_CAP_USDC=300
# 交易模式三选一(热更):options=单独期权 / perp_options=永期对冲 / options_options=期期对冲
# 选单独期权时隐藏对冲导航与对冲配置;选对冲时不可单独开期权,仓位按「对冲组数上限」
OKX_TRADE_MODE=options
diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py
index 2670968..d1b33a7 100644
--- a/crypto_monitor_okx/app.py
+++ b/crypto_monitor_okx/app.py
@@ -6875,6 +6875,10 @@ def render_main_page(page="trade", embed_mode=None):
hedge_plan_budget_buffer=float(os.getenv("HEDGE_PLAN_BUDGET_BUFFER") or "0.95"),
options_trade_budget=OKX_OPTIONS_TRADE_BUDGET_USDC,
options_budget_buffer=float(os.getenv("OKX_OPTIONS_BUDGET_BUFFER") or "0.95"),
+ options_compound_full_enabled=os.getenv(
+ "OKX_OPTIONS_COMPOUND_FULL_ENABLED", "true"
+ ).lower()
+ in ("1", "true", "yes", "on"),
options_compound_full_cap_enabled=os.getenv(
"OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED", "false"
).lower()
diff --git a/lib/common/static/options_panel.js b/lib/common/static/options_panel.js
index 797e83c..f1a6ca1 100644
--- a/lib/common/static/options_panel.js
+++ b/lib/common/static/options_panel.js
@@ -276,18 +276,36 @@
return el ? el.value : "sheets";
}
+ function compoundFullEnabled() {
+ return !!(root && String(root.dataset.compoundFullEnabled || "1") === "1");
+ }
+
function updateSizeInputs() {
const mode = currentSizeMode();
const sheetsEl = document.getElementById("opt-sheets-amount");
const ethEl = document.getElementById("opt-eth-amount");
const hint = document.getElementById("opt-budget-full-hint");
const compoundHint = document.getElementById("opt-compound-full-hint");
+ const compoundWrap = document.getElementById("opt-size-mode-compound-wrap");
const capEl = document.getElementById("opt-budget-full-cap");
const compoundCapLine = document.getElementById("opt-compound-cap-line");
- if (sheetsEl) sheetsEl.style.display = mode === "sheets" ? "" : "none";
- if (ethEl) ethEl.style.display = mode === "eth_amount" ? "" : "none";
- if (hint) hint.style.display = mode === "budget_full" ? "" : "none";
- if (compoundHint) compoundHint.style.display = mode === "compound_full" ? "" : "none";
+ const compoundOn = compoundFullEnabled();
+ if (compoundWrap) {
+ compoundWrap.hidden = !compoundOn;
+ const radio = compoundWrap.querySelector('input[name="opt-size-mode"]');
+ if (radio) radio.disabled = !compoundOn;
+ if (!compoundOn && mode === "compound_full") {
+ const sheetsRadio = document.querySelector('input[name="opt-size-mode"][value="sheets"]');
+ if (sheetsRadio) {
+ sheetsRadio.checked = true;
+ }
+ }
+ }
+ const modeNow = currentSizeMode();
+ if (sheetsEl) sheetsEl.style.display = modeNow === "sheets" ? "" : "none";
+ if (ethEl) ethEl.style.display = modeNow === "eth_amount" ? "" : "none";
+ if (hint) hint.style.display = modeNow === "budget_full" ? "" : "none";
+ if (compoundHint) compoundHint.style.display = modeNow === "compound_full" && compoundOn ? "" : "none";
if (capEl && root && root.dataset.tradeBudget) {
const n = Number(root.dataset.tradeBudget);
if (Number.isFinite(n) && n > 0) capEl.textContent = n.toFixed(2);
diff --git a/lib/env/env_ui_manifest.py b/lib/env/env_ui_manifest.py
index 739964e..b288608 100644
--- a/lib/env/env_ui_manifest.py
+++ b/lib/env/env_ui_manifest.py
@@ -145,15 +145,20 @@ _OPTIONS_SECTION: dict[str, Any] = {
("OKX_OPTIONS_ACCOUNT_LABEL", "期权账户备注", ""),
("OKX_OPTIONS_TRADE_BUDGET_USDC", "单笔预算(USDC)", ""),
("OKX_OPTIONS_BUDGET_BUFFER", "预算缓冲比例", "如 0.95;打满/全仓复利共用"),
+ (
+ "OKX_OPTIONS_COMPOUND_FULL_ENABLED",
+ "全仓复利开关",
+ "默认 true;开启后下单页出现「全仓复利」模式;关闭则隐藏且拒绝该模式开仓",
+ ),
(
"OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED",
"全仓复利上限开关",
- "默认 false=关闭上限,用期权户全部可用;true 时按下方上限封顶",
+ "仅全仓复利开启时有意义;默认 false=不设上限用期权户全部可用;true 时按下方上限封顶",
),
(
"OKX_OPTIONS_COMPOUND_FULL_CAP_USDC",
"全仓复利上限(USDC)",
- "仅上限开关开启时生效;例如 300",
+ "仅「全仓复利」且「上限开关」都开启时生效;例如 300",
),
(
"OKX_OPTIONS_MAX_ACTIVE_POSITIONS",
diff --git a/lib/options/options_register.py b/lib/options/options_register.py
index 4f175d4..3130237 100644
--- a/lib/options/options_register.py
+++ b/lib/options/options_register.py
@@ -103,6 +103,7 @@ def _build_cfg(app_module: Any) -> dict[str, Any]:
"render_main_page": app_module.render_main_page,
"trade_budget": _env_float("OKX_OPTIONS_TRADE_BUDGET_USDC", 10.0),
"budget_buffer": _env_float("OKX_OPTIONS_BUDGET_BUFFER", 0.95),
+ "compound_full_enabled": _env_bool("OKX_OPTIONS_COMPOUND_FULL_ENABLED", True),
"compound_full_cap_enabled": _env_bool("OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED", False),
"compound_full_cap_usdc": _env_float("OKX_OPTIONS_COMPOUND_FULL_CAP_USDC", 300.0),
"default_underly": (os.getenv("OKX_OPTIONS_DEFAULT_UNDERLY") or "ETH").strip().upper(),
@@ -176,8 +177,14 @@ def _budget_full_usdc(cfg: dict[str, Any], ex: Any) -> tuple[float | None, str]:
return resolve_budget_full_usdc(trading, float(cap)), ""
+def _compound_full_enabled() -> bool:
+ return _env_bool("OKX_OPTIONS_COMPOUND_FULL_ENABLED", True)
+
+
def _compound_full_usdc(cfg: dict[str, Any], ex: Any) -> tuple[float | None, str]:
"""全仓复利 = 期权交易户可用(可选上限封顶);再由 calc_order_size × budget_buffer."""
+ if not _compound_full_enabled():
+ return None, "全仓复利未开启(OKX_OPTIONS_COMPOUND_FULL_ENABLED)"
from lib.exchange.okx_options_lib import fetch_options_trading_usdc
from lib.options.options_pricing_lib import resolve_compound_full_usdc
@@ -472,6 +479,8 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
available_usdc = fetch_options_trading_usdc(ex)
elif mode == "compound_full":
+ if not _compound_full_enabled():
+ return jsonify({"ok": False, "msg": "全仓复利未开启(OKX_OPTIONS_COMPOUND_FULL_ENABLED)"})
budget, budget_err = _compound_full_usdc(cfg, ex)
if budget is None:
return jsonify({"ok": False, "msg": budget_err})
@@ -785,6 +794,8 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
return jsonify({"ok": False, "msg": budget_err})
budget_cap = budget
elif mode == "compound_full":
+ if not _compound_full_enabled():
+ return jsonify({"ok": False, "msg": "全仓复利未开启(OKX_OPTIONS_COMPOUND_FULL_ENABLED)"})
budget, budget_err = _compound_full_usdc(cfg, ex)
if budget is None:
return jsonify({"ok": False, "msg": budget_err})
diff --git a/lib/options/templates/options_panel.html b/lib/options/templates/options_panel.html
index f59d771..bc98b4c 100644
--- a/lib/options/templates/options_panel.html
+++ b/lib/options/templates/options_panel.html
@@ -2,6 +2,7 @@
data-default-underly="{{ options_default_underly | default('ETH') }}"
data-budget-buffer="{{ options_budget_buffer | default(0.95) }}"
data-trade-budget="{{ options_trade_budget | default(10) }}"
+ data-compound-full-enabled="{% if options_compound_full_enabled %}1{% else %}0{% endif %}"
data-compound-cap-enabled="{% if options_compound_full_cap_enabled %}1{% else %}0{% endif %}"
data-compound-cap-usdc="{{ '%.2f'|format(options_compound_full_cap_usdc|default(300)|float) }}"
data-ask-liq-filter="{% if options_chain_ask_liq_filter is defined %}{{ '1' if options_chain_ask_liq_filter else '0' }}{% else %}1{% endif %}">
@@ -134,8 +135,8 @@
按可用余额打满
-