Intro to Monte Carlo Simulation Using Business Examples | by Bassel Karami | Towards Data Science

Monte Carlo simulation is a computational technique that can be used for a wide range of functions such as solving some of the more difficult mathematical problems as well as risk management.
We will go through 2 examples to demonstrate how Monte Carlo simulations can help you quantify risks in your next project or business decision.
Suppose you have an innovative product that you have been selling for the past year.
You have decided to apply for a patent to protect your IP, but you also did that in order to increase sales as you are aware that businesses deem a patented product more worthy.
Let’s make the following assumptions:
There is a 50% chance that your product gets patented
If it does get patented, your sales go up by 25% — 75%, with 50% being the most likely case
Without a patent you expect to sell between $1 — $9 million next year, with $3 million being the most likely case
We do not have to consider any costs or expenses
Problem: Suppose a wholesaler offers to buy your entire production and inventory for the year for $6 million (you won’t be able to sell anything else), would you accept the offer?
Monte Carlo simulation can help in problems like this. In a Monte Carlo simulation, we randomly generate multiple scenarios to understand the probabilities of achieving different sales figures. We could for example have a scenario where we don’t get a patent and we sell $3 million or... we don’t get a patent and sell $9 million or... we do get a patent and sell $15 million etc.
Different scenarios have different likelihood of being generated based on their likelihood of occurring in the real world.
Below are 10 randomly generated scenarios.
We can see, for example, that in 5 out of the 10 scenarios we would generate sales exceeding the $6 million offer. So far, it is hard to tell if this is a good deal.
To draw better insights we will re-run the simulation using 10,000 rounds/scenarios instead. Below is the code to run the simulation in Python, but you can also run your Monte Carlo simulations in other programming languages or even in Excel.
import numpy as np# Specify number of monte carlo simulationsN_ROUNDS = 10000results = []for rnd in range(N_ROUNDS):    prob_patent = np.random.randint(0, 2)    base_sales = round(np.random.triangular(1e6, 3e6, 9e6)        / 1000000, 2)    patent_markup = np.random.triangular(0.25, 0.5, 0.75)    sales = round(base_sales + (base_sales         * prob_patent * patent_markup), 2)        results.append(sales)
Note: We use a triangular distribution to model the base sales which ranges between $1–9m and the increase in sales due to patenting which ranges between 25% — 75%.
The triangular distribution is appropriate to use when we have an understanding of the minimum of a range, the maximum, and the mode (most likely case).
When generating random samples using a triangular distribution, we are more likely to sample values near the mode (most likely case) compared to the extremes (min/max). For example, a scenario where the increase in sales due to patenting is 47% is more likely to be generated compared to one where the increase is 27% since 47% is closer to the mode that we specified in our assumptions (i.e. 50%).
Now that we have simulated 10,000 scenarios, let’s see whether or not we received a good offer.
We’ll check for:
Average sales we would expect if we don’t accept the wholesaler’s dealThe probability of selling more than $6 million (offered deal)
Average sales we would expect if we don’t accept the wholesaler’s deal
The probability of selling more than $6 million (offered deal)
# Average sales across all the simulated scenariosaverage_sales = np.mean(results)# Split the results to reflect whether sales exceed 6 mlnbinned_results = pd.cut(results, bins=[0, 6, 20], labels=[    "Less than or equal to $6 mln",    "Greater than $6 mln",])# Plot the resultssns.barplot(x=binned_results.value_counts().index,            y=binned_results.value_counts().values)for bar_no, bar_val in enumerate(binned_results.value_counts()):    plt.text(bar_no, bar_val,              f"{round(bar_val * 100 / N_ROUNDS, 2)}%")plt.title(f"""Average sales across the simulations is ${round(np.mean(results), 2)} mlnThere is a {round(100 * np.mean(np.array(results) > 6), 2)}% probability ofselling more than $6 mln""")plt.tick_params(labelleft=False, left=False)plt.show()
We can now see that we only have a 36% probability of generating more than $6 million of sales. If we average across all 10,000 simulations, we expect to sell $5.45 million on average.
Not only does this deal eliminate the variability in sales, but it is a good deal that you are less likely to beat if you forgo the wholesaler’s offer.
Of course, if you still prefer to take on the risk with the hope of achieving a higher sales figure, let’s say +$10 million, then you can take that call. The Monte Carlo simulations would help you by giving you a heads-up that you would have a 5.9% chance of achieving that according to the simulations.
Now suppose you are running an online subscription-based service.
Let’s assume:
The subscription fee is $10 per month per user and can be cancelled any month
You have 10,000 users. Your monthly recurring revenue MRR is 10,000 x $10 = $100,000.
Your annual cost of maintaining the service is fixed at $1 million per year regardless of users
Below shows the monthly net growth in the % of users over the past 5 years which could be negative if churn exceeds growth. For the sake of simplicity, there is no seasonality and you expect the coming 12 months over the next year to be similar to any of the previous 60 months.
Problem: You want to set aside enough cash today to ensure, with 99% confidence, that you will have at least $1 million to pay for your annual costs of maintaining the service next year.
What we can do in this case is simulate scenarios of what the next 12 months could look like based on the available history.
In the example below, the -5.4% growth in Month 1 was randomly chosen from the historical data (Month 4 in history), the 0.3% growth for Month 2 corresponds to Month 45 in the historical data and so on...
Note: We did not make any assumptions about the statistical distribution of the data in contrast to what we did in Example 1, where we assumed triangular distributions for some variables.
Our approach in this 2nd example is entirely non-parametric since we are now directly sampling net growth values from examples in history.
The only assumption we make is that net growth in the next year should resemble historical net growth figures over the past 5 years.
Next, based on the randomly simulated 12 months, we calculate the total revenue accumulated over the simulated 12 months.
In the above example, we can see that $1.18 million was generated in this scenario which is enough to cover the $1 million of expenses. If the revenue is below $1 million we should have access to some cash that was set aside in order to cover the expenses.
Let’s simulate 10,000 scenarios...
np.random.seed(1)# Prepare hypothetical data for 5 years i.e. 12 monthssubscription_data = {"Month": range(1, 61),                      "Net Growth (%)": np.random.normal(0, 0.05, 60)}# Specify number of monte carlo simulationsN_ROUNDS = 10000results = []historical_data = subscription_data["Net Growth (%)"]for rnd in range(N_ROUNDS):    # Randomy selected growth rates from 12 months of history    idx = np.random.randint(0, 60, 12)    selected_growth_rates = historical_data[idx]        # We are starting with 10,000 users    users = 10000    revenue_per_user = 10    starting_revenue = users * revenue_per_user        # Calculate revenue for each period then sum them up    # to get total revenue across the selected 12 months    # starting revenue is the first entry used to calculate    # subsequent months but we drop it from the final calculation    revenue_per_month = [starting_revenue]    for growth_rate in selected_growth_rates:        revenue_per_month.append(            (1 + growth_rate) * revenue_per_month[-1])        # Drop the pre-simulation starting month revenue    total_revenue = np.sum(revenue_per_month[1:])    results.append(total_revenue)
Now let’s look at the distribution of revenue...
ax = sns.distplot(results, kde=False, norm_hist=True)ax.set_xlabel("Annual revenue across simulated 12 months")ax.set_ylabel("# of simulations")plt.title(f"""Average annual revenue across simulations: USD {round(np.mean(results) / 1e6, 2)} million""")plt.show()
The average annual revenue is USD 1.2 million across all 10,000 simulated scenarios. The chart above shows the probability distribution for total accumulated annual revenue. For example, if we look at the area between USD 800,000 and USD 1 million, we can see the small bars reflecting that such low revenue figures have low likelihood of occurring, but are still possible.
To solve our problem, we need to know what annual revenue are we able to achieve or exceed with 99% confidence. We can sort the 10,000 simulated total revenue results from lowest to highest and take the 101st lowest result. 100 out of 10,000 (1%) simulated total revenue results are lower than this number, 99% are equal or higher, so we are 99% confident we would achieve or exceed that number.
sorted(results)[100]
We are 99% confident that we can achieve or exceed $950,497 over the next 12 months based on this simulation. This would keep us $49,503 short of the $1 million needed to cover our annual expenses.
We need to set aside $49,503 to be 99% confident of being able to pay the $1 million of annual expenses in the next year.
The above examples are intended to demonstrate sample real-life problems Monte Carlo simulations can help with.
For many real-life use cases, the implementation would be more detailed where we would need to put additional effort, especially in handling correlations.
Monte Carlo simulations are flexible enough to help in many different use cases and there are plenty of use cases that cannot be encompassed in a few examples.