← Back to portfolio
Retail Operations · Time-Series Forecasting

Demand Forecasting & Safety Stock Optimization

Static reorder models miss event-driven demand spikes (Seahawks games, PAX West, Mariners season), causing stockouts during events and excess carrying cost in the quiet weeks between. A weekly-updating Prophet forecast with Seattle event regressors feeds directly into dynamic safety stock and reorder point calculations, replacing static tables with forward-looking signals.

8.3%
Avg. Prophet MAPE
33%
Fewer Stockouts
5
SKU Categories
104
Weeks of Demand Data
Python Prophet XGBoost STL Decomposition Streamlit Plotly
Part 1: Forecast Accuracy

Prophet vs. XGBoost, 8-Week Held-Out Test

Prophet with an event-calendar regressor consistently beats XGBoost on this problem: it handles the weekly/annual seasonality and event spikes directly, where XGBoost needs hand-engineered lag features to catch up.

SKUProphet MAPEXGBoost MAPEStockout Reduction
Beverages7.8%11.2%33%
Snacks8.1%12.4%28%
Merchandise9.2%13.8%41%
Accessories8.0%11.9%35%
Apparel8.6%14.1%29%
Average8.3%12.7%33%
Beverages
33%
Snacks
28%
Merchandise
41%
Accessories
35%
Apparel
29%

Stockout reduction by SKU category, dynamic reorder point vs. static baseline (bar length relative to Merchandise, the largest reduction).

From Forecast to Reorder Point

Forecasts alone don't prevent stockouts. The forecast has to feed a reorder policy. Safety stock is computed at a 95% service level: SS = Z × σ_LT × √LT, and the dynamic reorder point is ROP = (avg daily demand × LT) + SS, using the Prophet forecast's demand estimate instead of a trailing historical average, so the reorder point moves ahead of known events instead of reacting after the fact.

Why Prophet over ARIMA? Prophet handles multiple seasonalities (weekly, annual) and accepts external regressors cleanly: event attendance buckets plug in directly. ARIMA requires separate exogenous variable handling that adds complexity without accuracy gains here.

Why 95% service level? Standard for consumer retail; adjustable per SKU value (0.98 for high-value, 0.90 for commodity items).
Methodology

Pipeline

End-to-end pipeline from event calendar to ops dashboard, reproducible via run_pipeline.py.

01

Event Calendar

Seattle Special Events Office listings + team schedules (Seahawks, Mariners, PAX) compiled into a 55-event calendar with attendance figures, used as external regressors. WSDOT traffic volume data serves as an attendance proxy where official figures aren't published.

02

Forecast: Prophet + Event Regressors

Per-SKU Prophet models with the event calendar as an external regressor, compared against an XGBoost baseline with hand-engineered lag and calendar features. Evaluated on an 8-week held-out test window per SKU.

03

Dynamic Safety Stock & Reorder Point

Forecast demand and its uncertainty feed the safety-stock formula directly, replacing a static reorder table with a reorder point that moves ahead of known event-driven demand.

04

Stockout Simulation

Both policies (static ROP vs. dynamic ROP) are simulated against the same 8-week held-out demand to produce a like-for-like stockout-event comparison per SKU.

05

Ops Dashboard

Streamlit + Plotly dashboard surfacing the current forecast, reorder recommendations, and stockout risk, built for an operations manager, not a data scientist.

On the data: Demand figures are simulated using realistic retail parameters calibrated to BLS retail trade indices. Real proprietary retail data isn't available for a portfolio project. The event calendar itself is real (Seattle Special Events Office, team schedules). Simulating demand keeps the forecasting methodology fully inspectable and reproducible: the generation logic is in data/generate_data.py and any analyst can substitute real data without changing the pipeline.
View Repo on GitHub → ← Back to portfolio