Revenue forecast for a B2B manufacturer
B2B manufacturing (anonymized)Context
B2B manufacturing leadership needed forecasts at a level of granularity finance could plan on. The data was there - 1.8M+ transactions over 25 years - but the existing forecast wasn't beating naive baselines, which is the bar any real model has to clear.
The problem
Leadership needed a monthly revenue forecast they could actually trust for budget planning across regions and product lines. The existing approach was a smoothed average of last year, which works fine until the year stops looking like last year.
Data
1.8M+ transaction rows over 25+ years. Features: order date, net sales, region, product line, country. Daily transactions aggregated to weekly and monthly. The hard part wasn't quantity - it was the messiness and the COVID-era distribution shift.
Constraints
- •Strong seasonal demand cycles that don't all line up
- •25+ years of transaction data, much of it messy
- •2020 created a distribution shift that had to be handled explicitly
Methodology
Temporal split
Trained on history, validated on the most recent full year, held out the next year as a true test. Excluded 2020 from training because it's a distribution shift, not a normal year.
Feature engineering
Lag features at 1, 2, 3, 6, 12, 24 periods. Rolling moving averages and standard deviations. Domain-specific calendar flags (the fiscal calendar matters more than the Gregorian one).
Target encoding
Replaced high-cardinality categorical columns with smoothed target means. Reduces dimensionality without losing the signal.
Model choice
XGBoost over neural nets - tabular data, hundreds of thousands of rows, this is the boring right answer.
Aggregation level
The breakthrough. Tested Region × Product × Week vs Region × Week vs Product × Week vs Total × Week. The right level for this data was Region × Week.
Key Insight
"Most of the win wasn't model architecture. It was picking the right level of detail to forecast at. Region × Product × Country × Week was too sparse and MAPE blew up. Region × Week was the sweet spot."
Results
| Metric | Value | Status |
|---|---|---|
| Model | XGBoost | |
| Aggregation | Region × Week | Sweet spot |
| Validation MAPE | 14.1% | Meets <20% target |
| Test MAPE | 12.9% | Meets target |
| Beat naive (previous period) | Yes | Pass |
| Beat naive (same period last year) | Yes | Pass |
Technical Details
XGBoost regressor, hyperparameters tuned via cross-validation. Feature importance - lag features and calendar effects dominated. Shipped behind a FastAPI service with a scheduled retraining pipeline. Nothing exotic; everything in the stack is something the team can keep running after I leave.
Lessons Learned
- Simple model on clean data beats clever model on messy data. Almost always.
- If you don't beat naive baselines, you don't have a model - you have a science project.
- Domain calendar features captured patterns the generic time features missed entirely.
- 2020 had to be excluded explicitly. Pretending it was a normal year poisoned the validation.