diff --git a/lib/common/static/hedge_plan.js b/lib/common/static/hedge_plan.js
index 60100f4..a4f7555 100644
--- a/lib/common/static/hedge_plan.js
+++ b/lib/common/static/hedge_plan.js
@@ -31,6 +31,7 @@
ooSheetsMode: "same_sheets",
ooCloseModeEnabled: root.getAttribute("data-oo-close-mode-enabled") !== "0",
ooCloseMode: "close_all",
+ direction: "long",
tradingUsdc: null,
fundingUsdc: null,
tradeBudgetUsdc: null,
@@ -40,6 +41,32 @@
previewPlanType: null,
};
+ function getDirection() {
+ return (state.direction || "long").toLowerCase() === "short" ? "short" : "long";
+ }
+
+ function syncPoDirUI() {
+ const dir = getDirection();
+ document.querySelectorAll(".hp-po-dir").forEach(function (b) {
+ const on = (b.getAttribute("data-dir") || "") === dir;
+ b.classList.toggle("is-selected", on);
+ b.classList.toggle("active", on);
+ });
+ }
+
+ function setDirection(dir, forceReload) {
+ const next = (dir || "long").toLowerCase() === "short" ? "short" : "long";
+ const changed = next !== getDirection();
+ state.direction = next;
+ syncPoDirUI();
+ if (!changed && !forceReload) return;
+ state.selected = null;
+ if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
+ void loadMarket().then(function () {
+ renderListStrikes();
+ });
+ }
+
function $(id) {
return document.getElementById(id);
}
@@ -428,7 +455,7 @@
}
async function loadMarket() {
- const dir = ($("hp-direction") && $("hp-direction").value) || "long";
+ const dir = getDirection();
const d = await apiJson(
"/api/hedge-plan/market?base=" +
encodeURIComponent(state.underlying) +
@@ -441,26 +468,20 @@
const tag = $("hp-perp-acct-tag");
if (tag) tag.textContent = acctLabel;
const amtPrec = d.amount_precision != null ? Number(d.amount_precision) : 4;
+ const markEl = $("hp-po-mark");
+ if (markEl) markEl.textContent = "标记 " + fmt(d.mark, 2);
const q = $("hp-perp-quote");
if (q) {
q.innerHTML =
- "" +
- (d.base || state.underlying) +
- " · " +
- acctLabel +
"可用 " +
fmt(d.available_usdt, 2) +
- " USDT
标记 " +
- fmt(d.mark, 2) +
- " · 最新 " +
- fmt(d.last, 2) +
- " · 卖一 " +
+ " USDT · 卖一 " +
fmt(d.ask, 2) +
" · 买一 " +
fmt(d.bid, 2) +
" · 面值 " +
fmt(d.contract_size, 4) +
- " · 张精度 " +
+ " · 精度 " +
amtPrec +
" 位";
}
@@ -473,20 +494,18 @@
if (sz) {
if (d.full_margin_sizing) {
const s = d.full_margin_sizing;
- sz.textContent =
- "全仓建议(" +
- acctLabel +
- "):保证金 " +
- fmt(s.margin_capital, 2) +
- " USDT × " +
- s.leverage +
- "x → 名义 " +
- fmt(s.notional_value, 2) +
- " USDT · 建议 " +
+ sz.innerHTML =
+ "全仓建议 " +
fmt(d.suggest_contracts, amtPrec) +
- " 合约张(已按交易所精度)";
+ " 张 · 保证金 " +
+ fmt(s.margin_capital, 2) +
+ " × " +
+ s.leverage +
+ "x · 名义 " +
+ fmt(s.notional_value, 2) +
+ " USDT";
} else {
- sz.textContent = "非全仓或不具备保证金数据时仅手动填张数;永期开仓需全仓.";
+ sz.textContent = "非全仓时请手动填张数;永期开仓需全仓";
}
}
const entry = $("hp-entry");
@@ -507,9 +526,9 @@
const sl = Number(($("hp-sl") && $("hp-sl").value) || NaN);
const contracts = Number(($("hp-contracts") && $("hp-contracts").value) || NaN);
const cs = Number((state.market && state.market.contract_size) || 0.01);
- const dir = (($("hp-direction") && $("hp-direction").value) || "long").toLowerCase();
+ const dir = getDirection();
if (!(entry > 0) || !(contracts > 0) || !(cs > 0)) {
- el.textContent = "填写开仓价与张数后,输入止盈/止损可看永续盈亏金额(USDT)";
+ el.innerHTML = '填开仓价与张数后,输入止盈/止损可看盈亏';
return;
}
function pnlAt(exitPx) {
@@ -517,34 +536,25 @@
if (dir === "short") return (entry - exitPx) * coins;
return (exitPx - entry) * coins;
}
- const parts = [];
- if (tp > 0) {
- const p = pnlAt(tp);
- parts.push(
- "止盈预期 = 0 ? "hp-pnl-pos" : "hp-pnl-neg") +
- "\">" +
- (p >= 0 ? "+" : "") +
- fmt(p, 2) +
- " USDT"
+ function chip(lab, px) {
+ if (!(px > 0)) {
+ return '' + lab + " —";
+ }
+ const p = pnlAt(px);
+ const cls = p >= 0 ? "hp-pnl-pos" : "hp-pnl-neg";
+ const sign = p >= 0 ? "+" : "";
+ return (
+ '' +
+ lab +
+ ' ' +
+ sign +
+ fmt(p, 2) +
+ ""
);
- } else {
- parts.push("止盈预期 —");
}
- if (sl > 0) {
- const p = pnlAt(sl);
- parts.push(
- "止损预期 = 0 ? "hp-pnl-pos" : "hp-pnl-neg") +
- "\">" +
- (p >= 0 ? "+" : "") +
- fmt(p, 2) +
- " USDT"
- );
- } else {
- parts.push("止损预期 —");
- }
- el.innerHTML = parts.join(" · ");
+ el.innerHTML = chip("止盈", tp) + chip("止损", sl);
}
function fillExpSelect(sel, chain) {
@@ -569,12 +579,8 @@
"/api/hedge-plan/options-chain?underlying=" + encodeURIComponent(state.underlying)
);
state.chain = d;
- const uly = d.underlying || state.underlying;
const idx = $("hp-index-line");
- if (idx) {
- idx.textContent =
- "指数 " + uly + " " + fmt(d.index_px, 2) + " · " + (d.inst_family || "") + " · 实值含平值";
- }
+ if (idx) idx.textContent = "指数 " + fmt(d.index_px, 2);
const ooIdx = $("hp-oo-index");
if (ooIdx) ooIdx.textContent = "指数 " + fmt(d.index_px, 2);
setOptionsBalance(d);
@@ -622,8 +628,7 @@
function renderListStrikes() {
const tbody = $("hp-strike-tbody");
if (!tbody) return;
- const dir = ($("hp-direction") && $("hp-direction").value) || "long";
- const want = optTypeForDirection(dir);
+ const want = optTypeForDirection(getDirection());
const exp = currentExp("hp-exp-select");
const prevInst = state.selected && state.selected.inst_id;
tbody.innerHTML = "";
@@ -904,7 +909,7 @@
if (!entry || !tp || !sl || !contracts) throw new Error("请完整填写开仓/止盈/止损/张数");
body = {
plan_type: "perp_options",
- direction: ($("hp-direction") && $("hp-direction").value) || "long",
+ direction: getDirection(),
entry: entry,
tp: tp,
sl: sl,
@@ -1017,16 +1022,11 @@
renderListStrikes();
});
});
- const dir = $("hp-direction");
- if (dir) {
- dir.addEventListener("change", function () {
- state.selected = null;
- if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
- void loadMarket().then(function () {
- renderListStrikes();
- });
+ document.querySelectorAll(".hp-po-dir").forEach(function (b) {
+ b.addEventListener("click", function () {
+ setDirection(b.getAttribute("data-dir") || "long", true);
});
- }
+ });
["hp-entry", "hp-tp", "hp-sl", "hp-contracts"].forEach(function (id) {
const el = $(id);
if (el) el.addEventListener("input", updatePerpPnlHint);
@@ -1507,7 +1507,7 @@
body = {
plan_type: "perp_options",
underlying: state.underlying,
- direction: ($("hp-direction") && $("hp-direction").value) || "long",
+ direction: getDirection(),
entry: entry,
tp: tp,
sl: sl,
@@ -1561,6 +1561,7 @@
syncTabUI();
syncUnderlyingUI();
syncMoneyUI();
+ syncPoDirUI();
syncOoSizeModeUI();
syncOoCloseModeUI();
updateOoBudgetLine(null);
diff --git a/lib/common/static/instance_theme.css b/lib/common/static/instance_theme.css
index 8d4fd2c..8c9a274 100644
--- a/lib/common/static/instance_theme.css
+++ b/lib/common/static/instance_theme.css
@@ -3366,8 +3366,10 @@ html[data-theme="light"] .opt-be-dist-down {
}
.hedge-plan-page-wrap .hp-oo-size-mode.is-selected,
.hedge-plan-page-wrap .hp-oo-close-mode.is-selected,
+.hedge-plan-page-wrap .hp-po-dir.is-selected,
.hedge-plan-page-wrap .hp-oo-size-mode.active,
-.hedge-plan-page-wrap .hp-oo-close-mode.active {
+.hedge-plan-page-wrap .hp-oo-close-mode.active,
+.hedge-plan-page-wrap .hp-po-dir.active {
border-color: var(--accent, #00d4ff);
color: var(--text, #fff);
background: rgba(0, 212, 255, 0.16);
@@ -3376,11 +3378,118 @@ html[data-theme="light"] .opt-be-dist-down {
}
.hedge-plan-page-wrap .hp-oo-size-mode.is-selected .hp-oo-check,
.hedge-plan-page-wrap .hp-oo-close-mode.is-selected .hp-oo-check,
+.hedge-plan-page-wrap .hp-po-dir.is-selected .hp-oo-check,
.hedge-plan-page-wrap .hp-oo-size-mode.active .hp-oo-check,
-.hedge-plan-page-wrap .hp-oo-close-mode.active .hp-oo-check {
+.hedge-plan-page-wrap .hp-oo-close-mode.active .hp-oo-check,
+.hedge-plan-page-wrap .hp-po-dir.active .hp-oo-check {
display: inline;
color: var(--accent, #00d4ff);
}
+.hedge-plan-page-wrap .hp-po-top {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 12px 16px;
+ margin: 8px 0 4px;
+}
+.hedge-plan-page-wrap .hp-po-dir-seg {
+ flex: 1 1 180px;
+ max-width: 220px;
+}
+.hedge-plan-page-wrap .hp-po-mark {
+ font-size: 0.95rem;
+ font-weight: 700;
+ font-variant-numeric: tabular-nums;
+ color: #3dd68c;
+ white-space: nowrap;
+}
+.hedge-plan-page-wrap .hp-po-meta {
+ margin: 2px 0 8px;
+ font-size: 0.74rem;
+ line-height: 1.4;
+}
+.hedge-plan-page-wrap .hp-po-fields {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 10px 12px;
+ margin: 8px 0 6px;
+ padding: 10px 12px;
+ border-radius: 8px;
+ background: rgba(255, 255, 255, 0.03);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+}
+.hedge-plan-page-wrap .hp-po-field {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ min-width: 0;
+ margin: 0;
+ font-size: 0.78rem;
+}
+.hedge-plan-page-wrap .hp-po-field-lab {
+ color: #9aa4b2;
+ font-size: 0.72rem;
+}
+.hedge-plan-page-wrap .hp-po-field-lab em {
+ font-style: normal;
+ color: #6b7388;
+ margin-left: 2px;
+}
+.hedge-plan-page-wrap .hp-po-field input {
+ width: 100%;
+ min-width: 0;
+ box-sizing: border-box;
+}
+.hedge-plan-page-wrap .hp-po-field--tp input {
+ border-color: rgba(61, 214, 140, 0.45);
+}
+.hedge-plan-page-wrap .hp-po-field--sl input {
+ border-color: rgba(255, 138, 138, 0.45);
+}
+.hedge-plan-page-wrap .hp-po-summary {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ margin-top: 4px;
+}
+.hedge-plan-page-wrap .hp-po-pnl {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+.hedge-plan-page-wrap .hp-po-chip {
+ display: inline-flex;
+ align-items: baseline;
+ gap: 4px;
+ padding: 4px 8px;
+ border-radius: 6px;
+ background: rgba(255, 255, 255, 0.04);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ font-size: 0.76rem;
+}
+.hedge-plan-page-wrap .hp-po-sizing {
+ font-size: 0.74rem;
+ line-height: 1.4;
+}
+.hedge-plan-page-wrap .hp-po-opt-toolbar {
+ align-items: center;
+}
+.hedge-plan-page-wrap .hp-po-index {
+ margin-left: auto;
+ font-size: 0.9rem;
+ font-weight: 700;
+ font-variant-numeric: tabular-nums;
+ color: #3dd68c;
+ white-space: nowrap;
+}
+@media (max-width: 720px) {
+ .hedge-plan-page-wrap .hp-po-fields {
+ grid-template-columns: 1fr;
+ }
+ .hedge-plan-page-wrap .hp-po-index {
+ margin-left: 0;
+ }
+}
.hedge-plan-page-wrap #hp-oo-close-mode-row.hidden {
display: none !important;
}
diff --git a/lib/hedge_plan/templates/hedge_plan_panel.html b/lib/hedge_plan/templates/hedge_plan_panel.html
index 2251c23..f09d316 100644
--- a/lib/hedge_plan/templates/hedge_plan_panel.html
+++ b/lib/hedge_plan/templates/hedge_plan_panel.html
@@ -41,37 +41,53 @@