Add env toggles to show/hide perp and options hedge plan tabs.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -125,6 +125,9 @@ OKX_OPTIONS_ALLOW_MARKET_CLOSE=false
|
||||
# 对冲计划(仅 OKX;前端 env「对冲计划」;详见 docs/对冲计划开发方案.md)
|
||||
# =============================================================================
|
||||
HEDGE_PLAN_ENABLED=false
|
||||
# 页面 Tab 显示(默认全部显示,可单独关闭;不影响已有进行中/历史计划)
|
||||
HEDGE_PLAN_SHOW_PERP_OPTIONS=true
|
||||
HEDGE_PLAN_SHOW_OPTIONS_OPTIONS=true
|
||||
HEDGE_PLAN_LIVE_ORDER=false
|
||||
HEDGE_PLAN_OPEN_ORDER=options_first
|
||||
HEDGE_PLAN_ON_PERP_SL_CLOSE_OPTIONS=true
|
||||
|
||||
@@ -6785,6 +6785,10 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
options_nav_visible=True,
|
||||
hedge_plan_enabled=os.getenv("HEDGE_PLAN_ENABLED", "false").lower() in ("1", "true", "yes", "on"),
|
||||
hedge_plan_nav_visible=os.getenv("HEDGE_PLAN_ENABLED", "false").lower() in ("1", "true", "yes", "on"),
|
||||
hedge_plan_show_perp_options=os.getenv("HEDGE_PLAN_SHOW_PERP_OPTIONS", "true").lower()
|
||||
in ("1", "true", "yes", "on"),
|
||||
hedge_plan_show_options_options=os.getenv("HEDGE_PLAN_SHOW_OPTIONS_OPTIONS", "true").lower()
|
||||
in ("1", "true", "yes", "on"),
|
||||
options_trade_budget=OKX_OPTIONS_TRADE_BUDGET_USDC,
|
||||
options_default_underly=OKX_OPTIONS_DEFAULT_UNDERLY,
|
||||
risk_status=risk_status,
|
||||
|
||||
@@ -553,6 +553,8 @@ realized_pnl_total = pnl_option_close - abs(pnl_perp_sl)
|
||||
| 变量 | 前端标签 | 默认 | 控件 | 热更新 | 说明 |
|
||||
|------|----------|------|------|--------|------|
|
||||
| `HEDGE_PLAN_ENABLED` | 启用对冲计划 | false | bool | 热更优先 | 总开关:导航 + API |
|
||||
| `HEDGE_PLAN_SHOW_PERP_OPTIONS` | 显示永期对冲 | true | bool | 热更 | 关则隐藏永期 Tab,不可测算/开仓 |
|
||||
| `HEDGE_PLAN_SHOW_OPTIONS_OPTIONS` | 显示期期对冲 | true | bool | 热更 | 关则隐藏期期 Tab,不可测算/开仓 |
|
||||
| `HEDGE_PLAN_LIVE_ORDER` | 允许对冲真实下单 | false | bool | 热更 | 关则只测算/草稿 |
|
||||
| `HEDGE_PLAN_OPEN_ORDER` | 永期开仓顺序 | options_first | select:`options_first`/`perp_first` | 热更 | 默认先期权后永续 |
|
||||
| `HEDGE_PLAN_ON_PERP_SL_CLOSE_OPTIONS` | 永期止损后强制平期权 | true | bool | 热更 | **保护机制,默认 true** |
|
||||
@@ -577,6 +579,8 @@ realized_pnl_total = pnl_option_close - abs(pnl_perp_sl)
|
||||
```env
|
||||
# --- 对冲计划(仅 OKX;前端 env「对冲计划」) ---
|
||||
HEDGE_PLAN_ENABLED=false
|
||||
HEDGE_PLAN_SHOW_PERP_OPTIONS=true
|
||||
HEDGE_PLAN_SHOW_OPTIONS_OPTIONS=true
|
||||
HEDGE_PLAN_LIVE_ORDER=false
|
||||
HEDGE_PLAN_OPEN_ORDER=options_first
|
||||
HEDGE_PLAN_ON_PERP_SL_CLOSE_OPTIONS=true
|
||||
|
||||
@@ -5,9 +5,22 @@
|
||||
const root = document.getElementById("hedge-plan-root");
|
||||
if (!root) return;
|
||||
|
||||
function flagOn(attr) {
|
||||
return root.getAttribute(attr) !== "0";
|
||||
}
|
||||
|
||||
const showPerp = flagOn("data-show-perp");
|
||||
const showOo = flagOn("data-show-oo");
|
||||
|
||||
function pickDefaultTab() {
|
||||
if (showPerp) return "perp_options";
|
||||
if (showOo) return "options_options";
|
||||
return "active";
|
||||
}
|
||||
|
||||
const state = {
|
||||
tab: "perp_options",
|
||||
mode: "perp_options",
|
||||
tab: pickDefaultTab(),
|
||||
mode: showPerp ? "perp_options" : showOo ? "options_options" : "perp_options",
|
||||
underlying: root.getAttribute("data-default-underly") || "ETH",
|
||||
moneyFilter: "all",
|
||||
chain: null,
|
||||
@@ -103,7 +116,10 @@
|
||||
}
|
||||
|
||||
function syncTabUI() {
|
||||
const tab = state.tab || "perp_options";
|
||||
let tab = state.tab || pickDefaultTab();
|
||||
if (tab === "perp_options" && !showPerp) tab = pickDefaultTab();
|
||||
if (tab === "options_options" && !showOo) tab = pickDefaultTab();
|
||||
state.tab = tab;
|
||||
document.querySelectorAll(".hp-tab").forEach(function (b) {
|
||||
const on = b.getAttribute("data-tab") === tab;
|
||||
b.classList.toggle("active", on);
|
||||
@@ -120,6 +136,16 @@
|
||||
if (tab === "perp_options" || tab === "options_options") {
|
||||
state.mode = tab;
|
||||
}
|
||||
const hint = $("hp-acct-hint");
|
||||
if (hint) {
|
||||
if (tab === "options_options") {
|
||||
hint.textContent = "期期双腿→期权账户";
|
||||
} else if (tab === "perp_options") {
|
||||
hint.textContent = "永续腿→合约账户 · 期权腿→期权账户";
|
||||
} else {
|
||||
hint.textContent = "进行中/历史含永期与期期;显示开关只影响新建测算 Tab";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setGateLine(gates) {
|
||||
@@ -716,7 +742,10 @@
|
||||
function bind() {
|
||||
document.querySelectorAll(".hp-tab").forEach(function (b) {
|
||||
b.addEventListener("click", function () {
|
||||
state.tab = b.getAttribute("data-tab") || "perp_options";
|
||||
const next = b.getAttribute("data-tab") || pickDefaultTab();
|
||||
if (next === "perp_options" && !showPerp) return;
|
||||
if (next === "options_options" && !showOo) return;
|
||||
state.tab = next;
|
||||
syncTabUI();
|
||||
if (state.tab === "perp_options" || state.tab === "options_options") {
|
||||
void loadGates();
|
||||
|
||||
Vendored
+2
@@ -83,6 +83,8 @@ HOT_RELOAD_EXACT = frozenset({
|
||||
"APP_AUTH_DISABLED",
|
||||
"WECHAT_WEBHOOK",
|
||||
"HEDGE_PLAN_ENABLED",
|
||||
"HEDGE_PLAN_SHOW_PERP_OPTIONS",
|
||||
"HEDGE_PLAN_SHOW_OPTIONS_OPTIONS",
|
||||
"HEDGE_PLAN_LIVE_ORDER",
|
||||
"HEDGE_PLAN_OPEN_ORDER",
|
||||
"HEDGE_PLAN_ON_PERP_SL_CLOSE_OPTIONS",
|
||||
|
||||
Vendored
+4
@@ -134,6 +134,8 @@ _HEDGE_PLAN_SECTION: dict[str, Any] = {
|
||||
"exchanges": frozenset({"okx"}),
|
||||
"fields": [
|
||||
("HEDGE_PLAN_ENABLED", "启用对冲计划", "关闭则隐藏导航且不可开仓"),
|
||||
("HEDGE_PLAN_SHOW_PERP_OPTIONS", "显示永期对冲", "默认 true;关闭后隐藏永期 Tab,不可测算/开仓"),
|
||||
("HEDGE_PLAN_SHOW_OPTIONS_OPTIONS", "显示期期对冲", "默认 true;关闭后隐藏期期 Tab,不可测算/开仓"),
|
||||
("HEDGE_PLAN_LIVE_ORDER", "允许对冲真实下单", "再与实盘 LIVE_TRADING_ENABLED 同开才可启动永期"),
|
||||
("HEDGE_PLAN_OPEN_ORDER", "永期开仓顺序", "options_first 或 perp_first"),
|
||||
("HEDGE_PLAN_ON_PERP_SL_CLOSE_OPTIONS", "永期止损后强制平期权", "保护机制,建议保持 true"),
|
||||
@@ -153,6 +155,8 @@ _RUNTIME_ENV_DEFAULTS: dict[str, str] = {
|
||||
"RISK_COOLING_HOURS_MANUAL_JOURNAL": "1",
|
||||
"RISK_MANUAL_CLOSE_DAILY_LIMIT": "2",
|
||||
"RISK_MOOD_ISSUES_DAILY_FREEZE": "true",
|
||||
"HEDGE_PLAN_SHOW_PERP_OPTIONS": "true",
|
||||
"HEDGE_PLAN_SHOW_OPTIONS_OPTIONS": "true",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -334,6 +334,8 @@ def gate_status(
|
||||
live_trading: bool = False,
|
||||
active_count: int = 0,
|
||||
max_active: int = 1,
|
||||
show_perp_options: bool = True,
|
||||
show_options_options: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
from lib.trade.position_sizing_lib import is_full_margin_mode
|
||||
|
||||
@@ -349,6 +351,14 @@ def gate_status(
|
||||
can_preview = False
|
||||
can_start = False
|
||||
reasons.append("期权模块未启用")
|
||||
if pt == "perp_options" and not show_perp_options:
|
||||
can_preview = False
|
||||
can_start = False
|
||||
reasons.append("永期对冲已隐藏(HEDGE_PLAN_SHOW_PERP_OPTIONS)")
|
||||
if pt == "options_options" and not show_options_options:
|
||||
can_preview = False
|
||||
can_start = False
|
||||
reasons.append("期期对冲已隐藏(HEDGE_PLAN_SHOW_OPTIONS_OPTIONS)")
|
||||
if not live_order:
|
||||
can_start = False
|
||||
reasons.append("未允许对冲真实下单(HEDGE_PLAN_LIVE_ORDER)")
|
||||
@@ -379,6 +389,8 @@ def gate_status(
|
||||
"live_trading": live_trading,
|
||||
"active_count": active_count,
|
||||
"max_active": max_active,
|
||||
"show_perp_options": bool(show_perp_options),
|
||||
"show_options_options": bool(show_options_options),
|
||||
"can_preview": can_preview,
|
||||
"can_start": can_start,
|
||||
"reasons": reasons,
|
||||
|
||||
@@ -106,6 +106,14 @@ def _hedge_enabled() -> bool:
|
||||
return _env_bool("HEDGE_PLAN_ENABLED", False)
|
||||
|
||||
|
||||
def _show_perp_options() -> bool:
|
||||
return _env_bool("HEDGE_PLAN_SHOW_PERP_OPTIONS", True)
|
||||
|
||||
|
||||
def _show_options_options() -> bool:
|
||||
return _env_bool("HEDGE_PLAN_SHOW_OPTIONS_OPTIONS", True)
|
||||
|
||||
|
||||
def _live_order() -> bool:
|
||||
return _env_bool("HEDGE_PLAN_LIVE_ORDER", False)
|
||||
|
||||
@@ -140,6 +148,8 @@ def _gates_dict(cfg: dict[str, Any], plan_type: str) -> dict[str, Any]:
|
||||
live_trading=bool(cfg.get("live_trading")) or _env_bool("LIVE_TRADING_ENABLED", False),
|
||||
active_count=active,
|
||||
max_active=_max_active(),
|
||||
show_perp_options=_show_perp_options(),
|
||||
show_options_options=_show_options_options(),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
data-default-underly="{{ options_default_underly | default('ETH') }}"
|
||||
data-hedge-enabled="{{ '1' if hedge_plan_enabled else '0' }}"
|
||||
data-options-enabled="{{ '1' if options_enabled else '0' }}"
|
||||
data-show-perp="{{ '1' if hedge_plan_show_perp_options | default(true) else '0' }}"
|
||||
data-show-oo="{{ '1' if hedge_plan_show_options_options | default(true) else '0' }}"
|
||||
data-sizing-mode="{{ position_sizing_mode | default('risk') }}"
|
||||
data-is-full-margin="{{ '1' if position_sizing_mode == 'full_margin' else '0' }}">
|
||||
{% if not hedge_plan_enabled %}
|
||||
@@ -10,6 +12,9 @@
|
||||
{% if not options_enabled %}
|
||||
<div class="flash" style="margin-bottom:12px">期权模块未启用,无法拉期权链.请先配置期权账户.</div>
|
||||
{% endif %}
|
||||
{% if hedge_plan_enabled and not (hedge_plan_show_perp_options | default(true)) and not (hedge_plan_show_options_options | default(true)) %}
|
||||
<div class="flash" style="margin-bottom:12px">永期与期期 Tab 均已隐藏:可在 <code>env配置 → 对冲计划</code> 打开显示开关;进行中/历史仍可查看.</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card hp-head-card">
|
||||
<div class="hp-head-row">
|
||||
@@ -19,8 +24,12 @@
|
||||
<button type="button" class="btn-secondary" id="hp-refresh" title="刷新永续行情与期权链">刷新行情</button>
|
||||
</div>
|
||||
<div class="hp-tabs" role="tablist" aria-label="对冲计划分类">
|
||||
<button type="button" class="hp-tab active" role="tab" aria-selected="true" data-tab="perp_options">永期对冲</button>
|
||||
{% if hedge_plan_show_perp_options | default(true) %}
|
||||
<button type="button" class="hp-tab" role="tab" aria-selected="false" data-tab="perp_options">永期对冲</button>
|
||||
{% endif %}
|
||||
{% if hedge_plan_show_options_options | default(true) %}
|
||||
<button type="button" class="hp-tab" role="tab" aria-selected="false" data-tab="options_options">期期对冲</button>
|
||||
{% endif %}
|
||||
<button type="button" class="hp-tab" role="tab" aria-selected="false" data-tab="active">进行中的计划</button>
|
||||
<button type="button" class="hp-tab" role="tab" aria-selected="false" data-tab="history">历史记录</button>
|
||||
<button type="button" class="hp-tab" role="tab" aria-selected="false" data-tab="stats">统计分析</button>
|
||||
@@ -255,4 +264,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/static/hedge_plan.js?v=13"></script>
|
||||
<script src="/static/hedge_plan.js?v=14"></script>
|
||||
|
||||
@@ -69,6 +69,32 @@ class TestHedgePlanCalc(unittest.TestCase):
|
||||
self.assertFalse(g["can_start"])
|
||||
self.assertTrue(any("全仓" in r for r in g["reasons"]))
|
||||
|
||||
def test_gate_hidden_plan_type_blocks_preview(self):
|
||||
g = gate_status(
|
||||
hedge_enabled=True,
|
||||
sizing_mode="full_margin",
|
||||
plan_type="perp_options",
|
||||
options_enabled=True,
|
||||
live_order=True,
|
||||
live_trading=True,
|
||||
show_perp_options=False,
|
||||
)
|
||||
self.assertFalse(g["can_preview"])
|
||||
self.assertFalse(g["can_start"])
|
||||
self.assertTrue(any("隐藏" in r for r in g["reasons"]))
|
||||
self.assertFalse(g["show_perp_options"])
|
||||
self.assertTrue(g["show_options_options"])
|
||||
|
||||
g2 = gate_status(
|
||||
hedge_enabled=True,
|
||||
sizing_mode="risk",
|
||||
plan_type="options_options",
|
||||
options_enabled=True,
|
||||
show_options_options=False,
|
||||
)
|
||||
self.assertFalse(g2["can_preview"])
|
||||
self.assertTrue(any("期期" in r for r in g2["reasons"]))
|
||||
|
||||
def test_oo_expiry_loss_flag(self):
|
||||
a = {"opt_type": "C", "strike": 3300, "sheets": 1, "ct_mult": 0.01, "premium_paid": 5}
|
||||
b = {"opt_type": "P", "strike": 3100, "sheets": 1, "ct_mult": 0.01, "premium_paid": 5}
|
||||
|
||||
Reference in New Issue
Block a user