Fix btc_eth dialog sizing labels and buy-coin index.
Use option index for premium/U and buy-coin, perp underly for hedge size text/margin, so cross-asset no longer mislabels ETH qty as BTC. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -287,13 +287,35 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function indexPx() {
|
function indexPx() {
|
||||||
|
// 期权标的指数(链优先);跨币时勿与永续指数混用
|
||||||
|
return optionIndexPx();
|
||||||
|
}
|
||||||
|
|
||||||
|
function optionIndexPx() {
|
||||||
const fromChain = state.chain && Number(state.chain.index_px);
|
const fromChain = state.chain && Number(state.chain.index_px);
|
||||||
if (fromChain && !Number.isNaN(fromChain) && fromChain > 0) return fromChain;
|
if (fromChain && !Number.isNaN(fromChain) && fromChain > 0) return fromChain;
|
||||||
|
if (!state.crossAsset) {
|
||||||
const fromMkt = state.market && Number(state.market.index_px || state.market.mark);
|
const fromMkt = state.market && Number(state.market.index_px || state.market.mark);
|
||||||
if (fromMkt && !Number.isNaN(fromMkt) && fromMkt > 0) return fromMkt;
|
if (fromMkt && !Number.isNaN(fromMkt) && fromMkt > 0) return fromMkt;
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function perpIndexPx() {
|
||||||
|
const fromMkt = state.market && Number(state.market.index_px || state.market.mark);
|
||||||
|
if (fromMkt && !Number.isNaN(fromMkt) && fromMkt > 0) return fromMkt;
|
||||||
|
if (!state.crossAsset) return optionIndexPx();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function optionCoin() {
|
||||||
|
return (state.optionUnderly || state.underlying || "ETH").toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function perpCoin() {
|
||||||
|
return (state.perpUnderly || state.underlying || "ETH").toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
function currentContracts(expSelectId) {
|
function currentContracts(expSelectId) {
|
||||||
const exp = currentExp(expSelectId);
|
const exp = currentExp(expSelectId);
|
||||||
return (exp && exp.contracts) || [];
|
return (exp && exp.contracts) || [];
|
||||||
@@ -473,37 +495,38 @@
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let sheets;
|
let sheets;
|
||||||
let eth;
|
let coinQty;
|
||||||
if (sheetsOverride != null && Number(sheetsOverride) > 0) {
|
if (sheetsOverride != null && Number(sheetsOverride) > 0) {
|
||||||
sheets = Math.floor(Number(sheetsOverride) + 1e-12);
|
sheets = Math.floor(Number(sheetsOverride) + 1e-12);
|
||||||
if (!(sheets > 0)) return null;
|
if (!(sheets > 0)) return null;
|
||||||
eth = Math.round(sheets * ct * 100) / 100;
|
coinQty = Math.round(sheets * ct * 100) / 100;
|
||||||
} else {
|
} else {
|
||||||
if (!(budget > 0) || !(a > 0)) return null;
|
if (!(budget > 0) || !(a > 0)) return null;
|
||||||
eth = Math.floor((usable / a) * 100 + 1e-12) / 100;
|
coinQty = Math.floor((usable / a) * 100 + 1e-12) / 100;
|
||||||
if (!(eth > 0)) return null;
|
if (!(coinQty > 0)) return null;
|
||||||
sheets = Math.floor(eth / ct + 1e-12);
|
sheets = Math.floor(coinQty / ct + 1e-12);
|
||||||
if (!(sheets > 0)) return null;
|
if (!(sheets > 0)) return null;
|
||||||
eth = Math.round(sheets * ct * 100) / 100;
|
coinQty = Math.round(sheets * ct * 100) / 100;
|
||||||
}
|
}
|
||||||
// 跨币:期权币×期权指数×(1/ratio) / 永续指数 = 永续币
|
const optIdx = Number(optionIndexPx() || 0);
|
||||||
let perpQty = eth / ratio;
|
const perpIdx = Number(perpIndexPx() || 0);
|
||||||
|
let perpQty = coinQty / ratio;
|
||||||
let contracts = perpQty / cs;
|
let contracts = perpQty / cs;
|
||||||
let optNotional = null;
|
let optNotional = null;
|
||||||
let perpNotional = null;
|
let perpNotional = null;
|
||||||
if (state.crossAsset) {
|
// 有双指数时一律走名义U(同币/跨币同公式);跨币缺指数则不定仓
|
||||||
const optIdx = Number((state.chain && state.chain.index_px) || 0);
|
|
||||||
const perpIdx = Number((state.market && (state.market.index_px || state.market.mark)) || 0);
|
|
||||||
if (optIdx > 0 && perpIdx > 0) {
|
if (optIdx > 0 && perpIdx > 0) {
|
||||||
optNotional = eth * optIdx;
|
optNotional = coinQty * optIdx;
|
||||||
perpNotional = optNotional / ratio;
|
perpNotional = optNotional / ratio;
|
||||||
perpQty = perpNotional / perpIdx;
|
perpQty = perpNotional / perpIdx;
|
||||||
contracts = perpQty / cs;
|
contracts = perpQty / cs;
|
||||||
}
|
} else if (state.crossAsset) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
usable: usable,
|
usable: usable,
|
||||||
eth_qty: eth,
|
eth_qty: coinQty,
|
||||||
|
coin_qty: coinQty,
|
||||||
perp_qty: perpQty,
|
perp_qty: perpQty,
|
||||||
sheets: sheets,
|
sheets: sheets,
|
||||||
contracts: contracts,
|
contracts: contracts,
|
||||||
@@ -511,6 +534,8 @@
|
|||||||
ratio: ratio,
|
ratio: ratio,
|
||||||
option_notional_usdt: optNotional,
|
option_notional_usdt: optNotional,
|
||||||
perp_notional_usdt: perpNotional,
|
perp_notional_usdt: perpNotional,
|
||||||
|
option_index: optIdx > 0 ? optIdx : null,
|
||||||
|
perp_index: perpIdx > 0 ? perpIdx : null,
|
||||||
cross_asset: !!state.crossAsset,
|
cross_asset: !!state.crossAsset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -542,25 +567,25 @@
|
|||||||
|
|
||||||
function formatOpSizingHtml(sized, amtPrec) {
|
function formatOpSizingHtml(sized, amtPrec) {
|
||||||
if (!sized) return "";
|
if (!sized) return "";
|
||||||
const optCoin = (state.optionUnderly || state.underlying || "ETH").toUpperCase();
|
const optC = optionCoin();
|
||||||
const perpCoin = (state.perpUnderly || state.underlying || "ETH").toUpperCase();
|
const perpC = perpCoin();
|
||||||
const prec = amtPrec != null ? amtPrec : perpCoin === "BTC" ? 4 : 2;
|
const prec = amtPrec != null ? amtPrec : perpC === "BTC" ? 4 : 2;
|
||||||
let html =
|
let html =
|
||||||
"期权 <strong>" +
|
"期权 <strong>" +
|
||||||
fmt(sized.eth_qty, 2) +
|
fmt(sized.eth_qty, 2) +
|
||||||
"</strong> " +
|
"</strong> " +
|
||||||
optCoin +
|
optC +
|
||||||
" / " +
|
" / " +
|
||||||
sized.sheets +
|
sized.sheets +
|
||||||
" 张 → 永续 <strong>" +
|
" 张 → 永续 <strong>" +
|
||||||
fmt(sized.perp_qty, 4) +
|
fmt(sized.perp_qty, 4) +
|
||||||
"</strong> " +
|
"</strong> " +
|
||||||
perpCoin +
|
perpC +
|
||||||
" (" +
|
" (" +
|
||||||
fmt(sized.contracts, prec) +
|
fmt(sized.contracts, prec) +
|
||||||
" 张) · 比例 " +
|
" 张) · 比例 " +
|
||||||
fmt(sized.ratio, 2);
|
fmt(sized.ratio, 2);
|
||||||
if (sized.cross_asset && sized.option_notional_usdt != null) {
|
if (sized.option_notional_usdt != null && sized.perp_notional_usdt != null) {
|
||||||
html +=
|
html +=
|
||||||
" · 名义U " +
|
" · 名义U " +
|
||||||
fmt(sized.option_notional_usdt, 0) +
|
fmt(sized.option_notional_usdt, 0) +
|
||||||
@@ -580,21 +605,18 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 期权开仓最大张数:OKX 交易账户 USDT 共用.
|
* 期权开仓最大张数:OKX 交易账户 USDT 共用.
|
||||||
* 每张开仓需:买币(权利金×1.05×指数) + 永续保证金(对冲名义÷杠杆);
|
* 买币按期权指数折 U;永续保证金按期权名义U÷比例÷杠杆(跨币同口径).
|
||||||
* 选最大 N 时须保证扣完买币后剩余仍够永续保证金.
|
|
||||||
*/
|
*/
|
||||||
function computeMaxSheets() {
|
function computeMaxSheets() {
|
||||||
const ask = state.selected && Number(state.selected.ask);
|
const ask = state.selected && Number(state.selected.ask);
|
||||||
const ct = Number((state.selected && state.selected.ct_mult) || 0.01);
|
const ct = Number((state.selected && state.selected.ct_mult) || 0.01);
|
||||||
const ratio = numInput("hp-opt-perp-ratio", 2);
|
const ratio = numInput("hp-opt-perp-ratio", lockedRatio || 2);
|
||||||
const mark = Number(
|
const optIdx = Number(optionIndexPx() || 0);
|
||||||
(state.market && (state.market.mark || state.market.index_px)) || indexPx() || 0
|
const lev = defaultPerpLeverage(perpCoin());
|
||||||
);
|
|
||||||
const lev = defaultPerpLeverage(state.underlying);
|
|
||||||
const tradingUsdt = Number(
|
const tradingUsdt = Number(
|
||||||
state.tradingUsdt != null ? state.tradingUsdt : state.tradingUsdc
|
state.tradingUsdt != null ? state.tradingUsdt : state.tradingUsdc
|
||||||
);
|
);
|
||||||
if (!(ask > 0) || !(ct > 0) || !(ratio > 0) || !(mark > 0) || !(lev > 0)) {
|
if (!(ask > 0) || !(ct > 0) || !(ratio > 0) || !(optIdx > 0) || !(lev > 0)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!(tradingUsdt > 0)) {
|
if (!(tradingUsdt > 0)) {
|
||||||
@@ -607,10 +629,10 @@
|
|||||||
total_per_sheet: 0,
|
total_per_sheet: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// 买币 USDT/张 = 权利金币 × 1.05 × 指数
|
// 买币 USDT/张 = 权利金币 × 1.05 × 期权指数
|
||||||
const buyUsdtPerSheet = ask * ct * 1.05 * mark;
|
const buyUsdtPerSheet = ask * ct * 1.05 * optIdx;
|
||||||
// 永续名义/张 = (期权币/比例)×指数; 保证金 = 名义÷杠杆
|
// 永续名义U/张 = 期权币名义U / 比例
|
||||||
const perpNotionalPerSheet = (ct / ratio) * mark;
|
const perpNotionalPerSheet = (ct * optIdx) / ratio;
|
||||||
const marginPerSheet = perpNotionalPerSheet / lev;
|
const marginPerSheet = perpNotionalPerSheet / lev;
|
||||||
const totalPerSheet = buyUsdtPerSheet + marginPerSheet;
|
const totalPerSheet = buyUsdtPerSheet + marginPerSheet;
|
||||||
if (!(totalPerSheet > 0)) return null;
|
if (!(totalPerSheet > 0)) return null;
|
||||||
@@ -630,11 +652,11 @@
|
|||||||
const perpEl = $("hp-po-perp-auto");
|
const perpEl = $("hp-po-perp-auto");
|
||||||
const maxInfo = computeMaxSheets();
|
const maxInfo = computeMaxSheets();
|
||||||
const sized = currentOpSizing();
|
const sized = currentOpSizing();
|
||||||
const coin = (state.underlying || "ETH").toUpperCase();
|
const optC = optionCoin();
|
||||||
const lev = defaultPerpLeverage(state.underlying);
|
const perpC = perpCoin();
|
||||||
const mark = Number(
|
const lev = defaultPerpLeverage(perpC);
|
||||||
(state.market && (state.market.mark || state.market.index_px)) || indexPx() || 0
|
const optIdx = Number(optionIndexPx() || 0);
|
||||||
);
|
const perpIdx = Number(perpIndexPx() || 0);
|
||||||
if (maxEl) {
|
if (maxEl) {
|
||||||
if (maxInfo) {
|
if (maxInfo) {
|
||||||
maxEl.innerHTML =
|
maxEl.innerHTML =
|
||||||
@@ -648,17 +670,22 @@
|
|||||||
fmt(maxInfo.margin_per_sheet, 2) +
|
fmt(maxInfo.margin_per_sheet, 2) +
|
||||||
" (杠杆 " +
|
" (杠杆 " +
|
||||||
lev +
|
lev +
|
||||||
"x)";
|
"x · " +
|
||||||
|
perpC +
|
||||||
|
")";
|
||||||
} else {
|
} else {
|
||||||
maxEl.textContent =
|
maxEl.textContent =
|
||||||
"选择期权并刷新后显示:期权开仓最大张数(扣买币后剩余须够永续保证金)";
|
"选择期权并刷新后显示:期权开仓最大张数(扣买币后剩余须够永续保证金)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (perpEl) {
|
if (perpEl) {
|
||||||
if (sized) {
|
if (sized && optIdx > 0) {
|
||||||
const buyUsdt = sized.premium_est * 1.05 * (mark > 0 ? mark : 0);
|
const buyUsdt = sized.premium_est * 1.05 * optIdx;
|
||||||
const marginUsdt =
|
const perpNotional =
|
||||||
sized.perp_qty * (mark > 0 ? mark : 0) / (lev > 0 ? lev : 1);
|
sized.perp_notional_usdt != null
|
||||||
|
? Number(sized.perp_notional_usdt)
|
||||||
|
: sized.perp_qty * (perpIdx > 0 ? perpIdx : optIdx);
|
||||||
|
const marginUsdt = perpNotional / (lev > 0 ? lev : 1);
|
||||||
const remain =
|
const remain =
|
||||||
Number(state.tradingUsdt != null ? state.tradingUsdt : state.tradingUsdc) -
|
Number(state.tradingUsdt != null ? state.tradingUsdt : state.tradingUsdc) -
|
||||||
buyUsdt;
|
buyUsdt;
|
||||||
@@ -667,16 +694,20 @@
|
|||||||
"永续自动开 <strong>" +
|
"永续自动开 <strong>" +
|
||||||
fmt(sized.perp_qty, 4) +
|
fmt(sized.perp_qty, 4) +
|
||||||
"</strong> " +
|
"</strong> " +
|
||||||
coin +
|
perpC +
|
||||||
" / <strong>" +
|
" / <strong>" +
|
||||||
fmt(sized.contracts, coin === "BTC" ? 4 : 2) +
|
fmt(sized.contracts, perpC === "BTC" ? 4 : 2) +
|
||||||
"</strong> 张 · 买币约 " +
|
"</strong> 张 · 买" +
|
||||||
|
optC +
|
||||||
|
"约 " +
|
||||||
fmt(sized.premium_est * 1.05, 4) +
|
fmt(sized.premium_est * 1.05, 4) +
|
||||||
" " +
|
" " +
|
||||||
coin +
|
optC +
|
||||||
"(≈" +
|
"(≈" +
|
||||||
fmt(buyUsdt, 2) +
|
fmt(buyUsdt, 2) +
|
||||||
" U含5%) · 扣后剩 " +
|
" U含5%·参考" +
|
||||||
|
fmt(optIdx, 2) +
|
||||||
|
") · 扣后剩 " +
|
||||||
fmt(remain, 2) +
|
fmt(remain, 2) +
|
||||||
" U · 永续保证金约 " +
|
" U · 永续保证金约 " +
|
||||||
fmt(marginUsdt, 2) +
|
fmt(marginUsdt, 2) +
|
||||||
@@ -729,13 +760,21 @@
|
|||||||
b.setAttribute("aria-pressed", on ? "true" : "false");
|
b.setAttribute("aria-pressed", on ? "true" : "false");
|
||||||
});
|
});
|
||||||
const lab = $("hp-perp-uly-label");
|
const lab = $("hp-perp-uly-label");
|
||||||
if (lab) lab.textContent = uly;
|
if (lab) {
|
||||||
|
if (state.mode === "perp_options" && state.crossAsset) {
|
||||||
|
lab.textContent = optionCoin() + "期权/" + perpCoin() + "永续";
|
||||||
|
} else if (state.mode === "perp_options") {
|
||||||
|
lab.textContent = optionCoin();
|
||||||
|
} else {
|
||||||
|
lab.textContent = uly;
|
||||||
|
}
|
||||||
|
}
|
||||||
const ooLab = $("hp-oo-uly-label");
|
const ooLab = $("hp-oo-uly-label");
|
||||||
if (ooLab) ooLab.textContent = uly;
|
if (ooLab) ooLab.textContent = uly;
|
||||||
// 永续开仓杠杆跟随 env(BTC_LEVERAGE / ALT_LEVERAGE=ETH)
|
// 永续开仓杠杆跟随 env(BTC_LEVERAGE / ALT_LEVERAGE=ETH)
|
||||||
const levEl = $("hp-perp-leverage");
|
const levEl = $("hp-perp-leverage");
|
||||||
if (levEl && !state.perpLevTouched) {
|
if (levEl && !state.perpLevTouched) {
|
||||||
levEl.value = String(defaultPerpLeverage(uly));
|
levEl.value = String(defaultPerpLeverage(perpCoin()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2317,19 +2356,18 @@
|
|||||||
const ct = Number(state.selected.ct_mult || 0.01);
|
const ct = Number(state.selected.ct_mult || 0.01);
|
||||||
const ask = Number(state.selected.ask || 0);
|
const ask = Number(state.selected.ask || 0);
|
||||||
const prem = ask * sheets * ct;
|
const prem = ask * sheets * ct;
|
||||||
// U 以买币参考价(指数)为准,与开仓买币口径一致
|
// U 以期权标的指数为准(买 BTC/ETH 开期权);跨币不可用永续 ETH 价
|
||||||
const buyPx = Number(
|
const buyPx = Number(optionIndexPx() || 0);
|
||||||
(state.market && (state.market.index_px || state.market.mark)) || indexPx() || 0
|
|
||||||
);
|
|
||||||
const premU = buyPx > 0 ? prem * buyPx : null;
|
const premU = buyPx > 0 ? prem * buyPx : null;
|
||||||
line.textContent =
|
line.textContent =
|
||||||
"权利金 ≈ " +
|
"权利金 ≈ " +
|
||||||
fmt(prem, 4) +
|
fmt(prem, 4) +
|
||||||
" 币" +
|
" " +
|
||||||
|
optionCoin() +
|
||||||
(premU != null ? " / " + fmt(premU, 2) + " U" : "") +
|
(premU != null ? " / " + fmt(premU, 2) + " U" : "") +
|
||||||
" · 卖一 " +
|
" · 卖一 " +
|
||||||
fmt(ask, 4) +
|
fmt(ask, 4) +
|
||||||
(buyPx > 0 ? " · 买币参考 " + fmt(buyPx, 2) : "");
|
(buyPx > 0 ? " · 买" + optionCoin() + "参考 " + fmt(buyPx, 2) : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildStraddleRows(contracts) {
|
function buildStraddleRows(contracts) {
|
||||||
|
|||||||
@@ -508,4 +508,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/static/hedge_plan.js?v=64"></script>
|
<script src="/static/hedge_plan.js?v=65"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user