Loading
Malick A. Sarr

Data Scientist

Data Analyst

Malick A. Sarr

Data Scientist

Data Analyst

Blog Post

How to calculate the Economic Order Quantity with Python? [8 Variations]

How to calculate the Economic Order Quantity with Python? [8 Variations]
 

In the realm of effective inventory management, one concept stands out as a cornerstone of optimal operations: the Economic Order Quantity, or EOQ. Whether you’re a business owner, a supply chain manager, or a curious learner, understanding EOQ is crucial to minimizing costs while maximizing inventory efficiency. In this guide, we’ll delve into the world of EOQ calculation using the power of Python, a versatile programming language.

 

By the end of this article, you’ll not only grasp the fundamentals of EOQ but also be equipped with practical skills to implement EOQ calculations in your inventory management endeavors. In this article, we demystify the EOQ formula and bring this essential concept to life through the lens of Python programming. Let’s unlock the potential of data-driven inventory optimization together.

 

What is the Economic Order Quantity?

The Economic Order Quantity, often called EOQ, signifies the optimal procurement quantity for a specific product to minimize holding costs, scarcity expenses, and ordering expenditures. This computation is primarily conducted using a model acknowledged as the Classical EOQ Model.

 

The Classical EOQ Model postulates a steady demand rate, instantaneous replenishment, and the absence of shortages. While this assumption might not perfectly mirror real-world scenarios, it remains highly applicable to many products.

 

What are the types of Economic Order Quantity (EOQ) Models?

Numerous iterations of the Classical EOQ Model exist. Here are a few notable ones:

 

  • EOQ with Quantity Discounts (EOQD): This model extends the Classical EOQ by considering suppliers’ quantity-based discounts. It aims to determine the order quantity that maximizes the total cost savings, factoring in both the discounted prices and the associated costs.
  • EOQ with Reorder Point (ROP) and Safety Stock: This model combines EOQ calculations with the concept of reorder points and safety stock. The reorder point represents the inventory level at which a new order should be placed, and safety stock is extra inventory held to mitigate unexpected fluctuations in demand or lead time.
  • EOQ for Perishable Goods: This model is designed for perishable items with limited shelf life. The EOQ calculation considers the economic order quantity and the optimal order frequency to avoid excessive stockouts or wastage due to expiration.
  • Dynamic EOQ Models: These models recognize that demand, lead times, and other factors can be variable over time. Dynamic models adjust the order quantity and timing based on changing conditions to optimize inventory management in dynamic environments.
  • EOQ with Limited Storage Space: This variant considers storage limitations in its calculations. It aims to find the order quantity that maximizes profit while staying within the available storage capacity.
  • EOQ for Multi-Item Orders: This model handles situations where a business procures multiple items simultaneously. It seeks to find the optimal order quantities for each item while considering their interactions and costs.
  • EOQ with Uncertain Demand (Stochastic EOQ): Unlike the Classical EOQ, which assumes constant demand, this model considers demand variability by using probabilistic distributions. It aims to minimize the risk of stockouts while keeping costs under control.

How to Calculate Economic Order Quantity

 

Calculate Economic Order Quantity with the Classical Model 

Assumptions of the Classical EOQ Model:

  • Demand Rate (D): The demand rate is constant and known over the planning period (usually a year).
  • Ordering Cost (S): The cost of placing an order (ordering cost) is fixed and remains the same for any order size.
  • Carrying Cost (H): The cost of carrying or holding one unit of inventory per year is constant and known.
  • No Shortages: There are no shortages or stockouts; the goal is to replenish inventory before it reaches zero.
  • Instantaneous Replenishment: Replenishment is instantaneous; when an order is placed, the items are immediately available.
  •  

Formula for Calculating the Classical EOQ:

The formula for calculating the Economic Order Quantity (EOQ) in the classical EOQ model is as follows:

 

EOQ = \sqrt{\frac{2 \cdot D \cdot S}{H}}

Where:

  • D is the annual demand in units.
  • S is the cost of placing an order.
  • H is the cost of carrying one unit of inventory for a year.

 

Classical EOQ  Code with Python

import math

