Root causes fixed:
1. Bug_Velocity_Mix_Static: bucket_v = market_vel_pm × share/100 made all
bucket velocities proportional to market share, so Σ bucket_v ≡ market_vel_pm
regardless of mix. Moving a slider changed share_pct but not the aggregate
срок продажи (velocity numerator and denominator both scaled with share).
2. Bug_Velocity_Unrealistic: rosreestr fallback used city-wide total_deals
(region 66, ~3815 deals/24 mo = 159 кв/мес) divided by district-only
competitors_weighted (~5-10 ЖК), yielding 16-32 кв/мес per project
and срок ~0.7 мес instead of realistic months.
Changes:
- Add _n_active_zhk_region(db, region_code): COUNT DISTINCT active ЖК
in the full region — used as city-wide denominator for rosreestr fallback.
Lazy-cached to avoid duplicate DB round-trips.
- Add _velocity_baseline_per_bucket(db, ...): query objective_corpus_room_month
grouped by room_bucket (same mapping as _elasticity_per_bucket_coef) to get
per-bucket median velocity. Returns None when < 3 observations.
- Replace static bucket_market_velocities computation in recommend_mix:
- Objective path: use _velocity_baseline_per_bucket per-bucket medians.
Each bucket is an independent constant (not share-derived).
- Rosreestr fallback: bucket_deals / months / N_active_region.
N_active_region (200-400+ for EKB) replaces competitors_district (5-10),
giving realistic per-project velocities (~0.1-0.6 кв/мес per bucket).
- Add bucket["velocity_source"] = "objective_per_bucket" | "rosreestr_fallback"
for transparency in API response and UI warnings.
- Add scope["n_active_region"] for debugging.
- Add 19 unit tests in test_recommend_mix_velocity.py covering both bugs.
|
||
|---|---|---|
| .. | ||
| api | ||
| core | ||
| models | ||
| observability | ||
| schemas | ||
| scrapers | ||
| services | ||
| templates | ||
| workers | ||
| __init__.py | ||
| main.py | ||