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:

  • 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

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
    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 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.

Thanks a lot!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s