The Stochastic Forecasting Engine: 4 Competing Models per SKU
Complete breakdown of the StochasticProjector engine: polynomial, sinusoidal, exponential, and logarithmic models compete for every product. Weighted R² with exponential decay, P10/P50/P90 Poisson confidence bands, demand cliff detection, and stockout prediction.
Demand forecasting is the foundation of any inventory management. Yet the vast majority of CIS retailers still use one of two approaches: either "order the same as last month" or a moving average in Excel. Both ignore seasonality, trends, and nonlinear demand patterns. Our StochasticProjector engine works fundamentally differently: for every SKU, 4 competing mathematical models are fitted, and the one that best describes that specific product wins.
Four Models
For each product in each period, the system fits parameters for four models:
1. Polynomial (polynomial degree 1). A simple trend line through the data. Best suited for stable products with steady demand — everyday essentials, hygiene products, consumables. Formula: y = a·x + b, where x is the period number.
2. Sinusoidal. Fits sin/cos functions with angular frequency ω = 2π/T, where T is the cycle length (12 for monthly data, 52 for weekly). Captures seasonal waves: cold remedies spiking in weeks 44-48, allergy products peaking in weeks 14-22, sunscreen with a summer maximum. Formula: y = a + b·sin(ωx) + c·cos(ωx).
3. Exponential. Performs the fit in log-space: ln(y) = a·x + b, then back-transforms. Best for new products with accelerating growth or declining products at end-of-life.
4. Logarithmic. Log-transforms the X axis (time): y = a·ln(x) + b. Captures the "tapering growth" pattern — a product that surged initially and is now leveling off.
None of these are neural networks. None require GPU training. They are classical statistical models, and they work — not because they're sophisticated, but because they're matched to the right data.
Winner Selection: Weighted R² with Exponential Decay
The naive approach is to pick the model with the lowest average error. But this gives equal weight to year-old data and last week's data. In real retail, demand patterns change: a product that was seasonal last year may have gone flat due to a new competitor. A product with exponential growth hit its ceiling.
We use weighted coefficient of determination R² with exponential recency weighting. The half-life is approximately length/5 of the time series. If a product has 50 weeks of history, the half-life is 10 weeks: data from 10 weeks ago carries half the weight of today's, 20 weeks ago — a quarter.
Weight formula for point at index i with series length N:
- •w(i) = exp(-ln(2) · (N - i) / half_life)
- •half_life ≈ N / 5
Weighted R² is calculated as: 1 - (Σ w_i · (y_i - ŷ_i)²) / (Σ w_i · (y_i - ȳ_w)²), where ȳ_w is the weighted mean. The model with the highest weighted R² wins for that SKU.
P10/P50/P90 Confidence Bands
A forecast of "500 units next month" is nearly useless for a buyer. What's needed is a range: "between 380 and 620 units, with 500 most likely."
We generate three scenarios based on a Poisson model:
- •P50 = model prediction (median)
- •P10 = prediction − 1.96 × √(λ + σ²_residual) — optimistic consumption scenario
- •P90 = prediction + 1.96 × √(λ + σ²_residual) — pessimistic scenario
Where λ is the Poisson rate parameter (estimated from data), and σ_residual is the standard deviation of model errors on historical data. The Poisson distribution is chosen deliberately: retail demand is count data (0, 1, 2, ... units, never 3.7).
Practical application: a store manager orders to P50 for regular products, P90 for critical items (where stockouts mean lost customers), P10 for low-margin items (where overstock is the bigger risk).
Demand Cliff Detection
In real retail, demand doesn't taper gracefully to 0.3 units per week. It stops. A product is either selling or it isn't. The built-in cliff detector fires when both conditions are met:
- •Forecast drops to 20% or less of the previous period's value
- •Absolute predicted value ≤ 3 units
When triggered, the forecast is truncated to zero. Without this, the system would generate phantom low-level forecasts leading to unnecessary small orders — each consuming buyer time, logistics resources, and freezing capital.
Stockout Prediction
Based on the forecast and current balance, the system calculates three potential stockout dates:
- •Optimistic — under P10 scenario (low consumption)
- •Neutral — under P50 (most likely consumption)
- •Pessimistic — under P90 (high consumption)
The calculation accounts for trend: if the linear regression slope shows growing demand, the projection accelerates. Days of stock are recalculated with weekend and holiday adjustments using the company's global seasonality coefficient (DEFAULT_WEEKEND_SALES_MULTIPLIER = 0.9).
Maximum stock days are capped at MAX_STOCK_DAYS = 365 — if the product will last a year or more at current velocity, further forecast precision has no practical value.
Seasonality Integration
The forecasting engine doesn't operate in a vacuum. For each SKU, seasonality coefficients are available from the classification engine:
- •monthly_seasonality — 12 coefficients by month
- •weekly_seasonality — 52 coefficients by ISO week
- •weekly_day_seasonality — 7 coefficients by day of week
The base forecast is modulated by seasonal coefficients: if the current week's coefficient is 1.3, the forecast increases by 30%. If the weekend coefficient is 0.9, Saturday and Sunday forecasts decrease by 10%.
Period Support
The engine works with three period types:
| Type | Code | Typical Use |
|---|---|---|
| Daily | 0 | Short horizons, high-frequency products |
| Weekly | 1 | Primary mode, smooths daily fluctuations |
| Monthly | 2 | Long-term trends, slow categories |
Period choice affects parameters: the sinusoidal model uses ω = 2π/12 for monthly data and ω = 2π/52 for weekly. Daily-to-weekly aggregation is handled by aggregate_sales(), which also handles date duplicates and gaps.
Results on Real Data
On a retail chain of 8 locations (1,000 SKUs, 78 categories, 72 suppliers), the StochasticProjector demonstrated:
- •P50 forecast accuracy: 85-92% (MAPE 8-15%)
- •Average computation time: <2 seconds per SKU for a 52-week horizon
- •Share of SKUs with R² > 0.7: 68% (good model quality)
- •Share of SKUs with cliff detection: 12% (119 products with ceased demand)
Most frequent model winner: polynomial (42%), sinusoidal (31%), logarithmic (18%), exponential (9%). The sinusoidal model dominates seasonal categories with pronounced seasonality — cold remedies, allergy products, vitamins.
Key Takeaways
- •Model competition beats a single model. Different products follow different patterns. A system using only ARIMA or only exponential smoothing will be good for some of the assortment and poor for the rest.
- •Data freshness matters more than volume. Exponential decay weighting with half_life = N/5 means the system adapts to changes in weeks, not quarters.
- •Range matters more than point. P10/P50/P90 enables risk-aware decisions rather than hoping a single forecast is right.
- •Cliffs must be detected explicitly. Without cliff detection, the system wastes capital on phantom orders for products that have stopped selling.
Research & Insights
Start free audit