Fix review chart markers to use containing bar time, not nearest open.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+49
-20
@@ -1,6 +1,7 @@
|
||||
/** 复盘四周期:固定根数裁剪;进出场只用圆点,价签放在图外图例 */
|
||||
/** 复盘四周期:按「包含成交时刻」的 K 线落点,避免偏到下一根 */
|
||||
(function () {
|
||||
var TF_MAX = { M1: 500, M5: 200, M15: 100, H1: 50 };
|
||||
var TF_SEC = { M1: 60, M5: 300, M15: 900, H1: 3600 };
|
||||
|
||||
function num(v) {
|
||||
if (v == null || v === "") return null;
|
||||
@@ -8,18 +9,23 @@
|
||||
return Number.isFinite(n) ? n : null;
|
||||
}
|
||||
|
||||
function nearestBarIndex(bars, ts) {
|
||||
/** 找到覆盖 ts 的那根 K 线:barOpen <= ts < barOpen+period(最后一根含右边界) */
|
||||
function barIndexAt(bars, ts, tf) {
|
||||
if (ts == null || !bars.length) return -1;
|
||||
var best = 0;
|
||||
var bestDiff = Math.abs(Number(bars[0].ts) - ts);
|
||||
for (var i = 1; i < bars.length; i++) {
|
||||
var d = Math.abs(Number(bars[i].ts) - ts);
|
||||
if (d < bestDiff) {
|
||||
bestDiff = d;
|
||||
best = i;
|
||||
var period = TF_SEC[tf] || 60;
|
||||
var t = Number(ts);
|
||||
var i;
|
||||
for (i = 0; i < bars.length; i++) {
|
||||
var open = Number(bars[i].ts);
|
||||
var end = open + period;
|
||||
var last = i === bars.length - 1;
|
||||
if (t >= open && (t < end || (last && t <= end))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
// 落在首根之前 / 末根之后:夹到边界
|
||||
if (t < Number(bars[0].ts)) return 0;
|
||||
return bars.length - 1;
|
||||
}
|
||||
|
||||
function cropBars(bars, markers, tf) {
|
||||
@@ -27,8 +33,9 @@
|
||||
var maxN = TF_MAX[tf] || 100;
|
||||
if (bars.length <= maxN) return bars;
|
||||
|
||||
var a = nearestBarIndex(bars, markers.openTs);
|
||||
var b = markers.closeTs != null ? nearestBarIndex(bars, markers.closeTs) : a;
|
||||
var a = barIndexAt(bars, markers.openTs, tf);
|
||||
var b =
|
||||
markers.closeTs != null ? barIndexAt(bars, markers.closeTs, tf) : a;
|
||||
if (a < 0) a = Math.floor(bars.length / 2);
|
||||
if (b < 0) b = a;
|
||||
var lo = Math.min(a, b);
|
||||
@@ -41,6 +48,17 @@
|
||||
return bars.slice(start, end);
|
||||
}
|
||||
|
||||
function fmtBjt(ts) {
|
||||
if (ts == null) return "";
|
||||
var d = new Date(Number(ts) * 1000);
|
||||
// 用 UTC+8 显示,与复盘页北京时间一致
|
||||
var utc = d.getTime() + d.getTimezoneOffset() * 60000;
|
||||
var bjt = new Date(utc + 8 * 3600000);
|
||||
var hh = String(bjt.getHours()).padStart(2, "0");
|
||||
var mm = String(bjt.getMinutes()).padStart(2, "0");
|
||||
return hh + ":" + mm;
|
||||
}
|
||||
|
||||
function drawCandleChart(canvas, bars, markers, tf) {
|
||||
if (!canvas || !bars || !bars.length) return;
|
||||
bars = cropBars(bars, markers || {}, tf);
|
||||
@@ -57,7 +75,7 @@
|
||||
var padL = 4;
|
||||
var padR = 4;
|
||||
var padT = 4;
|
||||
var padB = 4;
|
||||
var padB = 16; // 底部时间轴
|
||||
|
||||
var min = Infinity;
|
||||
var max = -Infinity;
|
||||
@@ -112,12 +130,20 @@
|
||||
ctx.fillRect(x - bodyW / 2, top, bodyW, bh);
|
||||
});
|
||||
|
||||
// 仅圆点,不画横线/竖线/价签(价签在图例外)
|
||||
// 底部左右时间(北京时间),方便对照成交时刻
|
||||
ctx.fillStyle = "#888899";
|
||||
ctx.font = "10px Microsoft YaHei UI, sans-serif";
|
||||
ctx.textBaseline = "top";
|
||||
ctx.textAlign = "left";
|
||||
ctx.fillText(fmtBjt(bars[0].ts), padL, cssH - padB + 2);
|
||||
ctx.textAlign = "right";
|
||||
ctx.fillText(fmtBjt(bars[n - 1].ts), cssW - padR, cssH - padB + 2);
|
||||
|
||||
var entryColor = "#4cc2ff";
|
||||
var exitColor = "#f0b429";
|
||||
var entryIdx = nearestBarIndex(bars, markers.openTs);
|
||||
var entryIdx = barIndexAt(bars, markers.openTs, tf);
|
||||
var exitIdx =
|
||||
markers.closeTs != null ? nearestBarIndex(bars, markers.closeTs) : -1;
|
||||
markers.closeTs != null ? barIndexAt(bars, markers.closeTs, tf) : -1;
|
||||
var hasExit =
|
||||
markers.closeTs != null &&
|
||||
markers.closePrice != null &&
|
||||
@@ -143,7 +169,6 @@
|
||||
pe = drawDot(entryIdx, markers.openPrice, entryColor);
|
||||
}
|
||||
if (hasExit) {
|
||||
// 同一根或相邻:出场点略向右错开,避免叠成一团
|
||||
if (Math.abs(exitIdx - entryIdx) <= 1) {
|
||||
var x = xOf(exitIdx) + Math.max(6, slot * 0.8);
|
||||
var y = yOf(markers.closePrice);
|
||||
@@ -163,7 +188,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 极短引线:点旁只写「入」「出」一字,不写价格
|
||||
ctx.font = "bold 10px Microsoft YaHei UI, sans-serif";
|
||||
if (pe) {
|
||||
ctx.fillStyle = entryColor;
|
||||
@@ -178,10 +202,15 @@
|
||||
ctx.fillText("出", px.x, px.y - 6);
|
||||
}
|
||||
|
||||
// 更新图外图例里的实际显示根数
|
||||
var meta = canvas.parentElement && canvas.parentElement.querySelector(".chart-meta");
|
||||
if (meta) {
|
||||
meta.textContent = bars.length + " 根";
|
||||
meta.textContent =
|
||||
bars.length +
|
||||
" 根 · " +
|
||||
fmtBjt(bars[0].ts) +
|
||||
"–" +
|
||||
fmtBjt(bars[n - 1].ts) +
|
||||
" 北京时间";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user