def calculate_eoq(demand_in_units, cost_of_ordering, cost_of_carrying):
    """
    Calculate the Economic Order Quantity (EOQ) using the Classical EOQ model.

    Args:
        demand_in_units (int): Annual demand in units.
        cost_of_ordering (float): Cost of placing an order.
        cost_of_carrying (float): Cost of carrying one unit of inventory for a year.

    Returns:
        float: Calculated Economic Order Quantity (EOQ).
    """
    eoq = math.sqrt((2 * demand_in_units * cost_of_ordering) / cost_of_carrying)
    return eoq

# Example usage
demand = 5000
ordering_cost = 300
carrying_cost = 10

calculated_eoq = calculate_eoq(demand, ordering_cost, carrying_cost)
print('Calculated EOQ:', calculated_eoq)
Calculated EOQ: 547.7225575051662

Calculate the  Economic Order Quantity with Quantity Discounts (EOQD)

The formula for calculating the Economic Order Quantity with Quantity Discounts (EOQD) involves finding the order quantity that minimizes the total cost, considering the potential discounts. The EOQD model calculates the optimal order quantity at which the total cost, including the effects of varying unit prices, is minimized.

 

While the EOQD model does not have a simple closed-form formula like the classical EOQ model, it involves iterating through different order quantities and calculating the total cost for each quantity, considering the applicable unit price. The order quantity that results in the lowest total cost is considered the EOQD.

 

EOQ with quantity discount in Python

import math

def calculate_eoqd(demand_in_units, 
                   cost_of_ordering, 
                   cost_of_carrying, 
                   discount_price_threshold, 
                   discount_rate):
    """
    Calculate the Economic Order Quantity with Quantity Discounts (EOQD).

    Args:
        demand_in_units (int): Annual demand in units.
        cost_of_ordering (float): Cost of placing an order.
        cost_of_carrying (float): Cost of carrying one unit of inventory for a year.
        discount_price_threshold (int): Quantity at which the discount applies.
        discount_rate (float): Discount rate for quantities above the threshold.

    Returns:
        float: Calculated Economic Order Quantity (EOQD).
    """
    if demand_in_units <= discount_price_threshold:
        eoqd = math.sqrt((2 * demand_in_units * cost_of_ordering) / cost_of_carrying)
    else:
        eoqd = math.sqrt((2 * demand_in_units * cost_of_ordering * (1 - (discount_rate / 100))) / cost_of_carrying)
    return eoqd

# Example usage
demand = 5000
ordering_cost = 300
carrying_cost = 10
discount_threshold = 500
discount_rate = 10  # 10% discount for quantities above threshold

calculated_eoqd = calculate_eoqd(demand, ordering_cost, carrying_cost, discount_threshold, discount_rate)
print('Calculated EOQD:', calculated_eoqd)
Calculated EOQD: 519.6152422706632

 

Calculate the  Economic Order Quantity with Reorder Point (ROP) and Safety Stock

 

The EOQ with Reorder Point (ROP) and Safety Stock considers both the EOQ and the optimal reorder point that triggers an order when inventory reaches a certain level. The safety stock is calculated based on demand variability and desired service level.

 

The assumptions are the same as the classical model with Lead Time added (the time it takes to place an order) and the desired services (probability of not experiencing a stockout)

 

The formula for calculating the EOQ with ROP and Safety Stock involves the following steps:

  1. Calculate Average Daily Demand (D’): Divide the annual demand (D) by the number of working days in a year (usually 250 or 365).
  2. Calculate Safety Stock (SS): Safety stock is calculated based on the desired service level and demand variability during lead time. It’s the z-score multiplied by the standard deviation of demand during lead time.
  3. Calculate Reorder Point (ROP): ROP is calculated as the lead time demand plus safety stock.
  4. Calculate EOQ: Calculate the EOQ as in the classical EOQ model.

 

EOQ with Reorder Point (ROP) and Safety Stock in Python

import math
from scipy.stats import norm

