Key takeaways
- Sales are not demand. When a slot sells out, a moving average learns the slot's capacity, not what people wanted.
- Uncensor demand by estimating the sales rate from in-stock hours only, then account for substitution before you raise pars.
- Model three clocks of seasonality: hour of day, day of week and the site's own calendar.
- Forecast a range, not a number. Fresh items deserve a lower service level than canned drinks because waste and lost sales cost different amounts.
- Judge a forecast by stockouts, waste and visits, not by error metrics alone.
The most common forecast in vending isn’t called a forecast. It’s the par level: the fill-to quantity for each slot, adjusted now and then by looking at what sold over the last few weeks. Under the hood, that’s a moving average, and moving averages are perfectly good tools for demand that is steady and fully observed. Vending demand is neither. It swings by hour, weekday and season, and every time a slot sells out, the most important part of the signal disappears. Here’s what breaks, why it breaks, and what a better forecasting stack looks like.
What a moving average quietly assumes
A four-week moving average makes three assumptions:
- Demand is stable. Last month is a good guide to next week.
- Every week counts equally. The week of a holiday shutdown weighs as much as a normal one.
- Sales equal demand. What you sold is what people wanted.
The third assumption is the one that hurts.
Failure 1: stockouts hide demand
Statisticians call this censored demand. Once a slot is empty, sales stop but demand doesn’t. Your data records zero, and the average learns the wrong lesson.
A worked example. A cold brew slot holds 10 units. The machine is serviced every 4 days and sees about 14 selling hours a day (6 a.m. to 8 p.m.), so 56 selling hours per cycle. True demand is about 4 units a day.
- Over each cycle, people want about 16 units. The slot holds 10, so it sells out after roughly 35 selling hours: around lunchtime on day 3.
- Recorded sales: 10 units per 4 days, or 2.5 a day.
- The moving average says 2.5 a day. Times 4 days, that’s 10 units. The par of 10 looks perfect, so nobody changes it.
The forecast has learned the slot’s capacity, not the demand, and the loop confirms itself every cycle. Meanwhile, the slower item in the next slot never sells out, so its data is honest and it keeps its generous space.
The fix is to estimate the sales rate from in-stock time only. Ten units in 35 in-stock selling hours is about 0.29 units an hour. Over 56 selling hours, that’s about 16 units of demand per cycle, matching reality.
One caution before you raise the par to 16: some of that hidden demand didn’t vanish. It moved to a neighboring slot when cold brew ran out. Give cold brew more space and the neighbor’s sales will dip a little. Good systems estimate demand at the category level as well as the item level, so they don’t count the same thirsty person twice.
None of this works without time-stamped sales. A DEX audit pulled at each visit tells you the totals between visits, not when the last unit sold. You need telemetry.
Failure 2: three clocks of seasonality
Vending demand runs on three clocks at once:
- Hour of day. Breakfast, lunch, mid-afternoon and shift changes. This decides when an item sells out, which decides whether it’s gone before or after the lunch peak.
- Day of week. A hybrid office might be busy Tuesday to Thursday and quiet on Mondays and Fridays. A plant might run lighter weekend shifts.
- The site’s own calendar. University terms and finals, plant shutdowns, hospital census swings, holidays and summer. These are known in advance, which makes them the cheapest accuracy you can buy.
A moving average flattens all three. Worse, it lags. After a two-week shutdown, a four-week average spends the next month underestimating demand, so you under-fill exactly when people come back hungry.
Failure 3: slow movers
Many vending SKUs sell in sparse, lumpy patterns: 0, 0, 1, 0, 3, 0, 1. A moving average turns that into 0.7 a day, which is never what happens on any actual day. For these items, methods built for intermittent demand behave better. Croston’s method, for example, forecasts the size of each sale and the gap between sales separately. Often the better answer is to forecast the category at the site and allocate down, because “someone will buy a salty snack” is far more predictable than “someone will buy the jalapeño ones.”
Failure 4: one number when you need a range
A replenishment decision isn’t “how many will sell?” It’s “how many should I put in, given what it costs to be wrong in each direction?” That’s the classic newsvendor problem, and the answer depends on the item.
Take an illustrative fresh sandwich that costs $2.10 and sells for $5.50, with no salvage value after its date:
- Cost of stocking one too few: the lost margin, $3.40.
- Cost of stocking one too many: the wasted product, $2.10.
- Optimal service level: $3.40 ÷ ($3.40 + $2.10) ≈ 62%.
So you stock to the 62nd percentile of forecast demand, not the 95th. A canned drink with months of shelf life has almost no waste cost, so it can be stocked to a much higher percentile and should rarely sell out. A moving average hands both items one number and treats them identically. A forecast that produces a distribution lets each item get the service level its economics deserve.
Failure 5: things change
Price changes, planogram moves, new items and packaging changes all break the history a moving average depends on, and a new SKU has no history at all. Better models handle this by pooling: a new protein bar borrows its first forecast from similar bars at the same site and from the same bar at similar sites, then learns its own pattern as data arrives. Price changes become a feature in the model rather than an unexplained jump.
What replaces the moving average
You don’t need a research lab. You need a stack that handles these failures in order:
- Clean the history. Flag in-stock and out-of-stock periods, prices, planogram changes and site events.
- Uncensor demand. Estimate rates from in-stock time, with substitution handled at the category level.
- Model the three clocks. Seasonal exponential smoothing (Holt-Winters) is a solid baseline. Gradient-boosted models with calendar, price and site features often do better at fleet scale.
- Pool across the hierarchy. Item-at-site, category-at-site and item-across-sites forecasts should agree with one another.
- Forecast distributions. Output quantiles, then set each item’s service level from its own costs.
- Turn forecasts into decisions. The fill quantity is forecast demand between this visit and the next one, at the chosen service level, minus the expected on-hand when the driver arrives, capped at slot capacity. The next visit date itself comes from routing, which is why forecasting and pre-kitting work as one system.
| Method | Seasonality | Stockouts | Slow movers | New items | Data needed |
|---|---|---|---|---|---|
| Moving average | No | No | Poor | No | Visit totals |
| Seasonal smoothing | Yes | Only if uncensored first | Fair | No | Daily sales |
| Croston-type | No | No | Good | No | Daily sales |
| Feature-based model with pooling | Yes | Yes, with in-stock flags | Good, via the category | Yes | Time-stamped sales, calendar, price |
Measuring forecasts honestly
- Use WAPE, not MAPE. Mean absolute percentage error divides by actual sales, which explodes on slow movers that sold zero. Weighted absolute percentage error divides total error by total sales and stays sane.
- Beat a baseline. Compare every model against a naive forecast such as “same as last week.” If it doesn’t win clearly, it isn’t worth the complexity.
- Score on uncensored periods, or you’ll reward models that predict the stockouts you’re trying to eliminate.
- Watch the business metrics. Out-of-stock events, waste and service visits matter more than any error score. A model can improve WAPE and still make routes worse.
A worked comparison
Here’s how the pieces add up for one illustrative machine with 30 slots. Under moving-average pars, 6 slots sell out before every visit, like the cold brew above. Uncensoring those 6 raises their pars, and the space comes from 4 slow movers that never came close to empty. Fresh items move to a service level near the 62% from the sandwich example, and their waste falls. Shelf-stable drinks rise to a high service level and stop selling out. The machine holds the same number of units; they’re just in the right slots. That’s where most early forecasting gains come from: not a cleverer model, but a model that sees the demand a sold-out slot was hiding.
A checklist before you trust a forecast
- Does it know when each item sold out, to the hour?
- Does it handle substitution when it corrects for stockouts?
- Does it use site calendars (shutdowns, terms, holidays)?
- Does it produce a range, and can service levels differ by item?
- How does it forecast a brand-new SKU?
- Is it measured against a naive baseline, and on stockouts and waste?
How 8Kava approaches this
Forecasting & Auto-Replenishment forecasts at the item level from time-stamped telemetry, corrects for stockout-hidden demand and feeds the result straight into pick lists and route plans. Across our deployments, out-of-stock events fell 41% after operators turned it on. For legacy machines, a Kava Link retrofit supplies the time-stamped sales and inventory data the model needs. And because fresh items need different service levels from shelf-stable ones, the same forecasts drive expiry-aware pricing for the tail.
