This commit is contained in:
Chris Amow 2026-08-15 03:50:38 -05:00
parent 73570d2904
commit 0c7e81d222
3 changed files with 13 additions and 3 deletions

View file

@ -1069,3 +1069,8 @@ so nine elapsed minutes occupy nine logical slots and the line remains straight.
Gaps over 30 minutes stay compressed, preserving the established treatment of the
65-minute settlement break and weekends. A browser regression pins both the
nine-slot spacing and equal historical/future screen slope.
Production then showed that whitespace-only points could still disappear from the
shared scale. The time-scale owner now uses transparent zero-valued points with an
`autoscaleInfoProvider` that returns null. They remain invisible and cannot affect
price range, but Lightweight Charts retains their timestamps as real series data.

View file

@ -233,6 +233,7 @@ class ConfluenceChart {
// something to label and so timeToCoordinate answers out there.
this.futureSpace = this.chart.addSeries(LightweightCharts.LineSeries, {
priceScaleId: 'right',
color: 'rgba(0,0,0,0)',
lastValueVisible: false,
priceLineVisible: false,
crosshairMarkerVisible: false,
@ -531,7 +532,7 @@ class ConfluenceChart {
if (step <= 0) return [];
const slots = [];
for (let index = 1; index <= ConfluenceChart.FUTURE_SLOTS; index += 1) {
slots.push({ time: last.t + index * step });
slots.push({ time: last.t + index * step, value: 0 });
}
return slots;
}
@ -544,7 +545,7 @@ class ConfluenceChart {
const gap = bars[index].t - bars[index - 1].t;
if (gap <= step || gap > ConfluenceChart.MAX_INTRADAY_GAP_SECONDS) continue;
for (let time = bars[index - 1].t + step; time < bars[index].t; time += step) {
slots.push({ time });
slots.push({ time, value: 0 });
}
}
return [...slots, ...this.futureTimes(bars)];
@ -585,7 +586,7 @@ class ConfluenceChart {
return;
}
const futureTime = bar.t + ConfluenceChart.FUTURE_SLOTS * step;
this.futureSpace.update({ time: futureTime });
this.futureSpace.update({ time: futureTime, value: 0 });
for (const level of this.levels) {
if (level.kind !== 'manual' || level.hidden || ConfluenceChart.isFlat(level)
|| futureTime < level.anchor_t || futureTime > (level.cutoff_t ?? Infinity)) continue;

View file

@ -201,11 +201,15 @@ test('a short final data gap does not kink a future trendline',
historicalSlope: (current.y - previous.y) / (current.x - previous.x),
futureSlope: (future.y - current.y) / (future.x - current.x),
historicalSlots: (current.x - previous.x) / (future.x - current.x),
missingSlotOwned: c.futureSpace.data()
.some(point => Number(point.time) === previousTime + minute),
};
});
assert.ok(Math.abs(result.historicalSlots - 9) < 0.01,
`a nine-minute data gap occupied ${result.historicalSlots} slots`);
assert.equal(result.missingSlotOwned, true,
'the invisible scale series discarded the missing intraday timestamp');
assert.ok(Math.abs(result.historicalSlope - result.futureSlope) < 1e-9,
`line kinked from ${result.historicalSlope} to ${result.futureSlope}`);
assertNoPageErrors(page, assert);