Playground / Inventory

Critical Ratio Explorer

How much should you stock when demand is uncertain? Price the two ways to be wrong, describe what demand might do, and the optimal order falls out. Move the sliders and watch it move.

Inputs

Costs and demand

Cost of each unit of demand you could not serve: lost margin, plus any penalty or goodwill.
Cost of each unit left over: purchase cost minus salvage, plus holding until it can be used.
Derive the costs from unit economics

cu=p−cc_u = p - c, co=c−vc_o = c - v.

Critical ratio
–
cu/(cu+co)c_u/(c_u+c_o)
Optimal order Q*
–
units
P(no stockout)
–
cycle service level
Fill rate
–
share of demand served
Expected cost
–
vs ordering the mean

Exhibit 1

Stock to the critical-ratio quantile of demand

Shaded: the probability that demand is at most Q*, which equals the critical ratio. Dashed: mean demand.

Exhibit 2

Expected cost is lowest at Q*, and the curve is asymmetric

Expected overage cost rises with Q; expected underage cost falls. Their sum is convex, minimized at Q*.

The math

You choose one order quantity QQ before demand DD is known. Each unit you are short costs cuc_u; each unit left over costs coc_o. The expected cost is

E[C(Q)]=co E[(Q−D)+]+cu E[(D−Q)+].\mathbb{E}[C(Q)] = c_o\,\mathbb{E}[(Q-D)^+] + c_u\,\mathbb{E}[(D-Q)^+].

Raise QQ by one unit and two things change. With probability F(Q)F(Q) demand would not have reached that unit, so it becomes leftover and costs coc_o. With probability 1−F(Q)1-F(Q) it is sold and saves a shortage worth cuc_u. Keep adding units while the expected saving exceeds the expected cost, and stop where they balance:

co F(Q∗)=cu (1−F(Q∗))⟹F(Q∗)=cucu+co.c_o\,F(Q^*) = c_u\,\bigl(1-F(Q^*)\bigr) \quad\Longrightarrow\quad F(Q^*) = \frac{c_u}{c_u+c_o}.

That fraction is the critical ratio. The optimal order is the critical-ratio quantile of the demand distribution, Q∗=F−1 ⁣(cucu+co)Q^* = F^{-1}\!\left(\tfrac{c_u}{c_u+c_o}\right). For normal demand that is Q∗=μ+z σQ^* = \mu + z\,\sigma with z=Φ−1(CR)z = \Phi^{-1}(\text{CR}). For discrete demand such as Poisson, Q∗Q^* is the smallest integer with F(Q∗)≥CRF(Q^*) \ge \text{CR}.

How to read it

  • The ratio, not the costs, decides the quantile. Costs of 12 and 4 give the same answer as 3 and 1. Only how much worse one mistake is than the other matters.
  • A ratio above one half orders above the mean. When running short costs more than running over, you buy insurance. Below one half, you order less than you expect to sell, because the leftover risk is not worth carrying.
  • Uncertainty is priced, not padded. More variability pushes Q∗Q^* further from the mean in whichever direction the ratio points. Low-ratio items can end up with less stock as demand gets noisier.
  • The in-stock probability equals the ratio, the fill rate is higher. A 75% ratio means a 75% chance of not running out, but because shortfalls are usually small, the share of demand served is well above 75%.

Assumptions

  • One decision, one period: leftover stock does not carry over to a next order. For repeated orders, the same logic applies per review period with holding cost in coc_o; see the explainer on order economics and planning horizons.
  • Linear costs: no fixed ordering cost, no minimum order, no quantity discount.
  • No starting inventory, and the full order arrives before demand.
  • The demand distribution is known. In practice it comes from a probabilistic forecast, and its error flows straight into Q∗Q^*.
  • Normal demand is taken on the whole real line, as in the textbook model. When it puts noticeable probability below zero, the explorer says so; lognormal is the safer choice for skewed, non-negative demand. Orders are clipped at zero.
  • When co=0c_o = 0 the ratio is 1 and no finite order is optimal: excess is free, so more is never worse.

The same calculation in Python

from scipy.stats import norm, lognorm, poisson
import numpy as np

cu, co = 12.0, 4.0                     # underage, overage cost per unit
cr = cu / (cu + co)                    # critical ratio = 0.75

# Normal demand
mu, sigma = 500, 80
q_normal = max(0.0, norm.ppf(cr, loc=mu, scale=sigma))   # 553.96

# Lognormal demand with the same mean and standard deviation
s2 = np.log(1 + sigma**2 / mu**2)
q_lognormal = lognorm.ppf(cr, s=np.sqrt(s2), scale=np.exp(np.log(mu) - s2 / 2))

# Poisson demand: smallest integer with F(Q) >= CR
q_poisson = int(poisson.ppf(cr, mu))

The explorer computes everything in your browser with tested numerical routines: the normal CDF and its inverse agree with Python's reference implementation to better than 10−910^{-9}, and the optimal quantity is checked against a brute-force minimization of expected cost.