Back to Case Studies

Revenue forecast for a B2B manufacturer

B2B manufacturing (anonymized)
PythonXGBoostscikit-learnFastAPI

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

1

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.

2

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).

3

Target encoding

Replaced high-cardinality categorical columns with smoothed target means. Reduces dimensionality without losing the signal.

4

Model choice

XGBoost over neural nets - tabular data, hundreds of thousands of rows, this is the boring right answer.

5

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

MetricValueStatus
ModelXGBoost
AggregationRegion × WeekSweet spot
Validation MAPE14.1%Meets <20% target
Test MAPE12.9%Meets target
Beat naive (previous period)YesPass
Beat naive (same period last year)YesPass

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.