Magento 2 demand forecasting with Python + Prophet, FAQs
Frequently asked questions about training a Facebook Prophet model on Magento sales_order data and shipping 90-day SKU forecasts back into the admin.
How much sales history do I need before Prophet is usable?
The honest minimum is 12 weeks of daily sales data per SKU, that’s enough for Prophet to lock onto a weekly pattern and produce a usable 4-6 week forecast. With 26 weeks you start getting yearly seasonality. With 52+ weeks (one full annual cycle including your peak season) Prophet really shines and you can forecast 90 days out with sub-15% MAPE on most retail SKUs. Below 12 weeks, do NOT use Prophet, fall back to a 28-day moving average plus a manual safety-stock multiplier. The model will overfit garbage if you feed it too little.
Was this helpful?
Why Prophet over ARIMA for ecommerce?
Three reasons. (1) Holidays, Prophet has a first-class holiday regressor with country presets. ARIMA needs you to hand-encode every holiday as an exogenous variable, which gets brittle. (2) Multiple seasonalities, ecommerce demand has weekly AND yearly patterns. Prophet handles both natively; ARIMA needs SARIMA + careful order selection per SKU. (3) Defaults that work, Prophet ships with sensible priors. ARIMA needs ACF/PACF inspection per SKU, which doesn’t scale to a 5,000-SKU catalog. ARIMA is still the right pick for stable, single-seasonality B2B SKUs, just not for the typical Magento mix.
Was this helpful?
Will demand forecasting work for a brand-new SKU with no history?
Not directly. A brand-new SKU has zero data points; Prophet can’t fit a model. The pattern that works: category-level forecast with an analog-SKU scale factor. (1) Forecast aggregate daily demand for the category (jackets, hoodies, whatever) at the category level using all available history. (2) Pick 2-3 “analog” SKUs with similar price + size + style that have at least 12 weeks of history. (3) Compute the analog SKUs’ share of category demand. (4) Scale the category forecast by that share for the new SKU. After 60-90 days the new SKU has enough data to switch to its own Prophet model. Re-evaluate weekly.
Was this helpful?
How often do I need to retrain the model?
Weekly retraining is the sweet spot for retail Magento stores. Schedule a Sunday night cron that fits the model on the last 18 months of data and writes 90-day forecasts to panth_forecast. Monthly is too slow, you miss real demand shifts. Daily is overkill for most SKUs and burns CPU pointlessly. Two exceptions where you retrain immediately: (1) a 30%+ spike in rolling 14-day MAPE (model drift detected), and (2) any time you launch a new SKU, kill an old one, or run a flash-promo that wasn’t in the holiday regressor. Treat those as “intervention events” that justify an unscheduled refit.
Was this helpful?
Can I run this inside the Magento container or do I need a separate Python sidecar?
Always run Prophet as a separate Python sidecar container. Three reasons: (1) Prophet pulls in pystan/cmdstan which is a 400MB+ install, you don’t want that bloating your PHP container. (2) Python and PHP have different OS-package conflicts (gcc, libffi versions), mixing them is asking for trouble. (3) The sidecar can scale independently, spin up a beefy container only on training nights, scale down to nothing during the day. The Python sidecar talks to MySQL (read sales_order, write panth_forecast) and that’s it. Use the existing docker-compose.python-expose.yml pattern in the repo, add a forecaster service alongside the MCP server.
Was this helpful?
Does it handle promotion-driven demand spikes (BFCM, flash sales)?
Partly, with help from you. Prophet has first-class holiday support, so recurring promotions (Black Friday, Cyber Monday, Boxing Day, Diwali, EOFY) are easy: add them to the holidays DataFrame with appropriate lower_window and upper_window values to capture the lead-up + tail. Ad-hoc flash sales are harder: treat them as a custom regressor with a binary on/off flag per day, and include the regressor in future only for planned upcoming promos. Critical: don’t let flash-sale spikes corrupt the baseline, either tag them as holidays or use Prophet’s outliers support to mark them as exceptional days the model should ignore for trend.
Was this helpful?
What’s the expected accuracy (MAPE) I should target?
It depends on the SKU. For fast-mover, low-variance SKUs (consistent daily demand > 5 units): target 8-12% MAPE. Achievable. For typical mid-velocity SKUs (1-5 units/day): 12-18% MAPE is realistic and operationally useful. For slow-movers (< 1 unit/day, lumpy demand): MAPE is a bad metric, use intermittent-demand metrics like Croston’s method or just classify into a manual reorder bucket. As a rule of thumb: if your overall portfolio MAPE is under 15% across your top 80% of revenue, the model is production-ready for replenishment decisions. Under 10% is excellent and probably means you have a stable B2B catalog.
Was this helpful?
How do I push forecasts back into Magento’s admin?
Three patterns, in order of complexity. (1) Custom admin block reading from a panth_forecast table, cleanest. The Python sidecar UPSERTs predictions per (sku, ds); a Magento block renders the next 30 days on the product-edit page. (2) REST API push to POST /V1/inventory/source-items, updates a recommended-quantity field on the MSI source. Works for multi-source inventory. (3) Webhook into a custom controller, the sidecar POSTs HMAC-signed batches; a controller validates and writes via the repository pattern. Pick (1) for stores under $5M GMV, (2) for $5-50M with MSI, (3) for enterprises with a real eventing platform.
Was this helpful?
Can I use this for multi-warehouse / multi-source inventory?
Yes, but with a tweak. The default model forecasts total demand per SKU. For MSI (multi-source inventory), you have two choices: (A) Forecast total demand, then split by historical source-share. Simple, accurate when source-allocation is stable. (B) Forecast per source-SKU pair directly. More accurate when sources serve different geographies / channels but multiplies your training cost by the source count. For 90% of Magento MSI setups, choice (A) is good enough. Run the per-source split as a simple ratio job after the Prophet forecast lands. Pick (B) only when you have material per-source seasonality differences (e.g., a US warehouse vs an EU warehouse with different BFCM weeks).
Was this helpful?
Is this cheaper than buying a SaaS like Inventory Planner or Lokad?
Long-term, yes. Short-term, it depends. SaaS economics: Inventory Planner is $150-500/mo; Lokad is $1.5k, 5k/mo; RELEX / o9 are enterprise-only with 6-figure annual contracts. Build economics: 2 weeks of senior Python work (~$5k) + a few hours/mo of maintenance (~$200/mo). The custom build pays back the SaaS license within 6-18 months at Inventory Planner pricing, and within 1-3 months at Lokad pricing. The non-financial trade-off: SaaS tools come with support, training, and a working product on day one. The custom build needs in-house Python ownership forever. If you have a data team or a strong Magento dev, build. If not, buy.
Was this helpful?
Will it scale to a 50,000-SKU catalogue?
Yes, with the right architecture. A single Prophet fit takes 1-3 seconds on CPU; serial training of 50k SKUs takes 14-42 hours. Parallelize: use concurrent.futures.ProcessPoolExecutor with N workers = CPU cores. On a 16-core machine you train 50k SKUs in 1-3 hours. Three scale tricks: (1) Skip SKUs with < 12 weeks of history or fewer than 30 non-zero days, fall back to category forecast. (2) Use Prophet’s uncertainty_samples=0 when you don’t need confidence intervals (3x faster fit). (3) Cluster slow-mover SKUs and forecast at the cluster level. With these tricks, 50k SKUs is a comfortable nightly job on a single beefy box; you don’t need a Spark cluster.
Was this helpful?
How do I handle outliers and dirty order data?
Three layers of defense. (1) Filter at the SQL layer: exclude canceled / held / pending_payment orders, exclude refunded line-items, exclude test orders (filter on customer_email or store_id). (2) Detect outliers in Python: compute rolling 14-day median absolute deviation; flag any day > 4 MAD from the median. Mark those days as NaN in the y column, Prophet handles missing days correctly, but lets you blame the outlier on a real-world event later. (3) Use Prophet’s built-in outlier robustness by setting changepoint_prior_scale low (0.01-0.05) for SKUs with known dirty history. Garbage-in-garbage-out: a 30-line data-quality check before training is the single highest-leverage thing you can write.
Was this helpful?
Request a quote
I'll reply within 2-4 hours business with a written quote and timeline.