Economic Order Quantity (EOQ)
Overview
EOQ determines the order quantity that minimizes total inventory cost = ordering cost + holding cost. Formula: EOQ = √(2DS/H) where D=annual demand, S=ordering cost per order, H=holding cost per unit per year. Assumes constant demand and instantaneous replenishment.
When to Use
Trigger conditions:
- Setting standard order quantities for inventory replenishment
- Balancing ordering frequency against warehousing costs
- Baseline calculation before applying safety stock adjustments
When NOT to use:
- When demand is highly uncertain (use newsvendor model)
- When products are perishable with short shelf life
- When quantity discounts change the cost structure significantly
Algorithm
IRON LAW: EOQ Assumes CONSTANT, KNOWN Demand
If demand is variable or uncertain, EOQ gives the wrong answer.
Real-world application: use EOQ as a starting point, then add
safety stock for demand variability and lead time uncertainty.
Total cost curve is flat near EOQ — ±20% from optimal Q changes
total cost by only ~2%.
Phase 1: Input Validation
Determine: D (annual demand in units), S (fixed cost per order), H (holding cost per unit per year = unit cost × holding rate, typically 20-30% of unit value).
Gate: All costs positive, demand estimate reasonable.
Phase 2: Core Algorithm
- EOQ = √(2 × D × S / H)
- Number of orders per year = D / EOQ
- Reorder point = d × L (daily demand × lead time in days)
- Total annual cost = (D/Q × S) + (Q/2 × H) at Q = EOQ
Phase 3: Verification
Check: ordering cost component ≈ holding cost component (they're equal at EOQ). Total cost is at minimum.
Gate: Ordering cost ≈ holding cost (±5%).
Phase 4: Output
Return EOQ with cost breakdown and reorder point.
Output Format
json
{
"eoq": 500,
"orders_per_year": 20,
"reorder_point": 150,
"annual_cost": {"ordering": 2000, "holding": 2000, "total": 4000},
"metadata": {"demand": 10000, "order_cost": 100, "holding_cost": 4.0}
}
Examples
Sample I/O
Input: D=10,000 units/year, S=$100/order, H=$4/unit/year
Expected: EOQ = √(2×10000×100/4) = √500000 = 707 units
Edge Cases
| Input | Expected | Why |
|---|
| Very high S, low H | Large EOQ, few orders | Minimize expensive ordering |
| Very low S, high H | Small EOQ, frequent orders | Minimize expensive holding |
| D = 0 | EOQ = 0, no ordering | No demand, no orders needed |
Gotchas
- Holding cost underestimation: H should include: capital cost, storage, insurance, obsolescence, handling. Companies often only count warehouse rent, understating true H.
- Flat cost curve: Total cost is insensitive near EOQ. Rounding EOQ to a convenient number (full pallet, container) costs very little.
- Quantity discounts: Price breaks at certain quantities may make it cheaper to order MORE than EOQ. Compare total cost at EOQ vs discount breakpoints.
- Lead time variability: EOQ doesn't address when to order, only how much. Add safety stock: SS = z × σ_demand × √(lead time).
- Multi-item coordination: When multiple items share ordering costs (same supplier), use joint replenishment models, not individual EOQs.
Scripts
| Script | Description | Usage |
|---|
| Compute Economic Order Quantity and cost breakdown | python scripts/eoq.py --help
|
Run
python scripts/eoq.py --verify
to execute built-in sanity tests.
References
- For EOQ with quantity discounts, see
references/eoq-discounts.md
- For safety stock calculation, see algo-sc-safety-stock