def calculate_eoq_with_rop(demand_in_units, 
                           cost_of_ordering, 
                           cost_of_carrying,
                           lead_time_in_days,
                           desired_service_level):
    """
    Calculate the Economic Order Quantity (EOQ) with Reorder Point (ROP) and Safety Stock.

    Args:
        demand_in_units (int): Annual demand in units.
        cost_of_ordering (float): Cost of placing an order.
        cost_of_carrying (float): Cost of carrying one unit of inventory for a year.
        lead_time_in_days (int): Lead time in days for order fulfillment.
        desired_service_level (float): Desired service level as a percentage.

    Returns:
        float: Calculated EOQ with Reorder Point and Safety Stock.
    """
    demand_per_day = demand_in_units / 365  # Convert annual demand to daily demand
    lead_time_demand = demand_per_day * lead_time_in_days
    z_score = norm.ppf(desired_service_level + (1 - desired_service_level) / 2)
    
    safety_stock = z_score * math.sqrt(lead_time_in_days * demand_per_day * demand_per_day)
    
    eoq_with_rop = math.sqrt((2 * demand_per_day * cost_of_ordering) * (lead_time_demand + safety_stock) / cost_of_carrying)
    return eoq_with_rop

# Example usage
demand = 5000
ordering_cost = 300
carrying_cost = 10
lead_time = 7
service_level = 0.95  # 95% desired service level

calculated_eoq_with_rop = calculate_eoq_with_rop(demand, ordering_cost, carrying_cost, lead_time, service_level)
print('Calculated EOQ with ROP and Safety Stock:', calculated_eoq_with_rop)
Calculated EOQ with ROP and Safety Stock: 370.4041290061373

 

Calculate the  Economic Order Quantity for Perishable Goods

The EOQ for perishable goods considers both the ordering costs, carrying costs, and the cost associated with potential wastage due to expired items.

 

The assumptions are the same as the classical model with Shelf Life (expiration period of the items) added.

 

The formula for calculating the EOQ for perishable goods involves the following steps:

  1. Calculate Average Daily Demand (D’): Divide the annual demand (D) by the number of working days in a year (usually 250 or 365).
  2. Calculate EOQ: Calculate the EOQ as in the classical EOQ model.
  3. Calculate Optimal Order Frequency (f): Calculate the optimal order frequency based on the shelf life. This is done by dividing the shelf life by the lead time (the time between placing an order and receiving it).

This model helps businesses manage their perishable inventory efficiently to minimize wastage and associated costs.

 

EOQ  for Perishable Goods in Python

import math

def calculate_eoq_perishable(demand_in_units, 
                             cost_of_ordering, 
                             cost_of_carrying, 
                             shelf_life_in_days):
    """
    Calculate the Economic Order Quantity (EOQ) for Perishable Goods.

    Args:
        demand_in_units (int): Annual demand in units.
        cost_of_ordering (float): Cost of placing an order.
        cost_of_carrying (float): Cost of carrying one unit of inventory for a year.
        shelf_life_in_days (int): Shelf life of the perishable goods in days.

    Returns:
        float: Calculated Economic Order Quantity for perishable goods.
    """
    demand_per_day = demand_in_units / 365  # Convert annual demand to daily demand
    eoq_perishable = math.sqrt((2 * demand_per_day * cost_of_ordering) * shelf_life_in_days / cost_of_carrying)
    return eoq_perishable

# Example usage
demand = 5000
ordering_cost = 300
carrying_cost = 10
shelf_life = 30  # Perishable goods have a shelf life of 30 days

calculated_eoq_perishable = calculate_eoq_perishable(demand, ordering_cost, carrying_cost, shelf_life)
print('Calculated EOQ for Perishable Goods:', calculated_eoq_perishable)
Calculated EOQ for Perishable Goods: 157.0271767770641

 

Calculate the  Dynamic EOQ Models

Dynamic Economic Order Quantity (EOQ) models, also known as time-varying EOQ models, consider variations in demand and other factors over time. These models provide a way to adapt the optimal order quantity as conditions change. Here’s an overview of the math behind dynamic EOQ models:

 

Assumptions of Dynamic EOQ Models:

Time-Varying Demand (D_t): The demand rate is not constant and can vary over time.

Cost of Ordering (S): The cost of placing an order remains constant.

Cost of Carrying (H): The cost of carrying one unit of inventory per year remains constant.

Lead Time (L): The time it takes for an order to be delivered after it’s placed is known and constant.

No Shortages: The goal is to replenish inventory before it reaches zero.

Instantaneous Replenishment: Replenishment is instantaneous; the items are immediately available when an order is placed.

 

Formula for Calculating Dynamic EOQ:

In dynamic EOQ models, the challenge is that demand is not constant, which means the optimal order quantity should adapt to changing conditions. Different dynamic EOQ models use various approaches to consider variations in demand, such as moving averages, exponential smoothing, or considering demand variability.

 

