def Compound_Option_Pricing_Closed_Form(S0, X1, X2, r, T1, T2, sigma): # Use fslove and Black Scholes formula to sovle I f1 = lambda I: bsformula(1, I, X2, r, T2 - T1, sigma)[0] - X1[0] f2 = lambda I: bsformula(-1, I, X2, r, T2 - T1, sigma)[0] - X1[1] I = [fsolve(f1, S0), fsolve(f2, S0)]; I = np.array(I) y1 = (np.log((S0) / I) + (r + sigma ** 2 / 2) * T1) / (sigma * np.sqrt(T1)) y2 = y1 - sigma * np.sqrt(T1) z1 = (np.log((S0) / X2) + (r + sigma ** 2 / 2) * T2) / (sigma * np.sqrt(T2)) z2 = z1 - sigma * np.sqrt(T2) rho = np.sqrt(T1 / T2) # Compound option pricing low = np.array([-np.inf, -np.inf]); M = mvn.mvndst # mvn.mvndst(low,upp,[0,0],rho)[1] if INFIN(I) = 0, integration range is (-infinity, UPPER(I) CC = S0 * M(low, np.array([z1, y1[0]]), [0, 0], rho)[1] - \ X2 * np.exp(-r * T2) * M(low, np.array([z2, y2[0]]), [0, 0], rho)[1] - \ X1[0] * np.exp(-r * T1) * norm.cdf(y2[0]) PC = X2 * np.exp(-r * T2) * M(low, np.array([z2, -y2[0]]), [0, 0], -rho)[1] - \ S0 * M(low, np.array([z1, -y1[0]]), [0, 0], -rho)[1] + \ X1[0] * np.exp(-r * T1) * norm.cdf(-y2[0]) CP = X2 * np.exp(-r * T2) * M(low, np.array([-z2, -y2[1]]), [0, 0], rho)[1] - \ S0 * M(low, np.array([-z1, -y1[1]]), [0, 0], rho)[1] - \ X1[1] * np.exp(-r * T1) * norm.cdf(-y2[1]) PP = S0 * M(low, np.array([-z1, y1[1]]), [0, 0], -rho)[1] - \ X2 * np.exp(-r * T2) * M(low, np.array([-z2, y2[1]]), [0, 0], -rho)[1] + \ X1[1] * np.exp(-r * T1) * norm.cdf(y2[1]) return (CC, PC, CP, PP)
def newton(callput, S0, K, r, T, q, price, sigma, tolerance, it=100): """ :param callput: Indicates if the option is a Call or Put option :param S0: Stock price :param K: Strike price :param r: Risk-free rate :param T: Time to expiration :param q: Dividend rate :param price: Price of option :param sigma: Initial Volatility :param tolerance: The precision of the solution :param it: Maximum number of iterations :return: Implied volatility """ i = 0 vega_init = 0 while i < it: bsret = bsformula(callput, S0, K, r, T, sigma, q) optionValue = bsret[0] vega = bsret[2] if (vega == 0): return sigma vega_init = vega sigma_est = float(sigma) - (float(optionValue) - float(price)) / float(vega) if (abs(sigma_est - sigma) < tolerance): return sigma_est sigma = sigma_est i += 1 return float('NaN')
def bsimpvol(callput, S0, K, r, T, price, q=0., priceTolerance=0.01, method='bisect', reportCalls=False): ''' :param callput: Callput is 1 for a call and -1 for a put :param S0: The stock price at time zero :param K: The strike price :param r: Risk-free interest rate :param T: The maturity time :param price: The price of the option :param q: A continuous return rate on the underlying :param priceTolerance: :param method: The method used to calculate sigma :param reportCalls: The record of call bsformula :return: sigma, numbers of calling bsformula ''' y = lambda sigma: bsformula(callput, S0, K, r, T, sigma)[0] dy = lambda sigma: bsformula(callput, S0, K, r, T, sigma)[2] if method == 'bisect': if bisect(price, y, bounds=[0.01, 1])[0] == None: return None item = bisect(price, y, bounds=[0.01, 1])[0] elif method == 'newton': if newton(price, y, dy, start=1, bounds=[0.01, 1])[0] == None: return None item = newton(price, y, dy, start=1, bounds=[0.01, 1])[0] if item != None: sigma = item[-1] else: sigma = item return None if reportCalls == True: return (sigma, len(item)) else: return sigma
def binomialAmerican(S0, Ks, r, T, sigma, q, callputs, M): """ Computed values """ dt = T / M # Single time step, in years df = np.exp(-(r - q) * dt) # Discount factor u = np.exp(sigma * np.sqrt(dt)) d = 1. / u p = (np.exp((r - q) * dt) - d) / (u - d) q = 1 - p # Initialize a 2D tree at T=0 STs = [np.array([S0])] # Simulate the possible stock prices path for i in range(M): prev_branches = STs[-1] st = np.concatenate((prev_branches * u, [prev_branches[-1] * d])) STs.append(st) # Add nodes at each time step payoffs = np.maximum(0, (STs[M] - Ks) if callputs == 1 else (Ks - STs[M])) for j in reversed(range(M)): # The payoffs from NOT exercising the option payoffs = (payoffs[:-1] * p + payoffs[1:] * q) * df early_ex_payoff = (STs[j] - Ks) if callputs == 1 else (Ks - STs[j]) payoffs = np.maximum(payoffs, early_ex_payoff) price = bsformula(callput=callputs, S0=S0, K=Ks, r=r, T=T, sigma=sigma, q=q)[0] error = price - payoffs[0] return payoffs[0], error
def targetfunction(x): return bsformula(callput, S0, K, r, T, x, q)[0]
S0, r, T1, T2 = [50.0, 0.025, 1.0, 2.0]; X2 = S0 # Different client's view views = {'Mrs Smith': ([r + 0.03, 0.15], [r + 0.005, 0.3]), 'Mr Johnson': ([r - 0.03, 0.2], [r - 0.01, 0.18]), 'Ms Williams': ([r - 0.03, 0.18], [r + 0.03, 0.12]), 'Mr Jones': ([r + 0.02, 0.35], [r + 0.02, 0.1]), 'Miss Brown': ([r + 0.03, 0.15], [r - 0.05, 0.15])} names = ('Mrs Smith', 'Mr Johnson', 'Ms Williams', 'Mr Jones', 'Miss Brown') # in different views 'Question c1-Antithetic Sampling methods. ComOpt4 and ComOpt5 is the prices of compound options' ComOpt4 = [0] * len(names); option = ('Call', 'Put') ComOpt5 = [0] * len(names) j = 0 for v in names: mu1, sigma1, mu2, sigma2 = views[v][0][0], views[v][0][1], views[v][1][0], views[v][1][1] # At the money X1 = [bsformula(1, S0, X2, r, T2 - T1, sigma2)[0], bsformula(-1, S0, X2, r, T2 - T1, sigma2)[0]] print() print("Antithetic Sampling") # For different underlying options ComOpt4[j] = [0] * 2; i = 0 for underlying in option: # Antithetic Sampling start_c1 = time.clock() ComOpt4[j][i] = [0] * 4 ComOpt4[j][i] = Comp_Opt_Var_Vol(S0, X1, X2, r, T1, T2, mu1, mu2, sigma1, sigma2).antiSampling(underlying, int(10000), 777) # Get enough size of number to satisfy The Central Limit Theorem N = int(floor(std(ComOpt4[j][i][3]) ** 2.0 * 1.96 ** 2.0 * 1002001 / (mean(ComOpt4[j][i][3]) ** 2.0))) print("Using Antithetic Sampling, simulaiton number is", N)
def vega(sigma, callput, S0, K, r, T): return bsformula(callput, S0, K, r, T, sigma, q=0.)[2]
def bsmvsact(sigma, callput, S0, K, r, T, price): return bsformula(callput, S0, K, r, T, sigma, q=0.)[0] - price
def f_prime(x): return bsformula(callput, S0, K, r, T, x, q)[2]
def f(x): return bsformula(callput, S0, K, r, T, x, q)[0] - price
r=np.interp(T,tenors,rateCurve) m=range(100,2010,100); n=range(1,101,1) # create dynamic M and N print('Varied N is %s'%n,'Varied M is %s'%m) 'Vary N' error_n=np.zeros((len(n),3)) checkpoints2 = [10,m[-1]] num1_1=0 for k in n: samples2 = rand(k, m[-1]) t2 = array([T / k] * k) # Monthly num2_1 = 0 for u in ['standard', 'euler', 'milstein']: # calculate error in different integrator methods test= MCOptionPrices(S0, K, T, rateCurve, sigma, t2, checkpoints2, samples2, u) TV = test['TV'] error_n[num1_1,num2_1] = abs(TV[0] - bsformula(1, S0, K, r, T / 12., sigma)[0]) num2_1+=1 num1_1+=1 'Vary M' error_m = np.zeros((len(m), 3)) t2 = array([T / n[-1]] * n[-1]) # Monthly num1_2=0 for l in m: samples2 = rand(n[-1], l) checkpoints2 = [10,l] num2_2 = 0 for u in ['standard', 'euler', 'milstein']: # calculate error in different integrator methods test = MCOptionPrices(S0, K, T, rateCurve, sigma, t2, checkpoints2, samples2, u) TV = test['TV'] error_m[num1_2, num2_2] = abs(TV[0] - bsformula(1, S0, K, r, T / 12., sigma)[0])
def fdAmerican(callput, S0, K, r, T, sigma, q, M, N, S_max, tol): #dS = S_max / float(M) dt = T / float(N) i_values = np.arange(M+1) j_values = np.arange(N+1) boundary_conds = np.linspace(0, S_max, M+1) if callput == 1: payoffs = np.maximum( boundary_conds[1:M]-K, 0) past_values = payoffs lower_boundary_values = np.maximum(S_max - K, 0) * \ np.exp(-r * dt * (N-j_values)) upper_boundary_values = 0* j_values elif callput == -1: payoffs = np.maximum( K-boundary_conds[1:M], 0) past_values = payoffs upper_boundary_values = K * \ np.exp(-r * dt * (N-j_values)) lower_boundary_values = 0* j_values alpha = 0.25*dt*((sigma**2)*(i_values**2) - r* i_values) beta = -dt*0.5*((sigma**2)*(i_values**2) +r) gamma = 0.25*dt*((sigma**2)*(i_values**2) +r*i_values) M1 = -np.diag(alpha[2:M], -1) + \ np.diag(1-beta[1:M]) - \ np.diag(gamma[1:M-1], 1) M2 = np.diag(alpha[2:M], -1) + \ np.diag(1+beta[1:M]) + \ np.diag(gamma[1:M-1], 1) P, L, U = linalg.lu(M1) aux = np.zeros(M-1) new_values = np.zeros(M-1) for j in reversed(range(N)): aux[0] = alpha[1]*(upper_boundary_values[j] + upper_boundary_values[j+1]) aux[M-2]= gamma[M-1]*(lower_boundary_values[j]+ lower_boundary_values[j+1]) rhs = np.dot(M2, past_values) + aux old_values = np.copy(past_values) error = sys.float_info.max while tol < error: new_values[0] = \ max(payoffs[0], old_values[0] + 1.0/(1-beta[1]) * (rhs[0] - (1-beta[1])*old_values[0] + (gamma[1]*old_values[1]))) for k in range(M-2)[1:]: new_values[k] = \ max(payoffs[k], old_values[k] + 1.0/(1-beta[k+1]) * (rhs[k] + alpha[k+1]*new_values[k-1] - (1-beta[k+1])*old_values[k] + gamma[k+1]*old_values[k+1])) new_values[-1] = \ max(payoffs[-1], old_values[-1] + 1.0/(1-beta[-2]) * (rhs[-1] + alpha[-2]*new_values[-2] - (1-beta[-2])*old_values[-1])) error = np.linalg.norm(new_values - old_values) old_values = np.copy(new_values) past_values = np.copy(new_values) values = np.concatenate(([upper_boundary_values[0]], new_values, [0])) price = bsformula(callput = callput, S0 = S0, K = K, r = r, T = T, sigma = sigma, q = q)[0] diff = price - np.interp(S0, boundary_conds, values) return np.interp(S0, boundary_conds, values), diff
def optVega(x): return bsformula(callput, S0, K, r, T, x, q)[2]
def optionValue(x): return bsformula(callput, S0, K, r, T, x, q)[0]