-- Serving mart: KC=F Coffee C futures prices, analytics-ready. -- -- Adds moving averages (20-day, 50-day SMA) and 52-week high/low range. -- Filtered to trading days only (NULL close rows excluded upstream). -- -- Grain: one row per trade_date. MODEL ( name serving.coffee_prices, kind INCREMENTAL_BY_TIME_RANGE ( time_column trade_date ), grain (trade_date), start '1971-08-16', cron '@daily' ); WITH base AS ( SELECT f.trade_date, f.open, f.high, f.low, f.close, f.adj_close, f.volume, -- Daily return: (close - prev_close) / prev_close * 100 round( (f.close - LAG(f.close, 1) OVER (ORDER BY f.trade_date)) / NULLIF(LAG(f.close, 1) OVER (ORDER BY f.trade_date), 0) * 100, 4 ) AS daily_return_pct, -- 20-day simple moving average (1 trading month) round( AVG(f.close) OVER (ORDER BY f.trade_date ROWS BETWEEN 19 PRECEDING AND CURRENT ROW), 4 ) AS sma_20d, -- 50-day simple moving average (2.5 trading months) round( AVG(f.close) OVER (ORDER BY f.trade_date ROWS BETWEEN 49 PRECEDING AND CURRENT ROW), 4 ) AS sma_50d, -- 52-week high (approximately 252 trading days) MAX(f.high) OVER (ORDER BY f.trade_date ROWS BETWEEN 251 PRECEDING AND CURRENT ROW) AS high_52w, -- 52-week low MIN(f.low) OVER (ORDER BY f.trade_date ROWS BETWEEN 251 PRECEDING AND CURRENT ROW) AS low_52w FROM foundation.fct_coffee_prices f WHERE f.trade_date BETWEEN @start_ds AND @end_ds ) SELECT b.trade_date, d.commodity_name, d.ticker, b.open, b.high, b.low, b.close, b.adj_close, b.volume, b.daily_return_pct, b.sma_20d, b.sma_50d, b.high_52w, b.low_52w FROM base b CROSS JOIN foundation.dim_commodity d WHERE d.ticker = 'KC=F' ORDER BY b.trade_date