For example, one common dynamic EOQ model uses the following formula:

EOQ_t = \sqrt{\frac{2 \cdot D_t \cdot S}{H \cdot (1 - \frac{\sigma_{D_t}}{D_t})}}

 

Where:

 

EOQ_t is the optimal order quantity at time t.

 D t is the standard deviation of demand at time t.

\sigma_{D_t} is the standard deviation of demand at time t.

S is the cost of placing an order.

H is the cost of carrying one unit of inventory for a yer.

[Code]

 

Implementing dynamic EOQ models involves developing methods to forecast future demand based on historical data and adjusting the order quantity accordingly. Techniques from time series analysis and forecasting are often employed.

 

Dynamic EOQ in Python

import math

def calculate_dynamic_eoq(demand_profile, 
                          cost_of_ordering, 
                          cost_of_carrying):
    """
    Calculate the Economic Order Quantity (EOQ) using a Dynamic EOQ model.

    Args:
        demand_profile (list): List of demand values over time.
        cost_of_ordering (float): Cost of placing an order.
        cost_of_carrying (float): Cost of carrying one unit of inventory for a period.

    Returns:
        float: Calculated Dynamic EOQ.
    """
    total_demand = sum(demand_profile)
    average_demand = total_demand / len(demand_profile)
    
    variability_sum = sum([(demand - average_demand) ** 2 for demand in demand_profile])
    demand_variance = variability_sum / (len(demand_profile) - 1)
    demand_standard_deviation = math.sqrt(demand_variance)
    
    dynamic_eoq = math.sqrt((2 * total_demand * cost_of_ordering) / (cost_of_carrying * (1 - (demand_standard_deviation / average_demand))))
    return dynamic_eoq

# Example usage
demand_profile = [100, 120, 90, 110, 130]  # Example demand values over time
ordering_cost = 75
carrying_cost = 18

calculated_dynamic_eoq = calculate_dynamic_eoq(demand_profile, ordering_cost, carrying_cost)
print('Calculated Dynamic EOQ:', calculated_dynamic_eoq)
Calculated Dynamic EOQ: 73.16238702342547

 

Calculate the  Economic Order Quantity with Limited Storage Space

The Economic Order Quantity (EOQ) with Limited Storage Space model introduces a constraint on the maximum storage capacity available for storing inventory. This constraint affects the order quantity calculation and aims to find the optimal order quantity that respects the storage capacity while minimizing costs. 

 

The assumptions are the same as the Classical model but with added storage capacity.

The formula for calculating the EOQ with Limited Storage Space involves two primary steps: calculating the EOQ as in the classical EOQ model and then adjusting the EOQ to fit within the storage capacity.

 

  1. Calculate the Classical EOQ (EOQ)
  2. Adjust EOQ for Storage Capacity (EOQ_Limited): Compare the calculated EOQ with the available storage capacity. If the EOQ exceeds the storage capacity (EOQ > SC), set the EOQ_Limited to the storage capacity. Otherwise, EOQ_Limited remains the same as EOQ.

 

EOQ with Limited Storage in Python

import math

def calculate_eoq_limited_storage(demand_in_units, 
                                  cost_of_ordering, 
                                  cost_of_carrying,
                                  storage_capacity):
    """
    Calculate the Economic Order Quantity (EOQ) with Limited Storage Space.

    Args:
        demand_in_units (int): Annual demand in units.
        cost_of_ordering (float): Cost of placing an order.
        cost_of_carrying (float): Cost of carrying one unit of inventory for a year.
        storage_capacity (int): Maximum storage capacity in units.

    Returns:
        float: Calculated EOQ with Limited Storage Space.
    """
    eoq = math.sqrt((2 * demand_in_units * cost_of_ordering) / cost_of_carrying)
    eoq_limited_storage = min(eoq, storage_capacity)
    return eoq_limited_storage

# Example usage
demand = 10000
ordering_cost = 90
carrying_cost = 15
storage_capacity = 2000  # Maximum storage capacity

calculated_eoq_limited_storage = calculate_eoq_limited_storage(demand, ordering_cost, carrying_cost, storage_capacity)
print('Calculated EOQ with Limited Storage Space:', calculated_eoq_limited_storage)
Calculated EOQ with Limited Storage Space: 346.41016151377545

 

