fix(db): fill-forward candles buckets

This commit is contained in:
u1
2026-02-02 23:05:04 +01:00
parent ef8f7cbeaa
commit 144f6e7c86

View File

@@ -162,10 +162,12 @@ RETURNS SETOF public.drift_candles
LANGUAGE sql LANGUAGE sql
STABLE STABLE
AS $$ AS $$
-- Zwraca zawsze "ciągłe" buckety (fill forward), nawet jeśli nie było ticków w danej sekundzie/minucie.
-- Dzięki temu frontend może rysować regularną oś czasu (np. 1px = 1s) bez dziwnych przeskoków.
WITH src AS ( WITH src AS (
SELECT COALESCE(p_source, '') AS source_key SELECT COALESCE(p_source, '') AS source_key
), ),
cached AS ( raw_cached AS (
SELECT SELECT
c.bucket, c.bucket,
c.open, c.open,
@@ -181,7 +183,7 @@ AS $$
ORDER BY c.bucket DESC ORDER BY c.bucket DESC
LIMIT p_limit LIMIT p_limit
), ),
fallback AS ( raw_fallback AS (
SELECT SELECT
time_bucket(make_interval(secs => p_bucket_seconds), ts) AS bucket, time_bucket(make_interval(secs => p_bucket_seconds), ts) AS bucket,
ts, ts,
@@ -201,13 +203,88 @@ AS $$
(array_agg(px ORDER BY ts DESC))[1] AS close, (array_agg(px ORDER BY ts DESC))[1] AS close,
(array_agg(oracle_px ORDER BY ts DESC))[1] AS oracle_close, (array_agg(oracle_px ORDER BY ts DESC))[1] AS oracle_close,
count(*) AS ticks count(*) AS ticks
FROM fallback FROM raw_fallback
GROUP BY bucket GROUP BY bucket
),
data AS (
SELECT * FROM raw_cached
UNION ALL
SELECT * FROM computed
WHERE NOT EXISTS (SELECT 1 FROM raw_cached)
),
bounds AS (
SELECT max(bucket) AS end_bucket FROM data
),
params AS (
SELECT
make_interval(secs => p_bucket_seconds) AS step,
make_interval(secs => (p_bucket_seconds * (p_limit - 1))) AS span
),
series AS (
SELECT generate_series(
bounds.end_bucket - params.span,
bounds.end_bucket,
params.step
) AS bucket
FROM bounds, params
WHERE bounds.end_bucket IS NOT NULL
),
joined AS (
SELECT
s.bucket,
d.open,
d.high,
d.low,
d.close,
d.oracle_close,
d.ticks
FROM series s
LEFT JOIN data d USING (bucket)
ORDER BY s.bucket ASC
),
grouped AS (
SELECT
*,
sum(CASE WHEN close IS NOT NULL THEN 1 ELSE 0 END) OVER (ORDER BY bucket ASC) AS grp_close,
sum(CASE WHEN oracle_close IS NOT NULL THEN 1 ELSE 0 END) OVER (ORDER BY bucket ASC) AS grp_oracle
FROM joined
),
first_vals AS (
SELECT
(SELECT close FROM grouped WHERE close IS NOT NULL ORDER BY bucket ASC LIMIT 1) AS first_close,
(SELECT oracle_close FROM grouped WHERE oracle_close IS NOT NULL ORDER BY bucket ASC LIMIT 1) AS first_oracle
),
ff AS (
SELECT
g.bucket,
g.open,
g.high,
g.low,
g.close,
g.oracle_close,
g.ticks,
COALESCE(
g.close,
max(g.close) OVER (PARTITION BY g.grp_close),
f.first_close
) AS ff_close,
COALESCE(
g.oracle_close,
max(g.oracle_close) OVER (PARTITION BY g.grp_oracle),
f.first_oracle
) AS ff_oracle
FROM grouped g
CROSS JOIN first_vals f
) )
SELECT * FROM cached SELECT
UNION ALL bucket,
SELECT * FROM computed COALESCE(open, ff_close) AS open,
WHERE NOT EXISTS (SELECT 1 FROM cached) COALESCE(high, ff_close) AS high,
COALESCE(low, ff_close) AS low,
COALESCE(close, ff_close) AS close,
COALESCE(oracle_close, ff_oracle) AS oracle_close,
COALESCE(ticks, 0) AS ticks
FROM ff
ORDER BY bucket DESC ORDER BY bucket DESC
LIMIT p_limit; LIMIT p_limit;
$$; $$;