Untitled
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Thu Dec 19 12:44:22 2024 @author: giacomostella """ """ The following code lines are divided as follows: -Import libraries -Classes of Models -Black and Scholes -Local Volatility -Heston Model with Fourier inversion) -Portfolio and Option classes -Application of the Models -Portfolio of stocks data import -Assumptions input -Option price creation -Results print In between these lines you will kindly find an explanation of the models behind the calculations and you will understand how option pricing is set and utilized. Thus will comprehend three option pricing models that will deliver a series of prices. Results could be suited for the inclusion of some level of protection into a random portfolio of stocks. My ultimate objective is to calculate price for both call and put option related to each stock; thus serving for taking more informed decisions in the area of portfolio risk management. """ #Import libraries according to what I did in Python class import yfinance as yf #for importing data from Yahoo Finance import pandas as pd import numpy as np from scipy.stats import norm from scipy.integrate import quad from itertools import groupby #Creation of Classes of Models """ As seen in class, I will procede setting the basis of my coding. I create a set of classes that define the functioning of my calculus; thus including variables and formulas that are drafted to calculate the price of call and put options. """ # Black-Scholes Model class BlackScholes: def __init__(self, S, K, T, r, sigma): self.S = S self.K = K self.T = T self.r = r self.sigma = sigma def d1(self): return (np.log(self.S / self.K) + (self.r + 0.5 * self.sigma**2) * self.T) / (self.sigma * np.sqrt(self.T)) def d2(self): return self.d1() - self.sigma * np.sqrt(self.T) def call_price(self): d1 = self.d1() d2 = self.d2() return self.S * norm.cdf(d1) - self.K * np.exp(-self.r * self.T) * norm.cdf(d2) def put_price(self): d1 = self.d1() d2 = self.d2() return self.K * np.exp(-self.r * self.T) * norm.cdf(-d2) - self.S * norm.cdf(-d1) # Local Volatility Model class LocalVolatility: def __init__(self, S, K, T, r, local_vol_surface): self.S = S self.K = K self.T = T self.r = r self.local_vol_surface = local_vol_surface def local_vol(self): return self.local_vol_surface(self.S, self.K, self.T) def call_price(self): sigma = self.local_vol() return BlackScholes(self.S, self.K, self.T, self.r, sigma).call_price() def put_price(self): sigma = self.local_vol() return BlackScholes(self.S, self.K, self.T, self.r, sigma).put_price() # Heston Model with Fourier Inversion class HestonModel: def __init__(self, S, K, T, r, v0, kappa, theta, sigma, rho): self.S = S self.K = K self.T = T self.r = r self.v0 = v0 self.kappa = kappa self.theta = theta self.sigma = sigma self.rho = rho def characteristic_function(self, phi): u = -0.5 b = self.kappa + self.rho * self.sigma * phi * 1j d = np.sqrt(b**2 - self.sigma**2 * (2 * u * 1j * phi - phi**2)) g = (b - d) / (b + d) C = self.r * phi * 1j * self.T + (self.kappa * self.theta / self.sigma**2) * ( (b - d) * self.T - 2 * np.log((1 - g * np.exp(-d * self.T)) / (1 - g)) ) D = ((b - d) / self.sigma**2) * ((1 - np.exp(-d * self.T)) / (1 - g * np.exp(-d * self.T))) return np.exp(C + D * self.v0 + 1j * phi * np.log(self.S)) def call_price(self): def integrand(phi): return np.real( np.exp(-1j * phi * np.log(self.K)) * self.characteristic_function(phi) / (1j * phi) ) integral = quad(integrand, 0, np.inf)[0] return 0.5 * (self.S - self.K * np.exp(-self.r * self.T)) + (1 / np.pi) * integral def put_price(self): call_price = self.call_price() return call_price + self.K * np.exp(-self.r * self.T) - self.S # Portfolio and Option Classes """ In the same way I defined the classes for each Model, I set the classes for a portfolio of stocks and for option pricing. In the first class (Portfolio) I create a more complicated class, as I include that the portfolio is constructed basing on real-time data imported from Yahoo Finance. (In this regard I know that some Windows users may not access the data , as YF now restrticted his data export. I am working on a Mac-rule type of coding , so I can ensure data import from Yahoo Finance only from Mac users). """ class Portfolio: def __init__(self, tickers): self.tickers = tickers self.prices = self.get_prices() self.options = [] def get_prices(self): """Fetch real-time prices for the tickers.""" data = yf.download(self.tickers, period="1d", interval="1m")['Adj Close'] return data.iloc[-1] # Get the latest prices def add_option(self, option): """Add an option to the portfolio.""" self.options.append(option) class Option: def __init__(self, underlying, option_type, strike_price, expiry, pricing_model): self.underlying = underlying self.option_type = option_type self.strike_price = strike_price self.expiry = expiry self.pricing_model = pricing_model def price(self): """Calculate the price of the option using the selected pricing model.""" if self.option_type == 'call': return self.pricing_model.call_price() elif self.option_type == 'put': return self.pricing_model.put_price() """For now, I set the basis for the option pricing, using three different model I am able to apply their different rules into a set of stocks, delivering a set of call and put prices. The models are created in order to give useful information that could simulate the reality of the market. In the following lines of code I import a portfolio of stocks, with the aim of applying those models to real world data. The following lines work as follows: 1. I set and print the different stock prices imported from Yahoo Finance. 2. For each of the models (Black and Scholes, Local Volatility and Heston Model, I code and insert directly the variables needed to work the model in a proper way. 3. For each stock in the portfolio I deliver a call price and a put price relative to each Model applied. 4. I print results in an understandable way. """ portfolio_tickers = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA", "META", "NVDA", "NFLX", "ADBE", "INTC"] portfolio = Portfolio(portfolio_tickers) # Display the portfolio prices print("Portfolio Prices:") print(portfolio.prices) # Defining horizon of the option and different variables of the model T = 1 # 1 year to maturity r = 0.05 # Risk-free rate sigma = 0.2 # Assumed volatility for ticker, price in portfolio.prices.items(): # Add call and put options for each stock call_option = Option( underlying=ticker, option_type='call', strike_price=price, # At-the-money strike expiry=T, pricing_model=BlackScholes(price, price, T, r, sigma) ) put_option = Option( underlying=ticker, option_type='put', strike_price=price, # At-the-money strike expiry=T, pricing_model=BlackScholes(price, price, T, r, sigma) ) portfolio.add_option(call_option) portfolio.add_option(put_option) ## Define options for the portfolio T = 1 # 1 year to maturity r = 0.05 # Risk-free rate # Local volatility surface example: linear volatility change with stock price local_vol_surface = lambda S, K, T: 0.2 + 0.1 * (S - 100) / 100 # Heston model parameters v0 = 0.04 # Initial variance kappa = 2.0 # Mean reversion speed theta = 0.04 # Long-term variance sigma_vol = 0.3 # Volatility of volatility rho = -0.7 # Correlation print("\nOption Prices:") for ticker, price in portfolio.prices.items(): # Add all options for each stock print(f"\n{ticker}:") for model_name, model_class in [ ("BlackScholes", BlackScholes(price, price, T, r, 0.2)), ("LocalVolatility", LocalVolatility(price, price, T, r, local_vol_surface)), ("HestonModel", HestonModel(price, price, T, r, v0, kappa, theta, sigma_vol, rho)) ]: call_option = Option( underlying=ticker, option_type='call', strike_price=price, # At-the-money strike expiry=T, pricing_model=model_class ) put_option = Option( underlying=ticker, option_type='put', strike_price=price, # At-the-money strike expiry=T, pricing_model=model_class ) print(f" {call_option.option_type.capitalize()} ({model_name}): {call_option.price():.2f}") print(f" {put_option.option_type.capitalize()} ({model_name}): {put_option.price():.2f}")
Leave a Comment