# How to Generate Correlated Assets and Why?

As soon as you will get into pretty complex derivatives, for example, you will need to generate correlated assets for pricing purposes. Example of such derivatives can be:

• Rainbox options
• Moutain ranges (created by Société Générale)

The most complex amongst these derivatives cannot be priced using closed form formulae, Monte Carlo simulations are therefore used most of the time.

### How to generate 2 correlated assets

Let $S_1$ and $S_2$ be two assets with the following dynamics: $\frac{dS_1}{S_1} = \mu_1 dt + \sigma_1 dW_t^1$ $\frac{dS_2}{S_2} = \mu_2 dt + \sigma_2 dW_t^2$

with $dW_t^1 dW_t^2 = \rho dt$

To generate these two correlared brownian motions $dW_t^1$ and $dW_t^2$ we need two independant brownian motion $dB_1$ and $dB_2$: $dW_t^1 = dB_1$ $dW_t^2 = \rho^2 dB_1 + \sqrt{1-\rho^2} dB_2$

And a quick reminder : $B_T = \sqrt{T} X$ with $X \sim \mathcal{N}(0,1)$.

### What if there are more than 2 correlated assets?

In this case, you will need to perform a Cholesky factorization (by Andre-Louis Cholesky) of the correlation matrix A to determine the lower triangular matrix L so that $A = LL^T$. This factorization is possible on a symmetric positive-definite matrix, here the correlation fits pretty well (at least semi positive-definite and symmetric).

Let $X_n$ be a vector of independent standard normal random variable and L the lower triangular matrix from the Cholesky decomposition of the correlation matrix. The vector $Y_n$ of correlated standard normal random variable is obtained by computing : $Y_n = LX_n$ Simulation of 3 correlated assets

In the simulation above, the correlation matrix is : $A = \begin{pmatrix} 1 & -0.7 & -0.8 \\ -0.7 & 1 & 0.6 \\ -0.8 & 0.6 & 1 \end{pmatrix}$

### Example

Let’s try to price a basket call with the following payoff : $C_T = max(\sum_{i=0}^n w_iS_i - K ; 0)$

Here is the pricer in Python, I also implemented the Margrabe’s formula in order to check the results. Notice that we only need the final value of the assets since this is not a path-dependent option.

import numpy as np
from scipy import stats

class BlackScholes:<span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start"></span>
@staticmethod
def Generate_Asset(S_0,R,T,Vol,X):
return S_0*np.exp((R-Vol**2/2)*T + Vol*np.sqrt(T)*X)

class Monte_Carlo:
@staticmethod
def __Get_Correlated_Brownian(nb_assets,nb_simulation,correlation_matrix):
"""Function that returns a matrix with all the correlated brownian for all the simulations by proceeding a Cholesky decomposition"""
X = np.random.randn(nb_simulation,nb_assets)
lower_triang_cholesky = np.linalg.cholesky(correlation_matrix)
for i in range(nb_simulation):
X[i,:]=np.dot(lower_triang_cholesky,X[i,:])  #np.dot perform a matrix product
return X

@staticmethod
nb_assets = len(starting_asset_values)

#Generate independant random variable:
X = Monte_Carlo.__Get_Correlated_Brownian(nb_assets,nb_simulation,correlation_matrix)

Final_Stock_values = BlackScholes.Generate_Asset(starting_asset_values[:],risk_free_rate,maturity,asset_vol[:],X[:])

#print(Final_Stock_values[:])
#print(weights)
#print(Final_Stock_values[:]*weights)
#print(np.sum(Final_Stock_values[:]*weights,axis=1))
#print(np.maximum(np.sum(Final_Stock_values[:]*weights,axis=1)-strike,0))

Payoffs = np.maximum(np.sum(Final_Stock_values[:]*weights,axis=1)-strike,0)
return np.mean(Payoffs)*np.exp(-risk_free_rate*maturity)

class Margrabe:
@staticmethod
def __d1(S1,S2,T,sigma_eq):
return (np.log(S1/S2)+0.5*T*sigma_eq**2)/(sigma_eq*np.sqrt(T))

@staticmethod
def __d2(S1,S2,T,sigma_eq):
return Margrabe.__d1(S1,S2,T,sigma_eq)-sigma_eq*np.sqrt(T)

@staticmethod
def __sigma(vol1,vol2,correlation):
return np.sqrt(vol1**2 + vol2**2 - 2*correlation*vol1*vol2)

@staticmethod
sigma_eq = Margrabe.__sigma(vol1,vol2,correlation)
d1 = Margrabe.__d1(S1,S2,T,sigma_eq)
d2 = Margrabe.__d2(S1,S2,T,sigma_eq)
return S1*stats.norm.cdf(d1)-S2*stats.norm.cdf(d2)

option_parameters_1 = {
'starting_asset_values' : np.array([100,100]),
'correlation_matrix':[[1,0.3],[0.3,1]],
'asset_vol' : np.array([0.2,0.25]),
'maturity' : 1,
'nb_simulation' : 5000000,
'risk_free_rate' : 0.01,
'weights' : np.array([1,-1]),
'strike' : 0
}

option_parameters_2 = {
'starting_asset_values' : np.array([100,100,100]),
'correlation_matrix':[[1,0.5,0.1],[0.5,1,0.7],[0.1,0.7,1]],
'asset_vol' : np.array([0.2,0.25,0.22]),
'maturity' : 1,
'nb_simulation' : 5000000,
'risk_free_rate' : 0.01,
'weights' : np.array([0.4,0.2,0.4]),
'strike' : 100
}

#Checking the result:

#Example on a basket option with 3 stocks :



To check the results I took 2 stocks $S_1$ and $S_2$ with $w_1 = 1$ and $w_2 = -1$. This case is equivalent to a spread option whose the payoff is $max(S_T^1 - S_T^2 ; 0)$ and Margrabe came up with a closed form formulae to price it : $V_t = S_t^1N(d) - S_t^2N(d-\sigma_{eq} \sqrt{T-t})$

with $d = \frac{ln(\frac{S_t^1}{S_t^2}) + \frac{\sigma_{eq}^2}{2}(T-t)}{\sigma_{eq}(T-t)}$ $\sigma_{eq}^2 = \sigma_1^2 + \sigma_2^2 - 2\rho \sigma_1 \sigma_2$

Here are the results with 5.000.000 simulations:

Margrabe’s Formula : Price = 10.709€
Monte Carlo : Price = 10.704€

### Bloomberg

I tried to find a basket option on the OVME function of Bloomberg but according to the Helpdesk it’s not available on the regular Bloomberg… Multileg option strategy pricing – {OVME} on Bloomberg

Also, I haven’t found the function OVX used to price exotic options, I don’t really know whether this is because the function has been removed or because of the regular access I have. If anyone knows, I would be glad to hear more about it.