window.AlphaXCharts = (function () { var cssInjected = false; function injectCss() { if (cssInjected) return; cssInjected = true; var style = document.createElement("style"); style.textContent = [ ".ax-chart{position:relative;width:100%;height:100%;min-height:220px;outline:none;touch-action:pan-y}", ".ax-chart canvas{display:block;width:100%;height:100%}", ".ax-tooltip{position:absolute;z-index:4;min-width:172px;max-width:240px;padding:10px 11px;border:1px solid rgba(148,163,184,.32);border-radius:14px;background:rgba(255,255,255,.94);box-shadow:0 18px 42px rgba(15,23,42,.16);backdrop-filter:blur(10px);font-size:12px;color:var(--slate,#4b5563);pointer-events:none;transform:translate(-50%,-112%);opacity:0;transition:opacity .12s ease}", ".ax-tooltip.show{opacity:1}", ".ax-tip-date{font-weight:950;color:var(--ink,#111827);margin-bottom:6px}", ".ax-tip-row{display:flex;justify-content:space-between;gap:14px;line-height:1.55}", ".ax-tip-row b{color:var(--ink,#111827)}", ".ax-tip-row .pos{color:var(--green,#00b473)}", ".ax-tip-row .neg{color:var(--red,#e53e3e)}", ".ax-hint{position:absolute;right:10px;bottom:8px;border:1px solid rgba(148,163,184,.24);background:rgba(255,255,255,.72);border-radius:999px;padding:4px 8px;color:var(--stone,#8e91a0);font-size:10px;font-weight:850;pointer-events:none}" ].join(""); document.head.appendChild(style); } function esc(v) { return String(v == null ? "" : v).replace(/[&<>"']/g, function (c) { return {"&": "&", "<": "<", ">": ">", '"': """, "'": "'"}[c]; }); } function fmt(v, d) { v = Number(v || 0); return v.toLocaleString(undefined, {maximumFractionDigits: d == null ? 2 : d, minimumFractionDigits: 0}); } function color(name, fallback) { var value = getComputedStyle(document.documentElement).getPropertyValue(name).trim(); return value || fallback; } function cls(v) { v = Number(v || 0); return v > 0 ? "pos" : v < 0 ? "neg" : ""; } function renderEquity(container, payload) { injectCss(); var points = (payload && payload.points) || []; if (!container) return; if (container._axDestroy) { container._axDestroy(); container._axDestroy = null; } if (!points.length) { container.innerHTML = '
暂无收益曲线数据
'; return; } container.innerHTML = '
hover 查看明细
'; var root = container.querySelector(".ax-chart"); var canvas = root.querySelector("canvas"); var ctx = canvas.getContext("2d"); var tip = root.querySelector(".ax-tooltip"); var locked = false; var activeIdx = points.length - 1; var ro = null; var equities = points.map(function (p) { return Number(p.equity_usdt || 0); }); var pnls = points.map(function (p) { return Number(p.daily_pnl_usdt || 0); }); var minEq = Math.min.apply(null, equities); var maxEq = Math.max.apply(null, equities); if (maxEq === minEq) { maxEq += 1; minEq -= 1; } var maxAbs = Math.max.apply(null, [1].concat(pnls.map(function (v) { return Math.abs(v); }))); function metrics() { var rect = root.getBoundingClientRect(); var w = Math.max(320, rect.width || container.clientWidth || 960); var h = Math.max(220, rect.height || container.clientHeight || 260); var padL = 4, padR = 4, top = 22, barBase = h - 36, lineH = Math.max(96, barBase - top - 52); var chartW = w - padL - padR; return {w: w, h: h, padL: padL, padR: padR, top: top, barBase: barBase, lineH: lineH, chartW: chartW}; } function x(i, m) { return m.padL + (points.length === 1 ? m.chartW / 2 : i * m.chartW / (points.length - 1)); } function y(v, m) { return m.top + (maxEq - v) * m.lineH / (maxEq - minEq); } function resizeCanvas(m) { var dpr = Math.max(1, window.devicePixelRatio || 1); var bw = Math.round(m.w * dpr); var bh = Math.round(m.h * dpr); if (canvas.width !== bw || canvas.height !== bh) { canvas.width = bw; canvas.height = bh; } canvas.style.width = m.w + "px"; canvas.style.height = m.h + "px"; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); } function drawGrid(m) { ctx.strokeStyle = "rgba(148,163,184,.25)"; ctx.lineWidth = 1; [m.top, m.top + m.lineH / 2, m.top + m.lineH].forEach(function (gy) { ctx.beginPath(); ctx.moveTo(m.padL, gy); ctx.lineTo(m.w - m.padR, gy); ctx.stroke(); }); } function drawBars(m) { var barW = Math.max(3, m.chartW / points.length * .55); points.forEach(function (p, i) { var v = Number(p.daily_pnl_usdt || 0); var bh = Math.abs(v) / maxAbs * 44; var bx = x(i, m) - barW / 2; var by = v >= 0 ? m.barBase - bh : m.barBase; ctx.fillStyle = v >= 0 ? "rgba(0,180,115,.45)" : "rgba(229,62,62,.42)"; ctx.fillRect(bx, by, barW, Math.max(1, bh)); }); } function drawEquity(m) { var blue = color("--blue", "#4262ff"); var area = ctx.createLinearGradient(0, m.top, 0, m.barBase); area.addColorStop(0, "rgba(66,98,255,.16)"); area.addColorStop(1, "rgba(66,98,255,.015)"); ctx.beginPath(); ctx.moveTo(x(0, m), m.barBase); points.forEach(function (p, i) { ctx.lineTo(x(i, m), y(Number(p.equity_usdt || 0), m)); }); ctx.lineTo(x(points.length - 1, m), m.barBase); ctx.closePath(); ctx.fillStyle = area; ctx.fill(); ctx.beginPath(); points.forEach(function (p, i) { var px = x(i, m), py = y(Number(p.equity_usdt || 0), m); if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py); }); ctx.strokeStyle = blue; ctx.lineWidth = 3; ctx.lineCap = "round"; ctx.lineJoin = "round"; ctx.stroke(); } function drawLabels(m) { ctx.fillStyle = color("--stone", "#8e91a0"); ctx.font = "800 11px ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"; ctx.textBaseline = "middle"; ctx.textAlign = "left"; ctx.fillText(String(points[0].date || ""), m.padL + 6, m.h - 14); ctx.fillText("权益 " + fmt(minEq, 0) + "U - " + fmt(maxEq, 0) + "U", m.padL + 6, 14); ctx.textAlign = "right"; ctx.fillText(String(points[points.length - 1].date || ""), m.w - m.padR - 6, m.h - 14); ctx.fillText("点击锁定 · Esc 取消", m.w - m.padR - 6, 14); } function drawFocus(m) { if (activeIdx == null) return; var p = points[activeIdx]; var px = x(activeIdx, m); var py = y(Number(p.equity_usdt || 0), m); ctx.save(); ctx.strokeStyle = "rgba(15,23,42,.38)"; ctx.lineWidth = 1; ctx.setLineDash([4, 4]); ctx.beginPath(); ctx.moveTo(px, m.top); ctx.lineTo(px, m.barBase); ctx.moveTo(m.padL, py); ctx.lineTo(m.w - m.padR, py); ctx.stroke(); ctx.restore(); ctx.beginPath(); ctx.arc(px, py, 5, 0, Math.PI * 2); ctx.fillStyle = color("--canvas", "#fff"); ctx.fill(); ctx.strokeStyle = color("--blue", "#4262ff"); ctx.lineWidth = 3; ctx.stroke(); placeTip(p, px, py, m); } function render() { var m = metrics(); resizeCanvas(m); ctx.clearRect(0, 0, m.w, m.h); drawGrid(m); drawBars(m); drawEquity(m); drawFocus(m); drawLabels(m); } function nearestFromEvent(ev) { var rect = canvas.getBoundingClientRect(); var sx = ev.clientX - rect.left; var m = metrics(); var idx = Math.round((sx - m.padL) / Math.max(1, m.chartW) * (points.length - 1)); return Math.max(0, Math.min(points.length - 1, idx)); } function placeTip(p, px, py, m) { tip.innerHTML = '
' + esc(p.date || "--") + '
' + '
账户权益' + fmt(p.equity_usdt, 2) + 'U
' + '
每日收益' + (Number(p.daily_pnl_usdt || 0) > 0 ? "+" : "") + fmt(p.daily_pnl_usdt, 2) + 'U
' + '
累计收益' + (Number(p.total_pnl_usdt || 0) > 0 ? "+" : "") + fmt(p.total_pnl_usdt, 2) + 'U
'; tip.style.left = Math.max(96, Math.min(m.w - 96, px)) + "px"; tip.style.top = Math.max(42, py) + "px"; tip.classList.add("show"); } function hide() { if (locked) return; activeIdx = null; tip.classList.remove("show"); render(); } root.addEventListener("mousemove", function (ev) { if (locked) return; activeIdx = nearestFromEvent(ev); render(); }); root.addEventListener("mouseleave", hide); root.addEventListener("click", function (ev) { activeIdx = nearestFromEvent(ev); locked = !locked; render(); }); root.addEventListener("keydown", function (ev) { if (ev.key === "Escape") { locked = false; hide(); } }); if (window.ResizeObserver) { ro = new ResizeObserver(render); ro.observe(root); } else { window.addEventListener("resize", render); } render(); root._axDestroy = function () { if (ro) ro.disconnect(); window.removeEventListener("resize", render); }; container._axDestroy = root._axDestroy; } function priceDecimals(ref) { ref = Math.abs(Number(ref || 0)); if (ref >= 1000) return 1; if (ref >= 100) return 2; if (ref >= 1) return 4; if (ref >= 0.01) return 6; return 8; } function fmtPrice(v, decimals) { v = Number(v || 0); if (!Number.isFinite(v)) return "--"; return v.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals == null ? priceDecimals(v) : decimals }); } function candleTime(c) { var t = c && (c.time || c.timestamp || c.open_time || c.openTime || c.t); if (typeof t === "string") { var parsed = Date.parse(t); return Number.isFinite(parsed) ? parsed : 0; } t = Number(t || 0); return t > 0 && t < 100000000000 ? t * 1000 : t; } function fmtCandleDate(t) { if (!t) return "--"; var d = new Date(t); if (Number.isNaN(d.getTime())) return "--"; var mm = String(d.getMonth() + 1).padStart(2, "0"); var dd = String(d.getDate()).padStart(2, "0"); var hh = String(d.getHours()).padStart(2, "0"); var mi = String(d.getMinutes()).padStart(2, "0"); return mm + "-" + dd + " " + hh + ":" + mi; } function roundedRect(ctx, x, y, w, h, r) { r = Math.max(0, Math.min(r || 0, w / 2, h / 2)); ctx.moveTo(x + r, y); ctx.lineTo(x + w - r, y); ctx.quadraticCurveTo(x + w, y, x + w, y + r); ctx.lineTo(x + w, y + h - r); ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); ctx.lineTo(x + r, y + h); ctx.quadraticCurveTo(x, y + h, x, y + h - r); ctx.lineTo(x, y + r); ctx.quadraticCurveTo(x, y, x + r, y); } function renderKline(container, payload) { injectCss(); if (!container) return; if (container._axDestroy) { container._axDestroy(); container._axDestroy = null; } var candles = ((payload && payload.candles) || []).slice(-60).map(function (c) { return { time: candleTime(c), open: Number(c.open || 0), high: Number(c.high || 0), low: Number(c.low || 0), close: Number(c.close || 0), volume: Number(c.volume || 0) }; }).filter(function (c) { return c.high > 0 && c.low > 0 && c.close > 0; }); if (candles.length < 5) { container.innerHTML = '
K线数据不足
'; return; } var symbol = (payload && payload.symbol) || ""; var entryPrice = Number((payload && payload.entryPrice) || 0); var stopLoss = Number((payload && payload.stopLoss) || 0); var tp1 = Number((payload && payload.tp1) || 0); var recTime = payload && payload.recTime ? Date.parse(payload.recTime) : 0; var tp1Time = payload && payload.tp1Time ? Date.parse(payload.tp1Time) : 0; var slTime = payload && payload.slTime ? Date.parse(payload.slTime) : 0; var actionStatus = (payload && payload.actionStatus) || ""; var tradeMarkers = ((payload && payload.tradeMarkers) || []).map(function (marker) { return { time: marker && marker.time ? Date.parse(marker.time) : 0, price: Number((marker && marker.price) || 0), label: String((marker && marker.label) || "操作"), type: String((marker && marker.type) || "event"), note: String((marker && marker.note) || "") }; }).filter(function (marker) { return marker.time > 0; }); var refPrice = Number((payload && payload.refPrice) || entryPrice || candles[candles.length - 1].close || 0); var decimals = priceDecimals(refPrice || entryPrice || candles[candles.length - 1].close); container.innerHTML = '
hover 查看K线
'; var root = container.querySelector(".ax-chart"); var canvas = root.querySelector("canvas"); var ctx = canvas.getContext("2d"); var tip = root.querySelector(".ax-tooltip"); var locked = false; var activeIdx = null; var ro = null; var minPrice = Infinity, maxPrice = -Infinity, maxVol = 1; candles.forEach(function (c) { minPrice = Math.min(minPrice, c.low); maxPrice = Math.max(maxPrice, c.high); maxVol = Math.max(maxVol, c.volume || 0); }); [entryPrice, stopLoss, tp1].forEach(function (p) { if (p > 0) { minPrice = Math.min(minPrice, p); maxPrice = Math.max(maxPrice, p); } }); tradeMarkers.forEach(function (marker) { if (marker.price > 0) { minPrice = Math.min(minPrice, marker.price); maxPrice = Math.max(maxPrice, marker.price); } }); var padding = Math.max((maxPrice - minPrice) * 0.06, maxPrice * 0.001); minPrice -= padding; maxPrice += padding; if (maxPrice <= minPrice) maxPrice = minPrice + 1; function metrics() { var rect = root.getBoundingClientRect(); var w = Math.max(300, rect.width || container.clientWidth || 360); var h = Math.max(190, rect.height || container.clientHeight || 200); var padL = 4, padR = 4, padT = 12, padB = 20, volH = 32; var chartH = Math.max(108, h - padT - padB - volH); var chartW = Math.max(1, w - padL - padR); return {w: w, h: h, padL: padL, padR: padR, padT: padT, padB: padB, volH: volH, chartH: chartH, chartW: chartW}; } function resizeCanvas(m) { var dpr = Math.max(1, window.devicePixelRatio || 1); var bw = Math.round(m.w * dpr); var bh = Math.round(m.h * dpr); if (canvas.width !== bw || canvas.height !== bh) { canvas.width = bw; canvas.height = bh; } canvas.style.width = m.w + "px"; canvas.style.height = m.h + "px"; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); } function x(i, m) { var step = m.chartW / candles.length; return m.padL + step * i + step / 2; } function y(price, m) { return m.padT + (maxPrice - price) * m.chartH / (maxPrice - minPrice); } function nearestIndexFromTime(ts) { if (!ts) return -1; var best = -1, diff = Infinity; candles.forEach(function (c, i) { var d = Math.abs(c.time - ts); if (d < diff) { diff = d; best = i; } }); return best; } function drawGrid(m) { ctx.strokeStyle = "rgba(148,163,184,.20)"; ctx.lineWidth = 1; ctx.font = "800 10px ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"; ctx.fillStyle = color("--stone", "#8e91a0"); ctx.textBaseline = "middle"; ctx.textAlign = "right"; for (var i = 0; i <= 3; i++) { var gy = m.padT + m.chartH * i / 3; var p = maxPrice - (maxPrice - minPrice) * i / 3; ctx.beginPath(); ctx.moveTo(m.padL, gy); ctx.lineTo(m.w - m.padR, gy); ctx.stroke(); ctx.fillText(fmtPrice(p, decimals), m.w - m.padR - 4, gy + 8); } } function drawCandles(m) { var step = m.chartW / candles.length; var cw = Math.max(2, Math.min(9, step * 0.62)); candles.forEach(function (c, i) { var up = c.close >= c.open; var cx = x(i, m); var x0 = cx - cw / 2; var yo = y(c.open, m), yc = y(c.close, m), yh = y(c.high, m), yl = y(c.low, m); ctx.strokeStyle = up ? "rgba(0,180,115,.95)" : "rgba(229,62,62,.92)"; ctx.fillStyle = up ? "rgba(0,180,115,.86)" : "rgba(229,62,62,.84)"; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(cx, yh); ctx.lineTo(cx, yl); ctx.stroke(); ctx.fillRect(x0, Math.min(yo, yc), cw, Math.max(1, Math.abs(yc - yo))); var vh = Math.max(1, (c.volume || 0) / maxVol * (m.volH - 4)); ctx.fillStyle = up ? "rgba(0,180,115,.24)" : "rgba(229,62,62,.20)"; ctx.fillRect(x0, m.h - m.padB - vh, cw, vh); }); } function drawPriceMarker(m, value, label, markerColor) { if (!(value > 0)) return; var py = y(value, m); ctx.save(); ctx.strokeStyle = markerColor; ctx.globalAlpha = 0.75; ctx.lineWidth = 1; ctx.setLineDash([5, 4]); ctx.beginPath(); ctx.moveTo(m.padL, py); ctx.lineTo(m.w - m.padR, py); ctx.stroke(); ctx.restore(); var text = label + " " + fmtPrice(value, decimals); ctx.font = "900 10px ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"; var tw = ctx.measureText(text).width + 10; ctx.fillStyle = "rgba(255,255,255,.88)"; ctx.strokeStyle = markerColor; ctx.lineWidth = 1; ctx.beginPath(); roundedRect(ctx, m.padL + 4, py - 9, tw, 18, 9); ctx.fill(); ctx.stroke(); ctx.fillStyle = markerColor; ctx.textAlign = "left"; ctx.textBaseline = "middle"; ctx.fillText(text, m.padL + 9, py); } function drawEventMarker(m, ts, label, markerColor, price) { var idx = nearestIndexFromTime(ts); if (idx < 0) return; var px = x(idx, m); var baseY = m.h - m.padB - m.volH + 14; ctx.save(); ctx.strokeStyle = markerColor; ctx.globalAlpha = 0.78; ctx.setLineDash([3, 4]); ctx.beginPath(); ctx.moveTo(px, y(candles[idx].low, m) + 3); ctx.lineTo(px, baseY); ctx.stroke(); ctx.restore(); ctx.font = "950 12px ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillStyle = markerColor; ctx.fillText(label, px, baseY + 9); if (price > 0) { ctx.font = "850 9px ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"; ctx.fillText("$" + fmtPrice(price, decimals), px, baseY + 22); } } function markerColorFor(marker) { var type = String((marker && marker.type) || ""); if (type === "open" || type === "fill") return color("--green", "#00b473"); if (type === "close") return color("--blue", "#4262ff"); if (type === "cancel") return color("--red", "#e53e3e"); if (type === "trailing") return "#a05a00"; if (type === "order") return color("--yellow-deep", "#fcb900"); return color("--stone", "#8e91a0"); } function drawTradeMarker(m, marker, stack) { var idx = nearestIndexFromTime(marker.time); if (idx < 0) return; var px = x(idx, m); var rawY = marker.price > 0 ? y(marker.price, m) : y(candles[idx].close, m); var py = Math.max(m.padT + 16, Math.min(m.padT + m.chartH - 12, rawY)); var markerColor = markerColorFor(marker); var label = String(marker.label || "操作").slice(0, 4); var labelY = Math.max(m.padT + 10, py - 20 - (stack % 3) * 18); ctx.save(); ctx.strokeStyle = markerColor; ctx.globalAlpha = 0.78; ctx.setLineDash([3, 4]); ctx.beginPath(); ctx.moveTo(px, m.padT); ctx.lineTo(px, m.h - m.padB - m.volH + 10); ctx.stroke(); ctx.restore(); ctx.beginPath(); ctx.arc(px, py, 4.5, 0, Math.PI * 2); ctx.fillStyle = markerColor; ctx.fill(); ctx.strokeStyle = "rgba(255,255,255,.92)"; ctx.lineWidth = 2; ctx.stroke(); ctx.font = "950 10px ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"; var tw = ctx.measureText(label).width + 12; var x0 = Math.max(m.padL + 2, Math.min(m.w - m.padR - tw - 2, px - tw / 2)); ctx.fillStyle = "rgba(255,255,255,.92)"; ctx.strokeStyle = markerColor; ctx.lineWidth = 1; ctx.beginPath(); roundedRect(ctx, x0, labelY - 9, tw, 18, 9); ctx.fill(); ctx.stroke(); ctx.fillStyle = markerColor; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText(label, x0 + tw / 2, labelY); } function drawLabels(m) { ctx.fillStyle = color("--stone", "#8e91a0"); ctx.font = "850 10px ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"; ctx.textBaseline = "middle"; ctx.textAlign = "left"; ctx.fillText(fmtCandleDate(candles[0].time), m.padL + 6, m.h - 10); ctx.textAlign = "right"; ctx.fillText(fmtCandleDate(candles[candles.length - 1].time), m.w - m.padR - 6, m.h - 10); } function drawFocus(m) { if (activeIdx == null) return; var c = candles[activeIdx]; var px = x(activeIdx, m); var py = y(c.close, m); ctx.save(); ctx.strokeStyle = "rgba(15,23,42,.35)"; ctx.lineWidth = 1; ctx.setLineDash([4, 4]); ctx.beginPath(); ctx.moveTo(px, m.padT); ctx.lineTo(px, m.h - m.padB); ctx.moveTo(m.padL, py); ctx.lineTo(m.w - m.padR, py); ctx.stroke(); ctx.restore(); ctx.beginPath(); ctx.arc(px, py, 4, 0, Math.PI * 2); ctx.fillStyle = color("--canvas", "#fff"); ctx.fill(); ctx.strokeStyle = color("--blue", "#4262ff"); ctx.lineWidth = 2; ctx.stroke(); placeTip(c, px, py, m); } function render() { var m = metrics(); resizeCanvas(m); ctx.clearRect(0, 0, m.w, m.h); drawGrid(m); drawPriceMarker(m, tp1, "TP", color("--green", "#00b473")); drawPriceMarker(m, entryPrice, actionStatus === "等回踩" ? "回踩" : "入场", color("--blue", "#4262ff")); drawPriceMarker(m, stopLoss, "SL", color("--red", "#e53e3e")); drawCandles(m); if (tradeMarkers.length) { var stacks = {}; tradeMarkers.forEach(function (marker) { var idx = nearestIndexFromTime(marker.time); if (idx < 0) return; stacks[idx] = stacks[idx] || 0; drawTradeMarker(m, marker, stacks[idx]); stacks[idx] += 1; }); } else { if (recTime) drawEventMarker(m, recTime, actionStatus === "等回踩" ? "△" : "▲", actionStatus === "等回踩" ? "#8e91a0" : color("--blue", "#4262ff"), entryPrice); if (tp1Time) drawEventMarker(m, tp1Time, "✓", color("--green", "#00b473"), tp1); if (slTime) drawEventMarker(m, slTime, "×", color("--red", "#e53e3e"), stopLoss); } drawFocus(m); drawLabels(m); } function nearestFromEvent(ev) { var rect = canvas.getBoundingClientRect(); var sx = ev.clientX - rect.left; var m = metrics(); var idx = Math.floor((sx - m.padL) / Math.max(1, m.chartW) * candles.length); return Math.max(0, Math.min(candles.length - 1, idx)); } function placeTip(c, px, py, m) { var pct = c.open ? (c.close / c.open - 1) * 100 : 0; tip.innerHTML = '
' + esc(symbol ? symbol + " · " : "") + esc(fmtCandleDate(c.time)) + '
' + '
$' + fmtPrice(c.open, decimals) + '
' + '
高 / 低$' + fmtPrice(c.high, decimals) + ' / $' + fmtPrice(c.low, decimals) + '
' + '
$' + fmtPrice(c.close, decimals) + ' (' + (pct > 0 ? "+" : "") + pct.toFixed(2) + '%)
' + '
成交量' + fmt(c.volume, 0) + '
'; tip.style.left = Math.max(96, Math.min(m.w - 96, px)) + "px"; tip.style.top = Math.max(42, py) + "px"; tip.classList.add("show"); } function hide() { if (locked) return; activeIdx = null; tip.classList.remove("show"); render(); } root.addEventListener("mousemove", function (ev) { if (locked) return; activeIdx = nearestFromEvent(ev); render(); }); root.addEventListener("mouseleave", hide); root.addEventListener("click", function (ev) { activeIdx = nearestFromEvent(ev); locked = !locked; render(); }); root.addEventListener("keydown", function (ev) { if (ev.key === "Escape") { locked = false; hide(); } }); if (window.ResizeObserver) { ro = new ResizeObserver(render); ro.observe(root); } else { window.addEventListener("resize", render); } render(); root._axDestroy = function () { if (ro) ro.disconnect(); window.removeEventListener("resize", render); }; container._axDestroy = root._axDestroy; } return { renderEquity: renderEquity, renderKline: renderKline }; })();