Randomness for simulation
Randomness for Simulation¶
We will simulate random events by pulling randomly from the uniform distribution 0 to 1, and then converting this into multiples. By doing this, we are able to get a random sample which fits the interpolated distribution. One note, I set the seed in the first line to make it so that these experiments can be replicated. When we set the seed, it makes it so the random numbers that come out are the same each time. Different seeds will yield different random numbers, but using the same seed (10), you should be able to get the same numbers as I do for the sake of replicating.
#Set the seed
np.random.seed(10)
#Pull a random value
cdf_val = np.random.uniform(0,1)
#Inverse the transformation to get the random multiple of he company, in this case 2.95
print(inverse_transform(cdf_val, actual_cdf, multiples))
2.9497334903833354
What about simulating 25 companies at random? If you pass in a third argument, it will return that number of random samples. The output to get 25 random samples for example....
#Set the seed
np.random.seed(0)
#Get the random values
cdf_vals = np.random.uniform(0,1, 25)
print(cdf_vals)
[0.5488135 0.71518937 0.60276338 0.54488318 0.4236548 0.64589411
0.43758721 0.891773 0.96366276 0.38344152 0.79172504 0.52889492
0.56804456 0.92559664 0.07103606 0.0871293 0.0202184 0.83261985
0.77815675 0.87001215 0.97861834 0.79915856 0.46147936 0.78052918
0.11827443]
Let's do the inverse transformation to find 25 random multiples.
#Set the seed
np.random.seed(0)
#Compute the random multiples
cdf_vals = np.random.uniform(0,1, 25)
valuations = [inverse_transform(x, actual_cdf, multiples) for x in cdf_vals]
print(valuations)
print()
#Find the mean of the multiples
print(np.mean(valuations))
[0.8469344196409332, 2.0622824722912165, 0.9301903951722899, 0.8408691095631124, 0.6537882705847294, 0.9967501744855803, 0.6752889062695872, 4.854118589439996, 11.465104200411725, 0.5917307389286692, 3.27233261790774, 0.8161958638162106, 0.876611976996809, 7.08446087225941, 0.10962354660167738, 0.13445879583571096, 0.031201230617786603, 3.9188908387025765, 3.0578142442664107, 4.510073490068287, 17.447336893105607, 3.389858722794049, 0.7121595096495862, 3.0953229452404023, 0.18252226214341546]
2.902236843471741
Computing Annualized Return¶
Assuming an equal weighting between the 25 investments, and a time horizon of 8 years, we can compute the annualized return of this random sample. Recall that CAGR (compounded annualized growth rate) can be found from the holding period return like so:
$ CAGR = (1 + HPR)^{\frac{1}{t}} - 1$
where
$ CAGR = \text{Compounded Annualized Growth Rate} $
$ HPR = \text{Holding Period Return} $
$ t = \text{Number of Years} $
Since the mean exit value is equal to $1 + HPR$, the following will give us the annualized return on this set of 25 companies which is 14.2% a year. Not bad!
#Compute CAGR
print(np.mean(valuations) ** (1/8) - 1)
0.14246158618538907
With all of this code that we have written, we are ready to simulate 10,000 simulations of holding 25 companies from this distribution.
#Set the seed
np.random.seed(0)
#Set a blank list to record each return for the simulation
returns = []
#Iterate through 100,000 simulations
for _ in range(100000):
#Randomly pull 25 values from the uniform distribution
cdf_vals = np.random.uniform(0,1, 25)
#Convert it into valuations
valuations = [inverse_transform(x, actual_cdf, multiples) for x in cdf_vals]
#Find the annualized return and append it to the list
returns.append(np.mean(valuations) ** (1/8) - 1)
#Compute the mean and standard deviation
print(np.mean(returns))
print(np.std(returns))
0.1127546856779015
0.06443560939513421
#Plot the histogram
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.hist(returns, bins=30)
ax.xaxis.set_major_formatter(PercentFormatter(1))
ax.set_xlabel("Fund CAGR")
ax.set_ylabel("Frequency")
ax.set_title("Linear Interpolation Simulated Funds N=25")
plt.show()
#Repeat with just 10 investments
np.random.seed(0)
returns2 = []
for _ in range(100000):
cdf_vals = np.random.uniform(0,1, 10)
valuations = [inverse_transform(x, actual_cdf, multiples) for x in cdf_vals]
returns2.append(np.mean(valuations) ** (1/8) - 1)
print(np.mean(returns2))
print(np.std(returns2))
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.hist(returns2, bins=30)
ax.xaxis.set_major_formatter(PercentFormatter(1))
ax.set_xlabel("Fund CAGR")
ax.set_ylabel("Frequency")
ax.set_title("Linear Interpolation Simulated Funds N=10")
plt.show()
0.09822101908926366
0.09099641235554093
#Repeat with 50 investments
np.random.seed(0)
returns3 = []
for _ in range(100000):
cdf_vals = np.random.uniform(0,1, 50)
valuations = [inverse_transform(x, actual_cdf, multiples) for x in cdf_vals]
returns3.append(np.mean(valuations) ** (1/8) - 1)
print(np.mean(returns3))
print(np.std(returns3))
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.hist(returns3, bins=30)
ax.xaxis.set_major_formatter(PercentFormatter(1))
ax.set_xlabel("Fund CAGR")
ax.set_ylabel("Frequency")
ax.set_title("Linear Interpolation Simulated Funds N=50")
plt.show()
0.11889495517374847
0.048139217907553704
#Plot the 3 together
fig, axs = plt.subplots(3, 1, sharex=True, sharey=True,figsize=(5,10))
ax = axs[0]
ax.hist(returns2, bins=30)
ax.xaxis.set_major_formatter(PercentFormatter(1))
ax.set_xlabel("Fund CAGR")
ax.set_ylabel("Frequency")
ax.set_title("Linear Interpolation Simulated Funds N=10")
ax = axs[1]
ax.hist(returns, bins=30)
ax.xaxis.set_major_formatter(PercentFormatter(1))
ax.set_xlabel("Fund CAGR")
ax.set_ylabel("Frequency")
ax.set_title("Linear Interpolation Simulated Funds N=25")
ax = axs[2]
ax.hist(returns3, bins=30)
ax.xaxis.set_major_formatter(PercentFormatter(1))
ax.set_xlabel("Fund CAGR")
ax.set_ylabel("Frequency")
ax.set_title("Linear Interpolation Simulated Funds N=50")
plt.show()