demand_only overlay: deficit_index ранжирует форматы по rate-крутизне (§9.5 segment_steepness), а не по силе спроса — §9.2/§9.4-компоненты сокращаются #1593

Closed
opened 2026-06-16 17:14:44 +00:00 by bot-backend · 0 comments
Collaborator

Severity: medium · Категория: logic/wrong-result · Файл: backend/app/services/forecasting/recommendation.py:584-649

Файл backend/app/services/forecasting/recommendation.py:585-586, 614-644 (_demand_only_overlay). pace = base_pace × norm.coefficient × macro_coef.coefficient, deficit_index = pace/max_pace. Проблема: (1) base_pace = compute_market_metrics(db, district=district).unit_velocity — без room_bucket-фильтра (market_metrics.py:349 не принимает room_bucket) → одинаков для всех 5 bucket'ов; (2) norm.coefficient (§9.4) одинаков для всех bucket'ов, т.к. его β фитится через compute_rate_regime_sensitivity → compute_district_rate_regression(district, obj_class) (regression.py:764-768) и ИГНОРИРУЕТ spec.room_bucket. Оба постоянных множителя сокращаются в pace/max_pace, и порядок форматов определяется ИСКЛЮЧИТЕЛЬНО macro_coef.coefficient = segment_steepness(room_bucket) (macro_coefficient.py:464). ИМПАКТ: docstring (recommendation.py:566-568) обещает deficit_index как прокси силы спроса per room-bucket, но фактически он отражает лишь rate-чувствительность нарезки; downstream usp (§10.5) и class_reco (§10.2) предлагают пользователю строить формат с максимальной rate-крутизной как «самый дефицитный». Остаётся в рамках ADVISORY-стека (не production-решение), отсюда medium. ФИКС: ввести room_bucket-зависимый сигнал спроса — либо передавать room_bucket в base_pace (per-bucket unit_velocity из sold_by_room, который уже считается в market_metrics.py:_query_sales_window), либо пробросить room_bucket в β/§9.6; пока этого нет — скорректировать docstring, честно сообщив, что в demand_only ранжирование = rate-steepness, а не демпинг спроса.

Почему баг: Docstring и module-doc прямо обещают, что deficit_index в demand_only = «ПРОКСИ относительной силы спроса» (pace = §9.2 unit_velocity × §9.4 × §9.5 per room-bucket). Но §9.2-компонента (unit_velocity) одинакова для всех форматов, §9.4-компонента тоже одинакова, и они сокращаются. Итоговый порядок форматов отражает лишь то, какой формат сильнее реагирует на ставку (steepness крупных/дорогих нарезок), а не то, какой формат быстрее поглощается рынком. Это class_reco (§10.2), usp (§10.5) и downstream product_scoring.differentiation строят на ложном сигнале: пользователю в режиме без геометрии участка предлагается строить формат с максимальной rate-крутизной как «самый востребованный».

Как чинить: Брать unit_velocity ПЕР-bucket: вызывать compute_market_metrics/sales-серию с фильтром по room_bucket внутри цикла (строка 606+), чтобы base_pace различался по форматам. Либо честно переформулировать семантику deficit_index в demand_only (это steepness-ранкинг, а не демандо-прокси) и убрать сокращающиеся множители из формулы.


deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.9 · unit B23

**Severity:** medium · **Категория:** logic/wrong-result · **Файл:** `backend/app/services/forecasting/recommendation.py:584-649` Файл backend/app/services/forecasting/recommendation.py:585-586, 614-644 (_demand_only_overlay). pace = base_pace × norm.coefficient × macro_coef.coefficient, deficit_index = pace/max_pace. Проблема: (1) base_pace = compute_market_metrics(db, district=district).unit_velocity — без room_bucket-фильтра (market_metrics.py:349 не принимает room_bucket) → одинаков для всех 5 bucket'ов; (2) norm.coefficient (§9.4) одинаков для всех bucket'ов, т.к. его β фитится через compute_rate_regime_sensitivity → compute_district_rate_regression(district, obj_class) (regression.py:764-768) и ИГНОРИРУЕТ spec.room_bucket. Оба постоянных множителя сокращаются в pace/max_pace, и порядок форматов определяется ИСКЛЮЧИТЕЛЬНО macro_coef.coefficient = segment_steepness(room_bucket) (macro_coefficient.py:464). ИМПАКТ: docstring (recommendation.py:566-568) обещает deficit_index как прокси силы спроса per room-bucket, но фактически он отражает лишь rate-чувствительность нарезки; downstream usp (§10.5) и class_reco (§10.2) предлагают пользователю строить формат с максимальной rate-крутизной как «самый дефицитный». Остаётся в рамках ADVISORY-стека (не production-решение), отсюда medium. ФИКС: ввести room_bucket-зависимый сигнал спроса — либо передавать room_bucket в base_pace (per-bucket unit_velocity из sold_by_room, который уже считается в market_metrics.py:_query_sales_window), либо пробросить room_bucket в β/§9.6; пока этого нет — скорректировать docstring, честно сообщив, что в demand_only ранжирование = rate-steepness, а не демпинг спроса. **Почему баг:** Docstring и module-doc прямо обещают, что deficit_index в demand_only = «ПРОКСИ относительной силы спроса» (pace = §9.2 unit_velocity × §9.4 × §9.5 per room-bucket). Но §9.2-компонента (unit_velocity) одинакова для всех форматов, §9.4-компонента тоже одинакова, и они сокращаются. Итоговый порядок форматов отражает лишь то, какой формат сильнее реагирует на ставку (steepness крупных/дорогих нарезок), а не то, какой формат быстрее поглощается рынком. Это `class_reco` (§10.2), `usp` (§10.5) и downstream `product_scoring.differentiation` строят на ложном сигнале: пользователю в режиме без геометрии участка предлагается строить формат с максимальной rate-крутизной как «самый востребованный». **Как чинить:** Брать unit_velocity ПЕР-bucket: вызывать `compute_market_metrics`/sales-серию с фильтром по room_bucket внутри цикла (строка 606+), чтобы base_pace различался по форматам. Либо честно переформулировать семантику deficit_index в demand_only (это steepness-ранкинг, а не демандо-прокси) и убрать сокращающиеся множители из формулы. --- <sub>deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.9 · unit B23</sub>
bot-backend added the
week ревью 1
label 2026-06-16 17:14:44 +00:00
bot-backend added the
needs-human
label 2026-06-17 06:26:27 +00:00
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#1593
No description provided.