Calculate the  Economic Order Quantity for Multi-Item Orders

Implementing the EOQ for Multi-Item Orders involves applying the EOQ formula separately to each item. 

import math

def calculate_multi_item_eoq(items_info):
    """
    Calculate the Economic Order Quantity (EOQ) for Multi-Item Orders.

    Args:
        items_info (list): List of tuples containing item information (demand, ordering cost, carrying cost).

    Returns:
        list: Calculated EOQ for each item.
    """
    eoq_list = []
    for item in items_info:
        demand, ordering_cost, carrying_cost = item
        eoq = math.sqrt((2 * demand * ordering_cost) / carrying_cost)
        eoq_list.append(eoq)
    return eoq_list

# Example usage
items_information = [
    (10000, 80, 20),  # Item 1: Demand, Ordering Cost, Carrying Cost
    (15000, 120, 18),  # Item 2: Demand, Ordering Cost, Carrying Cost
    (8000, 70, 15)  # Item 3: Demand, Ordering Cost, Carrying Cost
]

calculated_eoqs = calculate_multi_item_eoq(items_information)
for idx, eoq in enumerate(calculated_eoqs, start=1):
    print(f'Calculated EOQ for Item {idx}: {eoq}')
Calculated EOQ for Item 1: 282.842712474619
Calculated EOQ for Item 2: 447.21359549995793
Calculated EOQ for Item 3: 273.2520204255893

 

Calculate the  Economic Order Quantity with Uncertain Demand (Stochastic EOQ)

The Economic Order Quantity (EOQ) with Uncertain Demand, often referred to as the Stochastic EOQ, accounts for variability in demand when calculating the optimal order quantity. This model accounts for the uncertainty in demand by using statistical methods to determine the order quantity that balances ordering costs and carrying costs while accounting for demand variability.

 

The Stochastic EOQ model considers demand variability when calculating the optimal order quantity. One common approach is to use the square root of the Continuous Review Model formula. For a normal distribution, the formula is as follows:

EOQ = \sqrt{\frac{2 \cdot D \cdot S}{H} \cdot \frac{\Phi^{-1}(1 - p)}{\Phi^{-1}(p)}}

Where:

 

 D is the mean demand rate.

 S is the cost of placing an order.

 H is the cost of carrying one unit of inventory per year.

 \Phi^{-1}(p) is the inverse of the cumulative distribution function (CDF) of the normal distribution, representing the z-score corresponding to the service level 

 p is the desired service level, representing the probability of not experiencing a stockout.

 

Stochastic EOQ in Python

import math
import numpy as np

def calculate_stochastic_eoq(demand_distribution, 
                             cost_of_ordering, 
                             cost_of_carrying):
    """
    Calculate the Economic Order Quantity (EOQ) with Uncertain Demand (Stochastic EOQ).

    Args:
        demand_distribution (list): List of demand samples representing demand variability.
        cost_of_ordering (float): Cost of placing an order.
        cost_of_carrying (float): Cost of carrying one unit of inventory for a year.

    Returns:
        float: Calculated Stochastic EOQ.
    """
    average_demand = np.mean(demand_distribution)
    demand_variance = np.var(demand_distribution)
    
    z_score = norm.ppf(0.5)  # Z-score for 50th percentile (mean)
    
    safety_stock = z_score * math.sqrt(demand_variance)
    
    stochastic_eoq = math.sqrt((2 * average_demand * cost_of_ordering) * (safety_stock) / cost_of_carrying)
    return stochastic_eoq

# Example usage
demand_samples = [80, 90, 100, 110, 120]  # Example demand samples (uncertain demand)
ordering_cost = 50
carrying_cost = 12

calculated_stochastic_eoq = calculate_stochastic_eoq(demand_samples, ordering_cost, carrying_cost)
print('Calculated Stochastic EOQ:', calculated_stochastic_eoq)
Calculated Stochastic EOQ: 0.0

 

If you made this far in the article, thank you very much.

 

I hope this information was of use to you. 

 

Feel free to use any information from this page. I’d appreciate it if you can simply link to this article as the source. If you have any additional questions, you can reach out to malick@malicksarr.com  or message me on Twitter. If you want more content like this, join my email list to receive the latest articles. I promise I do not spam. 

 

[boldgrid_component type=”wp_mc4wp_form_widget”]

 

 

 

 

Tags: