A time-series forecasting model that projects brand/category volume 12 months forward using Holt-Winters exponential smoothing — with an honest, back-tested accuracy check before it ever forecasts into the future.
- Builds a monthly volume history with trend, seasonality, and noise
- Splits the data into train/test and fits Holt-Winters on the training window only
- Back-tests the fitted model against the held-out months and reports MAPE (Mean Absolute Percentage Error) — so the accuracy number is genuine, not measured on data the model already saw
- Refits on the full history and forecasts the next 12 months forward
- Outputs a chart (actual vs. forecast) and a CSV of the forward forecast
sales-volume-forecasting/
├── README.md
├── Sales_Volume_Forecasting.pyw
├── volume_forecast_next_12_months.csv
├── screenshots/
│ └── volume_forecast.png
└── LICENSE
Note: the forecast output was originally saved with an
.xlsextension, but its contents are plain CSV (Month,Forecast_volume). Rename it to.csvbefore uploading — Excel will otherwise flag it as an unrecognized/corrupt file when opened directly, even though it opens fine as text.
36 months (Jan-2023 – Dec-2025) of simulated monthly volume, built from three components:
trend = linear growth from 100 to 130 (000s units)
seasonality = sinusoidal annual cycle + an extra Q4 holiday bump in December
noise = random Gaussian noise (σ = 4)
volume = trend + seasonality + noise
The last 6 months are held out as a test set. Holt-Winters (trend="add", seasonal="add", seasonal_periods=12) is fit on the remaining 30 months, then used to forecast those 6 held-out months. Comparing the forecast to the actual held-out values gives:
MAPE = mean(|Actual − Forecast| / Actual)
This run achieved a 3.7% backtested MAPE — the model's forecasts were off by less than 4% on average against data it never trained on, which is the number that should be quoted as the model's real-world accuracy (not an in-sample fit statistic).
Once accuracy is validated, the same Holt-Winters specification is refit on the entire 36-month history (using all available signal) and used to forecast 12 months forward (Jan-2026 – Dec-2026).
| Month | Forecast Volume (000s units) |
|---|---|
| Jan-2026 | 145.7 |
| Feb-2026 | 140.9 |
| Mar-2026 | 135.4 |
| Apr-2026 | 133.6 |
| May-2026 | 123.3 |
| Jun-2026 | 122.0 |
| Jul-2026 | 122.6 |
| Aug-2026 | 128.3 |
| Sep-2026 | 135.3 |
| Oct-2026 | 141.8 |
| Nov-2026 | 151.5 |
| Dec-2026 | 162.3 |
The forecast shows a dip through mid-2026 followed by a strong Q4 ramp — consistent with the seasonal (holiday-driven) pattern learned from history.
Solid navy line = actual history · dashed gold line = 12-month forward forecast · title shows the backtested MAPE.
pip install numpy pandas matplotlib statsmodels
python Sales_Volume_Forecasting.pywThis will:
- Print the backtested MAPE and the 12-month forecast table to the console
- Save the chart to
volume_forecast.png - Save the forward forecast to
volume_forecast_next_12_months.csv
- Model:
statsmodels.tsa.holtwinters.ExponentialSmoothing, additive trend + additive seasonality, 12-month seasonal period np.random.seed(42)makes the simulated history reproducible run-to-run- Swap in real historical volume (indexed by month) in place of the simulated series to use this on actual data — the rest of the pipeline (backtest → MAPE → refit → forecast) needs no changes
- Because the seasonal component is additive, this fits data where seasonal swings stay roughly constant in absolute size; for volume series where seasonal swings scale with the trend level, switch to
seasonal="mul"
This project is licensed under the MIT License — see LICENSE for details.
