Extend basket analytic solution (equal weighted) to a various weight basket, also put formula
Extend basket analytic solution (equal weighted) to a various weight basket, also put formula
Loading saved threads...
Matt · External communityPost link
External question — Quantitative Finance Stack Exchange
Author: Matt
Original post: https://quant.stackexchange.com/questions/70402
License: CC BY-SA 4.0 — https://creativecommons.org/licenses/by-sa/4.0/
Adaptation: HTML converted to plain text; contact email addresses removed.
So I coded up the solution from here:
Do basket options have a closed form valuation formula?
Which provides a good solution for equally-weighted underlyings under a Black model. The simplified Python code is here after modifying it quite a bit. I tried to add a weight vector to the code which seems to diverge when ATM from the referenced solution, so I'm pretty sure my implementation is wrong (the analytical one, not MC). If I set all the weights back to equal (sum=1), then the solution seems to converge when ATM and OTM pretty well, so something is definitely wrong with my weight formula in the analytical solution. Hoping someone can spot my error here:
import numpy as np
from scipy.stats import multivariate_normal, norm
def Basket_Euro_MC(F, vols, corr, T, strike, paths, weights, callput):
seed = 1
means = np.zeros(F.shape[0])
cov_mat = np.diag(vols).dot(corr).dot(np.diag(vols))
results = np.zeros((paths, F.shape[0]))
rng = multivariate_normal(means, cov_mat).rvs(size=paths, random_state=seed)
for i in range(paths):
results[i] = F * np.exp(-0.5*vols**2*T) * np.exp(T * rng[i])
if callput == 1:
return max(np.mean(np.sum(results*weights, axis=1)-strike),0)
else:
return max(np.mean(strike-np.sum(results*weights, axis=1)),0)
def Basket_Euro_Analytic(F, vols, corr, T, strike, weights, callput):
mod_vol = vols.dot(corr).dot(vols) / len(vols)**2
mod_fwd = np.product(F)**(1/len(vols))
d_plus = (np.log(mod_fwd / max(strike,0.0001)) + 0.5 * mod_vol * T) / np.sqrt(mod_vol * T)
d_minus = d_plus - np.sqrt(mod_vol * T)
if callput == 1: # call formula is fine
return np.sum(weights.dot(mod_fwd * norm.cdf(d_plus) - strike * norm.cdf(d_minus)))
else: # put formula looks pretty suspect...
return np.sum(weights.dot(max(-(norm.cdf(-d_minus) * mod_fwd - strike * norm.cdf(-d_plus)),0)))
if __name__ == '__main__':
F = np.array([80., 85., 82., 81., 84.])
corr = np.array(([1, 0.1, -0.1, 0, 0], [0.1, 1, 0, 0, 0.2], [-0.1, 0, 1, 0, 0], [0, 0, 0, 1, 0.15], [0, 0.2, 0, 0.15, 1]))
vols = np.array([0.1, 0.12, 0.13, 0.09, 0.11])
paths = 100000
T=1
strike = 78# note BS makes strike >0 in divide!
weights = np.array([0.25,0.1,0.2,0.2,0.25])
callput = 1
MC_result = Basket_Euro_MC(F, vols, corr, T, strike, paths, weights, callput)
analytic_result = Basket_Euro_Analytic(F, vols, corr, T, strike, weights, callput)
print('MC result:', MC_result, 'Analytic Result', analytic_result)
Quote
Report
danp · External communityPost link
External answer — Quantitative Finance Stack Exchange
Author: danp
Original post: https://quant.stackexchange.com/a/82251
License: CC BY-SA 4.0 — https://creativecommons.org/licenses/by-sa/4.0/
Adaptation: HTML converted to plain text; contact email addresses removed.
The weights in the analytical formula should be "value-weighted weights"
$p_i = w_i F_i/\sum_j w_j F_j$
. These weights add up to 1.
The normalization sum in the denominator is the "basket forward"
$F_B = \sum_j w_j F_j$
. With the numerical values in your code this comes out
$F_B = 82.1$
.
As a sanity check of the MC computation one can price the basket option as an European option with volatility
$\sigma_B^2 = \sum_{i,j} \rho_{ij} p_i p_j \sigma_i \sigma_j$
. Plugging the numerical values for the correlation matrix and vols from the code, gives
$\sigma_B=5.247\%$
.
The table below compares the MC result with this analytical result using the Black-Scholes formula and the fixed basket volatility
$\sigma_B$
. The agreement is always better than 1%. Not bad for such a simple estimate.
An improved estimate includes also the skew of the basket implied volatility, using the method from this paper
https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4702005
Quote
Report
Post Reply
Quoted from Forex.com.bd-Editorial External answer — Quantitative Finance Stack Exchange Author: danp Source score (net votes, not local likes): 0 Original post: https://quant.stackexchange.com/a/82251 License: CC BY-SA 4.0 — https://creativecommons.org/licenses/by-sa/4.0/ Adaptation: HTML converted to plain text; contact email addresses removed. The weights in the analytical formula should be "value-weighted weights" $p_i = w_i F_i/\sum_j w_j F_j$ . These weights add up to 1. The normalization sum in the denominator is the "basket forward" $F_B = \sum_j w_j F_j$ . With the numerical values in your code this comes out $F_B = 82.1$ . As a sanity check of the MC computation one can price the basket option as an European option with volatility $\sigma_B^2 = \sum_{i,j} \rho_{ij} p_i p_j \sigma_i \sigma_j$ . Plugging the numerical values for the correlation matrix and vols from the code, gives $\sigma_B=5.247\%$ . The table below compares the MC result with this analytical result using the Black-Scholes formula and the fixed basket volatility $\sigma_B$ . The agreement is always better than 1%. Not bad for such a simple estimate. An improved estimate includes also the skew of the basket implied volatility, using the method from this paper https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4702005
Checking account access…