// ============================================================
// PRNG UTILITIES
// ============================================================
rng_utils = {
function mulberry32(seed) {
return function() {
seed |= 0; seed = seed + 0x6D2B79F5 | 0
let t = Math.imul(seed ^ seed >>> 15, 1 | seed)
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t
return ((t ^ t >>> 14) >>> 0) / 4294967296
}
}
function boxMuller(rng) {
const u1 = rng(), u2 = rng()
return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2)
}
return { mulberry32, boxMuller }
}Cholesky Decomposition
Interactive visualization of transforming uncorrelated shocks into correlated shocks via Cholesky decomposition
When simulating multivariate returns, random number generators produce uncorrelated standard normal draws \(z^u_{t+1}\). To simulate returns for a portfolio of correlated assets, we must transform these into properly correlated shocks (Christoffersen 2012, chap. 8):
\[ z_{t+1} = \Upsilon^{1/2} z^u_{t+1} \]
where \(\Upsilon^{1/2}\) is the Cholesky decomposition (lower triangular matrix square root) of the correlation matrix \(\Upsilon\), satisfying \(\Upsilon^{1/2}(\Upsilon^{1/2})' = \Upsilon\).
In the bivariate case:
\[ \Upsilon^{1/2} = \begin{bmatrix} 1 & 0 \\ \rho_{1,2} & \sqrt{1-\rho_{1,2}^2} \end{bmatrix} \]
which gives the correlated shocks:
\[ z_{1,t+1} = z^u_{1,t+1}, \qquad z_{2,t+1} = \rho_{1,2}\, z^u_{1,t+1} + \sqrt{1-\rho_{1,2}^2}\, z^u_{2,t+1} \]
Note
Why Cholesky? The Cholesky decomposition guarantees that the resulting shocks have exactly the target correlation matrix while preserving zero means and unit variances. It is the standard method for correlating random draws in financial simulation.
References
Christoffersen, Peter F. 2012. Elements of Financial Risk Management. 2nd ed. Academic Press.