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
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.
Related Tools
Statistical Forecast Methods Reference Guide
A searchable table of forecast methods with the demand pattern each suits, the data needed, key parameters and typical pitfalls.
Developer ToolsSQL Monthly Demand History Query Generator
Generate SQL that turns order or shipment lines into a clean monthly demand history per item, with gap months filled with zero.
Supply Chain & InventoryDemand Pattern ADI and CV2 Classification Calculator
Enter a few counts from an item's demand history and get its ADI, CV2 and demand class so you can choose a suitable forecast method.
Supply Chain & InventoryExcel Forecast Formula Generator (WMAPE, Bias, Tracking Signal)
Enter your actual and forecast ranges and get paste-ready Excel formulas for WMAPE, bias, MAE, RMSE and tracking signal.
More Developer Tools tools
See all- SAP Sales Order ABAP SELECT Generator (VBAK, VBAP, VBFA)
- SAP Sales Order Create BAPI ABAP Generator (BAPI_SALESORDER_CREATEFROMDAT2)
- SAP ABAP Open Items SELECT Generator
- SAP ACDOCA SQL Query Generator
- SAP QM Inspection Lot ABAP SELECT Generator
- SAP Quality Notification Pareto SQL Query Generator
- SAP QM Usage Decision BAPI Skeleton
- ABAP Custom Exception Class Generator
- ABAP Unit Test Class Generator