Created
February 29, 2024 19:04
-
-
Save ninenine/b02bb254f11d256a161c70274dd9b4a3 to your computer and use it in GitHub Desktop.
Option Pricing with the Black–Scholes Model and Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import asyncio | |
import logging | |
from typing import Literal | |
import yfinance as yf | |
import numpy as np | |
from scipy.stats import norm | |
import coloredlogs | |
from concurrent.futures import ThreadPoolExecutor | |
# Define a custom logging format | |
log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" | |
# Configure coloredlogs for better logging visualization | |
coloredlogs.install( | |
level="INFO", | |
fmt=log_format, | |
datefmt="%Y-%m-%d %H:%M:%S %Z", # Date format including timezone | |
# Customize the log level styles | |
level_styles={ | |
"debug": {"color": "green"}, | |
"info": {"color": "cyan"}, | |
"warning": {"color": "yellow"}, | |
"error": {"color": "red"}, | |
"critical": {"color": "red", "bold": True}, | |
}, | |
# Customize the field styles | |
field_styles={ | |
"asctime": {"color": "green"}, | |
"hostname": {"color": "magenta"}, | |
"levelname": {"color": "black", "bold": True}, | |
"name": {"color": "blue"}, | |
"programname": {"color": "cyan"}, | |
}, | |
) | |
# Create a ThreadPoolExecutor for running blocking operations in separate threads | |
executor = ThreadPoolExecutor(max_workers=10) | |
# Market Data Fetcher class for fetching stock prices | |
class MarketDataFetcher: | |
def __init__(self, ticker: str): | |
self.ticker = ticker | |
# Asynchronous method to get stock price | |
async def get_stock_price(self) -> float: | |
loop = asyncio.get_running_loop() | |
# Run the blocking _fetch_price method in a separate thread | |
return await loop.run_in_executor(executor, self._fetch_price) | |
# Method to fetch stock price using yfinance | |
def _fetch_price(self) -> float: | |
try: | |
stock = yf.Ticker(self.ticker) | |
# Fetch the stock price for the last 5 days | |
todays_data = stock.history(period="5d") | |
return float(todays_data["Close"].iloc[-1]) | |
except Exception as e: | |
logging.error(f"🚨 Error fetching stock price for {self.ticker}: {e}") | |
raise | |
# Black-Scholes Calculator class for calculating option prices | |
class BlackScholesCalculator: | |
def __init__(self, S: float, K: float, T: float, r: float, sigma: float): | |
""" | |
Calculate the Black-Scholes option price. | |
Parameters: | |
- S: Current stock price | |
- K: Strike price | |
- T: Time to expiration in years | |
- r: Risk-free interest rate | |
- sigma: Volatility of the stock | |
""" | |
self.S = S | |
self.K = K | |
self.T = T | |
self.r = r | |
self.sigma = sigma | |
# Method to calculate option price based on Black-Scholes formula | |
def calculate_option_price(self, option_type: Literal["call", "put"]) -> float: | |
try: | |
# Calculate d1 and d2 | |
d1 = ( | |
np.log(self.S / self.K) + (self.r + 0.5 * self.sigma**2) * self.T | |
) / (self.sigma * np.sqrt(self.T)) | |
d2 = d1 - self.sigma * np.sqrt(self.T) | |
if option_type == "call": | |
# Calculate and return the call option price | |
return self.S * norm.cdf(d1) - self.K * np.exp( | |
-self.r * self.T | |
) * norm.cdf(d2) | |
elif option_type == "put": | |
# Calculate and return the put option price | |
return self.K * np.exp(-self.r * self.T) * norm.cdf( | |
-d2 | |
) - self.S * norm.cdf(-d1) | |
else: | |
raise ValueError("🚨 Invalid option type. Use 'call' or 'put'.") | |
except Exception as e: | |
logging.error(f"🚨 Error calculating option price: {e}") | |
raise | |
# Main function to fetch stock price and calculate option prices | |
async def main(ticker: str, K: float, T: float, r: float, sigma: float) -> None: | |
try: | |
logging.info(f"Fetching stock price for {ticker}...") | |
fetcher = MarketDataFetcher(ticker) | |
S = await fetcher.get_stock_price() | |
logging.info(f"Calculating option prices for {ticker}...") | |
calculator = BlackScholesCalculator(S, K, T, r, sigma) | |
call_price = calculator.calculate_option_price("call") | |
put_price = calculator.calculate_option_price("put") | |
# Log the results | |
logging.info(f"Stock Price for {ticker}: {S:.2f}") | |
logging.info(f"Call Option Price: {call_price:.2f}") | |
logging.info(f"Put Option Price: {put_price:.2f}") | |
logging.info("=" * 50) | |
except Exception as e: | |
logging.error(f"🚨 Error in main function: {e}") | |
raise | |
# Function to run the main function for all tickers | |
async def run_all(tickers): | |
tasks = [main(ticker, K, T, r, sigma) for ticker, K, T, r, sigma in tickers] | |
await asyncio.gather(*tasks) | |
if __name__ == "__main__": | |
# List of tickers with their respective parameters | |
tickers = [ | |
("AAPL", 150, 1, 0.05, 0.2), # Apple Inc. - Technology | |
("MSFT", 300, 1, 0.05, 0.2), # Microsoft Corp. - Technology | |
("AMZN", 3500, 1, 0.05, 0.3), # Amazon.com Inc. - Consumer Retail | |
("GOOGL", 2500, 1, 0.05, 0.25), # Alphabet Inc. (Google) - Technology | |
("META", 350, 1, 0.05, 0.25), # Meta Platforms Inc. (Facebook) - Technology | |
("TSLA", 700, 0.5, 0.03, 0.3), # Tesla Inc. - Automotive & Energy | |
("JNJ", 165, 1, 0.05, 0.2), # Johnson & Johnson - Healthcare | |
("JPM", 125, 1, 0.05, 0.2), # JPMorgan Chase & Co. - Finance | |
("V", 220, 1, 0.05, 0.2), # Visa Inc. - Financial Services | |
("PG", 140, 1, 0.05, 0.2), # Procter & Gamble Co. - Consumer Goods | |
("UNH", 500, 1, 0.05, 0.2), # UnitedHealth Group Inc. - Healthcare | |
("MA", 360, 1, 0.05, 0.2), # Mastercard Inc. - Financial Services | |
("NVDA", 200, 1, 0.05, 0.25), # NVIDIA Corp. - Semiconductors | |
("HD", 330, 1, 0.05, 0.2), # Home Depot Inc. - Retail | |
("BABA", 220, 1, 0.05, 0.3), # Alibaba Group Holding Ltd - Technology & Retail | |
("XOM", 90, 1, 0.05, 0.2), # Exxon Mobil Corp. - Energy | |
("WMT", 140, 1, 0.05, 0.2), # Walmart Inc. - Retail | |
("BP", 36, 1, 0.05, 0.25), # BP plc - Energy | |
("T", 28, 1, 0.05, 0.2), # AT&T Inc. - Telecommunications | |
("VZ", 58, 1, 0.05, 0.2), # Verizon Communications Inc. - Telecommunications | |
("PFE", 42, 1, 0.05, 0.2), # Pfizer Inc. - Healthcare | |
("NFLX", 600, 1, 0.05, 0.3), # Netflix Inc. - Entertainment | |
("DIS", 130, 1, 0.05, 0.25), # Walt Disney Co. - Entertainment | |
] | |
# Run the main function for all tickers | |
asyncio.run(run_all(tickers)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment