diff --git a/crypto_monitor_okx/.env.example b/crypto_monitor_okx/.env.example
index 2cd4ee2..0fbe210 100644
--- a/crypto_monitor_okx/.env.example
+++ b/crypto_monitor_okx/.env.example
@@ -141,6 +141,8 @@ HEDGE_PLAN_ENABLED=false
HEDGE_PLAN_SHOW_PERP_OPTIONS=true
HEDGE_PLAN_SHOW_OPTIONS_OPTIONS=true
HEDGE_PLAN_LIVE_ORDER=false
+# 永期子模式:true=以期权为主;false=保险模式(页面标题前标识,不可页内切换)
+HEDGE_PLAN_OPTION_PRIMARY=true
HEDGE_PLAN_OPEN_ORDER=options_first
HEDGE_PLAN_ON_PERP_SL_CLOSE_OPTIONS=true
HEDGE_PLAN_ON_PERP_TP_CLOSE_OPTIONS=false
diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py
index e367a31..b1d8bca 100644
--- a/crypto_monitor_okx/app.py
+++ b/crypto_monitor_okx/app.py
@@ -6870,6 +6870,8 @@ def render_main_page(page="trade", embed_mode=None):
hedge_plan_show_options_options=_okx_trade_mode == "options_options",
hedge_plan_oo_close_mode_enabled=os.getenv("HEDGE_PLAN_OO_CLOSE_MODE_ENABLED", "true").lower()
in ("1", "true", "yes", "on"),
+ hedge_plan_option_primary=os.getenv("HEDGE_PLAN_OPTION_PRIMARY", "true").lower()
+ in ("1", "true", "yes", "on"),
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"),
diff --git a/docs/对冲计划-以期权为主.md b/docs/对冲计划-以期权为主.md
index 30948bc..1f1f719 100644
--- a/docs/对冲计划-以期权为主.md
+++ b/docs/对冲计划-以期权为主.md
@@ -1,14 +1,16 @@
# 对冲计划 · 永期「以期权为主」
-> 实现日:2026-08-09 · 在现有永期**保险模式**上增加计划级开关,不新增 `OKX_TRADE_MODE`。
+> 实现日:2026-08-09 · 在现有永期**保险模式**上增加子模式,不新增 `OKX_TRADE_MODE`。
+> 模式由 env `HEDGE_PLAN_OPTION_PRIMARY` 切换(默认 `true`),页面标题前显示标识,不可在页内切换。
## 1. 模式对照
-| | 保险模式(开关关) | 以期权为主(开关开) |
+| | 保险模式(`OPTION_PRIMARY=false`) | 以期权为主(`true`) |
|--|------------------|-------------------|
| UI 做多 | 永续多 + 买 Put | 买 Call + 永续空 |
| UI 做空 | 永续空 + 买 Call | 买 Put + 永续多 |
-| 左卡 | 开仓价 / 张数 / TP / SL | 权利金 / 杠杆 / 比例 / 到期h / 间隔 / 目标点数 |
+| 左卡 | 开仓价 / 张数 / TP / SL | 资金与杠杆 / 选约条件 / 出场条件 三组 |
+| 右卡 | 上永续行情 · 下期权链 | 同上;期权表含杠杆(指数÷卖一),按类型自动匹配 |
| 选约 | 仅实值/平值 | 实/平/虚 + 间隔 + 杠杆门 |
| 开仓 | 受 `HEDGE_PLAN_OPEN_ORDER` | **强制先期权**,成交后**立即市价**开永续(**不挂**交易所 TP/SL) |
| 出场 | 交易所 TP/SL | 相对 K 的点数目标分叉 |
@@ -60,7 +62,7 @@ contracts = perp_eth / contract_size
| `hedge_plan_orders_lib.py` | 路径、开平永续、启动前定仓刷新 |
| `hedge_plan_monitor_lib.py` | `_tick_po_option_primary*` |
| `hedge_plan_register.py` / `hedge_plan_db.py` | preview/start/persist;`options-chain?option_primary&min_hours&strike_interval` |
-| `hedge_plan.js` + `hedge_plan_panel.html` | 开关与左右卡 |
+| `hedge_plan.js` + `hedge_plan_panel.html` | env 模式标识、左三组参数、右上永续/右下期权 |
## 6. 测试
diff --git a/lib/common/static/hedge_plan.js b/lib/common/static/hedge_plan.js
index 7abe93b..2f96525 100644
--- a/lib/common/static/hedge_plan.js
+++ b/lib/common/static/hedge_plan.js
@@ -37,7 +37,7 @@
ooCloseModeEnabled: root.getAttribute("data-oo-close-mode-enabled") !== "0",
ooCloseMode: "close_all",
direction: "long",
- optionPrimary: true,
+ optionPrimary: root.getAttribute("data-option-primary") !== "0",
opLevTouched: false,
opRatioTouched: false,
tradingUsdc: null,
@@ -283,14 +283,9 @@
function syncOptionPrimaryUI() {
const on = isOptionPrimary();
- document.querySelectorAll(".hp-po-mode").forEach(function (b) {
- const v = b.getAttribute("data-option-primary") === "1";
- b.classList.toggle("is-selected", v === on);
- b.classList.toggle("active", v === on);
- });
const ins = $("hp-po-fields-insurance");
const op = $("hp-po-fields-option-primary");
- // 保险模式只显示开仓价/张数/止盈止损;以期权为主只显示权利金等执行参数
+ // 保险模式只显示开仓价/张数/止盈止损;以期权为主显示三组参数(env 切换,页内不可改)
if (ins) {
ins.classList.toggle("hidden", on);
if (on) ins.setAttribute("hidden", "hidden");
@@ -303,48 +298,39 @@
else op.removeAttribute("hidden");
op.style.display = on ? "" : "none";
}
- const otmBtn = document.querySelector(".hp-money-otm");
- if (otmBtn) {
+ const insMoney = $("hp-po-ins-money");
+ if (insMoney) {
+ if (on) {
+ insMoney.setAttribute("hidden", "hidden");
+ insMoney.classList.add("hidden");
+ } else {
+ insMoney.removeAttribute("hidden");
+ insMoney.classList.remove("hidden");
+ }
+ }
+ document.querySelectorAll(".hp-money-otm").forEach(function (otmBtn) {
otmBtn.classList.toggle("hidden", !on);
if (!on) otmBtn.setAttribute("hidden", "hidden");
else otmBtn.removeAttribute("hidden");
- }
+ });
if (!on && state.moneyFilter === "otm") {
state.moneyFilter = "itm";
syncMoneyUI();
}
if (on) applyOpDefaultsFromMoney(false);
- const title = $("hp-po-card-title");
- if (title) title.textContent = on ? "执行参数" : "永续";
- const rightQ = $("hp-po-perp-quote-right");
- if (rightQ) {
- rightQ.classList.toggle("hidden", !on);
- if (!on) rightQ.setAttribute("hidden", "hidden");
- else rightQ.removeAttribute("hidden");
+ const badge = $("hp-po-mode-badge");
+ if (badge) {
+ badge.textContent = on ? "以期权为主" : "保险模式";
+ badge.classList.toggle("is-insurance", !on);
}
+ const title = $("hp-po-card-title");
+ if (title) title.textContent = "执行参数";
const dirLong = document.querySelector('.hp-po-dir[data-dir="long"]');
const dirShort = document.querySelector('.hp-po-dir[data-dir="short"]');
if (dirLong) dirLong.title = on ? "做多=买Call+永续空" : "做多永续";
if (dirShort) dirShort.title = on ? "做空=买Put+永续多" : "做空永续";
}
- function setOptionPrimary(on, forceReload) {
- const next = !!on;
- const changed = next !== isOptionPrimary();
- state.optionPrimary = next;
- if (changed) {
- state.opLevTouched = false;
- state.opRatioTouched = false;
- state.selected = null;
- if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
- }
- syncOptionPrimaryUI();
- if (!changed && !forceReload) return;
- void loadMarket().then(function () {
- return loadChain();
- });
- }
-
function hoursFromExpMs(expMs) {
const n = Number(expMs);
if (!n || Number.isNaN(n)) return null;
@@ -440,6 +426,37 @@
return (Math.round((k / a) * 10) / 10).toFixed(1) + "×";
}
+ /** 永期期权行情杠杆:指数÷卖一(与选约杠杆门一致). */
+ function calcIndexAskLeverage(ask) {
+ const idx = indexPx();
+ const a = Number(ask);
+ if (!(idx > 0) || !(a > 0)) return "—";
+ return (Math.round((idx / a) * 10) / 10).toFixed(1) + "×";
+ }
+
+ function clearPoSelection() {
+ state.selected = null;
+ if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
+ updatePremiumLine();
+ updatePerpPnlHint();
+ }
+
+ /** 以期权为主:按类型/间隔/杠杆自动匹配最近合约. */
+ function autoMatchPoOption(force) {
+ if (!isOptionPrimary()) return;
+ const want = optTypeForDirection(getDirection());
+ const sel = state.selected;
+ const stillOk =
+ !force &&
+ sel &&
+ String(sel.opt_type || "").toUpperCase() === want &&
+ matchesMoneyFilter(sel);
+ if (stillOk) return;
+ const c = pickClosestItmAtm(currentContracts("hp-exp-select"), want);
+ if (c) pickContract(c);
+ else clearPoSelection();
+ }
+
function findAtmStrikeFromRows(rows, idx) {
if (!rows.length) return null;
if (idx == null || Number.isNaN(Number(idx))) return rows[0].strike;
@@ -913,10 +930,6 @@
: "");
const q = $("hp-perp-quote");
if (q) q.innerHTML = quoteHtml;
- const rightQ = $("hp-po-perp-quote-right");
- if (rightQ && isOptionPrimary()) {
- rightQ.innerHTML = "永续行情 · " + quoteHtml;
- }
const contractsInput = $("hp-contracts");
if (contractsInput) {
const step = amtPrec <= 0 ? "1" : String(Math.pow(10, -amtPrec));
@@ -1130,7 +1143,8 @@
const prevInst = state.selected && state.selected.inst_id;
tbody.innerHTML = "";
if (!exp) {
- tbody.innerHTML = '
| 请选择到期日 |
';
+ tbody.innerHTML = '| 请选择到期日 |
';
+ if (isOptionPrimary()) clearPoSelection();
return;
}
const sameType = (exp.contracts || []).filter(function (c) {
@@ -1158,7 +1172,8 @@
(idx ? " · 指数 " + fmt(idx, 2) : "") +
" · 可换到期或点刷新链";
}
- tbody.innerHTML = '| ' + hint + " |
";
+ tbody.innerHTML = '| ' + hint + " |
";
+ if (isOptionPrimary()) clearPoSelection();
return;
}
list.forEach(function (c) {
@@ -1171,6 +1186,8 @@
c.strike +
"" +
moneynessBadge(c) +
+ ' | ' +
+ calcIndexAskLeverage(c.ask) +
' | ' +
fmtPxSz(c.ask, c.ask_sz, c.ask_estimated, c.tick_sz) +
' | ' +
@@ -1191,6 +1208,7 @@
pickContract(c);
});
});
+ autoMatchPoOption(false);
}
function updatePremiumLine() {
@@ -1729,7 +1747,7 @@
b.addEventListener("click", function () {
const m = b.getAttribute("data-money") || "itm";
if (m === "otm" && !isOptionPrimary()) {
- alert("永期保险腿仅允许实值或平值;请先打开「以期权为主」");
+ alert("永期保险腿仅允许实值或平值;请在 env 将 HEDGE_PLAN_OPTION_PRIMARY=true");
return;
}
state.moneyFilter = m === "otm" ? "otm" : m === "atm" ? "atm" : "itm";
@@ -1738,11 +1756,6 @@
renderListStrikes();
});
});
- document.querySelectorAll(".hp-po-mode").forEach(function (b) {
- b.addEventListener("click", function () {
- setOptionPrimary(b.getAttribute("data-option-primary") === "1", true);
- });
- });
if ($("hp-opt-leverage")) {
$("hp-opt-leverage").addEventListener("input", function () {
state.opLevTouched = true;
@@ -1789,6 +1802,13 @@
});
if ($("hp-recommend-opt")) {
$("hp-recommend-opt").addEventListener("click", function () {
+ if (isOptionPrimary()) {
+ autoMatchPoOption(true);
+ if (!state.selected) {
+ alert("当前选约条件下无匹配合约,请调整类型/间隔/杠杆或换到期");
+ }
+ return;
+ }
const want = optTypeForDirection(getDirection());
const c = pickClosestItmAtm(currentContracts("hp-exp-select"), want);
if (!c) {
diff --git a/lib/common/static/instance_theme.css b/lib/common/static/instance_theme.css
index 3749967..ba843ba 100644
--- a/lib/common/static/instance_theme.css
+++ b/lib/common/static/instance_theme.css
@@ -3470,6 +3470,34 @@ html[data-theme="light"] .opt-be-dist-down {
font-size: 0.74rem;
line-height: 1.4;
}
+.hedge-plan-page-wrap .hp-po-mode-badge {
+ display: inline-block;
+ margin-right: 6px;
+ padding: 2px 8px;
+ border-radius: 6px;
+ font-size: 0.78rem;
+ font-weight: 700;
+ letter-spacing: 0.02em;
+ color: #c4b5fd;
+ background: rgba(139, 92, 246, 0.22);
+ border: 1px solid rgba(167, 139, 250, 0.35);
+ vertical-align: middle;
+}
+.hedge-plan-page-wrap .hp-po-mode-badge.is-insurance {
+ color: #93c5fd;
+ background: rgba(59, 130, 246, 0.18);
+ border-color: rgba(96, 165, 250, 0.35);
+}
+.hedge-plan-page-wrap .hp-po-section {
+ margin: 8px 0 10px;
+}
+.hedge-plan-page-wrap .hp-po-section-title {
+ margin: 0 0 6px;
+ font-size: 0.8rem;
+ font-weight: 650;
+ color: #c5cdd9;
+ letter-spacing: 0.02em;
+}
.hedge-plan-page-wrap .hp-po-fields {
display: grid;
grid-template-columns: 1fr 1fr;
@@ -3480,11 +3508,53 @@ html[data-theme="light"] .opt-be-dist-down {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
}
+.hedge-plan-page-wrap .hp-po-fields--section {
+ margin: 0;
+}
+.hedge-plan-page-wrap .hp-po-field--type {
+ grid-column: 1 / -1;
+}
+.hedge-plan-page-wrap .hp-po-type-seg {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+.hedge-plan-page-wrap .hp-po-right-stack {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ min-width: 0;
+ min-height: 0;
+}
+.hedge-plan-page-wrap .hp-po-quote-head {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 4px;
+}
+.hedge-plan-page-wrap .hp-po-ins-money {
+ display: inline-flex;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+.hedge-plan-page-wrap .hp-po-ins-money[hidden],
+.hedge-plan-page-wrap .hp-po-ins-money.hidden {
+ display: none !important;
+}
/* display:grid 会盖掉 [hidden];模式切换必须显式 none */
.hedge-plan-page-wrap .hp-po-fields.hidden,
-.hedge-plan-page-wrap .hp-po-fields[hidden] {
+.hedge-plan-page-wrap .hp-po-fields[hidden],
+.hedge-plan-page-wrap #hp-po-fields-option-primary.hidden,
+.hedge-plan-page-wrap #hp-po-fields-option-primary[hidden] {
display: none !important;
}
+.hedge-plan-page-wrap .hp-strike-table-wrap--6 {
+ max-height: 280px;
+ min-height: 200px;
+ overflow-y: auto;
+ margin-top: 4px;
+ flex: 1 1 auto;
+}
.hedge-plan-page-wrap .hp-po-field {
display: flex;
flex-direction: column;
@@ -3731,6 +3801,7 @@ html[data-theme="light"] .opt-be-dist-down {
margin-top: 4px;
flex: 0 0 auto;
}
+.hedge-plan-page-wrap .opt-chain-lev,
.hedge-plan-page-wrap .hp-oo-table-wrap .opt-chain-lev {
text-align: center;
white-space: nowrap;
@@ -3747,12 +3818,20 @@ html[data-theme="light"] .opt-be-dist-down {
align-items: stretch;
margin-bottom: 28px;
}
-.hedge-plan-page-wrap .options-dual-grid > .card {
+.hedge-plan-page-wrap .options-dual-grid > .card,
+.hedge-plan-page-wrap .hp-po-right-stack > .card {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
}
+.hedge-plan-page-wrap .hp-po-right-stack > .hp-po-perp-quote-card {
+ height: auto;
+ flex: 0 0 auto;
+}
+.hedge-plan-page-wrap .hp-po-right-stack > .hp-opt-card {
+ flex: 1 1 auto;
+}
.hedge-plan-page-wrap .hp-action-row {
margin-top: 10px;
gap: 8px;
diff --git a/lib/env/env_schema.py b/lib/env/env_schema.py
index 2375aa9..ec90c54 100644
--- a/lib/env/env_schema.py
+++ b/lib/env/env_schema.py
@@ -96,6 +96,7 @@ HOT_RELOAD_EXACT = frozenset({
"OKX_TRADE_MODE",
"MAX_ACTIVE_HEDGE_PLANS",
"HEDGE_PLAN_LIVE_ORDER",
+ "HEDGE_PLAN_OPTION_PRIMARY",
"HEDGE_PLAN_OPEN_ORDER",
"HEDGE_PLAN_ON_PERP_SL_CLOSE_OPTIONS",
"HEDGE_PLAN_ON_PERP_TP_CLOSE_OPTIONS",
@@ -154,6 +155,10 @@ SELECT_OPTIONS: dict[str, tuple[tuple[str, str], ...]] = {
("perp_options", "永期对冲"),
("options_options", "期期对冲"),
),
+ "HEDGE_PLAN_OPTION_PRIMARY": (
+ ("true", "以期权为主"),
+ ("false", "保险模式"),
+ ),
}
_SELECT_ALIASES: dict[str, dict[str, str]] = {
diff --git a/lib/env/env_ui_manifest.py b/lib/env/env_ui_manifest.py
index 9dff06e..48205a3 100644
--- a/lib/env/env_ui_manifest.py
+++ b/lib/env/env_ui_manifest.py
@@ -186,6 +186,11 @@ _HEDGE_COMMON_FIELDS: list[tuple[str, str, str]] = [
]
_HEDGE_PO_FIELDS: list[tuple[str, str, str]] = [
+ (
+ "HEDGE_PLAN_OPTION_PRIMARY",
+ "永期模式(以期权为主/保险)",
+ "默认 true=以期权为主;false=保险模式;页面标题前显示标识,不可在页内切换",
+ ),
("HEDGE_PLAN_OPEN_ORDER", "永期开仓顺序", "options_first 或 perp_first"),
("HEDGE_PLAN_ON_PERP_SL_CLOSE_OPTIONS", "永期止损后强制平期权", "保护机制,建议保持 true"),
("HEDGE_PLAN_ON_PERP_TP_CLOSE_OPTIONS", "永期止盈后强制平期权", "默认 false,保险腿不平"),
@@ -263,6 +268,7 @@ _RUNTIME_ENV_DEFAULTS: dict[str, str] = {
"HEDGE_PLAN_OO_BIAS_SPLIT_BY": "budget",
"HEDGE_PLAN_OO_BIAS_RATIO": "0.7",
"HEDGE_PLAN_BUDGET_BUFFER": "0.95",
+ "HEDGE_PLAN_OPTION_PRIMARY": "true",
"HEDGE_PLAN_OPTIONS_MUTUAL_EXCLUSIVE": "true",
"HEDGE_PLAN_MANUAL_COMPLETE_ON_PARTIAL": "true",
}
diff --git a/lib/hedge_plan/templates/hedge_plan_panel.html b/lib/hedge_plan/templates/hedge_plan_panel.html
index 1f115c1..1712324 100644
--- a/lib/hedge_plan/templates/hedge_plan_panel.html
+++ b/lib/hedge_plan/templates/hedge_plan_panel.html
@@ -5,6 +5,7 @@
data-show-perp="{{ '1' if hedge_plan_show_perp_options else '0' }}"
data-show-oo="{{ '1' if hedge_plan_show_options_options else '0' }}"
data-oo-close-mode-enabled="{{ '1' if hedge_plan_oo_close_mode_enabled else '0' }}"
+ data-option-primary="{{ '1' if hedge_plan_option_primary|default(true) else '0' }}"
data-budget-buffer="{{ hedge_plan_budget_buffer | default(0.95) }}"
data-sizing-mode="{{ position_sizing_mode | default('risk') }}"
data-is-full-margin="{{ '1' if position_sizing_mode == 'full_margin' else '0' }}">
@@ -43,31 +44,31 @@
- 执行参数 · ETH 合约账户
+
+ 以期权为主
+ 执行参数
+ · ETH
+ 合约账户
+
规则说明
账户:永续腿走合约账户(USDT);期权腿走期权账户(USDC)。两账户分开下单、资金不互通。
- 保险模式(开关关):做多配 Put、做空配 Call;左填开仓/止盈止损;仅实值/平值;交易所 TP/SL 出场。
- 以期权为主(开关开):做多买 Call+永续空、做空买 Put+永续多;左填权利金/杠杆/比例/目标点数;开仓先期权后市价永续;期权目标验买一且净利>0后双平;永续目标只平永续、期权持有至到期。
+ 模式:在 env HEDGE_PLAN_OPTION_PRIMARY 切换(true=以期权为主 / false=保险模式);标题前标识当前模式。
+ 保险模式:做多配 Put、做空配 Call;左填开仓/止盈止损;仅实值/平值;交易所 TP/SL 出场。
+ 以期权为主:做多买 Call+永续空、做空买 Put+永续多;左分资金/选约/出场三组;右侧上永续下期权(含杠杆),按类型自动匹配;开仓先期权后市价永续。
-
-
-
-
- 加载中…
-
-
-
+
|