Не выдавать цену, когда рядом нет ни одного аналога #3537

Merged
lekss361 merged 3 commits from fix/estimator-zero-analogs-no-price into main 2026-09-16 17:16:27 +00:00

3 commits

Author SHA1 Message Date
bot-backend
39af474f44 style: ruff format после правки гейта
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 13s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m10s
2026-09-16 19:56:36 +03:00
bot-backend
182f1d2802 fix(estimator): резать общегородской коридор, а не любую цену без аналогов
Some checks failed
CI / changes (pull_request) Successful in 16s
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) Failing after 1m23s
CI Trade-In / changes (pull_request) Successful in 13s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
Первая версия гейта гасила цену всегда при n_analogs==0 — и уронила
контракт #oblast-D: тест test_non_ekb_empty_listings_uses_deals_headline
(Нижний Тагил, 12 сделок по улице) падал. Это не случайный тест, а фича,
без которой в область протекал екатеринбургский asking вшестеро выше.

Проверка на проде показала, где настоящая граница. У Янтарной в
Красногорске ОДНА сделка — меньше DKP_CORRIDOR_CITY_WIDE_MIN_N=3, поэтому
_fetch_dkp_corridor расширился до всего города: 4770 сделок, медиана
200 676 ₽/м². Пользователю показали среднюю цену Красногорска как оценку
его квартиры. А 12 сделок по улице в Тагиле — настоящий сигнал.

Коридор уже сам помечает себя scope='street' | 'city_wide', так что
правило формулируется точно: общегородской коридор не может стать
headline, уличный может. Подавление ответа осталось только оформлением
пустого результата — условие median_price <= 0 намеренно совпадает с
computed-полем AggregatedEstimate.insufficient_data, по которому фронт уже
рисует карточку, чтобы признак был один, а не два расходящихся.

Флаг переименован в estimate_require_local_evidence: он не про «требовать
аналоги», а про «цена обязана опираться на данные этого адреса». Обе части
правила под одним флагом — False возвращает прежнее поведение целиком.
2026-09-16 19:55:05 +03:00
bot-backend
4f4d8ee929 fix(estimator): не выдавать цену, когда рядом нет ни одного аналога
Some checks failed
CI / changes (pull_request) Successful in 12s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 9s
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Failing after 4m58s
Прод-репро: «Красногорск, Янтарная» возвращал 236 766 ₽/м² при n_analogs=0.
Тестировщик открывает список аналогов, видит пустоту и справедливо
перестаёт верить числу. Доля таких ответов 3.8% по ЕКБ и 4.8% по 77/50 —
дефект продукта, не регионального покрытия.

Источник цены нашёлся ровно один: deals-headline-fallback #oblast-D
(estimator.py:4184-4225). Когда листинговых аналогов нет совсем, headline
берётся из коридора ДКП Росреестра, а n_analogs остаётся 0. Остальные
кандидаты проверены и исключены: IMV-blend и same-building anchor цену из
воздуха не создают (первый no-op при median_price<=0, второй всегда
проставляет n_analogs=anchor['n']), deal_city_price_bands — только
sanity-clamp правдоподобия сделок.

Гейт сужен до n_analogs==0 AND reliability=='very_low', а не голого
n_analogs==0. Причина: случай #oblast-E (1-4 объявления плюс >=3 реальные
сделки) тоже зануляет n_analogs, но там reliability намеренно закреплена
на 'low' — коридор из полусотни сделок Росреестра это настоящий сигнал, и
гасить его было бы регрессией задокументированного решения от 10.08.2026.

Отказ переиспользует существующий паттерн _empty_estimate, но сохраняет
уже найденные адрес/координаты/дом — фронту есть что показать рядом с
причиной. Флаг estimate_require_analogs позволяет откатить без релиза.
2026-09-16 19:21:26 +03:00