diff --git a/lib/common/static/hedge_plan.js b/lib/common/static/hedge_plan.js index 6576ba3..4c35a65 100644 --- a/lib/common/static/hedge_plan.js +++ b/lib/common/static/hedge_plan.js @@ -506,46 +506,50 @@ return Number.isFinite(eth) && eth > 0 ? eth : 5; } - /** 按交易账户 USDT(买币+5%滑点)与合约账户永续杠杆算最大期权张数. */ + /** + * 期权开仓最大张数:OKX 交易账户 USDT 共用. + * 每张开仓需:买币(权利金×1.05×指数) + 永续保证金(对冲名义÷杠杆); + * 选最大 N 时须保证扣完买币后剩余仍够永续保证金. + */ function computeMaxSheets() { const ask = state.selected && Number(state.selected.ask); const ct = Number((state.selected && state.selected.ct_mult) || 0.01); const ratio = numInput("hp-opt-perp-ratio", 2); - const cs = Number((state.market && state.market.contract_size) || 0.01); - const mark = Number((state.market && (state.market.mark || state.market.index_px)) || indexPx() || 0); + const mark = Number( + (state.market && (state.market.mark || state.market.index_px)) || indexPx() || 0 + ); const lev = defaultPerpLeverage(state.underlying); - const tradingUsdt = Number(state.tradingUsdt != null ? state.tradingUsdt : state.tradingUsdc); - const perpAvail = Number((state.market && state.market.available_usdt) || 0); - if (!(ask > 0) || !(ct > 0) || !(ratio > 0) || !(cs > 0) || !(mark > 0) || !(lev > 0)) { + const tradingUsdt = Number( + state.tradingUsdt != null ? state.tradingUsdt : state.tradingUsdc + ); + if (!(ask > 0) || !(ct > 0) || !(ratio > 0) || !(mark > 0) || !(lev > 0)) { return null; } - // 买币:权利金币×1.05×指数 ≈ 所需 USDT - const coinPerSheet = ask * ct; - const usdtPerSheet = coinPerSheet * 1.05 * mark; - let maxByOpt = Infinity; - if (tradingUsdt > 0 && usdtPerSheet > 0) { - maxByOpt = Math.floor(tradingUsdt / usdtPerSheet + 1e-12); + if (!(tradingUsdt > 0)) { + return { + max_sheets: 0, + lev: lev, + trading_usdt: tradingUsdt, + buy_usdt_per_sheet: 0, + margin_per_sheet: 0, + total_per_sheet: 0, + }; } - // 永续:名义 = 保证金×杠杆; 永续币 = 期权币/比例; 合约张 = 永续币/cs - // 期权张 n → 永续名义 = (n*ct/ratio)*mark ≤ avail*lev - let maxByPerp = Infinity; - if (perpAvail > 0) { - const maxPerpNotional = perpAvail * lev; - const notionalPerSheet = (ct / ratio) * mark; - if (notionalPerSheet > 0) { - maxByPerp = Math.floor(maxPerpNotional / notionalPerSheet + 1e-12); - } - } - const maxSheets = Math.max(0, Math.min(maxByOpt, maxByPerp)); - if (!(maxSheets > 0) && !(tradingUsdt > 0) && !(perpAvail > 0)) return null; + // 买币 USDT/张 = 权利金币 × 1.05 × 指数 + const buyUsdtPerSheet = ask * ct * 1.05 * mark; + // 永续名义/张 = (期权币/比例)×指数; 保证金 = 名义÷杠杆 + const perpNotionalPerSheet = (ct / ratio) * mark; + const marginPerSheet = perpNotionalPerSheet / lev; + const totalPerSheet = buyUsdtPerSheet + marginPerSheet; + if (!(totalPerSheet > 0)) return null; + const maxSheets = Math.max(0, Math.floor(tradingUsdt / totalPerSheet + 1e-12)); return { max_sheets: maxSheets, - max_by_opt: Number.isFinite(maxByOpt) ? maxByOpt : null, - max_by_perp: Number.isFinite(maxByPerp) ? maxByPerp : null, lev: lev, trading_usdt: tradingUsdt, - perp_avail: perpAvail, - usdt_per_sheet: usdtPerSheet, + buy_usdt_per_sheet: buyUsdtPerSheet, + margin_per_sheet: marginPerSheet, + total_per_sheet: totalPerSheet, }; } @@ -556,23 +560,37 @@ const sized = currentOpSizing(); const coin = (state.underlying || "ETH").toUpperCase(); const lev = defaultPerpLeverage(state.underlying); + const mark = Number( + (state.market && (state.market.mark || state.market.index_px)) || indexPx() || 0 + ); if (maxEl) { if (maxInfo) { maxEl.innerHTML = - "最大约 " + + "期权开仓最大 " + maxInfo.max_sheets + - " 张 · 永续杠杆 " + - lev + - "x(env) · 交易 USDT " + + " 张 · 交易 USDT " + fmt(maxInfo.trading_usdt, 2) + - " · 合约可用 " + - fmt(maxInfo.perp_avail, 2); + " · 每张约买币 " + + fmt(maxInfo.buy_usdt_per_sheet, 2) + + " + 永续保证金 " + + fmt(maxInfo.margin_per_sheet, 2) + + " (杠杆 " + + lev + + "x)"; } else { - maxEl.textContent = "选择期权并刷新行情后显示最大开仓张数(交易账户买币+永续杠杆)"; + maxEl.textContent = + "选择期权并刷新后显示:期权开仓最大张数(扣买币后剩余须够永续保证金)"; } } if (perpEl) { if (sized) { + const buyUsdt = sized.premium_est * 1.05 * (mark > 0 ? mark : 0); + const marginUsdt = + sized.perp_qty * (mark > 0 ? mark : 0) / (lev > 0 ? lev : 1); + const remain = + Number(state.tradingUsdt != null ? state.tradingUsdt : state.tradingUsdc) - + buyUsdt; + const marginOk = remain + 1e-9 >= marginUsdt; perpEl.innerHTML = "永续自动开 " + fmt(sized.perp_qty, 4) + @@ -584,7 +602,14 @@ fmt(sized.premium_est * 1.05, 4) + " " + coin + - "(含5%滑点)"; + "(≈" + + fmt(buyUsdt, 2) + + " U含5%) · 扣后剩 " + + fmt(remain, 2) + + " U · 永续保证金约 " + + fmt(marginUsdt, 2) + + " U" + + (marginOk ? "" : ' 不足'); } else { perpEl.textContent = "永续开仓 — (填张数并选择期权后按比例自动算)"; }