# ABDALLAH ABDELKARIM
# Advanced Options Pricing using Monte Carlo Simulation in Python
# Import necessary libraries
import yfinance as yf
import pandas as pd
import numpy as np
# Downloading financial data using yfinance
ticker_data = [Link]('NVDA')
historical_data = ticker_data.history(period="5y") # Retrieving 5 years of historical data
# Displaying the first few rows of the data
print(historical_data.head())
Open High Low Close \
Date
2019-03-04 [Link]-05:00 39.233573 39.462021 38.364475 38.930630
2019-03-05 [Link]-05:00 38.774191 39.248471 38.230387 38.866070
2019-03-06 [Link]-05:00 38.776677 38.861103 37.728793 37.756107
2019-03-07 [Link]-05:00 37.520209 37.617051 36.601445 37.063309
2019-03-08 [Link]-05:00 36.169385 37.512764 35.955835 37.405987
Volume Dividends Stock Splits
Date
2019-03-04 [Link]-05:00 40999200 0.0 0.0
2019-03-05 [Link]-05:00 36292400 0.0 0.0
2019-03-06 [Link]-05:00 40352400 0.0 0.0
2019-03-07 [Link]-05:00 45010400 0.0 0.0
2019-03-08 [Link]-05:00 42241600 0.0 0.0
# Additional libraries for visualization
import [Link] as plt
# Plotting the historical stock prices
[Link](figsize=(10, 6))
[Link](historical_data.index, historical_data['Close'], label='Close Price')
[Link]('NVDA Stock Prices')
[Link]('Date')
[Link]('Price')
[Link]()
[Link](True)
# Understanding Options Pricing
import numpy as np
from [Link] import erf
class BlackScholesModel:
def __init__(self, spot_price, strike_price, risk_free_rate, volatility, time_to_maturity):
self.S = spot_price
self.K = strike_price
self.r = risk_free_rate
[Link] = volatility
self.T = time_to_maturity
def calculate_d1(self):
numerator = [Link](self.S / self.K) + (self.r + 0.5 * [Link] ** 2) * self.T
denominator = [Link] * [Link](self.T)
return numerator / denominator
def calculate_d2(self, d1):
return d1 - [Link] * [Link](self.T)
def call_option_price(self):
d1 = self.calculate_d1()
d2 = self.calculate_d2(d1)
option_price = (self.S * self.N(d1)) - (self.K * [Link](-self.r * self.T) * self.N(d2))
return option_price
def put_option_price(self):
d1 = self.calculate_d1()
d2 = self.calculate_d2(d1)
option_price = (self.K * [Link](-self.r * self.T) * self.N(-d2)) - (self.S * self.N(-d1))
return option_price
def N(self, x):
return (1 + erf(x / [Link](2))) / 2
# Next, we set the parameters required for the Black-Scholes model, such as the spot price,
# strike price, risk-free rate, volatility and time to maturity. We then create an instance
# of the Black-Scholes model and calculate the call and put option prices based on the given parameters.
# Parameters for the Black-Scholes model
spot_price = 100 # Current stock price
strike_price = 100 # Option strike price
risk_free_rate = 0.05 # Risk-free interest rate
volatility = 0.2 # Volatility of the underlying stock
time_to_maturity = 1 # Time to option maturity (in years)
# Creating an instance of the Black-Scholes model
bs_model = BlackScholesModel(spot_price, strike_price, risk_free_rate, volatility, time_to_maturity)
# Calculating call option price
call_price = bs_model.call_option_price()
print("Call Option Price:", call_price)
# Calculating put option price
put_price = bs_model.put_option_price()
print("Put Option Price:", put_price)
Call Option Price: 10.450583572185565
Put Option Price: 5.573526022256971
# Implementing Monte Carlo simulation
# In this section, we will implement the Monte Carlo simulation for options pricing in Python.
# The Monte Carlo method is a powerful tool for approximating option prices and evaluating complex
# financial derivatives. We will showcase its application by calculating call and put option prices
# through Monte Carlo simulation.
# Implementing Monte Carlo simulation
class MonteCarloOptionPricing:
def __init__(self, spot_price, strike_price, risk_free_rate, volatility, time_to_maturity, num_simulations,
self.S = spot_price
self.K = strike_price
self.r = risk_free_rate
[Link] = volatility
self.T = time_to_maturity
self.N = num_simulations
self.M = num_steps
def generate_price_paths(self):
dt = self.T / self.M
price_matrix = [Link]((self.M + 1, self.N))
price_matrix[0] = self.S
for t in range(1, self.M + 1):
rand_values = [Link](self.N)
price_matrix[t] = price_matrix[t - 1] * [Link]((self.r - 0.5 * [Link] ** 2) * dt + [Link] *
return price_matrix
def call_option_monte_carlo(self):
price_matrix = self.generate_price_paths()
payoffs = [Link](price_matrix[-1] - self.K, 0)
option_price = [Link](-self.r * self.T) * 1/self.N * [Link](payoffs)
return option_price
def put_option_monte_carlo(self):
price_matrix = self.generate_price_paths()
payoffs = [Link](self.K - price_matrix[-1], 0)
option_price = [Link](-self.r * self.T) * 1/self.N * [Link](payoffs)
return option_price
# Subsequently, we create an instance of the MonteCarloOptionPricing class and calculate the call and put
# option prices using the Monte Carlo simulation method.
# Creating an instance of the Monte Carlo option pricing model
monte_carlo_model = MonteCarloOptionPricing(spot_price, strike_price, risk_free_rate, volatility, time_to_maturity
# Calculating call option price with Monte Carlo simulation
call_price_mc = monte_carlo_model.call_option_monte_carlo()
print("Call Option Price (Monte Carlo):", call_price_mc)
# Calculating put option price with Monte Carlo simulation
put_price_mc = monte_carlo_model.put_option_monte_carlo()
print("Put Option Price (Monte Carlo):", put_price_mc)
Call Option Price (Monte Carlo): 10.678375165653872
Put Option Price (Monte Carlo): 5.543294643090881
# Adding advanced features and optimizations to the Monte Carlo simulation model
# In this section, we will introduce an advanced feature to optimize our Monte Carlo simulation model.
# We will implement the concept of antithetic variates to enhance the accuracy and efficiency of the option
# pricing simulation.
# Antithetic variates are employed to reduce variance in the Monte Carlo simulations,
# thus improving the precision of option price estimation. This technique involves generating random numbers
# in complementary pairs to introduce negative correlation in the simulation.
# Below is the Python code demonstrating the implementation of antithetic variates in the Monte Carlo
# simulation model for options pricing:
class AntitheticMonteCarloOptionPricing(MonteCarloOptionPricing):
def generate_price_paths(self):
dt = self.T / self.M
price_matrix = [Link]((self.M + 1, self.N))
price_matrix[0] = self.S
for t in range(1, self.M + 1):
rand_values = [Link](self.N)
price_matrix[t, :self.N//2] = price_matrix[t-1, :self.N//2] * [Link]((self.r - 0.5 * [Link] ** 2
price_matrix[t, self.N//2:] = price_matrix[t-1, self.N//2:] * [Link]((self.r - 0.5 * [Link] ** 2
return price_matrix
# Creating an instance of the Antithetic Monte Carlo option pricing model
antithetic_monte_carlo_model = AntitheticMonteCarloOptionPricing(spot_price, strike_price, risk_free_rate, volatility
# Calculating call option price with Antithetic Monte Carlo simulation
call_price_amc = antithetic_monte_carlo_model.call_option_monte_carlo()
print("Call Option Price (Antithetic Monte Carlo):", call_price_amc)
# Calculating put option price with Antithetic Monte Carlo simulation
put_price_amc = antithetic_monte_carlo_model.put_option_monte_carlo()
print("Put Option Price (Antithetic Monte Carlo):", put_price_amc)
Call Option Price (Antithetic Monte Carlo): 10.502875596909385
Put Option Price (Antithetic Monte Carlo): 5.615877853900334
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/[Link]