Developer ToolsFree generatorPDF, Excel, Word export

Python Holt-Winters Demand Forecast Script Generator (pandas and statsmodels)

A Holt-Winters model is a good first statistical forecast for items with trend and seasonality, and it is a few lines in Python. This generator writes a complete script that loads your monthly history, tests the model on a holdout against a seasonal naive baseline and writes the forecast to CSV.

Fill in

Generated code
Download:

Install

pip install pandas numpy statsmodels

Script

import numpy as np
import pandas as pd
from statsmodels.tsa.holtwinters import ExponentialSmoothing

SEASON = 12
HORIZON = 6
HOLDOUT = 6

df = pd.read_csv("demand_history.csv", parse_dates=["month_start"])
series = df.set_index("month_start")["qty"].sort_index().asfreq("MS").fillna(0.0)

if len(series) < 2 * SEASON + HOLDOUT:
    raise SystemExit("Need at least two full seasons plus the holdout of history")

def fit(train):
    model = ExponentialSmoothing(
        train,
        trend="add",
        seasonal="add",
        seasonal_periods=SEASON,
    )
    return model.fit()

def wmape(actual, forecast):
    a = np.asarray(actual, dtype=float)
    f = np.asarray(forecast, dtype=float)
    return np.abs(a - f).sum() / a.sum() if a.sum() else float("nan")

train = series.iloc[:-HOLDOUT]
test = series.iloc[-HOLDOUT:]
hw_test = fit(train).forecast(HOLDOUT)
naive_test = series.shift(SEASON).iloc[-HOLDOUT:]

print("Holt-Winters WMAPE:", round(wmape(test, hw_test), 3))
print("Seasonal naive WMAPE:", round(wmape(test, naive_test), 3))

final = fit(series).forecast(HORIZON)
out = final.rename("forecast").round(0).clip(lower=0).to_frame()
out.index.name = "month_start"
out.to_csv("forecast_output.csv")
print(out)

Multiplicative components require strictly positive history, so use additive when the series has zero months. For intermittent items do not use this script, use Croston style methods. Check the statsmodels version you have installed, as argument names can change between releases. Only adopt the model if its holdout error beats the seasonal naive baseline.

Forecasting inside your ERP workflow

Want model results reviewed by planners and loaded back with approval? Ask for an optional expert review.

Optional. The tool and every export work without this. No spam.

How to use the generator

Export monthly demand for one item with a date column and a quantity column, for example from the SQL generator on this site. Enter the file and column names, the season length, the horizon and the holdout. Copy the script, install the packages and run it. It prints the holdout error of Holt-Winters and of the seasonal naive baseline, then saves the forecast. Run it once per item or wrap it in a loop.

The method behind it

Holt-Winters keeps three smoothed components: level, trend and season. Additive seasonality suits a swing of constant size, multiplicative suits a swing that grows with the level. The script holds out the most recent periods, fits on the rest and scores both the model and a seasonal naive forecast with WMAPE. A model that does not beat that simple baseline should not replace it.

  • Needs two full seasons plus the holdout
  • Zero-fills missing months first
  • Compares to seasonal naive
  • Clips negative forecasts to zero

Reading the result

If Holt-Winters shows a WMAPE of 0.18 and the seasonal naive shows 0.25 on the same holdout, the model adds value and you can use its forecast as the statistical baseline. If the two are close, keep the simpler method. Always plot the history and forecast before sending numbers to sales, and review items with a recent level shift.

Common mistakes

Choosing multiplicative seasonality on a series that contains zeros makes the fit fail. Leaving gaps in the dates shifts the seasonal pattern. Evaluating only on the data used to fit the model overstates accuracy. Fitting every item with the same settings ignores that some are trending and some are flat, so consider classifying items first.

Frequently Asked Questions

How much data does Holt-Winters need?

At least two full seasons to estimate the seasonal shape, so 24 months for monthly seasonality. The script also reserves a holdout, so aim for more than 30 months. With less history use a moving average or seasonal naive.

Should I choose additive or multiplicative seasonality?

Choose additive if the seasonal swing stays about the same size as demand grows, and multiplicative if it grows with demand. Multiplicative cannot handle zeros. When unsure, test both on the holdout and keep the lower error.

Can I use this for slow moving spare parts?

No. Intermittent items with many zero months need Croston, SBA or TSB. Holt-Winters will forecast a smooth small number each month that does not match real order timing. Classify items with ADI and CV2 first.

Does this run inside my ERP?

No, it is a standalone script that works on exported CSV data. Any forecast you load back into an ERP should go through your normal planner review and import process, not be written automatically.

If you want model selection, holdout testing and write-back approval handled per item on your own servers, Netray can build that over your ERP data.