Black-scholes and options

github

def implied_volatility(market_price, S, K, T, r, option_type='call', tol=1e-6, max_iter=100):
sigma = math.sqrt((2 * math.pi) / T) * (market_price / S) # Initial Guess (Manaster-Koehler seed for SPX)
for i in range(max_iter):
# If sigma leaves bounds, go to Bisection immediately
if not (0.0001 < sigma < 5.0):
return implied_volatility_bisection(market_price, S, K, T, r, sigma, option_type, tol, max_iter)
if option_type == 'call':
price = call(S, K, T, r, sigma)
else:
price = put(S, K, T, r, sigma)
vega_value = vega(S, K, T, r, sigma)
price_diff = price - market_price
if abs(price_diff) < tol:
return sigma
# If Vega is too flat, go to Bisection
if abs(vega_value) < 1e-10:
return implied_volatility_bisection(market_price, S, K, T, r, sigma, option_type, tol, max_iter)
sigma -= price_diff / vega_value
# If no convergence, use final guess to warm-start bisection
return implied_volatility_bisection(market_price, S, K, T, r, sigma, option_type, tol, max_iter)
def implied_volatility_bisection(market_price, S, K, T, r, sigma_guess, option_type='call', tol=1e-6, max_iter=100):
low = 0.0001
high = 5.0
# Warm start
# Shrink search space to speed up bisection if Newton-Raphson guess is reasonable
if 0.0001 < sigma_guess < 5.0:
if option_type == 'call':
price_at_guess = call(S, K, T, r, sigma_guess)
else:
price_at_guess = put(S, K, T, r, sigma_guess)
if price_at_guess > market_price:
high = sigma_guess # Guess was too high, it becomes our new ceiling
else:
low = sigma_guess # Guess was too low, it becomes our new floor
# Normal Bisection
for i in range(max_iter):
mid = (low + high) / 2
if option_type == 'call':
price = call(S, K, T, r, mid)
else:
price = put(S, K, T, r, mid)
if abs(price - market_price) < tol:
return mid
if price > market_price:
high = mid
else:
low = mid
return (low + high) / 2

Explanation: This uses the newton-raphson method to find implied volatility, and if it does not converge within 100 iterations, goes to bisection to ensure finding a value for sigma.