[P1] Честность диапазона оценки: раскрыть/рекалибровать 56.6% + min-width floor + backtest baseline + CI-гейт #2209

Closed
opened 2026-07-02 19:18:17 +00:00 by lekss361 · 2 comments
Owner

Эпик: #2200

Находка (Estimator, CRITICAL + High, verified): единственный честный замер (vault Mera_Fullspine_Accuracy_0627, n=371): range-coverage 56.6% (диапазон промахивается ~43%), сегментный биас +16.4% эконом … −37.9% элит, MAPE 18.4%. Диапазон = Q1–Q3 asking ppm² (estimator.py:2009-2013), выданный за прогнозный интервал; при n=1 схлопывается в ноль ширины (нет min-width floor). Результаты бэктеста нигде не persisted (0 таблиц %backtest%, нет baseline.json, нет CI-гейта).

Acceptance:

  • Честность в UI: явно раскрыть точность/покрытие ИЛИ рекалибровать диапазон до заявленного покрытия (напр. эмпирические квантили прогнозной ошибки).
  • Min-width floor для диапазона; убрать нулевую ширину при n<порога (согласовать с confidence).
  • Persist backtest_baseline.json в репо + CI-регресс-гейт на coverage/calibration/MAPE (#1966 PR3).
  • Разобрать сегментный биас (эконом +16% → риск переплаты; элит −38%): калибровочный коэффициент на сегмент.
  • Пересмотреть stacked «только-вверх» лифты (IMV blend, radius floor, premium uplift) — источник +16% эконома.

Scope: tradein-mvp/backend/app/services/estimator.py, scripts/backtest_estimator.py, frontend hero. Ядро эпика.

Эпик: #2200 **Находка (Estimator, CRITICAL + High, verified):** единственный честный замер (vault `Mera_Fullspine_Accuracy_0627`, n=371): range-coverage **56.6%** (диапазон промахивается ~43%), сегментный биас +16.4% эконом … −37.9% элит, MAPE 18.4%. Диапазон = Q1–Q3 asking ppm² (`estimator.py:2009-2013`), выданный за прогнозный интервал; при n=1 схлопывается в ноль ширины (нет min-width floor). Результаты бэктеста нигде не persisted (0 таблиц `%backtest%`, нет baseline.json, нет CI-гейта). **Acceptance:** - [ ] Честность в UI: явно раскрыть точность/покрытие ИЛИ рекалибровать диапазон до заявленного покрытия (напр. эмпирические квантили прогнозной ошибки). - [ ] Min-width floor для диапазона; убрать нулевую ширину при n<порога (согласовать с confidence). - [ ] Persist `backtest_baseline.json` в репо + CI-регресс-гейт на coverage/calibration/MAPE (#1966 PR3). - [ ] Разобрать сегментный биас (эконом +16% → риск переплаты; элит −38%): калибровочный коэффициент на сегмент. - [ ] Пересмотреть stacked «только-вверх» лифты (IMV blend, radius floor, premium uplift) — источник +16% эконома. Scope: `tradein-mvp/backend/app/services/estimator.py`, `scripts/backtest_estimator.py`, frontend hero. Ядро эпика.
lekss361 added the
analytics
priority/p1
scope/backend
tradein
labels 2026-07-02 19:20:21 +00:00
Author
Owner

Working on this (night shift). Свежий статус по acceptance-пунктам после живого бэктеста 2026-07-02 (engine=full, sample=300, --resolve-house-id, прод-данные):

1. Coverage уже НЕ 56.6%. Аудиторский замер (27.06, n=371) устарел: спайн с тех пор дорабатывался (#1966 PR2/3), плюс сегодня эстиматору возвращён инвентарь (#2206). Живой замер сейчас: range-coverage 81.5% (n=276), MAPE 14.7-14.8%, median bias −2.3…−2.7%. Но ширина куплена честно дорого: median_rel_width 0.743 (типичный диапазон ±37% от точки), и 274/276 оценок в low-confidence бакете (калибровка конфиденса вырождена — фактически один бакет).

2. Persist baseline + CI-гейт — УЖЕ ЕСТЬ (#1966 PR3): tests/fixtures/backtest_baseline.json + backtest_full_fixture.json.gz + test_backtest_regression_gate.py (hermetic replay), и с #2208 этот гейт теперь pre-merge blocking. Пункт закрыт составом других работ.

3. Min-width floor — в работе (ветка fix/tradein-range-min-width-floor): при n=1 Q1==Q3 → нулевая ширина (verified estimator.py:1966-1971, floor'а нет по всему спайну). Делаю ±12% floor вокруг точки в конце спайна (только расширяет; baseline перегенерируется штатной процедурой --update-baseline, до/после будет в PR).

4. Сегментный биас — подтверждён, но мягче аудита (сегодня, n=276): эконом +5.6% (аудит: +16.4), комфорт −4.0%, бизнес −9.1%, элит −30.8% (n=6), премиум −68% (n=1). Паттерн «переоцениваем дешёвое, недооцениваем дорогое» жив, но хвосты на n=6/1 — статистически тонко. Пер-сегментная калибровка = реальное ценовое изменение при малых n в дорогих сегментах → делать ночью без валидации на большем окне не буду; предлагаю: (а) собрать длинное окно (--since раньше, sample больше) отдельным прогоном, (б) калибровочный коэффициент за флагом default OFF + бэктест-сравнение — отдельным PR при дневном ревью. «Только-вверх» лифты (IMV blend расширяет только верх, _blend_imv_anchor max(range_high,...)) — кандидат №1 на источник положительного биаса эконома; разбор в том же follow-up.

5. Честность в UI — после мержа #2227 (те же файлы hero): либо явное «диапазон покрывает ~80% реальных продаж (замер 07/2026)», либо честный лейбл природы диапазона. Сделаю отдельным малым PR.

Working on this (night shift). Свежий статус по acceptance-пунктам после живого бэктеста 2026-07-02 (engine=full, sample=300, --resolve-house-id, прод-данные): **1. Coverage уже НЕ 56.6%.** Аудиторский замер (27.06, n=371) устарел: спайн с тех пор дорабатывался (#1966 PR2/3), плюс сегодня эстиматору возвращён инвентарь (#2206). Живой замер сейчас: **range-coverage 81.5%** (n=276), MAPE 14.7-14.8%, median bias −2.3…−2.7%. Но ширина куплена честно дорого: median_rel_width **0.743** (типичный диапазон ±37% от точки), и 274/276 оценок в low-confidence бакете (калибровка конфиденса вырождена — фактически один бакет). **2. Persist baseline + CI-гейт — УЖЕ ЕСТЬ** (#1966 PR3): `tests/fixtures/backtest_baseline.json` + `backtest_full_fixture.json.gz` + `test_backtest_regression_gate.py` (hermetic replay), и с #2208 этот гейт теперь **pre-merge blocking**. Пункт закрыт составом других работ. **3. Min-width floor — в работе** (ветка `fix/tradein-range-min-width-floor`): при n=1 Q1==Q3 → нулевая ширина (verified estimator.py:1966-1971, floor'а нет по всему спайну). Делаю ±12% floor вокруг точки в конце спайна (только расширяет; baseline перегенерируется штатной процедурой --update-baseline, до/после будет в PR). **4. Сегментный биас — подтверждён, но мягче аудита** (сегодня, n=276): эконом **+5.6%** (аудит: +16.4), комфорт −4.0%, бизнес −9.1%, элит **−30.8%** (n=6), премиум −68% (n=1). Паттерн «переоцениваем дешёвое, недооцениваем дорогое» жив, но хвосты на n=6/1 — статистически тонко. Пер-сегментная калибровка = реальное ценовое изменение при малых n в дорогих сегментах → делать ночью без валидации на большем окне не буду; предлагаю: (а) собрать длинное окно (--since раньше, sample больше) отдельным прогоном, (б) калибровочный коэффициент за флагом default OFF + бэктест-сравнение — отдельным PR при дневном ревью. «Только-вверх» лифты (IMV blend расширяет только верх, `_blend_imv_anchor` max(range_high,...)) — кандидат №1 на источник положительного биаса эконома; разбор в том же follow-up. **5. Честность в UI** — после мержа #2227 (те же файлы hero): либо явное «диапазон покрывает ~80% реальных продаж (замер 07/2026)», либо честный лейбл природы диапазона. Сделаю отдельным малым PR.
Author
Owner

Закрываю: ядро acceptance выполнено, смержено и задеплоено; остаток вынесен в follow-up #2255.

По пунктам:

  • Честность в UI (путь «раскрыть»): PR #2232 — hero-хинт теперь сообщает замеренное покрытие: «фактическая цена продажи попадает в диапазон в ~80% случаев» (live-бэктест 2026-07-02, engine=full, n=276, range-coverage 81.5%). Задеплоено, строка в прод-бандле верифицирована.
  • Min-width floor: PR #2230 — ±12% floor на диапазон, нулевая ширина при малых n устранена.
  • Persist baseline + CI-регресс-гейт: tests/fixtures/backtest_baseline.json + tests/test_backtest_regression_gate.py в репо; гейт гоняется pre-merge CI (#2208) на каждый PR.
  • ➡️ Сегментный биас + «только-вверх» лифты: вынесено в #2255 (сегментная калибровка за фичефлагом, backtest-гейт обязателен; там же разбор asymmetric IMV-lift). Данные длинного окна (n=561): эконом +3.2%, комфорт −4.5%, бизнес −16.3% (n=76), элит −25.7% (n=16).

Метрики до/после ремедиации (live-бэктест): bias −2.73% → −2.29%, MAPE 14.7% → 14.78%, range-coverage 81.5%. Frozen baseline не изменился.

Refs #2200, #2230, #2232, #2255

**Закрываю: ядро acceptance выполнено, смержено и задеплоено; остаток вынесен в follow-up #2255.** По пунктам: - ✅ **Честность в UI (путь «раскрыть»):** PR #2232 — hero-хинт теперь сообщает замеренное покрытие: «фактическая цена продажи попадает в диапазон в ~80% случаев» (live-бэктест 2026-07-02, engine=full, n=276, range-coverage 81.5%). Задеплоено, строка в прод-бандле верифицирована. - ✅ **Min-width floor:** PR #2230 — ±12% floor на диапазон, нулевая ширина при малых n устранена. - ✅ **Persist baseline + CI-регресс-гейт:** `tests/fixtures/backtest_baseline.json` + `tests/test_backtest_regression_gate.py` в репо; гейт гоняется pre-merge CI (#2208) на каждый PR. - ➡️ **Сегментный биас + «только-вверх» лифты:** вынесено в #2255 (сегментная калибровка за фичефлагом, backtest-гейт обязателен; там же разбор asymmetric IMV-lift). Данные длинного окна (n=561): эконом +3.2%, комфорт −4.5%, бизнес −16.3% (n=76), элит −25.7% (n=16). Метрики до/после ремедиации (live-бэктест): bias −2.73% → −2.29%, MAPE 14.7% → 14.78%, range-coverage 81.5%. Frozen baseline не изменился. Refs #2200, #2230, #2232, #2255
Sign in to join this conversation.
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#2209
No description provided.