[SF-v2 #952] 952-A: Движок прогноза спрос/предложение 6/12/18 §9.7 #980

Closed
opened 2026-06-02 19:22:50 +00:00 by lekss361 · 3 comments
Owner

Часть EPIC #952 (Site Finder v2 / GG-форсайт). Декомпозиция критпути — ground-truth (прод-БД + код) + web-research, 2026-06-02.

Scope: Проекция спроса и предложения на горизонты 6/12/18 мес: 951-B регрессия (макро-путь) + трендовая экстраполяция (momentum) + предложение из 950-A completion dates.

Файлы: new backend/app/services/forecasting/forecast.py

DoD:

  • forecast(district, horizons=[6,12,18]) → demand+supply per horizon с confidence; детерминир. тест

Reuse: _district_velocity_trend L1443 momentum.
Зависит от: 951-B, 950-A
Effort: L

**Часть EPIC #952 (Site Finder v2 / GG-форсайт).** Декомпозиция критпути — ground-truth (прод-БД + код) + web-research, 2026-06-02. **Scope:** Проекция спроса и предложения на горизонты 6/12/18 мес: 951-B регрессия (макро-путь) + трендовая экстраполяция (momentum) + предложение из 950-A completion dates. **Файлы:** new backend/app/services/forecasting/forecast.py **DoD:** - forecast(district, horizons=[6,12,18]) → demand+supply per horizon с confidence; детерминир. тест **Reuse:** _district_velocity_trend L1443 momentum. **Зависит от:** 951-B, 950-A **Effort:** L
lekss361 added this to the Site Finder v2 — Foresight (GG-форсайт) milestone 2026-06-02 19:22:54 +00:00
lekss361 added the
analytics
GG-форсайт
scope/backend
site-finder
labels 2026-06-02 19:22:55 +00:00
Author
Owner

🔴 GG-critical (стратегсессия 2026-06-01)

Сердце того, что просит ГГ — прогноз распаковки рынка на год вперёд (00:14:33):

«Система видит динамику выбытия за какой-то период, понимает, что через год… и видит, что в данной локации есть проекты, которые будут распаковываться в течение этого года. Это либо следующие очереди существующих объектов, либо что-то новое у конкретного застройщика.»

→ Это центральный прогнозный движок (спрос+предложение на горизонт). Без него система остаётся «анализом СЕЙЧАС», а не прогнозом — ровно тот разрыв, который ГГ просит закрыть. priority/critical, ядро демо «Железнодорожники».

## 🔴 GG-critical (стратегсессия 2026-06-01) Сердце того, что просит ГГ — прогноз распаковки рынка на год вперёд (00:14:33): > «Система видит **динамику выбытия** за какой-то период, понимает, что через год… и видит, что в данной локации есть **проекты, которые будут распаковываться в течение этого года**. Это либо следующие очереди существующих объектов, либо что-то новое у конкретного застройщика.» → Это центральный прогнозный движок (спрос+предложение на горизонт). Без него система остаётся «анализом СЕЙЧАС», а не прогнозом — ровно тот разрыв, который ГГ просит закрыть. **priority/critical**, ядро демо «Железнодорожники».
lekss361 reopened this issue 2026-06-03 17:59:41 +00:00
Author
Owner

🟠 Reopen — MEDIUM: самый сильный дефицит выдаёт deficit_index=None вместо +1 (противоречит задокументированной семантике). Валидация #1012.

Баг. backend/app/services/forecasting/demand_supply_forecast.py:245-301. _project_supply = max(0, gross − min(gross, demand)). Когда projected_demand ≥ gross_supply → net supply = 0 → _balance_ratio = None → _deficit_index = None. То есть крайний случай недопредложения (спрос превышает ВЕСЬ конкурирующий сток) даёт deficit_index=None, а не +1, — прямо противоречит документированной семантике «+1 = strong deficit». Подтверждено собственным тестом test_demand_exceeds_supply_clamps_to_zero.
Также искажает магнитуду в мягких случаях: demand=100, gross=180 → net=80 → ratio=1.25 → deficit_index≈+0.32 (вычитает поглощённый сток ДО расчёта ratio).

Fix: считать deficit от gross supply, а не от net-of-absorption (или отдельно отдавать «спрос превысил весь сток» = max deficit).

Движок advisory (не wired в прод-эндпоинт) → blast radius ограничен, потому MEDIUM. Но это потребитель ранкера #981/индексов — семантика дефицита должна быть корректной до прод-вайринга.

🟠 **Reopen — MEDIUM: самый сильный дефицит выдаёт `deficit_index=None` вместо `+1` (противоречит задокументированной семантике).** Валидация #1012. **Баг.** `backend/app/services/forecasting/demand_supply_forecast.py:245-301`. `_project_supply` = `max(0, gross − min(gross, demand))`. Когда `projected_demand ≥ gross_supply` → net supply = 0 → `_balance_ratio` = None → `_deficit_index` = None. То есть крайний случай **недопредложения (спрос превышает ВЕСЬ конкурирующий сток)** даёт `deficit_index=None`, а не `+1`, — прямо противоречит документированной семантике «+1 = strong deficit». Подтверждено собственным тестом `test_demand_exceeds_supply_clamps_to_zero`. Также искажает магнитуду в мягких случаях: demand=100, gross=180 → net=80 → ratio=1.25 → `deficit_index≈+0.32` (вычитает поглощённый сток ДО расчёта ratio). **Fix:** считать deficit от gross supply, а не от net-of-absorption (или отдельно отдавать «спрос превысил весь сток» = max deficit). _Движок advisory (не wired в прод-эндпоинт) → blast radius ограничен, потому MEDIUM. Но это потребитель ранкера #981/индексов — семантика дефицита должна быть корректной до прод-вайринга._
Collaborator

FIXED (PR #1038). Strongest deficit (effective supply<=0 under positive demand) now caps deficit_index to +1.0 instead of None (was dropping the strongest build signal: market_fit->0.5, what_to_build dropped the cell). No-signal stays None. Regression test asserts deficit_index==1.0. 778 forecasting tests. Closing on test-verify; reopen if disagrees.

FIXED (PR #1038). Strongest deficit (effective supply<=0 under positive demand) now caps deficit_index to +1.0 instead of None (was dropping the strongest build signal: market_fit->0.5, what_to_build dropped the cell). No-signal stays None. Regression test asserts deficit_index==1.0. 778 forecasting tests. Closing on test-verify; reopen if disagrees.
Sign in to join this conversation.
No project
No assignees
2 participants
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#980
No description provided.