Modern Portfolio Theory and Mean-Variance Optimization › Practical Implementation with Simulated Return Data
Quantitative FinanceAll chapters Modern Portfolio Theory and Mean-Variance Optimization › Practical Implementation with Simulated Return DataOne section · Sections in this chapter8 / 12 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Let's apply mean-variance optimization to a seeded five-asset Gaussian return sample with a specified covariance structure. The 1,260 observations represent five simulated trading years, not historical market observations.
import numpy as np
# Simulated daily returns for 5 assets (252 trading days x 5 simulated years)
np.random.seed(42)
n_days = 252 * 5
n_assets = 5
asset_names = ["Large Cap", "Small Cap", "Intl Stocks", "Bonds", "REITs"]
# True parameters for simulation
true_annual_returns = np.array([0.10, 0.12, 0.08, 0.04, 0.09])
true_annual_vols = np.array([0.16, 0.22, 0.18, 0.04, 0.20])
# Correlation structure
true_corr = np.array(
[
[1.00, 0.75, 0.65, 0.10, 0.55],
[0.75, 1.00, 0.60, 0.05, 0.50],
[0.65, 0.60, 1.00, 0.15, 0.45],
[0.10, 0.05, 0.15, 1.00, 0.20],
[0.55, 0.50, 0.45, 0.20, 1.00],
]
)
# Convert to daily parameters
daily_returns_mean = true_annual_returns / 252
daily_vols = true_annual_vols / np.sqrt(252)
true_cov_daily = np.diag(daily_vols) @ true_corr @ np.diag(daily_vols)
# Generate returns using Cholesky decomposition
L = np.linalg.cholesky(true_cov_daily)
z = np.random.randn(n_days, n_assets)
returns = daily_returns_mean + z @ L.T# Estimate annualized parameters from simulated daily returns (IID arithmetic scaling)
estimated_mu = returns.mean(axis=0) * 252 # Annualize
estimated_cov = np.cov(returns.T) * 252 # Annualize
# Compute the long-only minimum-variance curve, including its lower branch
frontier_ret, frontier_risk, frontier_w = efficient_frontier(
estimated_mu, estimated_cov, n_points=100, allow_short=False
)
# Select the best sampled Sharpe-ratio portfolio (approximate tangent), rf = 2%
rf = 0.02
sharpe_ratios = (frontier_ret - rf) / frontier_risk
max_sharpe_idx = np.argmax(sharpe_ratios)
tangent_weights = frontier_w[max_sharpe_idx]
tangent_return = frontier_ret[max_sharpe_idx]
tangent_risk = frontier_risk[max_sharpe_idx]
max_sharpe_ratio = sharpe_ratios[max_sharpe_idx]
# Calculate Capital Allocation Line (CAL)
cml_risks = np.linspace(0, tangent_risk * 1.5, 50)
cml_returns = rf + max_sharpe_ratio * cml_risks
This chart uses simulated returns. The dashed capital-allocation line combines the 2% risk-free asset with the best Sharpe portfolio found on the finite grid. It approximates the tangency opportunity set in the stated mean-variance model; extending beyond the risky fund assumes borrowing at the same risk-free rate, without trading frictions or additional leverage constraints.
Approximate Tangent Portfolio (Best Sampled Sharpe Ratio): -------------------------------------------------- Large Cap : 14.49% Small Cap : 0.00% Intl Stocks : 0.00% Bonds : 7.02% REITs : 78.50% -------------------------------------------------- Expected Return: 13.45% Standard Deviation: 16.79% Sharpe Ratio: 0.682

The selected portfolio has the highest Sharpe ratio among the sampled targets. In the frictionless mean-variance model with a suitable tangency portfolio and unrestricted borrowing and lending at the same risk-free rate, efficient allocations combine that risky fund with the risk-free asset. Actual funding limits, objectives, and exposures can change this conclusion. The next chapter connects portfolio choice to CAPM equilibrium.