Monte Carlo Simulation of Investment Portfolio Performance:¶
Author: Arun Kumar Pandey
Descriptive Statistics, Hypothesis Testing, and Variance Reduction Techniques
Example¶
Objective: To estimate the potential future value of the investment portfolio (for example: stock+Bonds+Real states) and assess the associated risks using Monte Carlo simulation.
Steps:
Define the investment portfolio:
- Identify the assets in the portfolio.
- Assign weights to each asset, representing the allocation or proportion of the portfolio invested in that asset.
- Determine the historical returns and standard deviations of the assets.
Set simulation parameters:
- Specify the number of simulations to run.
- Define the time horizon for the simulation (e.g., number of years).
Perform Monte Carlo simulation:
- Generate random samples for each asset's future returns based on their historical return distributions.
- Calculate the future portfolio values for each simulation by combining the weighted returns of the assets over the time horizon.
Analyze the simulation results:
- Calculate descriptive statistics such as mean, standard deviation, minimum, and maximum of the simulated portfolio values.
- Construct confidence intervals to estimate the range of potential future portfolio values with a certain level of confidence.
- Visualize the distribution of portfolio values using histograms or density plots.
- Assess the portfolio's risk metrics, such as Value-at-Risk (VaR) or Conditional Value-at-Risk (CVaR), to measure the downside potential.
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import scipy.stats as stats
# Define the investment portfolio
assets = ['Stocks', 'Bonds', 'Real Estate']
weights = np.array([0.6, 0.3, 0.1]) # Asset allocation weights
returns = np.array([0.08, 0.04, 0.06]) # Historical returns
volatility = np.array([0.15, 0.08, 0.12]) # Standard deviations
# Set simulation parameters
num_simulations = 1000
num_years = 10
# Perform Monte Carlo simulation
portfolio_values = np.zeros((num_years + 1, num_simulations))
portfolio_values[0] = 100000 # Initial portfolio value
for year in range(1, num_years + 1):
for sim in range(num_simulations):
random_returns = np.random.normal(returns, volatility)
portfolio_value = portfolio_values[year - 1, sim] * np.dot(weights, 1 + random_returns)
portfolio_values[year, sim] = portfolio_value
# Analyze the simulation results
df_portfolio_values = pd.DataFrame(portfolio_values.T, columns=['Year ' + str(year) for year in range(num_years + 1)])
df_portfolio_values.head()
Year 0 | Year 1 | Year 2 | Year 3 | Year 4 | Year 5 | Year 6 | Year 7 | Year 8 | Year 9 | Year 10 | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 100000.0 | 109003.197885 | 113247.862822 | 131570.723783 | 123312.353384 | 146992.473321 | 142640.693484 | 131616.472718 | 135066.768355 | 141094.075161 | 162711.998639 |
1 | 100000.0 | 102981.339256 | 112062.865917 | 106110.447909 | 87606.608088 | 83313.313314 | 110015.367130 | 126320.294600 | 137647.045359 | 150210.163722 | 159066.457817 |
2 | 100000.0 | 105320.850973 | 106086.800437 | 108777.249393 | 116215.705323 | 119007.437712 | 139941.274079 | 167588.305769 | 152591.023526 | 160854.736596 | 176054.287396 |
3 | 100000.0 | 112835.211137 | 112555.005734 | 117231.177661 | 115043.166375 | 127394.400947 | 138639.717197 | 145530.524333 | 153497.546718 | 162871.663486 | 147985.410838 |
4 | 100000.0 | 99845.504383 | 111147.367423 | 137213.938505 | 141921.315183 | 166063.946705 | 170760.634883 | 169279.835359 | 172665.855638 | 196521.784696 | 212436.163506 |
# Calculate descriptive statistics
statistics = df_portfolio_values.describe().T[['mean', 'std', 'min', 'max']]
# Print results
print("Descriptive Statistics:")
print(statistics)
Descriptive Statistics: mean std min max Year 0 100000.000000 0.000000 100000.000000 100000.000000 Year 1 106750.715046 9427.376074 78859.567361 140016.548988 Year 2 114106.796433 14193.768631 74544.077801 176155.651830 Year 3 121441.052869 18757.112245 68808.813060 223375.584650 Year 4 129216.759328 22584.115990 72413.608004 238247.967635 Year 5 138049.365765 27326.931629 63843.233943 241799.936974 Year 6 146629.367755 31247.417576 64483.500162 280971.351222 Year 7 156569.820080 36011.934191 68640.823330 311978.989192 Year 8 166938.980230 41255.124863 77924.958400 323178.192312 Year 9 178311.842944 46033.540652 81556.458128 380367.023358 Year 10 189752.484011 50902.038938 72418.750290 408443.041096
# Calculate confidence intervals
confidence_level = 0.95
confidence_intervals = df_portfolio_values.quantile([(1 - confidence_level) / 2, 1 - (1 - confidence_level) / 2])
print("\nConfidence Intervals ({}% confidence level):".format(int(confidence_level * 100)))
confidence_intervals
Confidence Intervals (95% confidence level):
Year 0 | Year 1 | Year 2 | Year 3 | Year 4 | Year 5 | Year 6 | Year 7 | Year 8 | Year 9 | Year 10 | |
---|---|---|---|---|---|---|---|---|---|---|---|
0.025 | 100000.0 | 88198.786807 | 87429.170263 | 86309.697386 | 89342.298971 | 91039.199332 | 92057.071511 | 96414.814752 | 99899.123523 | 103061.988674 | 105815.787605 |
0.975 | 100000.0 | 125844.947228 | 141683.361824 | 160447.557544 | 176342.215568 | 202015.418457 | 212947.609183 | 236856.910089 | 256321.180752 | 278642.833325 | 300125.417856 |
# Calculate risk metrics
var_95 = df_portfolio_values['Year 10'].quantile(0.05)
cvar_95 = df_portfolio_values['Year 10'][df_portfolio_values['Year 10'] <= var_95].mean()
print("\nRisk Metrics:")
print("Value-at-Risk (VaR) at 95% confidence level:", var_95)
print("Conditional Value-at-Risk (CVaR) at 95% confidence level:", cvar_95)
Risk Metrics: Value-at-Risk (VaR) at 95% confidence level: 116970.35604380732 Conditional Value-at-Risk (CVaR) at 95% confidence level: 103471.82263857182
# Plotting a histogram of the portfolio values using seaborn
plt.figure(figsize=(12, 8), dpi=200)
sns.histplot(df_portfolio_values['Year 10'], bins=30, kde=True, element='step')
plt.xlabel('Portfolio Value')
plt.ylabel('Density')
plt.title('Histogram of Portfolio Values after 10 Years')
# Calculate risk metrics
var_95 = df_portfolio_values['Year 10'].quantile(0.05)
cvar_95 = df_portfolio_values['Year 10'][df_portfolio_values['Year 10'] <= var_95].mean()
# Add vertical lines to mark Value-at-Risk (VaR) and Conditional Value-at-Risk (CVaR)
plt.axvline(x=var_95, color='red', linestyle='--', label='VaR (95% confidence)')
plt.axvline(x=cvar_95, color='orange', linestyle='--', label='CVaR (95% confidence)')
plt.legend()
plt.show()
plt.figure(figsize=(12, 8), dpi=200)
for sim in range(num_simulations):
plt.plot(range(num_years + 1), df_portfolio_values.iloc[sim, :], linewidth=1)
plt.xlabel('Year')
plt.ylabel('Portfolio Value')
plt.title('Portfolio Values Over Time (All Simulations)')
plt.show()
Conclusion¶
In this example, we consider an investment portfolio consisting of stocks, bonds, and real estate. We specify the weights, historical returns, and standard deviations for each asset. We set the simulation parameters to run 1000 simulations over a 10-year time horizon.
The Monte Carlo simulation generates random samples for each asset's future returns using the normal distribution with the specified mean returns and standard deviations. The future portfolio values are calculated by multiplying the previous portfolio value by the weighted returns for each simulation.
The simulation results are stored in a dataframe
df_portfolio_values
, which contains the portfolio values for each simulation and year. We calculate descriptive statistics, including mean, standard deviation, minimum, and maximum values. We construct confidence intervals to estimate the range of potential future portfolio values.We visualize the distribution of portfolio values after 10 years using a histogram. Additionally, we calculate risk metrics such as
Value-at-Risk (VaR)
andConditional Value-at-Risk (CVaR)
at the95%
confidence level.
Note: Please note that this example assumes a simplified scenario and should not be considered as financial advice. It's crucial to conduct further research and consult with a financial professional before making any investment decisions.