Do not put off-window trendline times on the shared scale.

Sampling every source-TF timestamp pulled months of 30m/1d history
onto the 1m chart and dropped the lines. Clip samples to the displayed
window and never hand Lightweight Charts a null value.
This commit is contained in:
Chris Amow 2026-08-26 02:01:45 -05:00
parent becbc0e0b4
commit 715d3eaab8

View file

@ -1305,6 +1305,7 @@ class ConfluenceChart {
} }
} }
for (const level of flat) { for (const level of flat) {
if (!Number.isFinite(level.anchor_p)) continue;
const options = { const options = {
price: level.anchor_p, price: level.anchor_p,
color: ConfluenceChart.levelColor(level), color: ConfluenceChart.levelColor(level),
@ -1355,6 +1356,7 @@ class ConfluenceChart {
} }
return [...byTime.entries()] return [...byTime.entries()]
.sort((a, b) => a[0] - b[0]) .sort((a, b) => a[0] - b[0])
.filter(([, value]) => Number.isFinite(value))
.map(([time, value]) => ({ time, value })); .map(([time, value]) => ({ time, value }));
} }
@ -1418,6 +1420,14 @@ class ConfluenceChart {
} else { } else {
data = level.kind === 'manual' ? this.seriesLineData(level) : this.lineData(level); data = level.kind === 'manual' ? this.seriesLineData(level) : this.lineData(level);
} }
data = (data || []).filter(point => Number.isFinite(point?.value) && point.time != null);
if (!data.length) {
if (entry) {
this.chart.removeSeries(entry.series);
this.levelSeries.delete(level.id);
}
continue;
}
entry.series.setData(data); entry.series.setData(data);
} }
// Re-anchor once, after the level series have reshaped the scale. setBars // Re-anchor once, after the level series have reshaped the scale. setBars
@ -2494,19 +2504,27 @@ class ConfluenceChart {
let points; let points;
if (this.trendlineGeometryMode === 'source_tf') { if (this.trendlineGeometryMode === 'source_tf') {
const cutoff = level.cutoff_t ?? Infinity; const cutoff = level.cutoff_t ?? Infinity;
const first = this.bars[0].t;
const lastBar = this.bars[this.bars.length - 1].t;
const stamps = new Set(); const stamps = new Set();
const source = this.sourceSeries(level); const source = this.sourceSeries(level);
for (const time of ConfluenceChart.sourceTimes(source) || []) { const native = this.bars[0]?.tf === level.tf;
if (time >= level.anchor_t && time <= cutoff) stamps.add(time); if (native) {
for (const bar of this.bars) {
if (bar.t >= level.anchor_t && bar.t <= cutoff) stamps.add(bar.t);
}
} else {
for (const time of ConfluenceChart.sourceTimes(source) || []) {
if (time >= first && time <= lastBar && time >= level.anchor_t && time <= cutoff) {
stamps.add(time);
}
}
if (lastBar >= level.anchor_t && lastBar <= cutoff) stamps.add(lastBar);
} }
stamps.add(level.anchor_t);
stamps.add(level.last_t);
const lastBar = this.bars[this.bars.length - 1]?.t;
if (lastBar != null && lastBar >= level.anchor_t && lastBar <= cutoff) stamps.add(lastBar);
points = [...stamps] points = [...stamps]
.sort((a, b) => a - b) .sort((a, b) => a - b)
.map(time => ({ time, value: this.sourceLinePriceAt(level, time) })) .map(time => ({ time, value: this.sourceLinePriceAt(level, time) }))
.filter(point => point.value != null); .filter(point => Number.isFinite(point.value));
} else { } else {
const start = this.indexAt(level.anchor_t); const start = this.indexAt(level.anchor_t);
const lastBar = this.bars.length - 1; const lastBar = this.bars.length - 1;