fix(tradein/estimate): убрать квартал соседа из квартального индекса (координатный резолв — за флагом OFF) #2839
Merged
bot-backend
merged 3 commits from 2026-08-12 15:55:25 +00:00
fix/quarter-target-from-neighbour into main
3 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| b13880ff15 |
fix(tradein/estimate): координатный резолв квартала — за флагом OFF, порог 25 м
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
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 3m58s
CI Trade-In / browser-tests (pull_request) Has been skipped
Точность самого резолва измерена против НЕЗАВИСИМОЙ истины: 2398 домов ЕКБ, где квартал известен из кадастра DaData (ответ на адрес, а не KNN-подсказка — см. house_dedup_merge.py:126; вырожденный 66:41:0000000 у 146 домов исключён). порог ответ выдан чужой квартал точность покрытие 15 м 475 26 94.5% 19.8% 25 м 748 59 92.1% 31.2% 50 м 1341 271 79.8% 55.9% 100 м 2090 644 69.2% 87.2% Порог 50 м, заимствованный у cadastral_geo_match, замером НЕ подтверждён: каждый пятый ответ — чужой квартал, а промах стоит 1.111× по медиане и 1.447× в p90 (отношение индексов чужого и своего квартала). Порог 25 м обоснован замером. Разделение: снятие фолбэка на кадастр аналога поставляется включённым (измерено бэктестом, −0.55 пп MAPE), координатный резолв — за estimate_quarter_from_coords_enabled=False. Причина: точность РЕЗОЛВА измерена, эффект поправки на точность ЦЕНЫ — нет (фикстура гейта идёт с target_house_cadnum=None и координатный путь не проходит). Критерий приёмки и дата-предохранитель (2026-09-12) записаны в config рядом с флагом. На рабочем тракте (координаты оценок, а не домов справочника) точность ниже: из 20 оценок со своим кадастром ответ в 50 м выдан 9 раз, совпал 3; в 25 м — 3 ответа, 3 совпадения. Попадания на 0-4 м, промахи на 32-39 м. Один из промахов — Малышева 84, тот самый кейс, где координатный квартал двигал цену на −20.6%. |
|||
| 86cb294949 |
test(tradein/backtest): эталон гейта после снятия квартала соседа
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
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 / 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 4m6s
Гейт `test_backtest_regression_gate` реплеит замороженную фикстуру (277 сделок) прямо через `_price_from_inputs` с `target_house_cadnum=None`, минуя `estimate_quality`. То есть на фикстуре меряется ровно снятие поправки по кварталу СОСЕДА (координатный резолв в реплей не попадает — его делает вызывающий код). Затронуто 30 сделок из 277 (10.8% — сходится с прод-оценкой поверхности ~10%); множитель цены стало/было: min 0.704, медиана 1.019, max 1.661 (17 вверх / 13 вниз). Сдвиг метрик (эталон → стало): expected_sold.overall.mape_pct 13.18 → 12.63 (−0.55) calibration.low.mape_pct 13.2 → 12.67 (−0.53) студии mape 19.38 → 16.96 (−2.42) 3к mape 9.34 → 7.79 (−1.55) 2к mape 18.26 → 17.39 (−0.87) комфорт mape 11.80 → 10.14 (−1.66) бизнес mape 14.65 → 13.65 (−1.00) УХУДШИЛОСЬ: эконом mape 13.71 → 14.20 (+0.49), bias 2.54 → 3.33 4+ mape 16.30 → 16.38 (+0.08), bias 3.34 → −1.32 хвосты: студии p75 34.32 → 38.82, бизнес p25 −22.93 → −27.15 Покрытие (82.09/82.16), sharpness (0.743), n по всем бакетам и headline не менялись. Атрибуция: на origin/main регенерация тем же путём даёт эталон байт-в-байт, расхождение появляется ровно от правки резолва квартала. |
|||
| 2bf9e76a10 |
fix(tradein/estimate): квартал цели — по её координатам, а не из кадастра аналога
Some checks failed
CI Trade-In / browser-tests (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) Failing after 3m57s
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI Trade-In / frontend-checks (pull_request) Has been skipped
`target_quarter` для квартального индекса (#764) резолвился так: свой кадастр цели (DaData) → а если пусто, кадастр ПЕРВОГО аналога с кадастром. Пусто оно в 15 из 15 применений на проде, то есть «индекс квартала цели» на витрине всегда был индексом квартала соседа. Замер 2026-08-12 (прод, 1081 оценка): - 15/15 применений — house_cadnum пуст; аналог-донор квартала стоял в 201-4087 м от цели (медиана ~580 м); - индексы кварталов на такой дистанции расходятся в 1.15-1.23 раза по медиане и в 1.6-1.8 по p90 → множитель на витрине был чужой; - индекс при этом НЕ шум: корреляция с независимой медианой живых объявлений того же квартала r(log)=0.84 (307 кварталов), 0.88 на плотных (n_deals>=50) — поэтому чужой квартал это реальный сдвиг цены, а не безобидная неточность. Фикс: квартал цели снимается по СОБСТВЕННЫМ координатам цели — ближайшее здание в cad_buildings_local (ЕКБ-зеркало Росреестра, 47 111 зданий) в пределах 50 м, тем же geo-nearest приёмом и с тем же порогом, каким уже заполняется listings.building_cadastral_number (tasks/cadastral_geo_match). Нет здания в 50 м → поправки нет: это честнее, чем взять квартал соседа. Эффект на 15 исторических применений: 7 теряют поправку (ближайшее здание 59-180 м), 8 пересчитываются от своего квартала; диапазон изменения цены −25.0% … +66.7%. |