← All projects

Case study · Data science & optimization

Demand-Driven Replenishment Optimization

A replenishment decision system for multi-store retail. An ML demand forecast sizes each store's safety stock and reorder point, every policy is scored in a period-by-period inventory simulation, and the service level is chosen to minimize total cost. The result is measured in the two numbers operations cares about: fill rate and inventory cost.

At a glance

Demand forecast
Gradient-boosted daily demand per store (calendar and lag features), walk-forward validated
Policy
Safety stock from forecast error: z * sigma * sqrt(protection period), plus reorder point and base stock
Simulation
Period-by-period inventory model with supplier lead time, review period, and backorders
Optimization
Per-store service level (0.50 to 0.99) chosen to minimize simulated total cost
Budget allocation
Safety-stock spend allocated across stores by marginal fill-rate gain per unit of holding cost
Fill rate result
81% (naive) to 98% (forecast-driven), demand-weighted
Cost result
Total cost (holding + stockout + ordering) down 88% against a naive days-of-supply policy
Stack
Python, pandas, scikit-learn, scipy, Dagster. 10 tests, CI green

Pipeline

Demand history → ML forecast → Safety stock + reorder point → Inventory simulation → Service-level optimization → Budget allocation

What I built

  • Forecast. A gradient-boosted demand model per store using calendar, lag, and rolling features, validated with a walk-forward holdout and scored against a naive baseline. Forecast error becomes the input to safety stock, so the model is not decoration.
  • Policy math. Safety stock from the service level and demand volatility (z * sigma * sqrt(protection period)), a reorder point from expected lead-time demand plus safety stock, and a base-stock level covering the lead time plus one review period. Also newsvendor and EOQ sizing.
  • Inventory simulation. A period-by-period model that receives orders after the lead time, fills demand, tracks backorders, and reports fill rate, stockout units, average on-hand, and the three cost components.
  • Service-level optimization. For each store, the policy is swept across service levels and the one minimizing total cost is selected, so the holding-versus-stockout trade-off is made explicitly rather than assumed.
  • Budget allocation. Under a fixed inventory budget, safety-stock increments are allocated greedily by demand-weighted fill-rate gain per unit of holding cost.
  • Orchestration and tests. A Dagster job wires demand, forecast, optimization/backtest, and reporting; 10 tests cover the policy math, simulation invariants, and the optimizer, and CI is green.

Results

Synthetic backtest, 30 stores, 29-day held-out window, against a naive days-of-supply policy sized from a trailing mean with no safety stock.

MetricNaive baselineForecast-driven
Weighted fill rate81.1%98.2%
Total cost (holding + stockout + ordering)393.5M48.5M
Average on-hand (units)7171,579
Demand forecast MAPE18.4%34.6% lower MAE than lag-7

Decisions worth noting

  • Why safety stock comes from forecast error, not a guess. Sizing buffers from the model's residual volatility ties inventory directly to how wrong the forecast actually is, which is the only defensible input.
  • Why simulate instead of trusting a formula. Service-level formulas assume clean demand and instant information. A simulation with lead time, review period, and backorders shows what the policy really costs before it is deployed.
  • Why a naive baseline. A days-of-supply rule is what most teams already run. Reporting against it answers the only question that matters: is the forecast actually worth the complexity.
  • Why non-stationary demand in the sample. Trend, promotions, and holiday peaks are baked into the synthetic data so the forecast advantage is real rather than an artifact of stationary demand.

Honest notes

The figures come from a synthetic backtest. They demonstrate the method, not a business result. The generator embeds a known demand process so the improvement is reproducible and checkable.

This is a portfolio project, not a production deployment. The policy is not live in an ERP or planning system.

Real demand is harder. Intermittent SKUs, supplier minimums, shelf life, and promotions all complicate the clean model here; the pipeline is structured so those enter as data, not as rewrites.