K-03
Forecast Accuracy — WAPE & MAPE
How far off was the forecast — and is your accuracy a volume-weighted error, or an average of per-item percentages that lets the smallest items dominate the number?
There is a reference build of this metric: the Demand Forecast Accuracy dashboard in the showcase runs this definition over frozen, simulated data — not a live client system.
Definitions and source tables below are current to the date above — verify against current SAP, Oracle, Microsoft, Infor, and Databricks documentation before you build.
What forecast accuracy measures
Forecast accuracy is forecast error, restated so that bigger is better. The error is the honest object: how far the forecast sat from what actually happened, measured at a declared grain, at a declared lag, under a declared weighting. Accuracy is then usually reported as one minus that error, floored at zero so it cannot go negative in front of an executive audience.
There is no standard here, and the gap between conventions is not small. WAPE weights every unit equally — it is absolute error summed over actuals summed. MAPE weights every row equally, which sounds fairer and is not: a slow-moving item that sold two units against a forecast of ten contributes a 400% error to the mean, and a hundred such items can drown out the entire high-volume portfolio. The two routinely differ by tens of points on identical data.
So the number is only meaningful with its settings attached. An accuracy of 78% means nothing until it says which error metric, at which lag, at which grain. Change any one of those and the same forecasts produce a different headline — which is why accuracy figures from two systems, or two years, should be assumed non-comparable until the three settings are shown to match.
Also answers to WAPE · wMAPE · MAPE · MAD/Mean · Forecast Error
The decision switches
Three switches decide what your accuracy number is. State all three on the chart, because two accuracy figures with different settings are not a comparison.
| Switch | Settings | Practice default |
|---|---|---|
| Error metric | WAPE · MAPE · MAD-based | WAPE — keep MAPE only where a legacy scorecard demands it, and never compare the two as if they measured the same thing. |
| Lag | Lag 1 · Lag 3 · Frozen at cycle | Report the lag with the number — an accuracy figure without a stated lag is not a metric. |
| Evaluation grain | Item-location-month · Aggregated first | Score at the grain someone acts on — the aggregate's error is not the average of the item errors. |
Error metric
- dependsWAPE — volume-weighted — absolute error over actuals, summed; the practice default
- dependsMAPE — an unweighted mean of per-row percentages, which explodes on small actuals
- dependsMAD-based — mean absolute deviation, reported in units rather than as a percentage
Practice default WAPE — keep MAPE only where a legacy scorecard demands it, and never compare the two as if they measured the same thing.
Lag
- raises the scoreLag 1 — the freshest forecast, made closest to the period it predicts
- dependsLag 3 — usually the lag planning actually commits supply at
- dependsFrozen at cycle — whatever the planning cycle locked, which may vary by item
Practice default Report the lag with the number — an accuracy figure without a stated lag is not a metric.
Evaluation grain
- lowers the scoreItem-location-month — errors cannot offset each other, so the number is harsher and more actionable
- raises the scoreAggregated first — over- and under-forecasts cancel before the error is taken
Practice default Score at the grain someone acts on — the aggregate's error is not the average of the item errors.
Formula & grain
WAPE = Σ |forecast − actual| ÷ Σ actual
- Numerator
- Absolute error summed across the evaluation grain
- Denominator
- Actual demand summed over the same rows
- Grain
- Item × location × period, at a declared lag
- Note
- Recompute from the summed components at every rollup — never average percentages up the hierarchy.
Common pitfalls
- Averaging per-SKU MAPEs — Intermittent-demand items produce percentage errors in the hundreds, and an unweighted mean lets them swamp the portfolio. If a headline number moves when you add slow movers that barely ship, it is measuring the tail, not the business.
- Division by zero — MAPE is undefined wherever actuals are zero, so implementations quietly drop those rows or substitute a value — both of which change the number. WAPE has no such hole, because the denominator is a sum.
- Comparing across lags or grains — A lag-1 national accuracy against a lag-3 item-DC accuracy is not a comparison, and the first will always look better. Fix the lag and the grain before anyone draws a trend line.
- Accuracy floors — One minus WAPE goes negative as soon as absolute error exceeds demand, which happens routinely at item grain. Decide whether to floor it at zero, and say so — an undeclared floor makes bad items look merely mediocre.
- A “MAPE” that is really a WAPE — Numbers arrive labelled MAPE from planning teams and vendor tools that actually compute summed absolute error over summed actuals — which is WAPE (the same formula this library elsewhere writes as WMAPE). The label does not settle the arithmetic, and the two constructions differ by tens of points on identical data, so ask for the formula before comparing any two accuracy figures.
- Intermittent demand and the flat-zero trap — Absolute-error measures of the MAE and MASE (scaled absolute error against a naive baseline) family are minimized by the conditional median, which for an item that sells nothing in most periods is zero — so ranking methods on one of them can crown a permanent forecast of zero on the intermittent tail. Score slow movers instead on a quantile (pinball) loss at the stocking quantile over the lead-time bucket, and separate the slow-moving tail from the fast movers before scoring: never one league table.
Source tables — SAP
Source tables — JD Edwards
Source tables — Dynamics 365
ForecastSales carries demand forecast lines per forecast model, so the model belongs in the key alongside item and period. Actuals come from sales lines and invoiced transactions, with the company pinned.
Source tables — Infor M3
Source tables — Oracle EBS
Forecast sets and their date buckets are the plan side; the designator is the identity of a forecast, so it belongs in the join. Order lines carry the actuals, under the usual operating-unit striping.
Source tables — Oracle Fusion
Fulfillment lines carry the demand actuals, and nothing else here does. Plans live in Supply Planning and reach the lakehouse through BICC extracts rather than through this table, so the accuracy fact is assembled downstream from two different extract paths.
Store the components, not the ratio
Every switch above is a different way of reading the same underlying facts, so the components — quantities, dates, values, flags — are what belongs in the gold layer, never the finished percentage. Bronze keeps the source tables as extracted, silver resolves the encodings once, and gold carries a component-level fact that each variant of Forecast Accuracy reads as a SELECT — the pattern worked through in full in the OTIF entry's landing pattern.