gendesign/tradein-mvp/backend/data/sql/292_cbr_macro_series.sql
bot-backend 2abab8218e
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m16s
feat(mera): макро-ряды ЦБ РФ — ипотека по субъектам и ключевая ставка
Три XLSX ЦБ (выдачи, ставка, задолженность) в разрезе субъектов, помесячно
с 01.2019, + ключевая ставка через SOAP DailyInfo.asmx (метод KeyRate; GET
на нём не работает, только POST). Всё анонимно, без ключа.

Таблицы: cbr_mortgage_series (region, period_month, series) и cbr_key_rate.
fetched_at НЕ обновляется в DO UPDATE — по уроку #2846 колонка значит
«когда мы ВПЕРВЫЕ увидели период» и по ней меряется такт публикации источника.

Раскладка листов у ЦБ РАЗНАЯ, и это ловушка: в 02_11/02_13 шапка периодов в
строке 3 текстом («Январь 2019»), а в 02_14 (задолженность) — уже в строке 2 и
настоящими datetime. С захардкоженным индексом строки серия debt_rub молча
давала НОЛЬ строк при зелёных тестах. Теперь строка шапки ищется динамически:
берётся строка с наибольшим числом распознанных периодов среди первых шести.

Проверено на живых файлах: каждая из трёх серий даёт 8 736 точек
(96 территорий × 91 месяц, 01.2019–07.2026); по Свердловской области 91 месяц,
последняя ставка 11.49.

estimator.py не тронут: как ипотечная ставка войдёт в оценку — отдельное
продуктовое решение, этот PR только про данные.

Миграции 292 (таблицы) и 293 (сид расписания, enabled=false, interval_days 7).
Новая зависимость: openpyxl.
2026-09-09 00:33:12 +03:00

87 lines
5.8 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 292_cbr_macro_series.sql
-- Макро-ряды ЦБ РФ: ипотека по субъектам + ключевая ставка.
--
-- Context:
-- ЦБ РФ публикует месячную статистику по ипотечным жилищным кредитам (объём выдач,
-- ставки, задолженность) в разрезе субъектов РФ — три XLSX-дашборда на cbr.ru
-- (см. app/services/cbr_macro.py::CBR_MORTGAGE_SERIES) — и дневную ключевую ставку
-- через SOAP-сервис DailyInfoWebServ. Обе серии нужны как макро-контекст для
-- продукта (региональная динамика ипотечного рынка); загрузку в estimator этот
-- PR не делает — только данные и их сбор (app/tasks/cbr_macro_pull.py).
--
-- Schema notes:
-- cbr_mortgage_series
-- * region — территория как в источнике (РФ целиком / федеральный округ /
-- субъект), без нормализации кодов ОКАТО.
-- * period_month — первое число отчётного месяца (шапка XLSX «Месяц Год»).
-- * series — slug серии (rate_rub / new_loans_rub / debt_rub, конфиг —
-- CBR_MORTGAGE_SERIES в коде).
-- * PRIMARY KEY (region, period_month, series) — одно значение на территорию×месяц×серию.
-- * fetched_at — «когда мы ВПЕРВЫЕ увидели этот период» (не время последней
-- загрузки — источник даёт всю серию целиком, отсечки по
-- периоду нет). Урок #2846 у sber_price_index: НЕ обновлять
-- fetched_at в ON CONFLICT DO UPDATE, иначе один прогон
-- перезаписывает метку у всех периодов, включая 2019-01.
-- cbr_key_rate
-- * rate_date — дата действия ключевой ставки (PRIMARY KEY).
-- * rate — значение ставки, % годовых.
-- * fetched_at — по той же логике, что и выше: не трогаем в DO UPDATE.
--
-- Idempotent: CREATE TABLE IF NOT EXISTS — безопасно переприменять. Только ADDITIVE.
--
-- Dependencies: нет (самостоятельные таблицы).
-- Deploy order: применить до app/services/cbr_macro.py + app/tasks/cbr_macro_pull.py
-- + миграции 291 (сид scrape_schedules).
BEGIN;
SET LOCAL lock_timeout = '5s';
CREATE TABLE IF NOT EXISTS cbr_mortgage_series (
region text NOT NULL,
period_month date NOT NULL,
series text NOT NULL,
value double precision NOT NULL,
source text NOT NULL DEFAULT 'cbr',
fetched_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (region, period_month, series)
);
COMMENT ON TABLE cbr_mortgage_series IS
'Месячные ряды ипотечной статистики ЦБ РФ по территориям (РФ / ФО / субъекты): '
'объём выдач, ставки, задолженность. Источник — XLSX-дашборды cbr.ru/vfs/statistics/'
'BankSector/Mortgage/. Загружается app/tasks/cbr_macro_pull.py через in-app '
'планировщик (scrape_schedules.source=''cbr_macro_pull'').';
COMMENT ON COLUMN cbr_mortgage_series.region IS
'Территория как в источнике (например «РОССИЙСКАЯ ФЕДЕРАЦИЯ», «ЦЕНТРАЛЬНЫЙ '
'ФЕДЕРАЛЬНЫЙ ОКРУГ», «Свердловская область»). Без нормализации ОКАТО/ISO.';
COMMENT ON COLUMN cbr_mortgage_series.period_month IS
'Первое число отчётного месяца (нормализовано из шапки «Месяц Год» XLSX).';
COMMENT ON COLUMN cbr_mortgage_series.series IS
'Slug серии, см. CBR_MORTGAGE_SERIES в app/services/cbr_macro.py '
'(rate_rub, new_loans_rub, debt_rub).';
COMMENT ON COLUMN cbr_mortgage_series.value IS
'Значение серии в единицах источника (% годовых для ставки, млн руб для объёмов).';
COMMENT ON COLUMN cbr_mortgage_series.fetched_at IS
'Когда мы ВПЕРВЫЕ увидели этот период (не время последней загрузки). '
'НЕ обновляется в ON CONFLICT DO UPDATE — см. урок #2846 у sber_price_index.';
CREATE INDEX IF NOT EXISTS idx_cbr_mortgage_series_region_series_period
ON cbr_mortgage_series (region, series, period_month DESC);
CREATE TABLE IF NOT EXISTS cbr_key_rate (
rate_date date PRIMARY KEY,
rate double precision NOT NULL,
fetched_at timestamptz NOT NULL DEFAULT now()
);
COMMENT ON TABLE cbr_key_rate IS
'Ключевая ставка ЦБ РФ по датам действия. Источник — SOAP-метод KeyRate '
'сервиса https://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx. Загружается '
'app/tasks/cbr_macro_pull.py.';
COMMENT ON COLUMN cbr_key_rate.rate_date IS 'Дата, с которой действует значение ставки.';
COMMENT ON COLUMN cbr_key_rate.rate IS 'Ключевая ставка, % годовых.';
COMMENT ON COLUMN cbr_key_rate.fetched_at IS
'Когда мы ВПЕРВЫЕ увидели эту дату. НЕ обновляется в ON CONFLICT DO UPDATE.';
COMMIT;