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:

- Basket options
- 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**

**How to generate 2 correlated assets**

Let and be two assets with the following dynamics:

with

To generate these two correlared brownian motions and we need two independant brownian motion and :

And a quick reminder : with .

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

**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 . This factorization is possible on a symmetric positive-definite matrix, here the correlation fits pretty well (at least semi positive-definite and symmetric).

Let 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 of correlated standard normal random variable is obtained by computing :

In the simulation above, the correlation matrix is :

*Example*

*Example*

Let’s try to price a basket call with the following payoff :

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 def Get_Basket_Call_Price(starting_asset_values,correlation_matrix,asset_vol,maturity,nb_simulation,risk_free_rate,weights,strike): 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 def Spread_Call_Price(S1,S2,vol1,vol2,T,correlation): 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: spread_option_price_MC = Monte_Carlo.Get_Basket_Call_Price(**option_parameters_1) spread_option_price_margrabe = Margrabe.Spread_Call_Price(100,100,0.2,0.25,1,0.3) #Example on a basket option with 3 stocks : basket_option_price = Monte_Carlo.Get_Basket_Call_Price(**option_parameters_2) print("Spread Option Price MC : {0}".format(spread_option_price_MC)) print("Spread Option Price Margrabe : {0}".format(spread_option_price_margrabe)) print("Basket Option Price on 3 stocks : {0}".format(basket_option_price))

To check the results I took 2 stocks and with and . This case is equivalent to a spread option whose the payoff is and Margrabe came up with a closed form formulae to price it :

with

Here are the results with 5.000.000 simulations:

Margrabe’s Formula : Price = 10.709€

Monte Carlo : Price = 10.704€

*Bloomberg*

*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…

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.

**Thanks a lot!**

**Thanks a lot!**