gendesign/backend/app/services/forecasting/__init__.py
Light1YT 81df075ccc feat(forecasting): §9.4 demand-normalization coefficient (#951f, advisory)
Add demand_normalization.py: norm = clamp(exp(beta·(rate_future −
rate_window_avg)), 0.5, 1.2). Discounts a sales pace observed under one rate
regime when projecting into a different (higher-rate) future — ТЗ §9.4: don't
carry a low-rate boom pace forward into high rates.

- beta reused from §9.6 rate_sensitivity (PR3, shrunk slope on Δln, gated <0);
  rate_window_avg = mean key_rate over the §9.6 window (PR2 macro_series).
- Honesty gate: β None / §9.6 confidence='low' / no rate window → norm=1.0,
  applied=False, confidence='low' (no naive boom, no fabricated discount).
  applied=False is distinct from a trustworthy coefficient==1.0 (regimes match).
  confidence never exceeds §9.6's.
- Pure normalization_factor() + frozen DemandNormalization + as_dict().
- 26 unit tests, no live DB (mocked PR2/PR3). ADVISORY: not wired into endpoints.

Completes the deterministic §9.4/§9.5/§9.6 forecasting engine (PR2/PR1/PR3/PR4/PR5).
PR6 follow-up: OverflowError guard on exported normalization_factor (unreachable
via orchestrator today, β bounded).
2026-06-03 11:28:10 +05:00

92 lines
3 KiB
Python

"""Forecasting services — детерминированный форсайт-слой Site Finder v2.
#951 (Site Finder v2 / «GG-форсайт», EPIC 7 «Чувствительность к ключевой ставке»).
Этот пакет — фундамент data-independent логики прогноза: monthly макро-ряды,
классификатор режима ставки, лаговые помощники. Всё ДЕТЕРМИНИРОВАННО, БЕЗ LLM.
Слои (по PR):
• macro_series (#951b) — monthly макро-ряд + классификатор режима ставки (X-ось §9.6).
• sales_series (#951c) — monthly ряд продаж по сегменту (Y-ось §9.6).
• rate_sensitivity (#951d) — §9.6 чувствительность продаж к key_rate (CORE, ADVISORY).
• macro_coefficient (#951e) — §9.5 макро-коэффициент (композитный множитель, ADVISORY).
• demand_normalization (#951f) — §9.4 нормализация спроса под смену режима ставки (ADVISORY).
Источники данных:
• макро — таблица macro_indicator через reader site_finder/macro.py (reuse).
• продажи — objective_corpus_room_month / objective_lots (см. sales_series).
"""
from __future__ import annotations
from app.services.forecasting.demand_normalization import (
DemandNormalization,
compute_demand_normalization,
normalization_factor,
)
from app.services.forecasting.macro_coefficient import (
MacroCoefficient,
assemble_coefficient,
compute_macro_coefficient,
f_issuance,
f_mortgage_rate,
f_overdue,
f_rate,
renormalize_contributions,
segment_steepness,
)
from app.services.forecasting.macro_series import (
MonthlyMacro,
classify_regime,
get_monthly_macro,
is_confounded_window,
macro_at_lag,
)
from app.services.forecasting.rate_sensitivity import (
RateSensitivity,
best_lag,
compute_rate_sensitivity,
ols_slope_r2,
shrink,
)
from app.services.forecasting.sales_series import (
SalesSeries,
SegmentSpec,
build_sales_series,
fill_month_grid,
log_diff,
price_bucket_of,
room_area_bucket_of,
)
__all__ = [
"DemandNormalization",
"MacroCoefficient",
"MonthlyMacro",
"RateSensitivity",
"SalesSeries",
"SegmentSpec",
"assemble_coefficient",
"best_lag",
"build_sales_series",
"classify_regime",
"compute_demand_normalization",
"compute_macro_coefficient",
"compute_rate_sensitivity",
"f_issuance",
"f_mortgage_rate",
"f_overdue",
"f_rate",
"fill_month_grid",
"get_monthly_macro",
"is_confounded_window",
"log_diff",
"macro_at_lag",
"normalization_factor",
"ols_slope_r2",
"price_bucket_of",
"renormalize_contributions",
"room_area_bucket_of",
"segment_steepness",
"shrink",
]