def black_scholes_merton(**kwargs): """ Black-Scholes-Merton Option price Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). option : Str Type of option. 'put' or 'call'. The default is 'call'. Returns ------- opt_price : Float Option Price. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] option = params['option'] b = r - q carry = np.exp((b - r) * T) d1 = ((np.log(S / K) + (b + (0.5 * sigma**2)) * T) / (sigma * np.sqrt(T))) d2 = ((np.log(S / K) + (b - (0.5 * sigma**2)) * T) / (sigma * np.sqrt(T))) # Cumulative normal distribution function Nd1 = si.norm.cdf(d1, 0.0, 1.0) minusNd1 = si.norm.cdf(-d1, 0.0, 1.0) Nd2 = si.norm.cdf(d2, 0.0, 1.0) minusNd2 = si.norm.cdf(-d2, 0.0, 1.0) if option == "call": opt_price = ((S * carry * Nd1) - (K * np.exp(-r * T) * Nd2)) if option == 'put': opt_price = ((K * np.exp(-r * T) * minusNd2) - (S * carry * minusNd1)) return opt_price
def black_76(**kwargs): """ Black 76 Futures Option price Parameters ---------- F : Float Discounted Futures Price. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) sigma : Float Implied Volatility. The default is 0.2 (20%). option : Str Type of option. 'put' or 'call'. The default is 'call'. Returns ------- opt_price : Float Option Price. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) F = params['F'] K = params['K'] T = params['T'] r = params['r'] sigma = params['sigma'] option = params['option'] carry = np.exp(-r * T) d1 = (np.log(F / K) + (0.5 * sigma**2) * T) / (sigma * np.sqrt(T)) d2 = (np.log(F / K) + (-0.5 * sigma**2) * T) / (sigma * np.sqrt(T)) # Cumulative normal distribution function Nd1 = si.norm.cdf(d1, 0.0, 1.0) minusNd1 = si.norm.cdf(-d1, 0.0, 1.0) Nd2 = si.norm.cdf(d2, 0.0, 1.0) minusNd2 = si.norm.cdf(-d2, 0.0, 1.0) if option == "call": opt_price = ((F * carry * Nd1) - (K * np.exp(-r * T) * Nd2)) if option == 'put': opt_price = ((K * np.exp(-r * T) * minusNd2) - (F * carry * minusNd1)) return opt_price
def __init__(self, **kwargs): # Import dictionary of default parameters self.default_dict = copy.deepcopy(models_params_dict) # Store initial inputs inputs = {} for key, value in kwargs.items(): inputs[key] = value # Initialise system parameters params = Utils.init_params(inputs) self.params = params
def black_scholes_merton_vega(**kwargs): """ Black-Scholes-Merton Option Vega Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). option : Str Type of option. 'put' or 'call'. The default is 'call'. Returns ------- opt_vega : Float Option Vega. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] b = r - q carry = np.exp((b - r) * T) d1 = ((np.log(S / K) + (b + (0.5 * sigma**2)) * T) / (sigma * np.sqrt(T))) nd1 = (1 / np.sqrt(2 * np.pi)) * (np.exp(-d1**2 * 0.5)) opt_vega = S * carry * nd1 * np.sqrt(T) return opt_vega
def crank_nicolson(**kwargs): """ Crank Nicolson Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). steps : Int Number of time steps. The default is 1000. nodes : Float Number of price steps. The default is 100. option : Str Type of option. 'put' or 'call'. The default is 'call'. american : Bool Whether the option is American. The default is False. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Float Option Price. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] steps = params['steps'] nodes = params['nodes'] option = params['option'] american = params['american'] if option == 'call': z = 1 else: z = -1 b = r - q dt = T / steps dx = sigma * np.sqrt(3 * dt) pu = -0.25 * dt * (((sigma / dx)**2) + (b - (sigma**2) / 2) / dx) pm = 1 + 0.5 * dt * ((sigma / dx)**2) + 0.5 * r * dt pd = -0.25 * dt * (((sigma / dx)**2) - (b - (sigma**2) / 2) / dx) St = np.zeros(nodes + 2) pmd = np.zeros(nodes + 1) p = np.zeros(nodes + 1) St[0] = S * np.exp(-nodes / 2 * dx) C = np.zeros((int(nodes / 2) + 2, nodes + 2), dtype='float') C[0, 0] = max(0, z * (St[0] - K)) for node in range(1, nodes + 1): St[node] = St[node - 1] * np.exp(dx) # Asset price at maturity C[0, node] = max(0, z * (St[node] - K)) # At maturity pmd[1] = pm + pd p[1] = (-pu * C[0, 2] - (pm - 2) * C[0, 1] - pd * C[0, 0] - pd * (St[1] - St[0])) step = steps - 1 while step > -1: for outer_node in range(2, nodes): p[outer_node] = (-pu * C[0, outer_node + 1] - (pm - 2) * C[0, outer_node] - pd * C[0, outer_node - 1] - p[outer_node - 1] * pd / pmd[outer_node - 1]) pmd[outer_node] = pm - pu * pd / pmd[outer_node - 1] for outer_node in range(nodes - 2, 0, -1): C[1, outer_node] = ((p[outer_node] - pu * C[1, outer_node + 1]) / pmd[outer_node]) for inner_node in range(nodes + 1): if american: C[0, inner_node] = max(C[1, inner_node], z * (St[inner_node] - K)) else: C[0, inner_node] = C[1, inner_node] step -= 1 result = C[0, int(nodes / 2)] return result
def explicit_finite_difference_lns(**kwargs): """ Explicit Finite Differences - rewrite BS-PDE in terms of ln(S) Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). steps : Int Number of time steps. The default is 1000. nodes : Float Number of price steps. The default is 100. option : Str Type of option. 'put' or 'call'. The default is 'call'. american : Bool Whether the option is American. The default is False. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Float Option Price. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] steps_itt = params['steps_itt'] nodes = params['nodes'] option = params['option'] american = params['american'] if option == 'call': z = 1 else: z = -1 b = r - q dt = T / steps_itt dx = sigma * np.sqrt(3 * dt) pu = 0.5 * dt * (((sigma / dx)**2) + (b - (sigma**2) / 2) / dx) pm = 1 - dt * ((sigma / dx)**2) - r * dt pd = 0.5 * dt * (((sigma / dx)**2) - (b - (sigma**2) / 2) / dx) St = np.zeros(nodes + 2) St[0] = S * np.exp(-nodes / 2 * dx) C = np.zeros((int(nodes / 2) + 1, nodes + 2), dtype='float') C[steps_itt, 0] = max(0, z * (St[0] - K)) for i in range(1, nodes + 1): St[i] = St[i - 1] * np.exp(dx) # Asset price at maturity C[steps_itt, i] = max(0, z * (St[i] - K)) # At maturity for j in range(steps_itt - 1, -1, -1): for i in range(1, nodes): C[j, i] = pu * C[j + 1, i + 1] + pm * C[j + 1, i] + ( pd * C[j + 1, i - 1]) if american: C[j, i] = max(C[j, i], z * (St[i] - K)) # Upper boundary C[j, nodes] = C[j, nodes - 1] + (St[nodes] - St[nodes - 1]) # Lower boundary C[j, 0] = C[j, 1] result = C[0, int(nodes / 2)] return result
def explicit_finite_difference(**kwargs): """ Explicit Finite Difference Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). nodes : Int Number of price steps. The default is 100. option : Str Type of option. 'put' or 'call'. The default is 'call'. american : Bool Whether the option is American. The default is False. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Float Option Price. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] nodes = params['nodes'] option = params['option'] american = params['american'] if option == 'call': z = 1 else: z = -1 b = r - q dS = S / nodes nodes = int(K / dS) * 2 St = np.zeros((nodes + 2), dtype='float') SGridtPt = int(S / dS) dt = (dS**2) / ((sigma**2) * 4 * (K**2)) N = int(T / dt) + 1 C = np.zeros((N + 1, nodes + 2), dtype='float') dt = T / N Df = 1 / (1 + r * dt) for i in range(nodes + 1): St[i] = i * dS # Asset price at maturity C[N, i] = max(0, z * (St[i] - K)) # At maturity for j in range(N - 1, -1, -1): for i in range(1, nodes): pu = 0.5 * ((sigma**2) * (i**2) + b * i) * dt pm = 1 - (sigma**2) * (i**2) * dt pd = 0.5 * ((sigma**2) * (i**2) - b * i) * dt C[j, i] = Df * (pu * C[j + 1, i + 1] + pm * C[j + 1, i] + pd * C[j + 1, i - 1]) if american: C[j, i] = max(z * (St[i] - K), C[j, i]) if z == 1: # Call option C[j, 0] = 0 C[j, nodes] = (St[i] - K) else: C[j, 0] = K C[j, nodes] = 0 result = C[0, SGridtPt] return result
def implicit_finite_difference(**kwargs): """ Implicit Finite Difference # Slow to converge - steps has small effect, need nodes 3000+ Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). steps : Int Number of time steps. The default is 1000. nodes : Float Number of price steps. The default is 100. option : Str Type of option. 'put' or 'call'. The default is 'call'. american : Bool Whether the option is American. The default is False. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Float Option Price. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] steps = params['steps'] nodes = params['nodes'] option = params['option'] american = params['american'] if option == 'call': z = 1 else: z = -1 # Make sure current asset price falls at grid point dS = 2 * S / nodes SGridtPt = int(S / dS) nodes = int(K / dS) * 2 dt = T / steps b = r - q CT = np.zeros(nodes + 1) p = np.zeros((nodes + 1, nodes + 1), dtype='float') for j in range(nodes + 1): CT[j] = max(0, z * (j * dS - K)) # At maturity for i in range(nodes + 1): p[j, i] = 0 p[0, 0] = 1 for i in range(1, nodes): p[i, i - 1] = 0.5 * i * (b - (sigma**2) * i) * dt p[i, i] = 1 + (r + (sigma**2) * (i**2)) * dt p[i, i + 1] = 0.5 * i * (-b - (sigma**2) * i) * dt p[nodes, nodes] = 1 C = np.matmul(np.linalg.inv(p), CT.T) for j in range(steps - 1, 0, -1): C = np.matmul(np.linalg.inv(p), C) if american: for i in range(1, nodes + 1): C[i] = max(float(C[i]), z * ((i - 1) * dS - K)) result = C[SGridtPt + 1] return result
def european_monte_carlo_with_greeks(**kwargs): """ Standard Monte Carlo with Greeks Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). simulations : Int Number of Monte Carlo runs. The default is 10000. option : Str Type of option. 'put' or 'call'. The default is 'call'. output_flag : Str Whether to return 'price', 'delta', 'gamma', 'theta', 'vega' or 'all'. The default is 'price'. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Various Depending on output flag: 'price' : Float; Option Price 'delta' : Float; Option Delta 'gamma' : Float; Option Gamma 'theta' : Float; Option Theta 'vega' : Float; Option Vega 'all' : Dict; Option Price, Option Delta, Option Gamma, Option Theta, Option Vega """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] simulations = params['simulations'] option = params['option'] output_flag = params['output_flag'] if option == 'call': z = 1 else: z = -1 b = r - q Drift = (b - (sigma**2) / 2) * T sigmarT = sigma * np.sqrt(T) val = 0 deltasum = 0 gammasum = 0 output = np.zeros((5)) counter = 1 while counter < simulations + 1: St = S * np.exp(Drift + sigmarT * norm.ppf(random.random(), loc=0, scale=1)) val = val + max(z * (St - K), 0) if z == 1 and St > K: deltasum = deltasum + St if z == -1 and St < K: deltasum = deltasum + St if abs(St - K) < 2: gammasum = gammasum + 1 counter += 1 # Option Value output[0] = np.exp(-r * T) * val / simulations # Delta output[1] = np.exp(-r * T) * deltasum / (simulations * S) # Gamma output[2] = (np.exp(-r * T) * ((K / S)**2) * gammasum / (4 * simulations)) # Theta output[3] = ((r * output[0] - b * S * output[1] - (0.5 * (sigma**2) * (S**2) * output[2])) / 365) # Vega output[4] = output[2] * sigma * (S**2) * T / 100 output_dict = { 'price': output[0], 'delta': output[1], 'gamma': output[2], 'theta': output[3], 'vega': output[4], 'all': { 'Price': output[0], 'Delta': output[1], 'Gamma': output[2], 'Theta': output[3], 'Vega': output[4] } } result = output_dict.get(output_flag, 'Please enter a valid output flag') # if output_flag == 'price': # result = output[0] # if output_flag == 'delta': # result = output[1] # if output_flag == 'gamma': # result = output[2] # if output_flag == 'theta': # result = output[3] # if output_flag == 'vega': # result = output[4] # if output_flag == 'all': # result = {'Price':output[0], # 'Delta':output[1], # 'Gamma':output[2], # 'Theta':output[3], # 'Vega':output[4]} return result
def implied_vol_naive(**kwargs): """ Finds implied volatility using simple naive iteration, increasing precision each time the difference changes sign. Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. cm : Float # Option price used to solve for vol. The default is 5. epsilon : Float Degree of precision. The default is 0.0001 option : Str Type of option. 'put' or 'call'. The default is 'call'. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Float Implied Volatility. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] cm = params['cm'] epsilon = params['epsilon'] option = params['option'] # Seed vol vi = 0.2 # Calculate starting option price using this vol ci = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) # Initial price difference price_diff = cm - ci if price_diff > 0: flag = 1 else: flag = -1 # Starting vol shift size shift = 0.01 price_diff_start = price_diff while abs(price_diff) > epsilon: # If the price difference changes sign after the vol shift, # reduce the decimal by one and reverse the sign if np.sign(price_diff) != np.sign(price_diff_start): shift = shift * -0.1 # Calculate new vol vi += (shift * flag) # Set initial price difference price_diff_start = price_diff # Calculate the option price with new vol ci = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) # Price difference after shifting vol price_diff = cm - ci # If values are diverging reverse the shift sign if abs(price_diff) > abs(price_diff_start): shift = -shift result = vi return result
def implied_vol_bisection(**kwargs): """ Finds implied volatility using bisection method. Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. cm : Float # Option price used to solve for vol. The default is 5. epsilon : Float Degree of precision. The default is 0.0001 option : Str Type of option. 'put' or 'call'. The default is 'call'. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Float Implied Volatility. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] cm = params['cm'] epsilon = params['epsilon'] option = params['option'] vLow = 0.005 vHigh = 4 cLow = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vLow, option=option, refresh=True) cHigh = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vHigh, option=option, refresh=True) counter = 0 vi = vLow + (cm - cLow) * (vHigh - vLow) / (cHigh - cLow) while abs(cm - AnalyticalMethods.black_scholes_merton( S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) ) > epsilon: counter = counter + 1 if counter == 100: result = 'NA' if AnalyticalMethods.black_scholes_merton( S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) < cm: vLow = vi else: vHigh = vi cLow = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vLow, option=option, refresh=True) cHigh = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vHigh, option=option, refresh=True) vi = vLow + (cm - cLow) * (vHigh - vLow) / (cHigh - cLow) result = vi return result
def leisen_reimer_binomial(**kwargs): """ Leisen Reimer Binomial Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). steps : Int Number of time steps. The default is 1000. option : Str Type of option. 'put' or 'call'. The default is 'call'. output_flag : Str Whether to return 'price', 'delta', 'gamma' or 'all'. The default is 'price'. american : Bool Whether the option is American. The default is False. Returns ------- result : Various Depending on output flag: 'price' : Float; Option Price 'delta' : Float; Option Delta 'gamma' : Float; Option Gamma 'all' : Tuple; Option Price, Option Delta, Option Gamma """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] steps = params['steps'] option = params['option'] output_flag = params['output_flag'] american = params['american'] if option == 'call': z = 1 else: z = -1 b = r - q d1 = ((np.log(S / K) + (b + (0.5 * sigma**2)) * T) / (sigma * np.sqrt(T))) d2 = ((np.log(S / K) + (b - (0.5 * sigma**2)) * T) / (sigma * np.sqrt(T))) hd1 = (0.5 + np.sign(d1) * (0.25 - 0.25 * np.exp(-(d1 / (steps + 1 / 3 + 0.1 / (steps + 1)))**2 * (steps + 1 / 6)))**(0.5)) hd2 = (0.5 + np.sign(d2) * (0.25 - 0.25 * np.exp(-(d2 / (steps + 1 / 3 + 0.1 / (steps + 1)))**2 * (steps + 1 / 6)))**(0.5)) dt = T / steps p = hd2 u = np.exp(b * dt) * hd1 / hd2 d = (np.exp(b * dt) - p * u) / (1 - p) df = np.exp(-r * dt) optionvalue = np.zeros((steps + 1)) returnvalue = np.zeros((4)) for i in range(steps + 1): optionvalue[i] = max(0, z * (S * (u**i) * (d**(steps - i)) - K)) for j in range(steps - 1, -1, -1): for i in range(j + 1): if american: optionvalue[i] = ((p * optionvalue[i + 1]) + ((1 - p) * optionvalue[i])) * df else: optionvalue[i] = max((z * (S * (u**i) * (d**(j - i)) - K)), ((p * optionvalue[i + 1]) + ((1 - p) * optionvalue[i])) * df) if j == 2: returnvalue[2] = (((optionvalue[2] - optionvalue[1]) / (S * (u**2) - S * u * d) - (optionvalue[1] - optionvalue[0]) / (S * u * d - S * (d**2))) / (0.5 * (S * (u**2) - S * (d**2)))) returnvalue[3] = optionvalue[1] if j == 1: returnvalue[1] = ((optionvalue[1] - optionvalue[0]) / (S * u - S * d)) returnvalue[0] = optionvalue[0] if output_flag == 'price': result = returnvalue[0] if output_flag == 'delta': result = returnvalue[1] if output_flag == 'gamma': result = returnvalue[2] if output_flag == 'all': result = { 'Price': returnvalue[0], 'Delta': returnvalue[1], 'Gamma': returnvalue[2] } return result
def european_binomial(**kwargs): """ European Binomial Option price. Combinatorial function limit c1000 Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). steps : Int Number of time steps. The default is 1000. option : Str Type of option. 'put' or 'call'. The default is 'call'. Returns ------- Float European Binomial Option Price. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] steps = params['steps'] option = params['option'] b = r - q dt = T / steps u = np.exp(sigma * np.sqrt(dt)) d = 1 / u p = (np.exp(b * dt) - d) / (u - d) a = int(np.log(K / (S * (d**steps))) / np.log(u / d)) + 1 val = 0 if option == 'call': for j in range(a, steps + 1): val = (val + (comb(steps, j) * (p**j) * ((1 - p)**(steps - j)) * ((S * (u**j) * (d**(steps - j))) - K))) if option == 'put': for j in range(0, a): val = (val + (comb(steps, j) * (p**j) * ((1 - p)**(steps - j)) * (K - ((S * (u**j)) * (d**(steps - j)))))) return np.exp(-r * T) * val
def hull_white_88(**kwargs): """ Hull White 1988 - Correlated Stochastic Volatility. Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sig0 : Float Initial Volatility. The default is 0.09 (9%). sigLR : Float Long run mean reversion level of volatility. The default is 0.0625 (6.25%). halflife : Float Half-life of volatility deviation. The default is 0.1. vvol : Float Vol of vol. The default is 0.5. rho : Float Correlation between asset price and volatility. The default is 0. option : Str Type of option. 'put' or 'call'. The default is 'call'. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Float Option price. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sig0 = params['sig0'] sigLR = params['sigLR'] halflife = params['halflife'] vvol = params['vvol'] rho = params['rho'] option = params['option'] b = r - q # Find constant, beta, from Half-life beta = -np.log(2) / halflife # Find constant, a, from long run volatility a = -beta * (sigLR**2) delta = beta * T ed = np.exp(delta) v = sig0**2 # Average expected variance if abs(beta) < 0.0001: vbar = v + 0.5 * a * T else: vbar = (v + (a / beta)) * ((ed - 1) / delta) - (a / beta) d1 = (np.log(S / K) + (b + (vbar / 2)) * T) / np.sqrt(vbar * T) d2 = d1 - np.sqrt(vbar * T) # standardised normal density function nd1 = (1 / np.sqrt(2 * np.pi)) * (np.exp(-d1**2 * 0.5)) # Cumulative normal distribution function Nd1 = si.norm.cdf(d1, 0.0, 1.0) Nd2 = si.norm.cdf(d2, 0.0, 1.0) # Partial derivatives cSV = (-S * np.exp((b - r) * T) * nd1 * (d2 / (2 * vbar))) cVV = ((S * np.exp( (b - r) * T) * nd1 * np.sqrt(T) / (4 * vbar**1.5)) * (d1 * d2 - 1)) cSVV = ((S * np.exp((b - r) * T) / (4 * vbar**2)) * nd1 * ((-d1 * (d2**2)) + d1 + (2 * d2))) cVVV = (((S * np.exp( (b - r) * T) * nd1 * np.sqrt(T)) / (8 * vbar**2.5)) * ((d1 * d2 - 1) * (d1 * d2 - 3) - ((d1**2) + (d2**2)))) if abs(beta) < 0.0001: f1 = rho * ((a * T / 3) + v) * (T / 2) * cSV phi1 = (rho**2) * ((a * T / 4) + v) * ((T**3) / 6) phi2 = (2 + (1 / (rho**2))) * phi1 phi3 = (rho**2) * (((a * T / 3) + v)**2) * ((T**4) / 8) phi4 = 2 * phi3 else: # Beta different from zero phi1 = (((rho**2) / (beta**4)) * (((a + (beta * v)) * ((ed * (((delta**2) / 2) - delta + 1)) - 1)) + (a * ((ed * (2 - delta)) - (2 + delta))))) phi2 = ((2 * phi1) + ((1 / (2 * (beta**4))) * (((a + (beta * v)) * ((ed**2) - (2 * delta * ed) - 1)) - ((a / 2) * ((ed**2) - (4 * ed) + (2 * delta) + 3))))) phi3 = (((rho**2) / (2 * (beta**6))) * ((((a + (beta * v)) * (ed - delta * ed - 1)) - (a * (1 + delta - ed)))**2)) phi4 = 2 * phi3 f1 = ((rho / ((beta**3) * T)) * (((a + (beta * v)) * (1 - ed + (delta * ed))) + (a * (1 + delta - ed))) * cSV) f0 = S * np.exp((b - r) * T) * Nd1 - (K * np.exp(-r * T) * Nd2) f2 = (((phi1 / T) * cSV) + ((phi2 / (T**2)) * cVV) + ((phi3 / (T**2)) * cSVV) + ((phi4 / (T**3)) * cVVV)) callvalue = f0 + f1 * vvol + f2 * vvol**2 if option == 'call': result = callvalue else: result = (callvalue - (S * np.exp( (b - r) * T)) + (K * np.exp(-r * T))) return result
def trinomial_tree(**kwargs): """ Trinomial Tree Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). steps : Int Number of time steps. The default is 1000. option : Str Type of option, 'put' or 'call'. The default is 'call'. output_flag : Str Whether to return 'price', 'delta', 'gamma', 'theta' or 'all'. The default is 'price'. american : Bool Whether the option is American. The default is False. Returns ------- result : Various Depending on output flag: 'price' : Float; Option Price 'delta' : Float; Option Delta 'gamma' : Float; Option Gamma 'theta' : Float; Option Theta 'all' : Tuple; Option Price, Option Delta, Option Gamma, Option Theta """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] steps = params['steps'] option = params['option'] output_flag = params['output_flag'] american = params['american'] if option == 'call': z = 1 else: z = -1 b = r - q dt = T / steps u = np.exp(sigma * np.sqrt(2 * dt)) d = np.exp(-sigma * np.sqrt(2 * dt)) pu = ((np.exp(b * dt / 2) - np.exp(-sigma * np.sqrt(dt / 2))) / (np.exp(sigma * np.sqrt(dt / 2)) - np.exp(-sigma * np.sqrt(dt / 2))))**2 pd = ((np.exp(sigma * np.sqrt(dt / 2)) - np.exp(b * dt / 2)) / (np.exp(sigma * np.sqrt(dt / 2)) - np.exp(-sigma * np.sqrt(dt / 2))))**2 pm = 1 - pu - pd df = np.exp(-r * dt) optionvalue = np.zeros((steps * 2 + 2)) returnvalue = np.zeros((4)) for i in range(2 * steps + 1): optionvalue[i] = max( 0, z * (S * (u**max(i - steps, 0)) * (d**(max( (steps - i), 0))) - K)) for j in range(steps - 1, -1, -1): for i in range(j * 2 + 1): optionvalue[i] = (pu * optionvalue[i + 2] + pm * optionvalue[i + 1] + pd * optionvalue[i]) * df if american: optionvalue[i] = max( z * (S * (u**max(i - j, 0)) * (d**(max( (j - i), 0))) - K), optionvalue[i]) if j == 1: returnvalue[1] = ((optionvalue[2] - optionvalue[0]) / (S * u - S * d)) returnvalue[2] = (((optionvalue[2] - optionvalue[1]) / (S * u - S) - (optionvalue[1] - optionvalue[0]) / (S - S * d)) / (0.5 * ((S * u) - (S * d)))) returnvalue[3] = optionvalue[0] returnvalue[3] = (returnvalue[3] - optionvalue[0]) / dt / 365 returnvalue[0] = optionvalue[0] if output_flag == 'price': result = returnvalue[0] if output_flag == 'delta': result = returnvalue[1] if output_flag == 'gamma': result = returnvalue[2] if output_flag == 'theta': result = returnvalue[3] if output_flag == 'all': result = { 'Price': returnvalue[0], 'Delta': returnvalue[1], 'Gamma': returnvalue[2], 'Theta': returnvalue[3] } return result
def hull_white_87(**kwargs): """ Hull White 1987 - Uncorrelated Stochastic Volatility. Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). vvol : Float Vol of vol. The default is 0.5. option : Str Type of option. 'put' or 'call'. The default is 'call'. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Float Option price. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] vvol = params['vvol'] option = params['option'] k = vvol**2 * T ek = np.exp(k) b = r - q d1 = ((np.log(S / K) + (b + (sigma**2) / 2) * T) / (sigma * np.sqrt(T))) d2 = d1 - sigma * np.sqrt(T) Nd1 = si.norm.cdf(d1, 0.0, 1.0) cgbs = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=sigma, option='call', refresh=True) # Partial Derivatives cVV = (S * np.exp( (b - r) * T) * np.sqrt(T) * Nd1 * (d1 * d2 - 1) / (4 * (sigma**3))) cVVV = (S * np.exp((b - r) * T) * np.sqrt(T) * Nd1 * ((d1 * d2 - 1) * (d1 * d2 - 3) - ((d1**2) + (d2**2))) / (8 * (sigma**5))) callvalue = (cgbs + 1 / 2 * cVV * (2 * sigma**4 * (ek - k - 1) / k**2 - sigma**4) + (1 / 6 * cVVV * sigma**6 * (ek**3 - (9 + 18 * k) * ek + 8 + 24 * k + 18 * k**2 + (6 * k**3)) / (3 * k**3))) if option == 'call': result = callvalue if option == 'put': # use put-call parity result = callvalue - S * np.exp((b - r) * T) + K * np.exp(-r * T) return result
def implied_trinomial_tree(cls, **kwargs): """ Implied Trinomial Tree Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). steps_itt : Int Number of time steps. The default is 10. option : Str Type of option. 'put' or 'call'. The default is 'call'. output_flag : Str UPM: A matrix of implied up transition probabilities UPni: The implied up transition probability at a single node DPM: A matrix of implied down transition probabilities DPni: The implied down transition probability at a single node LVM: A matrix of implied local volatilities LVni: The local volatility at a single node ADM: A matrix of Arrow-Debreu prices at a single node ADni: The Arrow-Debreu price at a single node (at time step - 'step' and state - 'state') price: The value of the European option step : Int Time step used for Arrow Debreu price at single node. The default is 5. state : Int State position used for Arrow Debreu price at single node. The default is 5. skew : Float Rate at which volatility increases (decreases) for every one point decrease (increase) in the strike price. The default is 0.0004. Returns ------- result : Various Depending on output flag: UPM: A matrix of implied up transition probabilities UPni: The implied up transition probability at a single node DPM: A matrix of implied down transition probabilities DPni: The implied down transition probability at a single node LVM: A matrix of implied local volatilities LVni: The local volatility at a single node ADM: A matrix of Arrow-Debreu prices at a single node ADni: The Arrow-Debreu price at a single node (at time step - 'step' and state - 'state') price: The European option price. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] steps_itt = params['steps_itt'] option = params['option'] output_flag = params['output_flag'] step = params['step'] state = params['state'] skew = params['skew'] if option == 'call': z = 1 else: z = -1 optionvaluenode = np.zeros((steps_itt * 2 + 1)) # Arrow Debreu prices ad = np.zeros((steps_itt + 1, steps_itt * 2 + 1), dtype='float') pu = np.zeros((steps_itt, steps_itt * 2 - 1), dtype='float') pd = np.zeros((steps_itt, steps_itt * 2 - 1), dtype='float') localvol = np.zeros((steps_itt, steps_itt * 2 - 1), dtype='float') dt = T / steps_itt u = np.exp(sigma * np.sqrt(2 * dt)) d = 1 / u df = np.exp(-r * dt) ad[0, 0] = 1 for n in range(steps_itt): for i in range(n * 2 + 1): val = 0 Si1 = (S * (u**(max(i - n, 0))) * (d**(max(n * 2 - n - i, 0)))) Si = Si1 * d Si2 = Si1 * u b = r - q Fi = Si1 * np.exp(b * dt) sigmai = sigma + (S - Si1) * skew if i < (n * 2) / 2 + 1: for j in range(i): Fj = (S * (u**(max(j - n, 0))) * (d**(max(n * 2 - n - j, 0))) * np.exp(b * dt)) val = val + ad[n, j] * (Si1 - Fj) optionvalue = cls.trinomial_tree(S=S, K=Si1, T=(n + 1) * dt, r=r, q=q, sigma=sigmai, steps=(n + 1), option='put', output_flag='price', american=False, refresh=True) qi = ((np.exp(r * dt) * optionvalue - val) / (ad[n, i] * (Si1 - Si))) pi = (Fi + qi * (Si1 - Si) - Si1) / (Si2 - Si1) else: optionvalue = cls.trinomial_tree(S=S, K=Si1, T=(n + 1) * dt, r=r, q=q, sigma=sigmai, steps=(n + 1), option='call', output_flag='price', american=False, refresh=True) val = 0 for j in range(i + 1, n * 2 + 1): Fj = (S * (u**(max(j - n, 0))) * (d**(max(n * 2 - n - j, 0))) * np.exp(b * dt)) val = val + ad[n, j] * (Fj - Si1) pi = ((np.exp(r * dt) * optionvalue - val) / (ad[n, i] * (Si2 - Si1))) qi = (Fi - pi * (Si2 - Si1) - Si1) / (Si - Si1) # Replacing negative probabilities if pi < 0 or pi > 1 or qi < 0 or qi > 1: if Si2 > Fi > Si1: pi = (1 / 2 * ((Fi - Si1) / (Si2 - Si1) + (Fi - Si) / (Si2 - Si))) qi = 1 / 2 * ((Si2 - Fi) / (Si2 - Si)) elif Si1 > Fi > Si: pi = 1 / 2 * ((Fi - Si) / (Si2 - Si)) qi = (1 / 2 * ((Si2 - Fi) / (Si2 - Si1) + (Si1 - Fi) / (Si1 - Si))) pd[n, i] = qi pu[n, i] = pi # Calculating local volatilities Fo = (pi * Si2 + qi * Si + (1 - pi - qi) * Si1) localvol[n, i] = np.sqrt( (pi * (Si2 - Fo)**2 + (1 - pi - qi) * (Si1 - Fo)**2 + qi * (Si - Fo)**2) / (Fo**2 * dt)) # Calculating Arrow-Debreu prices if n == 0: ad[n + 1, i] = qi * ad[n, i] * df ad[n + 1, i + 1] = (1 - pi - qi) * ad[n, i] * df ad[n + 1, i + 2] = pi * ad[n, i] * df elif n > 0 and i == 0: ad[n + 1, i] = qi * ad[n, i] * df elif n > 0 and i == n * 2: ad[n + 1, i] = (pu[n, i - 2] * ad[n, i - 2] * df + (1 - pu[n, i - 1] - pd[n, i - 1]) * (ad[n, i - 1]) * df + qi * (ad[n, i] * df)) ad[n + 1, i + 1] = (pu[n, i - 1] * (ad[n, i - 1]) * df + (1 - pi - qi) * (ad[n, i] * df)) ad[n + 1, i + 2] = pi * ad[n, i] * df elif n > 0 and i == 1: ad[n + 1, i] = ((1 - pu[n, i - 1] - (pd[n, i - 1])) * ad[n, i - 1] * df + (qi * ad[n, i] * df)) else: ad[n + 1, i] = (pu[n, i - 2] * (ad[n, i - 2]) * df + (1 - pu[n, i - 1] - pd[n, i - 1]) * (ad[n, i - 1]) * df + qi * (ad[n, i]) * df) # Calculation of option price using the implied trinomial tree for i in range(2 * steps_itt + 1): optionvaluenode[i] = max( 0, z * (S * (u**max(i - steps_itt, 0)) * (d**(max( (steps_itt - i), 0))) - K)) for n in range(steps_itt - 1, -1, -1): for i in range(n * 2 + 1): optionvaluenode[i] = ((pu[n, i] * optionvaluenode[i + 2] + (1 - pu[n, i] - pd[n, i]) * (optionvaluenode[i + 1]) + pd[n, i] * (optionvaluenode[i])) * df) price = optionvaluenode[0] output_dict = { 'UPM': pu, 'UPni': pu[step, state], 'DPM': pd, 'DPni': pd[step, state], 'LVM': localvol, 'LVni': localvol[step, state], 'ADM': ad, 'ADni': ad[step, state], 'price': price } return output_dict.get(output_flag, "Please select a valid output flag")
def implied_vol_newton_raphson(**kwargs): """ Finds implied volatility using Newton-Raphson method - needs knowledge of partial derivative of option pricing formula with respect to volatility (vega) Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. cm : Float # Option price used to solve for vol. The default is 5. epsilon : Float Degree of precision. The default is 0.0001 option : Str Type of option. 'put' or 'call'. The default is 'call'. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Float Implied Volatility. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] cm = params['cm'] epsilon = params['epsilon'] option = params['option'] # Manaster and Koehler seed value vi = np.sqrt(abs(np.log(S / K) + r * T) * (2 / T)) ci = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) vegai = AnalyticalMethods.black_scholes_merton_vega(S=S, K=K, T=T, r=r, q=q, sigma=vi, refresh=True) mindiff = abs(cm - ci) while abs(cm - ci) >= epsilon and abs(cm - ci) <= mindiff: vi = vi - (ci - cm) / vegai ci = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) vegai = AnalyticalMethods.black_scholes_merton_vega(S=S, K=K, T=T, r=r, q=q, sigma=vi, refresh=True) mindiff = abs(cm - ci) if abs(cm - ci) < epsilon: result = vi else: result = 'NA' return result
def cox_ross_rubinstein_binomial(**kwargs): """ Cox-Ross-Rubinstein Binomial model Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). steps : Int Number of time steps. The default is 1000. option : Str Type of option. 'put' or 'call'. The default is 'call'. output_flag : Str Whether to return 'price', 'delta', 'gamma', 'theta' or 'all'. The default is 'price'. american : Bool Whether the option is American. The default is False. Returns ------- result : Various Depending on output flag: 'price' : Float; Option Price 'delta' : Float; Option Delta 'gamma' : Float; Option Gamma 'theta' : Float; Option Theta 'all' : Tuple; Option Price, Option Delta, Option Gamma, Option Theta """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] steps = params['steps'] option = params['option'] output_flag = params['output_flag'] american = params['american'] if option == 'call': z = 1 else: z = -1 b = r - q dt = T / steps u = np.exp(sigma * np.sqrt(dt)) d = 1 / u p = (np.exp(b * dt) - d) / (u - d) df = np.exp(-r * dt) optionvalue = np.zeros((steps + 2)) returnvalue = np.zeros((4)) for i in range(steps + 1): optionvalue[i] = max(0, z * (S * (u**i) * (d**(steps - i)) - K)) for j in range(steps - 1, -1, -1): for i in range(j + 1): if american: optionvalue[i] = ((p * optionvalue[i + 1]) + ((1 - p) * optionvalue[i])) * df else: optionvalue[i] = max((z * (S * (u**i) * (d**(j - i)) - K)), ((p * optionvalue[i + 1]) + ((1 - p) * optionvalue[i])) * df) if j == 2: returnvalue[2] = (((optionvalue[2] - optionvalue[1]) / (S * (u**2) - S) - (optionvalue[1] - optionvalue[0]) / (S - S * (d**2))) / (0.5 * (S * (u**2) - S * (d**2)))) returnvalue[3] = optionvalue[1] if j == 1: returnvalue[1] = ((optionvalue[1] - optionvalue[0]) / (S * u - S * d)) returnvalue[3] = (returnvalue[3] - optionvalue[0]) / (2 * dt) / 365 returnvalue[0] = optionvalue[0] if output_flag == 'price': result = returnvalue[0] if output_flag == 'delta': result = returnvalue[1] if output_flag == 'gamma': result = returnvalue[2] if output_flag == 'theta': result = returnvalue[3] if output_flag == 'all': result = { 'Price': returnvalue[0], 'Delta': returnvalue[1], 'Gamma': returnvalue[2], 'Theta': returnvalue[3] } return result
def implied_vol_naive_verbose(**kwargs): """ Finds implied volatility using simple naive iteration, increasing precision each time the difference changes sign. Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. cm : Float # Option price used to solve for vol. The default is 5. epsilon : Float Degree of precision. The default is 0.0001 option : Str Type of option. 'put' or 'call'. The default is 'call'. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Float Implied Volatility. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] cm = params['cm'] epsilon = params['epsilon'] option = params['option'] vi = 0.2 ci = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) price_diff = cm - ci if price_diff > 0: flag = 1 else: flag = -1 while abs(price_diff) > epsilon: while price_diff * flag > 0: ci = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) price_diff = cm - ci vi += (0.01 * flag) while price_diff * flag < 0: ci = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) price_diff = cm - ci vi -= (0.001 * flag) while price_diff * flag > 0: ci = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) price_diff = cm - ci vi += (0.0001 * flag) while price_diff * flag < 0: ci = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) price_diff = cm - ci vi -= (0.00001 * flag) while price_diff * flag > 0: ci = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) price_diff = cm - ci vi += (0.000001 * flag) while price_diff * flag < 0: ci = AnalyticalMethods.black_scholes_merton(S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True) price_diff = cm - ci vi -= (0.0000001 * flag) result = vi return result
def european_monte_carlo(**kwargs): """ Standard Monte Carlo Parameters ---------- S : Float Stock Price. The default is 100. K : Float Strike Price. The default is 100. T : Float Time to Maturity. The default is 0.25 (3 Months). r : Float Interest Rate. The default is 0.005 (50bps) q : Float Dividend Yield. The default is 0. sigma : Float Implied Volatility. The default is 0.2 (20%). simulations : Int Number of Monte Carlo runs. The default is 10000. option : Str Type of option. 'put' or 'call'. The default is 'call'. default : Bool Whether the function is being called directly (in which case values that are not supplied are set to default values) or called from another function where they have already been updated. Returns ------- result : Float Option Price. """ # Update pricing input parameters to default if not supplied if 'refresh' in kwargs and kwargs['refresh']: params = Utils.init_params(kwargs) S = params['S'] K = params['K'] T = params['T'] r = params['r'] q = params['q'] sigma = params['sigma'] simulations = params['simulations'] option = params['option'] if option == 'call': z = 1 else: z = -1 b = r - q Drift = (b - (sigma**2) / 2) * T sigmarT = sigma * np.sqrt(T) val = 0 counter = 1 while counter < simulations + 1: St = S * np.exp(Drift + sigmarT * norm.ppf(random.random(), loc=0, scale=1)) val = val + max(z * (St - K), 0) counter += 1 result = np.exp(-r * T) * val / simulations return result