potential 1m fix
This commit is contained in:
parent
eb18de9bb9
commit
c28b0ce535
2 changed files with 70 additions and 5 deletions
|
|
@ -2514,10 +2514,18 @@ class ConfluenceChart {
|
|||
if (bar.t >= level.anchor_t && bar.t <= cutoff) stamps.add(bar.t);
|
||||
}
|
||||
} else {
|
||||
const displayed = this.bars.filter(bar => bar.t >= level.anchor_t && bar.t <= cutoff);
|
||||
for (const time of ConfluenceChart.sourceTimes(source) || []) {
|
||||
if (time >= first && time <= lastBar && time >= level.anchor_t && time <= cutoff) {
|
||||
stamps.add(time);
|
||||
if (time < first || time > lastBar || time < level.anchor_t || time > cutoff) continue;
|
||||
let lo = 0;
|
||||
let hi = displayed.length;
|
||||
while (lo < hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (displayed[mid].t < time) lo = mid + 1;
|
||||
else hi = mid;
|
||||
}
|
||||
const bar = displayed[lo] || displayed[displayed.length - 1];
|
||||
if (bar) stamps.add(bar.t);
|
||||
}
|
||||
if (lastBar >= level.anchor_t && lastBar <= cutoff) stamps.add(lastBar);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,9 +94,10 @@ test('a 30m line keeps source geometry when the 1m window starts after its ancho
|
|||
'the rollback path no longer restores displayed-grid geometry');
|
||||
assert.equal(result.shifted, result.sourceSecondBar,
|
||||
'a source-timeframe nudge did not move by one 30m bar');
|
||||
assert.deepEqual(result.sampledTimes.slice(0, result.displayedTimes.length), result.displayedTimes,
|
||||
'manual line samples no longer match the displayed candle grid');
|
||||
assert.ok(result.sampledTimes.slice(result.displayedTimes.length)
|
||||
const historical = result.sampledTimes.filter(time => time <= result.displayedTimes.at(-1));
|
||||
assert.deepEqual(historical, [result.displayedTimes[0], result.displayedTimes.at(-1)],
|
||||
'a 30m line on 1m should sample source-bar times, not every 1m candle');
|
||||
assert.ok(result.sampledTimes.slice(historical.length)
|
||||
.every(time => time > result.displayedTimes.at(-1)),
|
||||
'manual-line extension mixed future points into candle history');
|
||||
assert.deepEqual(result.trimmedTimes, result.expectedTrimmedTimes,
|
||||
|
|
@ -353,6 +354,62 @@ test('a short final data gap does not kink a future trendline',
|
|||
});
|
||||
});
|
||||
|
||||
test('a 30m line stays straight on 1m when the bucket is missing its first minutes',
|
||||
{ timeout: 180000 }, async () => {
|
||||
await withChart(async page => {
|
||||
const result = await page.evaluate(() => {
|
||||
const c = window.__chart;
|
||||
const minute = 60;
|
||||
const half = 30 * minute;
|
||||
const open = 400000;
|
||||
const bars = [];
|
||||
for (let t = open - 2 * half; t < open; t += minute) {
|
||||
bars.push({ tf: '1m', t, o: 100, h: 101, l: 99, c: 100, v: 1, closed: true });
|
||||
}
|
||||
for (let t = open + 9 * minute; t < open + 2 * half; t += minute) {
|
||||
bars.push({ tf: '1m', t, o: 100, h: 101, l: 99, c: 100, v: 1, closed: true });
|
||||
}
|
||||
const line = {
|
||||
id: 'late-open', kind: 'manual', tf: '30m', side: 'support', label: 'late',
|
||||
anchor_t: open - 2 * half, anchor_p: 100, last_t: open,
|
||||
slope: 3 / half, cutoff_t: null, hidden: false, color: '#65b7cf',
|
||||
line_width: 2, provisional: false,
|
||||
};
|
||||
c.setDisplayFutureTimes(Array.from(
|
||||
{ length: ConfluenceChart.FUTURE_SLOTS }, (_, index) => bars.at(-1).t + (index + 1) * minute,
|
||||
));
|
||||
c.setBars(bars);
|
||||
c.setTrendlineGeometry({
|
||||
mode: 'source_tf',
|
||||
series: { '30m': { times: [open - 2 * half, open - half, open, open + half], duration: half } },
|
||||
});
|
||||
c.syncLevels([line]);
|
||||
const data = c.levelSeries.get(line.id).series.data();
|
||||
const point = time => {
|
||||
const row = data.find(value => Number(value.time) === time);
|
||||
return {
|
||||
x: c.chart.timeScale().timeToCoordinate(time),
|
||||
y: row ? c.candles.priceToCoordinate(row.value) : null,
|
||||
};
|
||||
};
|
||||
const a = point(open - half);
|
||||
const b = point(open + 9 * minute);
|
||||
const d = point(open + half);
|
||||
return {
|
||||
snappedOpen: data.some(value => Number(value.time) === open + 9 * minute),
|
||||
injectedOpen: data.some(value => Number(value.time) === open),
|
||||
lateSlope: (b.y - a.y) / (b.x - a.x),
|
||||
fullSlope: (d.y - b.y) / (d.x - b.x),
|
||||
};
|
||||
});
|
||||
assert.equal(result.injectedOpen, false, '30m open that is not a 1m bar was placed on the scale');
|
||||
assert.equal(result.snappedOpen, true, 'missing 30m open was not snapped to the first 1m bar');
|
||||
assert.ok(Math.abs(result.lateSlope - result.fullSlope) < 1e-6,
|
||||
`late-open 30m segment kinked from ${result.lateSlope} to ${result.fullSlope}`);
|
||||
assertNoPageErrors(page, assert);
|
||||
});
|
||||
});
|
||||
|
||||
test('painted canvas pixels keep the same slope through the live edge',
|
||||
{ timeout: 180000 }, async () => {
|
||||
await withChart(async page => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue