修复趋势回调止盈误显与行情区成交量被裁切的问题

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-02 13:45:19 +08:00
parent 21ef97cbdb
commit 01d26e9833
5 changed files with 245 additions and 54 deletions
+1 -1
View File
@@ -166,7 +166,7 @@ def _okx_normalize_orders(raw: dict, channel: str) -> list[dict[str, Any]]:
info = {} info = {}
sl_trig = _coerce_float(info.get("slTriggerPx"), raw.get("stopLossPrice")) sl_trig = _coerce_float(info.get("slTriggerPx"), raw.get("stopLossPrice"))
tp_trig = _coerce_float(info.get("tpTriggerPx"), raw.get("takeProfitPrice")) tp_trig = _coerce_float(info.get("tpTriggerPx"), raw.get("takeProfitPrice"))
if sl_trig is None or tp_trig is None: if sl_trig is None or tp_trig is None or sl_trig == tp_trig:
return [n] return [n]
base_id = n["id"] base_id = n["id"]
rows: list[dict[str, Any]] = [] rows: list[dict[str, Any]] = []
+12 -2
View File
@@ -1969,14 +1969,19 @@ body.login-page {
.market-chart-wrap { .market-chart-wrap {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: min(72vh, 640px); height: min(76vh, 680px);
min-height: 360px; min-height: 380px;
border: 1px solid var(--border-soft); border: 1px solid var(--border-soft);
border-radius: var(--radius); border-radius: var(--radius);
background: #0a1018; background: #0a1018;
overflow: hidden; overflow: hidden;
} }
.market-chart-wrap.has-pos-panel {
height: min(80vh, 740px);
min-height: 440px;
}
.market-ohlcv-bar { .market-ohlcv-bar {
flex: 0 0 auto; flex: 0 0 auto;
padding: 8px 12px; padding: 8px 12px;
@@ -2147,6 +2152,11 @@ body.login-page {
font-size: 0.68rem; font-size: 0.68rem;
} }
.market-pos-tp-monitored {
color: #8fc8ff;
font-size: 0.72rem;
}
.sym-link { .sym-link {
background: none; background: none;
border: none; border: none;
+184 -40
View File
@@ -506,14 +506,146 @@
return (row && (row.key || row.id)) || exchangeId; return (row && (row.key || row.id)) || exchangeId;
} }
function buildPositionMarketContext(pos, monitorOrder) { function findTrendPlan(trends, symbol, side) {
const want = (side || "").toLowerCase();
for (const t of trends || []) {
const sym = t.symbol || t.exchange_symbol || "";
if (!symbolsMatchHub(sym, symbol)) continue;
const d = (t.direction || "").toLowerCase();
if (!d || d === want) return t;
}
return null;
}
function inferTpslFromCondOrders(side, cond, entry) {
const picked = pickExTpslOrders(cond);
let sl = picked.sl && picked.sl.trigger_price != null ? picked.sl.trigger_price : "";
let tp = picked.tp && picked.tp.trigger_price != null ? picked.tp.trigger_price : "";
if (sl !== "" && tp !== "" && Number(sl) !== Number(tp)) {
return { sl, tp };
}
const triggers = (cond || [])
.map(function (o) {
return { price: Number(o.trigger_price), label: o.label || "" };
})
.filter(function (o) {
return o.price != null && !Number.isNaN(o.price) && o.price > 0;
});
if (!triggers.length) return { sl: sl || "", tp: tp || "" };
const s = (side || "long").toLowerCase();
const e = entry != null && Number.isFinite(Number(entry)) ? Number(entry) : null;
if (e != null) {
const below = triggers.filter(function (t) {
return t.price < e;
});
const above = triggers.filter(function (t) {
return t.price > e;
});
if (s === "long") {
if (sl === "" && below.length) {
sl = Math.max.apply(
null,
below.map(function (t) {
return t.price;
})
);
}
if (tp === "" && above.length) {
tp = Math.min.apply(
null,
above.map(function (t) {
return t.price;
})
);
}
} else {
if (sl === "" && above.length) {
sl = Math.min.apply(
null,
above.map(function (t) {
return t.price;
})
);
}
if (tp === "" && below.length) {
tp = Math.max.apply(
null,
below.map(function (t) {
return t.price;
})
);
}
}
}
if (triggers.length === 1 && sl === "" && tp === "") {
const one = triggers[0];
const p = one.price;
const lbl = one.label;
if (e != null) {
if (s === "long") {
if (p < e) sl = p;
else if (p > e) tp = p;
} else if (p > e) sl = p;
else if (p < e) tp = p;
} else if (/止损/.test(lbl)) sl = p;
else if (/止盈/.test(lbl) && !/止盈止损/.test(lbl)) tp = p;
}
if (sl !== "" && tp !== "" && Number(sl) === Number(tp)) tp = "";
return { sl: sl || "", tp: tp || "" };
}
function resolvePositionTpsl(pos, monitorOrder, trendPlan) {
const mo = monitorOrder || {}; const mo = monitorOrder || {};
const tp = trendPlan || {};
const cond = condOrdersFromPosition(pos);
const entryRaw =
pos.entry_price != null
? pos.entry_price
: mo.trigger_price != null
? mo.trigger_price
: tp.avg_entry_price;
const entryN = entryRaw != null && entryRaw !== "" ? Number(entryRaw) : null;
const isTrend =
!!(trendPlan && trendPlan.id) || String(mo.monitor_type || "").trim() === "趋势回调";
let sl = mo.stop_loss != null && mo.stop_loss !== "" ? mo.stop_loss : "";
let takeProfit = mo.take_profit != null && mo.take_profit !== "" ? mo.take_profit : "";
let tpMonitored = false;
if (isTrend) {
tpMonitored = true;
takeProfit = "";
if (trendPlan && trendPlan.stop_loss != null && trendPlan.stop_loss !== "") {
sl = trendPlan.stop_loss;
}
}
const inferred = inferTpslFromCondOrders(pos.side, cond, entryN);
if (sl === "" || sl == null) sl = inferred.sl;
if (!tpMonitored && (takeProfit === "" || takeProfit == null)) takeProfit = inferred.tp;
if (sl !== "" && takeProfit !== "" && Number(sl) === Number(takeProfit)) {
takeProfit = "";
}
return {
entry: entryRaw,
sl,
tp: takeProfit,
tp_monitored: tpMonitored,
is_trend: isTrend,
};
}
function buildPositionMarketContext(pos, monitorOrder, trendPlan) {
const tpsl = resolvePositionTpsl(pos, monitorOrder, trendPlan);
const cond = condOrdersFromPosition(pos); const cond = condOrdersFromPosition(pos);
const reg = Array.isArray(pos.regular_orders) ? pos.regular_orders : []; const reg = Array.isArray(pos.regular_orders) ? pos.regular_orders : [];
const guess = guessTpslFromCondOrders(pos.side, cond);
const entry = pos.entry_price != null ? pos.entry_price : mo.trigger_price;
const sl = mo.stop_loss != null ? mo.stop_loss : guess.sl;
const tp = mo.take_profit != null ? mo.take_profit : guess.tp;
const num = function (v) { const num = function (v) {
if (v == null || v === "") return null; if (v == null || v === "") return null;
const n = Number(v); const n = Number(v);
@@ -538,9 +670,11 @@
}); });
return { return {
side: (pos.side || "long").toLowerCase(), side: (pos.side || "long").toLowerCase(),
entry: num(entry), entry: num(tpsl.entry),
stop_loss: num(sl), stop_loss: num(tpsl.sl),
take_profit: num(tp), take_profit: num(tpsl.tp),
tp_monitored: !!tpsl.tp_monitored,
is_trend: !!tpsl.is_trend,
contracts: num(pos.contracts), contracts: num(pos.contracts),
orders: orders, orders: orders,
}; };
@@ -565,10 +699,13 @@
} }
} }
function marketOpenBtnAttrs(exchangeId, exchangeKey, symbol, pos, monitorOrder) { function marketOpenBtnAttrs(exchangeId, exchangeKey, symbol, pos, monitorOrder, trendPlan) {
const symAttr = esc(symbol || "").replace(/"/g, "&quot;"); const symAttr = esc(symbol || "").replace(/"/g, "&quot;");
const exKeyAttr = esc(exchangeKey || exchangeId || "").replace(/"/g, "&quot;"); const exKeyAttr = esc(exchangeKey || exchangeId || "").replace(/"/g, "&quot;");
const ctxEnc = esc(encodePosCtx(buildPositionMarketContext(pos, monitorOrder))).replace(/"/g, "&quot;"); const ctxEnc = esc(encodePosCtx(buildPositionMarketContext(pos, monitorOrder, trendPlan))).replace(
/"/g,
"&quot;"
);
return ( return (
'data-ex-id="' + 'data-ex-id="' +
esc(exchangeId) + esc(exchangeId) +
@@ -706,18 +843,8 @@
return `<table class="data-table data-table-sub"><thead><tr><th>类型</th><th>数量</th><th>触发/价格</th><th>操作</th></tr></thead><tbody>${rows}</tbody></table>`; return `<table class="data-table data-table-sub"><thead><tr><th>类型</th><th>数量</th><th>触发/价格</th><th>操作</th></tr></thead><tbody>${rows}</tbody></table>`;
} }
function guessTpslFromCondOrders(side, cond) { function guessTpslFromCondOrders(side, cond, entry) {
const triggers = (cond || []) return inferTpslFromCondOrders(side, cond, entry);
.map((o) => o.trigger_price)
.filter((v) => v != null && !Number.isNaN(Number(v)))
.map(Number);
if (!triggers.length) return { sl: "", tp: "" };
triggers.sort((a, b) => a - b);
const s = (side || "long").toLowerCase();
if (s === "short") {
return { sl: triggers[triggers.length - 1], tp: triggers[0] };
}
return { sl: triggers[0], tp: triggers[triggers.length - 1] };
} }
function renderOrdersCollapse(exchangeId, symbol, cond, reg) { function renderOrdersCollapse(exchangeId, symbol, cond, reg) {
@@ -786,7 +913,7 @@
return row("止损", sl) + row("止盈", tp); return row("止损", sl) + row("止盈", tp);
} }
function renderLivePositionCard(exchangeId, exchangeKey, pos, monitorOrder) { function renderLivePositionCard(exchangeId, exchangeKey, pos, monitorOrder, trendPlan) {
const symbol = pos.symbol || ""; const symbol = pos.symbol || "";
const exKeyAttr = esc(exchangeKey || exchangeId || "").replace(/"/g, "&quot;"); const exKeyAttr = esc(exchangeKey || exchangeId || "").replace(/"/g, "&quot;");
const side = (pos.side || "long").toLowerCase(); const side = (pos.side || "long").toLowerCase();
@@ -795,16 +922,17 @@
const mo = monitorOrder || {}; const mo = monitorOrder || {};
const cond = condOrdersFromPosition(pos); const cond = condOrdersFromPosition(pos);
const reg = Array.isArray(pos.regular_orders) ? pos.regular_orders : []; const reg = Array.isArray(pos.regular_orders) ? pos.regular_orders : [];
const guess = guessTpslFromCondOrders(side, cond); const tpsl = resolvePositionTpsl(pos, mo, trendPlan);
const symAttr = esc(symbol).replace(/"/g, "&quot;"); const symAttr = esc(symbol).replace(/"/g, "&quot;");
const sideAttr = esc(side).replace(/"/g, "&quot;"); const sideAttr = esc(side).replace(/"/g, "&quot;");
const contractsAttr = esc(String(pos.contracts != null ? pos.contracts : "")).replace(/"/g, "&quot;"); const contractsAttr = esc(String(pos.contracts != null ? pos.contracts : "")).replace(/"/g, "&quot;");
const slAttr = esc(String(mo.stop_loss != null ? mo.stop_loss : guess.sl)).replace(/"/g, "&quot;"); const slAttr = esc(String(tpsl.sl)).replace(/"/g, "&quot;");
const tpAttr = esc(String(mo.take_profit != null ? mo.take_profit : guess.tp)).replace(/"/g, "&quot;"); const tpAttr = esc(String(tpsl.tp)).replace(/"/g, "&quot;");
const entry = pos.entry_price != null ? pos.entry_price : mo.trigger_price; const entry = tpsl.entry;
const sl = mo.stop_loss != null ? mo.stop_loss : guess.sl; const sl = tpsl.sl;
const tp = mo.take_profit != null ? mo.take_profit : guess.tp; const tp = tpsl.tp;
const rr = calcRrRatio(side, entry, sl, tp); const tpMonitored = tpsl.tp_monitored;
const rr = tpMonitored ? null : calcRrRatio(side, entry, sl, tp);
const upnl = pos.unrealized_pnl; const upnl = pos.unrealized_pnl;
let pnlText = fmt(upnl, 2) + "U"; let pnlText = fmt(upnl, 2) + "U";
if (pos.notional_usdt && upnl != null && Math.abs(Number(pos.notional_usdt)) > 1e-8) { if (pos.notional_usdt && upnl != null && Math.abs(Number(pos.notional_usdt)) > 1e-8) {
@@ -828,7 +956,7 @@
meta.push( meta.push(
`<span class="${beOn ? "pos-meta-on" : "pos-meta-off"}">移动保本:${beOn ? "开" : "关"}</span>` `<span class="${beOn ? "pos-meta-on" : "pos-meta-off"}">移动保本:${beOn ? "开" : "关"}</span>`
); );
const mktAttrs = marketOpenBtnAttrs(exchangeId, exchangeKey, symbol, pos, monitorOrder); const mktAttrs = marketOpenBtnAttrs(exchangeId, exchangeKey, symbol, pos, monitorOrder, trendPlan);
return `<div class="pos-card hub-pos-card"> return `<div class="pos-card hub-pos-card">
<div class="pos-card-head"> <div class="pos-card-head">
<div class="pos-card-symbol"> <div class="pos-card-symbol">
@@ -844,8 +972,8 @@
<div class="pos-grid"> <div class="pos-grid">
<div class="pos-cell"><span class="pos-label">成交价</span><span class="pos-value">${entry != null ? fmt(entry, 4) : "—"}</span></div> <div class="pos-cell"><span class="pos-label">成交价</span><span class="pos-value">${entry != null ? fmt(entry, 4) : "—"}</span></div>
<div class="pos-cell"><span class="pos-label">止损</span><span class="pos-value">${sl != null && sl !== "" ? fmt(sl, 4) : "—"}</span></div> <div class="pos-cell"><span class="pos-label">止损</span><span class="pos-value">${sl != null && sl !== "" ? fmt(sl, 4) : "—"}</span></div>
<div class="pos-cell"><span class="pos-label">止盈</span><span class="pos-value">${tp != null && tp !== "" ? fmt(tp, 4) : "—"}</span></div> <div class="pos-cell"><span class="pos-label">止盈</span><span class="pos-value">${tpMonitored ? "程序监控" : tp != null && tp !== "" ? fmt(tp, 4) : "—"}</span></div>
<div class="pos-cell"><span class="pos-label">盈亏比</span><span class="pos-value">${rr != null ? fmt(rr, 2) + ":1" : "-:1"}</span></div> <div class="pos-cell"><span class="pos-label">盈亏比</span><span class="pos-value">${tpMonitored ? "—" : rr != null ? fmt(rr, 2) + ":1" : "-:1"}</span></div>
<div class="pos-cell"><span class="pos-label">张数</span><span class="pos-value">${fmt(pos.contracts, 4)}</span></div> <div class="pos-cell"><span class="pos-label">张数</span><span class="pos-value">${fmt(pos.contracts, 4)}</span></div>
<div class="pos-cell"><span class="pos-label">浮盈亏</span><span class="pos-value ${pnlCls(upnl)}">${pnlText}</span></div> <div class="pos-cell"><span class="pos-label">浮盈亏</span><span class="pos-value ${pnlCls(upnl)}">${pnlText}</span></div>
</div> </div>
@@ -933,17 +1061,17 @@
.join(""); .join("");
} }
function renderPositionBlock(exchangeId, exchangeKey, x, monitorOrder) { function renderPositionBlock(exchangeId, exchangeKey, x, monitorOrder, trendPlan) {
const symAttr = esc(x.symbol || "").replace(/"/g, "&quot;"); const symAttr = esc(x.symbol || "").replace(/"/g, "&quot;");
const exKeyAttr = esc(exchangeKey || exchangeId || "").replace(/"/g, "&quot;"); const exKeyAttr = esc(exchangeKey || exchangeId || "").replace(/"/g, "&quot;");
const sideAttr = esc((x.side || "").toLowerCase()).replace(/"/g, "&quot;"); const sideAttr = esc((x.side || "").toLowerCase()).replace(/"/g, "&quot;");
const contractsAttr = esc(String(x.contracts != null ? x.contracts : "")).replace(/"/g, "&quot;"); const contractsAttr = esc(String(x.contracts != null ? x.contracts : "")).replace(/"/g, "&quot;");
const cond = condOrdersFromPosition(x); const cond = condOrdersFromPosition(x);
const reg = Array.isArray(x.regular_orders) ? x.regular_orders : []; const reg = Array.isArray(x.regular_orders) ? x.regular_orders : [];
const guess = guessTpslFromCondOrders(x.side, cond); const tpsl = resolvePositionTpsl(x, monitorOrder, trendPlan);
const slAttr = esc(String(guess.sl)).replace(/"/g, "&quot;"); const slAttr = esc(String(tpsl.sl)).replace(/"/g, "&quot;");
const tpAttr = esc(String(guess.tp)).replace(/"/g, "&quot;"); const tpAttr = esc(String(tpsl.tp)).replace(/"/g, "&quot;");
const mktAttrs = marketOpenBtnAttrs(exchangeId, exchangeKey, x.symbol, x, monitorOrder); const mktAttrs = marketOpenBtnAttrs(exchangeId, exchangeKey, x.symbol, x, monitorOrder, trendPlan);
return `<div class="pos-block"> return `<div class="pos-block">
<div class="table-scroll"> <div class="table-scroll">
<table class="data-table"><thead><tr><th>合约</th><th>方向</th><th>张数</th><th>浮盈</th><th>操作</th></tr></thead><tbody> <table class="data-table"><thead><tr><th>合约</th><th>方向</th><th>张数</th><th>浮盈</th><th>操作</th></tr></thead><tbody>
@@ -972,7 +1100,17 @@
</div>`; </div>`;
inner += `<div class="section-title">交易所持仓 · ${pos.length} 仓</div>`; inner += `<div class="section-title">交易所持仓 · ${pos.length} 仓</div>`;
if (pos.length) { if (pos.length) {
inner += pos.map((p) => renderPositionBlock(row.id, row.key || row.id, p, findMonitorOrder(orders, p.symbol, p.side))).join(""); inner += pos
.map((p) =>
renderPositionBlock(
row.id,
row.key || row.id,
p,
findMonitorOrder(orders, p.symbol, p.side),
findTrendPlan(trends, p.symbol, p.side)
)
)
.join("");
} else { } else {
inner += '<div class="empty-hint">无持仓</div>'; inner += '<div class="empty-hint">无持仓</div>';
} }
@@ -1065,7 +1203,13 @@
html += `<div class="hub-pos-list ${posListCls}" data-pos-count="${posCount}">`; html += `<div class="hub-pos-list ${posListCls}" data-pos-count="${posCount}">`;
if (posCount) { if (posCount) {
pos.forEach((p) => { pos.forEach((p) => {
html += renderLivePositionCard(row.id, row.key || row.id, p, findMonitorOrder(orders, p.symbol, p.side)); html += renderLivePositionCard(
row.id,
row.key || row.id,
p,
findMonitorOrder(orders, p.symbol, p.side),
findTrendPlan(trends, p.symbol, p.side)
);
}); });
} else { } else {
html += '<div class="pos-empty">暂无持仓</div>'; html += '<div class="pos-empty">暂无持仓</div>';
+45 -8
View File
@@ -5,6 +5,9 @@
const AUTO_REFRESH_MS = 5000; const AUTO_REFRESH_MS = 5000;
const DEFAULT_VISIBLE_BARS = 200; const DEFAULT_VISIBLE_BARS = 200;
const RIGHT_OFFSET_BARS = 10; const RIGHT_OFFSET_BARS = 10;
const CANDLE_SCALE_BOTTOM = 0.26;
const VOLUME_SCALE_TOP = 0.73;
const VOLUME_SCALE_BOTTOM = 0.06;
const TF_MS = { const TF_MS = {
"1m": 60_000, "1m": 60_000,
"5m": 5 * 60_000, "5m": 5 * 60_000,
@@ -111,6 +114,30 @@
if (el) el.textContent = "—"; if (el) el.textContent = "—";
}); });
if (elPosOrders) elPosOrders.innerHTML = ""; if (elPosOrders) elPosOrders.innerHTML = "";
syncChartWrapLayout();
}
function resizeChart() {
if (!chart || !chartHost) return;
chart.applyOptions({ width: chartHost.clientWidth, height: chartHost.clientHeight });
updatePriceTag();
}
let resizeChartRaf = 0;
function scheduleChartResize() {
if (resizeChartRaf) cancelAnimationFrame(resizeChartRaf);
resizeChartRaf = requestAnimationFrame(function () {
resizeChartRaf = 0;
syncChartWrapLayout();
});
}
function syncChartWrapLayout() {
const wrap = chartHost && chartHost.closest(".market-chart-wrap");
if (wrap && elPosPanel) {
wrap.classList.toggle("has-pos-panel", !elPosPanel.classList.contains("hidden"));
}
resizeChart();
} }
function renderPosPanel(ctx) { function renderPosPanel(ctx) {
@@ -126,7 +153,15 @@
} }
if (elPosEntry) elPosEntry.textContent = ctx.entry != null ? fmtPrice(ctx.entry) : "—"; if (elPosEntry) elPosEntry.textContent = ctx.entry != null ? fmtPrice(ctx.entry) : "—";
if (elPosSl) elPosSl.textContent = ctx.stop_loss != null ? fmtPrice(ctx.stop_loss) : "—"; if (elPosSl) elPosSl.textContent = ctx.stop_loss != null ? fmtPrice(ctx.stop_loss) : "—";
if (elPosTp) elPosTp.textContent = ctx.take_profit != null ? fmtPrice(ctx.take_profit) : "—"; if (elPosTp) {
if (ctx.tp_monitored) {
elPosTp.textContent = "程序监控";
elPosTp.classList.add("market-pos-tp-monitored");
} else {
elPosTp.textContent = ctx.take_profit != null ? fmtPrice(ctx.take_profit) : "—";
elPosTp.classList.remove("market-pos-tp-monitored");
}
}
if (elPosSize) elPosSize.textContent = ctx.contracts != null ? String(ctx.contracts) : "—"; if (elPosSize) elPosSize.textContent = ctx.contracts != null ? String(ctx.contracts) : "—";
if (elPosOrders) { if (elPosOrders) {
const orders = Array.isArray(ctx.orders) ? ctx.orders : []; const orders = Array.isArray(ctx.orders) ? ctx.orders : [];
@@ -155,6 +190,7 @@
.join(""); .join("");
} }
} }
scheduleChartResize();
} }
function clearPositionLines() { function clearPositionLines() {
@@ -172,8 +208,10 @@
const specs = [ const specs = [
{ price: posContext.entry, color: "#5b9cf5", title: "入场" }, { price: posContext.entry, color: "#5b9cf5", title: "入场" },
{ price: posContext.stop_loss, color: "#ff4d6d", title: "止损" }, { price: posContext.stop_loss, color: "#ff4d6d", title: "止损" },
{ price: posContext.take_profit, color: "#00ff9d", title: "止盈" },
]; ];
if (!posContext.tp_monitored && posContext.take_profit != null) {
specs.push({ price: posContext.take_profit, color: "#00ff9d", title: "止盈" });
}
specs.forEach(function (s) { specs.forEach(function (s) {
if (s.price == null || !Number.isFinite(Number(s.price))) return; if (s.price == null || !Number.isFinite(Number(s.price))) return;
positionLines.push( positionLines.push(
@@ -496,10 +534,10 @@
if (!volumeSeries) return false; if (!volumeSeries) return false;
chart.priceScale("right").applyOptions({ chart.priceScale("right").applyOptions({
scaleMargins: { top: 0.06, bottom: 0.28 }, scaleMargins: { top: 0.06, bottom: CANDLE_SCALE_BOTTOM },
}); });
chart.priceScale("volume").applyOptions({ chart.priceScale("volume").applyOptions({
scaleMargins: { top: 0.78, bottom: 0 }, scaleMargins: { top: VOLUME_SCALE_TOP, bottom: VOLUME_SCALE_BOTTOM },
}); });
applyPriceAutoScale(); applyPriceAutoScale();
@@ -522,11 +560,9 @@
}); });
window.addEventListener("resize", function () { window.addEventListener("resize", function () {
if (!chart) return; scheduleChartResize();
chart.applyOptions({ width: chartHost.clientWidth, height: chartHost.clientHeight });
updatePriceTag();
}); });
chart.applyOptions({ width: chartHost.clientWidth, height: chartHost.clientHeight }); scheduleChartResize();
return true; return true;
} }
@@ -712,6 +748,7 @@
updateVisibleRangeMarkers(); updateVisibleRangeMarkers();
syncPosContextForView(exKey, sym); syncPosContextForView(exKey, sym);
showLatestOhlcv(); showLatestOhlcv();
scheduleChartResize();
const limit = data.limit || lastCandles.length; const limit = data.limit || lastCandles.length;
let hint = let hint =
+3 -3
View File
@@ -8,7 +8,7 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'" /> <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'" />
<noscript><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" /></noscript> <noscript><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" /></noscript>
<link rel="stylesheet" href="/assets/app.css?v=20260528-hub-pos-market2" /> <link rel="stylesheet" href="/assets/app.css?v=20260528-hub-vol-fix" />
</head> </head>
<body> <body>
<div class="app-bg" aria-hidden="true"></div> <div class="app-bg" aria-hidden="true"></div>
@@ -204,7 +204,7 @@
<div id="toast"></div> <div id="toast"></div>
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script> <script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
<script src="/assets/chart.js?v=20260528-hub-pos-market2"></script> <script src="/assets/chart.js?v=20260528-hub-vol-fix"></script>
<script src="/assets/app.js?v=20260528-hub-pos-market2"></script> <script src="/assets/app.js?v=20260528-hub-tpsl-fix"></script>
</body> </body>
</html> </html>