fix(tradein): гейт правдоподобия на «медианный торг» — не показывать артефакт пейринга как рыночный факт (#2666) #2671

Merged
bot-backend merged 2 commits from fix/2666-discount-plausibility-gate into main 2026-08-05 19:20:09 +00:00

2 commits

Author SHA1 Message Date
77ae08f207 fix(tradein): отказ гейта — факт про выборку вместо обещания надёжности (#2666)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 7s
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 / frontend-checks (pull_request) Successful in 1m6s
CI Trade-In / backend-tests (pull_request) Successful in 2m46s
Правки по ревью PR #2671.

Текст «надёжная медиана начинается от 10» обещал то, чего мы гарантировать не
можем: пары — псевдореплики (одно объявление переиспользуется на многих
сделках, на живом кейсе Космонавтов 2-комн. 42 пары стоят на 2 различных
объявлениях), и 10 пар надёжности не дают. Теперь отказ сообщает факт: сколько
пар есть и что на такой выборке медиана гуляет на десятки п.п.

Формулировка диапазонной ветки укорочена: она дублировала street_only-
дисклеймер, который идёт следующим блоком. Проверено скриншотом отрендеренной
карточки — две формулировки подряд читались как стена текста; теперь три
однострочных хинта, на 820px — по две строки, переполнения нет.

В шапку секции добавлен потолок гейта, найденный ревью: бутстрап пересэмплировал
ПАРЫ, т.е. мерил дисперсию со стороны сделок, а доминирует дисперсия со стороны
ОБЪЯВЛЕНИЙ (джекнайф p90 17.3 п.п., max 63.8); 22 из 64 переживших групп стоят
на одном объявлении. Плюс нижняя граница оказалась слишком мягкой, а не строгой:
26 из 64 показываемых значений ниже −23.8%, самое глубокое −58.5%. Оба пункта —
отдельная задача, здесь только зафиксированы, чтобы порог не перечитали как
гарантию.

Тесты: пустое утверждение "1" in explanation (всегда истинно из-за "10")
заменено на «всего 1 —». Добавлены два недостающих — отсутствие пар со скидкой
даёт explanation=None, и порядок проверок (3 пары по +80% отчитываются «мало
пар», а не «вне диапазона»).
2026-08-06 00:16:13 +05:00
b88535425e fix(tradein): гейт правдоподобия на «медианный торг» — не показывать артефакт пейринга как рыночный факт (#2666)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
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 / frontend-checks (pull_request) Successful in 1m10s
CI Trade-In / backend-tests (pull_request) Successful in 2m51s
/sales-vs-listings отдавал median_discount_pct без всякой проверки: после
сегментного гарда #2660 по `%Космонавтов%` 2-комн. значение уехало с −11.9%
на +36.4%, то есть пользователю написали бы «продали на 36% дороже, чем
просили». Корень унаследованный — пейринг ДКП↔объявление идёт по улице без
номера дома (ADR #721), так что на длинной улице в пару попадают квартиры
разных ценовых классов. Пейринг здесь не чиним, перестаём показывать число,
которому нельзя верить.

Пороги подобраны по проду (симуляция эндпоинта на 238 реальных
пользовательских запросах из trade_in_estimates, 128 дали хотя бы одну пару):
- MIN_PAIRS = 10 — бутстрап по 12 плотным группам: p90 отклонения медианы
  подвыборки от полной 18.8 п.п. при k=5, 12.0 при k=10, 9.9 при k=15.
  Кривая ломается на 10; совпадает с уже принятым в продукте
  sell_time_sensitivity_min_n_lots.
- Санитарный диапазон [−60%, +20%] — асимметричный. Сверху распределение
  разорвано (…+16.9, пусто, +33.7…+103.1), отсечка попадает в разрыв; ни один
  городской бакет asking_to_sold_ratios не даёт плюса вообще (max 0.9132).
  Снизу разрыва нет (у большого минуса есть механизм — занижение цены в ДКП),
  граница грубая «заведомо не рынок»: 2.5× худшего бакета (студии, −23.8%).

Форма отказа — не пустота: новое поле median_discount_explanation по образцу
confidence_explanation оценщика, фронт рендерит его вместо числа. Гаснет ровно
строка «медианный торг»: сделки, медиана ₽/м², диапазон, linkage_rate_pct и
per-pair discount_pct не трогаются.
2026-08-05 23:49:03 +05:00