#############################################################################################################
### Program: SimTrade_standard_option_BSM_pricing_model_implied_volatility_Python_code                    ###
### Author: Saral Bindal                                                                                  ###
### Contact: saralbindal.24@kgpian.iitkgp.ac.in                                                           ###
### Article on the SimTrade blog: https://www.simtrade.fr/blog_simtrade/implied-volatility-option-prices/ ###
#############################################################################################################


# Python program used to compute the price of European-style call and put options using the Black-Scholes-Merton (BSM) model 
# and to estimate implied volatility numerically.

# Objectives of the program:
#   1) Compute the price of a European call or put option using the BSM model
#   2) Estimate implied volatility from a market option price


####################################################################################################
### Organization of the program:                                                                 ###
###   STEP 1: Environment setup and package loading                                              ###
###   STEP 2: Computation of the the option value with the BSM model                             ###
###   STEP 3: Computation of the implied volatility with the BSM model                           ###
####################################################################################################


####################################################################################################
### Parameters you can change for BSM option pricing:                                            ###
###   option_type : 'call' or 'put'  - type of the option                                        ###
###   S           : Current stock price                                                          ###
###   K           : Strike price of the option                                                   ###
###   T           : Time to maturity in years                                                    ###
###   r           : Risk-free interest rate (annualized)                                         ###
###   vol         : Volatility of the underlying asset (annualized)                              ###
####################################################################################################


####################################################################################################
### Parameters you can change for implied volatility estimation:                                 ###
###   C_market    : Observed market price of the option                                          ###
###   volatility  : Initial guess for the implied volatility solver                              ###
####################################################################################################


#################
# Documentation #
#################

# Python packages 
# https://docs.python.org/3/library/math.html
# https://numpy.org/doc/stable/
# https://docs.scipy.org/doc/scipy/
# https://github.com/yassinemaaroufi/MibianLib

# Academic articles
# Black F. and M. Scholes (1973) The pricing of options and corporate liabilities.Journal of Political Economy, 81(3), 637–654.
# Merton R.C. (1973) Theory of rational option pricing.The Bell Journal of Economics and Management Science, 4(1), 141–183.
# Hull J.C. (2022) Options, Futures, and Other Derivatives, 11th Global Edition, Chapter 15 – The Black–Scholes–Merton model, 338–365.
# Cox J.C. and M. Rubinstein (1985) Options Markets, First Edition, Chapter 5 – An Exact Option Pricing Formula, 165-252.
  

#################################################
# STEP 1: Environment setup and package loading #       
#################################################

import sys
import subprocess

# Function to install a package if it is not already installed
def install(package):
    subprocess.check_call([sys.executable, "-m", "pip", "install", package])

# Importing math module
try:
    import math
except ImportError:
    install("math")
    import math

# Importing numpy for numerical computations
try:
    import numpy as np
except ImportError:
    install("numpy")
    import numpy as np

# Importing norm from scipy.stats for normal distribution functions
try:
    from scipy.stats import norm
except ImportError:
    install("scipy")
    from scipy.stats import norm

# Importing mibian for option pricing and implied volatility calculations
try:
    import mibian
except ImportError:
    install("mibian")
    import mibian


##################################################################
# STEP 2: Computation of the the option value with the BSM model #
##################################################################

# Function to calculate price of a European-style call or put option using the Black-Scholes-Merton model
def BSM_calculator_option_price(option_type: str, S: float, K: float, T: int, r: float, vol: float):
    if option_type == 'call':
        # Calculate d1 and d2 for BSM formula
        d1 = (math.log(S/K) + (r + 0.5 * vol**2)*T ) / (vol * math.sqrt(T))
        d2 = d1 - (vol * math.sqrt(T))
        # Calculate call price
        C = S * norm.cdf(d1) - K * math.exp(-r * T) * norm.cdf(d2)
        # Print results
        print('The value of d1 is: ', round(d1, 4))
        print('The value of d2 is: ', round(d2, 4))
        print('The price of the call option is: $', round(C, 2))
        return
    elif option_type == 'put':
        # Calculate d1 and d2 for BSM formula
        d1 = (math.log(S/K) + (r + 0.5 * vol**2)*T ) / (vol * math.sqrt(T))
        d2 = d1 - (vol * math.sqrt(T))
        # Calculate put price
        P = K * math.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)
        # Print results
        print('The value of d1 is: ', round(d1, 4))
        print('The value of d2 is: ', round(d2, 4))
        print('The price of the put option is: $', round(P, 2))
        return
    else:
        print('Irrelevant option type')
        return   
    
# Example parameters for BSM call option
S = 45 
K = 40 
T = 2   
r = 0.1 
vol = 0.1 
option_type = 'call'

# Calculate option price
BSM_calculator_option_price(option_type, S, K, T, r, vol)
print('\n')


#################################################################################################
# STEP 3: Computation of the implied volatility from the option market price with the BSM model #
#################################################################################################

# Estimating implied volatility numerically using mibian
S = 145.65
K = 145
r = 5                       # interest rate in percent
days_to_expiration = 30     # days to expiration
C_market = 3.89             # market call price

# Calculate implied volatility using mibian's BS model
bs_model = mibian.BS([S, K, r, days_to_expiration], callPrice=C_market)

# Print implied volatility
print(f"The Implied Volatility is: {bs_model.impliedVolatility:.2f}%")
print('\n')


# End of the code