fix(tradein): city-scope ДКП-запросы — убрать oblast-контаминацию (C1+C2) #2489

Merged
bot-backend merged 1 commit from fix/tradein-oblast-city-scope-corridor-ratio into main 2026-07-12 17:20:32 +00:00
Collaborator

Проблема (Critical, из аудита 12.07)

Миграция 177 залила +47 183 ДКП-сделки по всей Свердловской обл. (регион 66, 368 городов, deals.city заполнен) — это намеренное расширение. Но потребители deals матчат по имени улицы без city-фильтра → одноимённые улицы дешёвых городов области контаминируют ЕКБ-числа.

C1 — street-deals + ДКП-коридор (клиент видит это СЕЙЧАС)

estimator._fetch_dkp_corridor + api/v1/trade_in.get_street_deals: добавлен city-скоуп
city IS NOT NULL AND :address ILIKE '%' || city || '%' — канонический deals.city (368 городов) как подстрока целевого адреса (self-normalizing: адрес всегда содержит город «…г. Екатеринбург…»).

Live-repro (Ленина, 2к, 12 мес): median 52 908 → 118 052 ₽/м² (было занижение ~−55%; Ленина есть почти в каждом городе области).

C2 — дневной пересчёт asking→sold ratio (imminent, «выкупная» −29% системно)

tasks/asking_to_sold_ratio: deal_side + deal_global скоупятся на ЕКБ. Обоснование: asking-сторона (listings) покрыта скрейпом только по ЕКБ (per-city scrape B1/B2 ещё нет; в listings даже нет колонки city) → sold и asking должны считаться по одному рынку. Константа _ASKING_CITY_PATTERN. ask-стороны не трогаем.

Live-repro (rooms=2): sold_median all-city 82 593 (ratio 0.622) → EKB 116 858 (ratio 0.880). Предотвращает обвал «выкупной» на следующем пересчёте (~06:58 UTC).

Когда появятся oblast-листинги (B1/B2) → заменить на per-city ratio через зарезервированный district (#647).

Проверка

  • Live DB (оба репро выше — hard numbers на проде).
  • 125 тестов зелёные: subset-тест 080↔refresh (city-guard нормализуется как #1186 segment-guard прецедент) + позитивный тест скоупа; regression-гейт байт-зелёный (коридор заморожен в фикстуре — dkp_raw frozen kwarg, не пересчитывается на replay); expected_sold / idor / 781.
  • Ортогонально DaData #2486. Seed 080 не трогаю (refresh-only предикат, как #1186 — daily refresh самокорректируется).
## Проблема (Critical, из аудита 12.07) Миграция 177 залила **+47 183 ДКП-сделки по всей Свердловской обл.** (регион 66, 368 городов, `deals.city` заполнен) — это намеренное расширение. Но потребители `deals` матчат по имени улицы **без city-фильтра** → одноимённые улицы дешёвых городов области контаминируют ЕКБ-числа. ## C1 — street-deals + ДКП-коридор (клиент видит это СЕЙЧАС) `estimator._fetch_dkp_corridor` + `api/v1/trade_in.get_street_deals`: добавлен city-скоуп `city IS NOT NULL AND :address ILIKE '%' || city || '%'` — канонический `deals.city` (368 городов) как подстрока целевого адреса (self-normalizing: адрес всегда содержит город «…г. Екатеринбург…»). **Live-repro** (Ленина, 2к, 12 мес): median **52 908 → 118 052 ₽/м²** (было занижение ~−55%; Ленина есть почти в каждом городе области). ## C2 — дневной пересчёт asking→sold ratio (imminent, «выкупная» −29% системно) `tasks/asking_to_sold_ratio`: `deal_side` + `deal_global` скоупятся на ЕКБ. Обоснование: asking-сторона (`listings`) покрыта скрейпом **только по ЕКБ** (per-city scrape B1/B2 ещё нет; в `listings` даже нет колонки `city`) → sold и asking должны считаться по одному рынку. Константа `_ASKING_CITY_PATTERN`. ask-стороны не трогаем. **Live-repro** (rooms=2): sold_median all-city **82 593** (ratio **0.622**) → EKB **116 858** (ratio **0.880**). Предотвращает обвал «выкупной» на следующем пересчёте (~06:58 UTC). Когда появятся oblast-листинги (B1/B2) → заменить на per-city ratio через зарезервированный `district` (#647). ## Проверка - **Live DB** (оба репро выше — hard numbers на проде). - **125 тестов зелёные**: subset-тест 080↔refresh (city-guard нормализуется как #1186 segment-guard прецедент) + позитивный тест скоупа; regression-гейт **байт-зелёный** (коридор заморожен в фикстуре — `dkp_raw` frozen kwarg, не пересчитывается на replay); expected_sold / idor / 781. - Ортогонально DaData #2486. Seed 080 не трогаю (refresh-only предикат, как #1186 — daily refresh самокорректируется).
bot-backend added 1 commit 2026-07-12 17:19:09 +00:00
fix(tradein): city-scope ДКП-запросы — убрать oblast-контаминацию (C1+C2)
All checks were successful
CI / changes (pull_request) Successful in 9s
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 51s
f631af4de3
Миграция 177 залила +47183 ДКП-сделки по всей Свердловской обл. (66, 368 городов,
deals.city заполнен). Потребители deals матчат по имени улицы БЕЗ city-фильтра →
одноимённые улицы дешёвых городов области контаминируют ЕКБ-числа.

C1 — street-deals + ДКП-коридор (клиент видит СЕЙЧАС):
- estimator._fetch_dkp_corridor + api/v1/trade_in.get_street_deals: добавлен city-скоуп
  `city IS NOT NULL AND :address ILIKE '%'||city||'%'` (канонический deals.city как
  подстрока целевого адреса — self-normalizing, адрес всегда содержит город).
- Live-repro (Ленина, 2к, 12мес): median 52 908 → 118 052 ₽/м² (было занижение ~−55%).

C2 — дневной пересчёт asking→sold ratio (imminent, «выкупная» −29%):
- tasks/asking_to_sold_ratio: deal_side + deal_global скоупятся на ЕКБ (asking-сторона =
  listings покрыты скрейпом только по ЕКБ; в listings нет колонки city). Константа
  _ASKING_CITY_PATTERN. ask-стороны не трогаем.
- Live-repro (rooms=2): sold_median all-city 82 593 (ratio 0.622) → EKB 116 858
  (ratio 0.880). Предотвращает обвал «выкупной» на след. пересчёте.
- Когда появятся oblast-листинги (B1/B2) → заменить на per-city ratio через `district`.

Тесты: subset-тест 080↔refresh (mirror _drop_segment_guard прецедента для city-guard) +
позитивный тест скоупа; 125 passed вкл. regression-гейт (байт-зелёный — коридор заморожен
в фикстуре), expected_sold, idor, 781. Regression-гейт не затронут (dkp_raw — frozen kwarg).
bot-backend merged commit 6936558f6f into main 2026-07-12 17:20:32 +00:00
bot-backend deleted branch fix/tradein-oblast-city-scope-corridor-ratio 2026-07-12 17:20:32 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2489
No description provided.