feat(hub): add yesterday close and high-low price line toggles on market chart
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -193,8 +193,13 @@
|
||||
const elIndEma = document.getElementById("market-ind-ema");
|
||||
const elIndMacd = document.getElementById("market-ind-macd");
|
||||
const elIndRsi = document.getElementById("market-ind-rsi");
|
||||
const elPrevCloseLine = document.getElementById("market-prev-close-line");
|
||||
const elPrevHlLines = document.getElementById("market-prev-hl-lines");
|
||||
const elDaySplit = document.getElementById("market-day-split");
|
||||
const PREV_CLOSE_LINE_STORAGE_KEY = "hub-market-prev-close-line";
|
||||
const PREV_HL_LINES_STORAGE_KEY = "hub-market-prev-hl-lines";
|
||||
const DAY_SPLIT_STORAGE_KEY = "hub-market-day-split";
|
||||
const BJ_OFFSET_SEC = 8 * 60 * 60;
|
||||
const elFsToolbar = document.getElementById("market-fs-toolbar");
|
||||
const elFsExchange = document.getElementById("market-fs-exchange");
|
||||
const elFsSymbol = document.getElementById("market-fs-symbol");
|
||||
@@ -226,6 +231,7 @@
|
||||
let priceTick = null;
|
||||
let priceAutoScale = true;
|
||||
let rangeMarkers = [];
|
||||
let yesterdayPriceLines = [];
|
||||
let positionLines = [];
|
||||
let posContext = null;
|
||||
let posPnlTimer = null;
|
||||
@@ -310,19 +316,129 @@
|
||||
syncChartWrapLayout();
|
||||
}
|
||||
|
||||
function loadDaySplitPref() {
|
||||
function loadBoolPref(key, defaultValue) {
|
||||
try {
|
||||
const raw = localStorage.getItem(DAY_SPLIT_STORAGE_KEY);
|
||||
const raw = localStorage.getItem(key);
|
||||
if (raw === "1" || raw === "true") return true;
|
||||
if (raw === "0" || raw === "false") return false;
|
||||
} catch (_) {}
|
||||
return false;
|
||||
return !!defaultValue;
|
||||
}
|
||||
|
||||
function saveBoolPref(key, on) {
|
||||
try {
|
||||
localStorage.setItem(key, on ? "1" : "0");
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function loadDaySplitPref() {
|
||||
return loadBoolPref(DAY_SPLIT_STORAGE_KEY, false);
|
||||
}
|
||||
|
||||
function saveDaySplitPref(on) {
|
||||
try {
|
||||
localStorage.setItem(DAY_SPLIT_STORAGE_KEY, on ? "1" : "0");
|
||||
} catch (_) {}
|
||||
saveBoolPref(DAY_SPLIT_STORAGE_KEY, on);
|
||||
}
|
||||
|
||||
function loadPrevCloseLinePref() {
|
||||
return loadBoolPref(PREV_CLOSE_LINE_STORAGE_KEY, false);
|
||||
}
|
||||
|
||||
function savePrevCloseLinePref(on) {
|
||||
saveBoolPref(PREV_CLOSE_LINE_STORAGE_KEY, on);
|
||||
}
|
||||
|
||||
function loadPrevHlLinesPref() {
|
||||
return loadBoolPref(PREV_HL_LINES_STORAGE_KEY, false);
|
||||
}
|
||||
|
||||
function savePrevHlLinesPref(on) {
|
||||
saveBoolPref(PREV_HL_LINES_STORAGE_KEY, on);
|
||||
}
|
||||
|
||||
function chartResetHour() {
|
||||
return chartMeta && chartMeta.volume_rank_reset_hour != null
|
||||
? Number(chartMeta.volume_rank_reset_hour)
|
||||
: 8;
|
||||
}
|
||||
|
||||
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 tradingDayKeyFromUtcSec(utcSec, resetHour) {
|
||||
const p = utcSecToBjParts(utcSec);
|
||||
let y = p.y;
|
||||
let m = p.m;
|
||||
let d = p.d;
|
||||
if (p.h < resetHour) {
|
||||
const prev = new Date(Date.UTC(y, m, d) - 86400000);
|
||||
y = prev.getUTCFullYear();
|
||||
m = prev.getUTCMonth();
|
||||
d = prev.getUTCDate();
|
||||
}
|
||||
return (
|
||||
y +
|
||||
"-" +
|
||||
String(m + 1).padStart(2, "0") +
|
||||
"-" +
|
||||
String(d).padStart(2, "0")
|
||||
);
|
||||
}
|
||||
|
||||
function prevTradingDayKey(tdKey) {
|
||||
const parts = String(tdKey || "").split("-");
|
||||
if (parts.length !== 3) return "";
|
||||
const dt = new Date(Date.UTC(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2])));
|
||||
const prev = new Date(dt.getTime() - 86400000);
|
||||
return (
|
||||
prev.getUTCFullYear() +
|
||||
"-" +
|
||||
String(prev.getUTCMonth() + 1).padStart(2, "0") +
|
||||
"-" +
|
||||
String(prev.getUTCDate()).padStart(2, "0")
|
||||
);
|
||||
}
|
||||
|
||||
function computePrevTradingDayOhlc(candles, resetHour) {
|
||||
if (!candles || !candles.length) return null;
|
||||
const curTd = tradingDayKeyFromUtcSec(candles[candles.length - 1].time, resetHour);
|
||||
const prevTd = prevTradingDayKey(curTd);
|
||||
if (!prevTd) return null;
|
||||
const dayCandles = candles
|
||||
.filter(function (c) {
|
||||
return c && tradingDayKeyFromUtcSec(c.time, resetHour) === prevTd;
|
||||
})
|
||||
.sort(function (a, b) {
|
||||
return a.time - b.time;
|
||||
});
|
||||
if (!dayCandles.length) return null;
|
||||
let hi = null;
|
||||
let lo = null;
|
||||
dayCandles.forEach(function (c) {
|
||||
if (!hi || c.high > hi) hi = c.high;
|
||||
if (!lo || c.low < lo) lo = c.low;
|
||||
});
|
||||
const last = dayCandles[dayCandles.length - 1];
|
||||
return {
|
||||
close: last.close,
|
||||
high: hi,
|
||||
low: lo,
|
||||
tradingDay: prevTd,
|
||||
};
|
||||
}
|
||||
|
||||
function syncPrevDayLineUi() {
|
||||
const closeOn = !!(elPrevCloseLine && elPrevCloseLine.checked);
|
||||
const hlOn = !!(elPrevHlLines && elPrevHlLines.checked);
|
||||
savePrevCloseLinePref(closeOn);
|
||||
savePrevHlLinesPref(hlOn);
|
||||
updateYesterdayPriceLines();
|
||||
}
|
||||
|
||||
function applyTradingDaySplit(enabled) {
|
||||
@@ -2127,6 +2243,74 @@
|
||||
rangeMarkers = [];
|
||||
}
|
||||
|
||||
function clearYesterdayPriceLines() {
|
||||
if (candleSeries) {
|
||||
yesterdayPriceLines.forEach(function (m) {
|
||||
try {
|
||||
candleSeries.removePriceLine(m);
|
||||
} catch (e) {}
|
||||
});
|
||||
}
|
||||
yesterdayPriceLines = [];
|
||||
}
|
||||
|
||||
function updateYesterdayPriceLines() {
|
||||
clearYesterdayPriceLines();
|
||||
if (!candleSeries || !lastCandles.length) return;
|
||||
const showClose = !!(elPrevCloseLine && elPrevCloseLine.checked);
|
||||
const showHl = !!(elPrevHlLines && elPrevHlLines.checked);
|
||||
if (!showClose && !showHl) return;
|
||||
const stats = computePrevTradingDayOhlc(lastCandles, chartResetHour());
|
||||
if (!stats) return;
|
||||
if (showClose && stats.close != null && Number.isFinite(Number(stats.close))) {
|
||||
const px = Number(roundToTick(stats.close));
|
||||
if (Number.isFinite(px)) {
|
||||
yesterdayPriceLines.push(
|
||||
candleSeries.createPriceLine({
|
||||
price: px,
|
||||
color: "#a78bfa",
|
||||
lineWidth: 1,
|
||||
lineStyle: 2,
|
||||
axisLabelVisible: true,
|
||||
title: "昨收",
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
if (showHl) {
|
||||
if (stats.high != null && Number.isFinite(Number(stats.high))) {
|
||||
const hiPx = Number(roundToTick(stats.high));
|
||||
if (Number.isFinite(hiPx)) {
|
||||
yesterdayPriceLines.push(
|
||||
candleSeries.createPriceLine({
|
||||
price: hiPx,
|
||||
color: "#ffb84d",
|
||||
lineWidth: 1,
|
||||
lineStyle: 2,
|
||||
axisLabelVisible: true,
|
||||
title: "昨高",
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
if (stats.low != null && Number.isFinite(Number(stats.low))) {
|
||||
const loPx = Number(roundToTick(stats.low));
|
||||
if (Number.isFinite(loPx)) {
|
||||
yesterdayPriceLines.push(
|
||||
candleSeries.createPriceLine({
|
||||
price: loPx,
|
||||
color: "#4cd97f",
|
||||
lineWidth: 1,
|
||||
lineStyle: 2,
|
||||
axisLabelVisible: true,
|
||||
title: "昨低",
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function viewKey(exKey, sym, tf) {
|
||||
return (exKey || "") + "|" + (sym || "") + "|" + (tf || "");
|
||||
}
|
||||
@@ -2199,6 +2383,7 @@
|
||||
applyChartRightGap();
|
||||
restoreVisibleLogicalRange(range, lastCandles.length);
|
||||
updateVisibleRangeMarkers();
|
||||
updateYesterdayPriceLines();
|
||||
}
|
||||
applyOnce();
|
||||
requestAnimationFrame(applyOnce);
|
||||
@@ -2249,6 +2434,7 @@
|
||||
function clearChartSeriesData() {
|
||||
lastCandles = [];
|
||||
candleByTime = {};
|
||||
clearYesterdayPriceLines();
|
||||
if (candleSeries) candleSeries.setData([]);
|
||||
if (volumeSeries) volumeSeries.setData([]);
|
||||
}
|
||||
@@ -2315,6 +2501,7 @@
|
||||
} catch (indErr) {}
|
||||
}
|
||||
updateVisibleRangeMarkers();
|
||||
updateYesterdayPriceLines();
|
||||
showLatestOhlcv();
|
||||
return true;
|
||||
}
|
||||
@@ -2349,6 +2536,7 @@
|
||||
applyPriceAutoScale();
|
||||
}
|
||||
updateVisibleRangeMarkers();
|
||||
updateYesterdayPriceLines();
|
||||
try {
|
||||
updateIndicators();
|
||||
} catch (indErr) {}
|
||||
@@ -3036,6 +3224,14 @@
|
||||
updateIndicators();
|
||||
});
|
||||
});
|
||||
if (elPrevCloseLine) {
|
||||
elPrevCloseLine.checked = loadPrevCloseLinePref();
|
||||
elPrevCloseLine.addEventListener("change", syncPrevDayLineUi);
|
||||
}
|
||||
if (elPrevHlLines) {
|
||||
elPrevHlLines.checked = loadPrevHlLinesPref();
|
||||
elPrevHlLines.addEventListener("change", syncPrevDayLineUi);
|
||||
}
|
||||
if (elDaySplit) {
|
||||
elDaySplit.checked = loadDaySplitPref();
|
||||
elDaySplit.addEventListener("change", syncTradingDaySplitUi);
|
||||
|
||||
@@ -132,6 +132,12 @@
|
||||
<span id="mkt-symbol-label">—</span>
|
||||
<span id="mkt-tf-label">1d</span>
|
||||
<div class="market-chart-actions">
|
||||
<label class="market-day-split-opt" title="上一交易日收盘价水平线(北京切日)">
|
||||
<input type="checkbox" id="market-prev-close-line" /> 昨日收盘价
|
||||
</label>
|
||||
<label class="market-day-split-opt" title="上一交易日最高/最低价水平线(北京切日)">
|
||||
<input type="checkbox" id="market-prev-hl-lines" /> 昨日高低点
|
||||
</label>
|
||||
<label class="market-day-split-opt" title="北京时间 8:00 交易切日竖线">
|
||||
<input type="checkbox" id="market-day-split" /> 交易间隔日
|
||||
</label>
|
||||
@@ -588,7 +594,7 @@
|
||||
<div id="toast"></div>
|
||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="/assets/chart_draw.js?v=20260609-market-day-split"></script>
|
||||
<script src="/assets/chart.js?v=20260609-market-day-split"></script>
|
||||
<script src="/assets/chart.js?v=20260609-prev-day-lines"></script>
|
||||
<script src="/assets/archive.js?v=20260612-archive-ai-chat"></script>
|
||||
<script src="/assets/funds.js?v=20260609-hub-funds-fold"></script>
|
||||
<script src="/assets/dashboard.js?v=20260612-dash-monitor-count"></script>
|
||||
|
||||
Reference in New Issue
Block a user