feat(tradein): СберИндекс time-adjust for frozen Rosreestr ДКП + per-dashboard filter fix (#794) #919
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#919
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/794-sber-time-adjust"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #794 (continues the data layer merged in #890/#887).
Context
#887 (PR #890) shipped the СберИндекс data layer (
sber_index.py, tablesber_price_index, monthly pull task + scheduler seed) but deliberately deferred the estimator wire (deliverable 3) and only half-did the asking benchmark (deliverable 4). It also shipped a filter bug. This PR closes the gaps and wires the coefficient live.🔴 Bug fixed: per-dashboard filter
build_sber_routehardcodedSOURCE:SI+REALTY:2for all three dashboards. Verified live against/api/sowa(2026-05-31):SI/REALTY:2)real_estate_dealsresidential_real_estate_pricesREAL_ESTATE_TYPE:1+REAL_ESTATE_NOVELTY:3→ 43 rowsdinamika-tsen-obyavleniiSOURCE:DK+PRICE_TYPE:1→ 52 rowsSo in prod the hedonic series (the preferred coefficient) and the asking benchmark were silently empty. Now each dashboard carries its own filter + segment field via a
SberDashboarddataclass. All three pull the secondary-market (вторичка) ряд. Live-verified latest values: deals 97 973 ₽/м², hedonic 109 688, asking 96 052 (REF_AREA 66 = Свердловская область).Deliverable 3 — estimator time-adjust wire (LIVE)
_fetch_dkp_corridor(#652 ДКП corridor) now re-bases each stale Rosreestr ДКП ppm² to the latest available index month:residential_real_estate_prices) → deals (real_estate_deals) fallback.sber_price_indexis empty/missing (so it is a no-op until the fixed pull populates the table — zero blast radius today, prod table currently empty since the monthly scheduler hasn't fired)._sber_time_factoris a pure, fully unit-tested function. Corridor return-dict shape unchanged;actual_dealsdisplay left untouched (shows recorded prices, not adjusted).Deliverable 4 — asking benchmark reconciliation
After the dinamika (asking) upsert, logs our listings scrape-median asking ppm² vs the СберИндекс asking series + divergence %, gated to the ЕКБ region (REF_AREA 66) since our listings are EKB-only. Best-effort, log-only data-quality signal.
Tests
test_sber_index.py— per-dashboard route filter (locks the bug fix), decoder against a real captured fixture, period parse.test_estimator_sber_time_adjust.py—_sber_time_factoredge cases (empty, latest, nearest-earlier, older-than-series, zero base, clamp hi/lo).Notes / follow-ups (non-blocking)
sber_price_index(092) is reused as-is.SberDashboard.extra_filterimmutability.🤖 Generated with Claude Code
✅ APPROVE — корректно, inert-today, хорошо покрыто тестами. Мержу. Один actionable follow-up (backtest gate) заведу отдельным issue.
Verified (проверял сам, не по телу PR)
deals.deal_dateсуществует —data/sql/002_core_tables.sql:107(date NOT NULL, indexed). ДобавленныйSELECT price_per_m2, deal_dateбезопасен, регрессии corridor→None нет._sber_time_factor— pure, clamp[0.7, 1.6], graceful→1.0 на empty/zero-base/recent-deal; 8 edge-кейсов покрывают все ветки (empty, ==latest, >latest, normal, clamp hi/lo, nearest-earlier, older-than-series, zero-base)._load_sber_index_series—CAST(:region AS text)/CAST(:dash AS text)(psycopg-v3 compliant, не:x::type); graceful{}; hedonic→deals preference.sber_price_indexпуст → factor 1.0 → нулевой blast radius; при заполнении clamp ограничивает эффект.actual_dealsdisplay не трогается, return-shape тот же.SberDashboard(slug/extra_filter/segment_field) —segment_fieldдержит(city,period_month,dashboard)PK одно-значным. Locked parametrized-тестом. Decoder теперь читает имена полей изfields-cell (робастнее хардкод-порядка), провалидирован реальным фикстуром.🟡 Advisory — backtest gate (завожу follow-up issue)
#887/#890 осознанно отложил этот wire с пометкой «client-visible change requires a backtest gate». Этот PR его включает с unit-тестами, но без estimator-backtest. Сегодня это безопасно (table пуст → no-op), НО когда месячный scheduler заполнит
sber_price_index, time-adjust активируется без валидации точности оценки. Митигировано clamp'ом и preference hedonic, но точность надо подтвердить черезscripts/backtest_estimator.pyДО/сразу при активации. Завожу issue — не блокер (inert merge), но gate перед реальной активацией.tech-debt (по плану автора, отдельные issue)
_fetch_dkp_corridor).SberDashboard.extra_filterimmutability.Squash-merge + delete branch.
Closes #794.Pushed
d2722df— fixes the 9 CI test regressions from the refactor (signature change broke pre-existingtests/test_sber_index.py+ 2 dkp tests intest_estimator_imv_blend.py; also deduped a straytests/services/test_sber_index.py). Full suite now green locally (1655 passed; the only 2 failures are the pre-existingapp.main→psycopg2import tests, unrelated, which pass in CI). app/ code unchanged in this commit — tests-only. CI re-running.