Skip to content

Module 6 — Forecasting

Hoda Rezvanjoo edited this page Jun 1, 2026 · 1 revision

File: modules/module6.py · Result: Module6Result

Module 6 turns each scenario's LP allocation into per-KPI forecasts with data-driven confidence bands.

forecast_count = x_{p,g} × (1 / cpu_{p,g,var}) × seasonality_g

Confidence bands — preference order

For each forecast, the band is selected from this hierarchy:

  1. Sample CV when kpi_observations[var] has ≥3 positive values. This is the actual noise in the data.
  2. Window-scaled priorband = DEFAULT_UNCERTAINTY_BAND × √(REF_WINDOW_DAYS / days). Defaults: DEFAULT_UNCERTAINTY_BAND = 0.30, REF_WINDOW_DAYS = 30. So 90 days → ~17% band; 7 days → ~62% band, floored at 7 days so the band doesn't blow up on thin history.
  3. Flat default of 0.30 when neither observations nor historical_days are available.

Bands are clamped to [_MIN_BAND=0.05, _MAX_BAND=1.00] so the engine doesn't claim false precision or produce a "forecast ± 200%" that's worse than useless.

This data-driven hierarchy is one of the engine's distinguishing choices — most planning tools either show no uncertainty or a flat "±X%" that has no relation to the input data. Here, more data visibly narrows the band; thin data visibly widens it.

Rate vs count KPIs

  • Count KPIs (impressions, clicks, leads, etc.) — forecast is allocation × (1/cpu). Seasonality applies.
  • Rate KPIs (engagement rate, view rate where present) — forecast is the historical rate, unchanged. No seasonality (rates don't shift with auction cost the way volumes do).

Why seasonality is applied in Module 6 too

Module 5 multiplies r_{p,g} by the seasonality index before solving. Module 6 multiplies count-KPI forecasts by the same factor afterwards. Without the second application, the predicted volume would diverge from what the LP optimised against; with it, allocation and forecast stay consistent.

Clone this wiki locally