lower time frame trendlines blocked from dma chart
This commit is contained in:
parent
7c5f0a7627
commit
913eec34ea
5 changed files with 56 additions and 5 deletions
|
|
@ -1094,6 +1094,12 @@ same effective style is applied to rare SVG endpoint bridges so an off-grid
|
|||
anchor cannot introduce a visible style seam. Browser coverage checks 30m-on-5m
|
||||
and native 30m rendering separately.
|
||||
|
||||
Sloped manual lines also stop propagating upward: a 30m line is available on
|
||||
30m and lower charts but hidden on 1h/1d. This is a display filter only; the
|
||||
drawing remains loaded and flat price levels remain visible everywhere. The
|
||||
daily-chart regression creates a real 30m drawing, confirms it is rendered on
|
||||
the lower chart, switches to 1d, and requires only its series to disappear.
|
||||
|
||||
### 2026-08-15 — 1m zoom-out died at ~1am because the socket sent 1,000 bars
|
||||
|
||||
Compressing the 1m time scale stopped around 01:00, then looked empty.
|
||||
|
|
|
|||
11
docs/plan.md
11
docs/plan.md
|
|
@ -122,8 +122,10 @@ if your parser doesn't filter those, that fixture will catch it.
|
|||
1d base chart + THE DAILY MAs weight 16 ← a switchable base timeframe
|
||||
```
|
||||
|
||||
Base timeframe controls the candles only. **Every level stays visible on every base
|
||||
timeframe** — the 200DMA on a 1-minute chart is the point, not a side effect.
|
||||
Derived levels and flat price levels stay visible on every base timeframe — the
|
||||
200DMA on a 1-minute chart is the point, not a side effect. Sloped manual
|
||||
trendlines are visible on their attributed timeframe and lower charts, but not
|
||||
on higher charts: 30m structure belongs on 30m/15m/5m/1m, not on 1h or 1d.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -917,8 +919,9 @@ LAYERS
|
|||
☐ Hidden levels still count toward confluence
|
||||
```
|
||||
|
||||
- **The base timeframe switcher changes only the candles.** Every level stays on screen
|
||||
— a 200DMA is equally valid on a 1m chart. That is the entire premise of the product.
|
||||
- **Derived and flat levels stay across base timeframes.** A 200DMA is equally
|
||||
valid on a 1m chart. Sloped manual trendlines are the exception: hide them
|
||||
above their attributed timeframe while retaining them on native/lower charts.
|
||||
- **The group checkbox is a master toggle** — unchecking "Daily MAs" hides all five at
|
||||
once; individual periods nest under it.
|
||||
- The colour swatch beside each timeframe is that timeframe's hue, used identically on
|
||||
|
|
|
|||
|
|
@ -1347,7 +1347,8 @@ createApp({
|
|||
function enabled(level) {
|
||||
if (level.kind === 'ma') return (prefs.value.enabled.ma[level.tf] || []).includes(level.period);
|
||||
if (level.kind === 'manual') {
|
||||
return prefs.value.enabled.drawings && prefs.value.enabled.manual;
|
||||
return prefs.value.enabled.drawings && prefs.value.enabled.manual
|
||||
&& ConfluenceChart.manualLevelVisibleOn(level, timeframe.value);
|
||||
}
|
||||
if (level.kind === 'horizontal') return prefs.value.enabled.horizontal;
|
||||
if (level.kind === 'vwap') return prefs.value.enabled.vwap;
|
||||
|
|
|
|||
|
|
@ -1250,6 +1250,11 @@ class ConfluenceChart {
|
|||
return level.kind === 'ma' || level.kind === 'vwap' || level.kind === 'horizontal';
|
||||
}
|
||||
|
||||
static manualLevelVisibleOn(level, displayedTf) {
|
||||
if (level.kind !== 'manual' || ConfluenceChart.isFlat(level)) return true;
|
||||
return ConfluenceChart.tfRank[level.tf] >= ConfluenceChart.tfRank[displayedTf];
|
||||
}
|
||||
|
||||
manualLineDisplayStyle(level) {
|
||||
const baseWidth = level.line_width || 1;
|
||||
const displayedTf = this.bars[0]?.tf;
|
||||
|
|
|
|||
|
|
@ -358,6 +358,42 @@ test('higher-timeframe trendlines are dashed and twice their stored width',
|
|||
});
|
||||
});
|
||||
|
||||
test('a lower-timeframe trendline is hidden from the daily chart',
|
||||
{ timeout: 180000 }, async () => {
|
||||
await withChart(async page => {
|
||||
const created = await page.evaluate(async () => {
|
||||
const bars = (await (await fetch('/api/bars?tf=30m&limit=2')).json()).bars;
|
||||
const first = bars[0];
|
||||
const second = bars[1];
|
||||
const response = await fetch('/api/lines', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
tf: '30m', side: 'support', anchor_t: first.t, anchor_p: first.l,
|
||||
end_t: second.t, end_p: first.l + 1, note: `daily filter ${Date.now()}`,
|
||||
}),
|
||||
});
|
||||
return response.json();
|
||||
});
|
||||
await page.waitForFunction(id => window.__chart.levelSeries.has(id), created.id);
|
||||
|
||||
await page.click('.timeframes button:text-is("1d")');
|
||||
await page.waitForFunction(() => window.__chart.bars[0]?.tf === '1d');
|
||||
await page.waitForFunction(id => !window.__chart.levelSeries.has(id), created.id);
|
||||
const state = await page.evaluate(async id => ({
|
||||
drawingStillStored: (await (await fetch('/api/drawings')).json()).drawings
|
||||
.some(drawing => drawing.id === id),
|
||||
flatLevelVisible: ConfluenceChart.manualLevelVisibleOn(
|
||||
{ kind: 'manual', slope: 0, tf: '30m' }, '1d',
|
||||
),
|
||||
}), created.id);
|
||||
|
||||
assert.equal(state.drawingStillStored, true, 'daily filtering deleted the drawing state');
|
||||
assert.equal(state.flatLevelVisible, true, 'daily filtering hid a flat price level');
|
||||
assertNoPageErrors(page, assert);
|
||||
});
|
||||
});
|
||||
|
||||
test('a click, a move, then a click starts at the first click', { timeout: 180000 }, async () => {
|
||||
await withChart(async page => {
|
||||
const box = await chartBox(page);
|
||||
|
|
|
|||
Loading…
Reference in a new issue