gendesign/tradein-mvp/backend/tests/fixtures/backtest_baseline.json
bot-backend f999011b30
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
fix(tradein/estimator): hedonic respects le_asking + hedonic unit test (#2002)
Review found the hedonic factor (≤1.30) was applied AFTER the
estimate_expected_sold_le_asking clamp, so expected_sold could exceed the
asking headline (median) by up to ~26% for new/small lots — an overpay risk
that violated the default-True le_asking invariant.

Fix: re-assert le_asking right after the hedonic block and before the
calibrated-PI range block. Final order: ratio clamp -> hedonic factor ->
le_asking re-clamp -> calibrated PI range (range still wraps the clamped
point). round(median_ppm2) keeps expected_sold_per_m2 an int.

The re-clamp does NOT materially erode the новостройка/бизнес benefit
(бизнес bias -9.15->-10.19, MAPE held 12.87) and actually improves overall
expected_sold MAPE on the frozen fixture (14.24->13.23) by capping
over-corrections that overshot actual sold prices.

Adds tests/test_estimator_hedonic.py (hermetic, no DB): factor magnitude vs
OFF, both clamp boundaries, neutral year term for target_year=None, and the
le_asking invariant under hedonic ON. Baseline regenerated; gate green.

Also clarifies the config comment: the quoted held-out fit metrics
(18.5->16.1 etc., n=2366) are distinct from the 277-deal backtest fixture
(18.63->13.23).
2026-06-27 19:03:01 +03:00

154 lines
3.1 KiB
JSON

{
"calibration": {
"high": {
"coverage_pct": null,
"mape_pct": null,
"n": 0,
"n_covered": 0
},
"low": {
"coverage_pct": 81.82,
"mape_pct": 13.23,
"n": 275,
"n_covered": 225
},
"medium": {
"coverage_pct": 100.0,
"mape_pct": 14.64,
"n": 2,
"n_covered": 2
}
},
"confidence_order": [
"high",
"medium",
"low"
],
"expected_sold": {
"overall": {
"mape_pct": 13.23,
"median_bias_pct": -2.87,
"n": 277,
"n_no_analogs": 0,
"p25_pct": -14.43,
"p75_pct": 12.51
},
"per_rooms": {
"0": {
"label": "студия",
"mape_pct": 19.38,
"median_bias_pct": 18.1,
"n": 37,
"n_no_analogs": 0,
"p25_pct": 1.66,
"p75_pct": 33.53
},
"1": {
"label": "1к",
"mape_pct": 10.97,
"median_bias_pct": -3.47,
"n": 93,
"n_no_analogs": 0,
"p25_pct": -12.48,
"p75_pct": 6.98
},
"2": {
"label": "2к",
"mape_pct": 17.39,
"median_bias_pct": -11.71,
"n": 74,
"n_no_analogs": 0,
"p25_pct": -23.37,
"p75_pct": -0.36
},
"3": {
"label": "3к",
"mape_pct": 9.77,
"median_bias_pct": -3.08,
"n": 43,
"n_no_analogs": 0,
"p25_pct": -10.26,
"p75_pct": 5.77
},
"4": {
"label": "4+",
"mape_pct": 20.27,
"median_bias_pct": 8.54,
"n": 30,
"n_no_analogs": 0,
"p25_pct": -3.78,
"p75_pct": 23.54
}
},
"per_segment": {
"бизнес": {
"mape_pct": 12.87,
"median_bias_pct": -10.19,
"n": 46,
"p25_pct": -22.6,
"p75_pct": -1.31
},
"комфорт": {
"mape_pct": 11.61,
"median_bias_pct": -4.07,
"n": 104,
"p25_pct": -15.64,
"p75_pct": 7.38
},
"премиум": {
"mape_pct": 68.92,
"median_bias_pct": -68.92,
"n": 1,
"p25_pct": -68.92,
"p75_pct": -68.92
},
"эконом": {
"mape_pct": 15.17,
"median_bias_pct": 4.28,
"n": 120,
"p25_pct": -6.59,
"p75_pct": 26.09
},
"элит": {
"mape_pct": 33.2,
"median_bias_pct": -33.2,
"n": 6,
"p25_pct": -42.75,
"p75_pct": -22.4
}
}
},
"headline": {
"ask_median_ppm2": 147545.8502510892,
"deal_median_ppm2": 125063.0,
"spread_pct": 17.98
},
"range_coverage": {
"overall": {
"coverage_pct": 81.95,
"n": 277,
"n_covered": 227
},
"per_confidence": {
"high": {
"coverage_pct": null,
"n": 0,
"n_covered": 0
},
"low": {
"coverage_pct": 81.82,
"n": 275,
"n_covered": 225
},
"medium": {
"coverage_pct": 100.0,
"n": 2,
"n_covered": 2
}
}
},
"sharpness": {
"median_rel_width": 0.743,
"n": 277
}
}