From 3f677a333c65f5a6e832e90edd6bd3164fc7de89 Mon Sep 17 00:00:00 2001 From: Chris Amow Date: Mon, 10 Aug 2026 23:32:39 -0500 Subject: [PATCH] Report where the snap indicator is drawn, not just what it computed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Diagnostic mode proved the arithmetic: on a 30m chart every sample snapped to the correct bar and to exactly that bar's low, while the user still saw the indicator sitting off the bars. That leaves the half never measured in their browser — whether the dot is drawn where the price says it should be. The report now carries the cursor's y, the dot's rendered y, the y the snapped price maps to, and the y coordinates of the snapped bar's high and low. A correctly computed answer drawn in the wrong place and a wrong answer drawn faithfully look identical on a screenshot; these four numbers separate them. Co-Authored-By: Claude Opus 5 --- app/api/routes.py | 9 +++++++++ static/chart.js | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/app/api/routes.py b/app/api/routes.py index 530748a..4ebbd0c 100644 --- a/app/api/routes.py +++ b/app/api/routes.py @@ -213,6 +213,15 @@ class SnapReport(BaseModel): last_bar_t: int | None = None tf: str | None = None chart_w: float | None = None + chart_h: float | None = None + cursor_y: float | None = None + # Where the indicator actually landed versus where the price says it should + # — the only way to tell a wrong answer from a correctly-computed one drawn + # in the wrong place. + dot_y: float | None = None + expected_y: float | None = None + bar_low_y: float | None = None + bar_high_y: float | None = None note: str | None = None diff --git a/static/chart.js b/static/chart.js index 7a5fe13..888facf 100644 --- a/static/chart.js +++ b/static/chart.js @@ -642,6 +642,23 @@ class ConfluenceChart { bars_held: this.bars.length, first_bar_t: this.bars[0].t, last_bar_t: this.bars[this.bars.length - 1].t, chart_w: this.chartEl.clientWidth, + chart_h: this.chartEl.clientHeight, + cursor_y: Math.round(point.y), + dot_y: this.snapDot && !this.snapDot.hidden + ? Math.round(this.snapDot.getBoundingClientRect().top + - this.chartEl.getBoundingClientRect().top) + 5 + : null, + expected_y: Math.round(y), + bar_low_y: (() => { + const bar = this.bars.find(candle => candle.t === snapped.t); + const at = bar && this.candles.priceToCoordinate(bar.l); + return at == null ? null : Math.round(at); + })(), + bar_high_y: (() => { + const bar = this.bars.find(candle => candle.t === snapped.t); + const at = bar && this.candles.priceToCoordinate(bar.h); + return at == null ? null : Math.round(at); + })(), note: point.t == null ? 'coordinateToTime returned null' : `drift ${drift}s (${Math.round(drift / step)} bars), step ${step}s`,