feat: add trading day split lines on hub market chart

Add toggle before technical indicators to show blue dashed vertical lines at Beijing 8:00 day boundaries.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-10 08:35:35 +08:00
parent b6d343a951
commit 6eb17b7ddc
4 changed files with 131 additions and 3 deletions
+62
View File
@@ -67,6 +67,8 @@
let menuEl = null;
let unsubClick = null;
let mainBound = false;
let tradingDaySplitEnabled = false;
const BJ_OFFSET_SEC = 8 * 60 * 60;
function uid() {
return "d" + Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
@@ -354,6 +356,59 @@
drawLine(ctx, x, 0, x, h, selected);
}
function utcSecToBjParts(utcSec) {
const d = new Date((Number(utcSec) + BJ_OFFSET_SEC) * 1000);
return {
y: d.getUTCFullYear(),
m: d.getUTCMonth(),
d: d.getUTCDate(),
h: d.getUTCHours(),
};
}
function collectTradingDayBoundaries(candles) {
if (!candles.length) return [];
const minT = Number(candles[0].time);
const maxT = Number(candles[candles.length - 1].time);
const minP = utcSecToBjParts(minT);
const maxP = utcSecToBjParts(maxT);
const out = [];
let curMs = Date.UTC(minP.y, minP.m, minP.d) - 86400000;
const endMs = Date.UTC(maxP.y, maxP.m, maxP.d) + 2 * 86400000;
while (curMs <= endMs) {
const boundary = Math.floor(curMs / 1000);
if (boundary >= minT - 3600 && boundary <= maxT + 3600) {
if (!out.length || out[out.length - 1] !== boundary) {
out.push(boundary);
}
}
curMs += 86400000;
}
return out;
}
function drawTradingDaySplits(ctx, w, h) {
if (!tradingDaySplitEnabled || !chart) return;
const candles = getCandles();
if (!candles.length) return;
const boundaries = collectTradingDayBoundaries(candles);
if (!boundaries.length) return;
ctx.save();
ctx.strokeStyle = "#3b82f6";
ctx.lineWidth = 1;
ctx.setLineDash([5, 4]);
boundaries.forEach(function (t) {
const x = timeToX(t);
if (x == null || !Number.isFinite(x) || x < -2 || x > w + 2) return;
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
ctx.stroke();
});
ctx.setLineDash([]);
ctx.restore();
}
function drawRect(ctx, x1, y1, x2, y2, selected) {
if (x1 == null || y1 == null || x2 == null || y2 == null) return;
const l = Math.min(x1, x2);
@@ -687,6 +742,7 @@
const w = hostEl.clientWidth;
const h = hostEl.clientHeight;
ctx.clearRect(0, 0, w, h);
drawTradingDaySplits(ctx, w, h);
drawings.forEach(function (d) {
if (d.hidden) ctx.globalAlpha = 0.14;
renderDrawing(ctx, d, w, h, d.id === selectedId);
@@ -1390,9 +1446,15 @@
setChartInteraction(true);
}
function setTradingDaySplit(enabled) {
tradingDaySplitEnabled = !!enabled;
scheduleRedraw();
}
window.HubChartDraw = {
attach: attach,
setViewKey: setViewKey,
setTradingDaySplit: setTradingDaySplit,
resize: scheduleRedraw,
redraw: scheduleRedraw,
destroy: destroy,