If Z∼N(0,I) and X=μ+LZ, then X is an affine transformation of a gaussian, hence gaussian. Mean: E[X]=μ+L⋅0=μ. Covariance: Cov(X)=L⋅Cov(Z)⋅L⊤=L⋅I⋅L⊤=LL⊤=Σ. So X∼N(μ,Σ).
Part 3 — Simulation
import numpy as np
mu = np.array([0.05, 0.08, 0.10])
Sigma = np.array([[0.04, 0.02, 0.01],
[0.02, 0.09, 0.03],
[0.01, 0.03, 0.16]])
L = np.linalg.cholesky(Sigma)
rng = np.random.default_rng(0)
N = 10_000Z = rng.standard_normal((N, 3))
X = mu + Z @ L.T
print("sample mean:", X.mean(axis=0).round(4))
# sample mean: [0.0499 0.0801 0.1006]print("sample cov:\n", np.cov(X.T).round(4))
# sample cov:# [[0.0399 0.0203 0.0098]# [0.0203 0.0906 0.0308]# [0.0098 0.0308 0.1576]]
Within sampling noise, both match μ and Σ.
Part 4 — Portfolio variance
w = np.array([1, 1, 1]) / 3# theoreticalvar_theoretical = w @ Sigma @ w
print(var_theoretical)
# 0.04888888888888889# empiricalport_returns = X @ w
var_empirical = port_returns.var(ddof=1)
print(var_empirical)
# 0.04936...print("relative diff:", abs(var_empirical/var_theoretical - 1))
# ~1%
Theoretical: w⊤Σw=(1/3)2⋅(0.04+0.09+0.16+2⋅0.02+2⋅0.01+2⋅0.03)=(1/9)⋅0.44=0.0489. Empirical agrees to within 1%, as expected from N=10,000 samples.
Takeaways
Cholesky factor is the computational engine for gaussian sampling in multi-dimensional Monte Carlo. Cheaper than eigen-decomposition when Σ is positive definite.
Affine transformations preserve gaussianity.X=μ+LZ is gaussian with the desired mean and covariance — the most useful identity in simulation.
Portfolio variance w⊤Σw is the scalar summary that matters for mean-variance and VaR.
When Σ is only PSD (not PD), Cholesky fails. Cures: pseudo-Cholesky (LDL decomposition), eigen-decomposition X=μ+QΛ1/2Z, or regularisation Σ←Σ+ϵI.