-- 092_sber_price_index.sql -- Issue #887 — city-level secondary-housing price index from СберИндекс (sberindex.ru). -- -- Context: -- СберИндекс publishes monthly city/region-level secondary-housing price statistics -- across three dashboards: -- * real_estate_deals — hedonic sold-price index (руб/м²) -- * residential_real_estate_prices — repeat-sales / asking-adjusted hedonic -- * dinamika-tsen-obyavlenii — asking-price benchmark (объявления, руб/м²) -- -- The pull service (app/services/sber_index.py) fetches all three series for configured -- cities (initially РФ-643, with EKB/MSK codes TBD — see TODO in sber_index.py) via the -- /api/sowa POST endpoint (reverse-engineered in #794 research note) and upserts rows here. -- -- The table feeds the estimator time-adjust coefficient (step 4, #887 out-of-scope — -- backtest-gated, separate follow-up). -- -- Schema notes: -- * city — REF_AREA label as returned by the API (e.g. «Россия», «Екатеринбург»). -- * period_month — first day of the reported month (normalised from API timestamp). -- * segment — realty type label from API (e.g. «Вторичный»). -- * dashboard — slug used in the route (e.g. 'real_estate_deals'). -- * PRIMARY KEY (city, period_month, dashboard) — one value per city×month×series; -- upsert on conflict keeps the freshest fetched_at. -- -- Idempotent: CREATE TABLE IF NOT EXISTS — safe to re-apply. ADDITIVE only, no drops. -- -- Dependencies: none (standalone table). -- -- Deploy order: apply before deploying app/services/sber_index.py + -- app/tasks/sber_index_pull.py + scheduler.py changes. BEGIN; CREATE TABLE IF NOT EXISTS sber_price_index ( city text NOT NULL, period_month date NOT NULL, segment text NOT NULL DEFAULT '', dashboard text NOT NULL, index_value_rub_m2 double precision NOT NULL, source text NOT NULL DEFAULT 'sberindex', fetched_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (city, period_month, dashboard) ); COMMENT ON TABLE sber_price_index IS 'Monthly city-level secondary-housing price index from СберИндекс (sberindex.ru). ' 'Three series: real_estate_deals (hedonic sold), residential_real_estate_prices ' '(repeat-sales hedonic), dinamika-tsen-obyavlenii (asking benchmark). ' 'Populated by app/tasks/sber_index_pull.py via in-app scheduler (#887). ' 'Estimator time-adjust wire is a separate backtest-gated follow-up (step 4, #887).'; COMMENT ON COLUMN sber_price_index.city IS 'REF_AREA label from the API response (e.g. «Россия», «Екатеринбург»).'; COMMENT ON COLUMN sber_price_index.period_month IS 'First day of the reported month (normalised from API ISO timestamp).'; COMMENT ON COLUMN sber_price_index.segment IS 'Realty type label from API (e.g. «Вторичный»). Empty string when absent.'; COMMENT ON COLUMN sber_price_index.dashboard IS 'Dataset slug used in the /dataset/v1/ route.'; COMMENT ON COLUMN sber_price_index.index_value_rub_m2 IS 'Price index in rubles per square metre.'; COMMENT ON COLUMN sber_price_index.fetched_at IS 'Wall-clock timestamp when this row was last written/updated.'; -- Index for time-series queries (latest N months per city × dashboard) CREATE INDEX IF NOT EXISTS idx_sber_price_index_city_dashboard_period ON sber_price_index (city, dashboard, period_month DESC); COMMIT;