I am trying to develop a pricer for Call VS Call and I'm using MonteCarlo method to do so because my stocks are correlated between each others.

Basically my inputs are stockswhich is a dataframe whose columns are stock name, volatility, weight for the basket, strike, spot and dividendes, datawhich is a dictionary with the risk free rate, the number of simulations to do and the time to maturity and finally correlationwhich is going to be the implied correlation between stocks (I chose to take it as a float instead of correlation matrix because there is usually only two stocks).

I do Monte-Carlo because there is correlation between my stocks but my option is not path dependent, so I thought I could skip generating X steps for each simulation and directly generate the move over the life of the option. Then I could generate an random array of length (number of stocks, number of simulation) and thus get rid of the usual double loop which is slowing the simulation. When I do like this, I'm able to generate about a million simulation in around 1 second.

However, I struggle to really understand if my way of doing it is good and if this approach is theoretically valid.

Here is my code so each of you can see how I do it :

import random 
import matplotlib as plt 
import numpy as np 
from math import * 
import pandas as pd 

class CallVsCallCalculation() : 
    def __init__(self,stocks,data,correlation):
        self.stocks = stocks 
        self.data = data 
        self.correlation = correlation 

        no_obs = int(self.data["correlation"]) 
        r = self.data["rate"]
        T= self.data["time_to_maturity"]
        self.weight = np.array(self.stocks["Weight"]).reshape(len(self.stocks["Weight"]),1)
        self.strike= np.array(self.stocks["Strike"])
        self.spot =np.array(self.stocks["Spot"])
        self.dividend =np.array(self.stocks["Dividendes"])
        means= [0]*len(self.stocks["Variance"])
        sd = np.diag(self.vol)

        observations=np.random.normal(0, 1,(no_cols, no_obs))

        cor_matrix = np.zeros(shape=(no_cols,no_cols))
        for i in range (no_cols):
            for j in range (no_cols):
               if i==j :
                else :

        cov_matrix = np.dot(sd,np.dot(cor_matrix, sd))
        Chol = np.linalg.cholesky(cov_matrix)
        self.chol = np.linalg.cholesky(cov_matrix)
        sam_eq_mean = chol.dot(observations)
        s = sam_eq_mean.transpose() + means
        samples = s.transpose()

        std = np.array(self.vol)
        std **=2
        std = -std/2
        std += r
        std -= self.dividend

        eps_array = samples*np.sqrt(T) 
        eps_array = eps_array*np.array(self.vol).reshape((2,1))
        expi = std.reshape(len(self.stocks["Variance"]),1) + eps_array
        #Computing the last last price, or the price at expiration
        self.last_price =  self.spot.reshape(len(self.stocks["Variance"]),1)*np.exp(expi) 
        #Dividing each price by the initial price
        self.last_price_perc = self.last_price/self.spot.reshape(len(self.stocks["Variance"]),1)
        #Getting each price regarding its weights
        self.last_price_weighted = self.weight.T @self.last_price_perc 
        #Calculating the strike
        self.strike_basket = (self.weight.T@self.strike)/((self.weight.T@self.spot)[0]) 
        #Discounting the striketo the basket
        self.basket_minus_strike = self.last_price_weighted-self.strike_basket
        #Getting the payoff for the basket
        self.basket_payoff = np.maximum(self.basket_minus_strike,e)
        self.short_payoff = self.basket_payoff.mean()
        #Getting the strike in term of percentage
        self.strike_stock - self.strike/self.spot
        #weighing the last price by the initial price
        self.last_price_on_spot = self.last_price/self.spot.reshape(len(self.stocks["Variance"]),1) 
        #Discounting the strike
        self.last_price_minus_strike = self.last_price_on_spot-self.strike_stock.reshape(len(self.stocks["Variance"]),1)
        #Getting the payoff
        self.payoff_long = np.maximum(self.last_price_minus_strike,0)
        #Taking the mean of each stocks
        self.payoff_long_mean = self.payoff_long.mean(axis=1)
        #Getting also the weight
        self.long_payoff = self.weight.T@self.payoff_long_mean
        #Getting the call price
        self.call_price = (self.long_payoff-self.short_payoff)[0]*np.exp(-r*T)

I also get used of that topic to ask a smaller question about the stock price formula : Stock price formula

I struggle to understand what is the real origin of W in this formula when you consider correlated basket. Is it only the random samples and is it random samples that were multiplied by the lower matrix of the Cholesky decomposition of the covariance matrix ?

Thanks everyone for your help on both question, I hope I was clear enough !

  • $\begingroup$ Hi, could you add the definition of the call-vs-call option, please? Thanks a lot $\endgroup$ Apr 5 at 19:59
  • $\begingroup$ A call VS call option is a option when you are long a call on every underlying of a basket with their weight and short a call on a basket composed of those underlying with their weight, it gives you exposure to the correlation between the underlyings $\endgroup$
    – Strauss
    Apr 5 at 21:55
  • $\begingroup$ Thanks! So the payoff is $\sum w_i (S_i-X_i)^+ - (\sum w_iS_i-X)^+$ ? $\endgroup$ Apr 6 at 4:25
  • $\begingroup$ Yes exactly, but for my question you can also consider that I'm getting the payoff only for the basket of stocks, as it juste a different computation ones you did simulates the prices $\endgroup$
    – Strauss
    Apr 6 at 8:38


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.