Source code for torchquantlib.core.asset_pricing.option.exotics

import torch
from torch import Tensor
import math
from scipy.stats import norm

[docs] def barrier_option(option_type: str, barrier_type: str, spot: Tensor, strike: Tensor, barrier: Tensor, expiry: Tensor, volatility: Tensor, rate: Tensor, steps: int) -> Tensor: """ Price a barrier option using a binomial tree model. Args: option_type (str): Type of option - either 'call' or 'put'. barrier_type (str): Type of barrier - 'up-and-out', 'down-and-out', 'up-and-in', or 'down-and-in'. spot (Tensor): Current price of the underlying asset. strike (Tensor): Strike price of the option. barrier (Tensor): Barrier price. expiry (Tensor): Time to expiration in years. volatility (Tensor): Volatility of the underlying asset. rate (Tensor): Risk-free interest rate (annualized). steps (int): Number of time steps in the binomial tree. Returns: Tensor: The price of the barrier option. """ # Calculate parameters for the binomial model dt = expiry / steps u = torch.exp(volatility * torch.sqrt(dt)) d = 1 / u p = (torch.exp(rate * dt) - d) / (u - d) # Initialize the price tree price_tree = torch.zeros((steps + 1, steps + 1)) for i in range(steps + 1): for j in range(i + 1): price_tree[j, i] = spot * (u ** (i - j)) * (d ** j) # Initialize the option value at expiration if option_type == 'call': value_tree = torch.maximum(price_tree[:, steps] - strike, torch.tensor(0.0)) elif option_type == 'put': value_tree = torch.maximum(strike - price_tree[:, steps], torch.tensor(0.0)) # Backward induction through the tree for i in range(steps - 1, -1, -1): for j in range(i + 1): value_tree[j] = (p * value_tree[j] + (1 - p) * value_tree[j + 1]) * torch.exp(-rate * dt) # Apply barrier conditions if barrier_type == 'up-and-out' and price_tree[j, i] >= barrier: value_tree[j] = torch.tensor(0.0) elif barrier_type == 'down-and-out' and price_tree[j, i] <= barrier: value_tree[j] = torch.tensor(0.0) elif barrier_type == 'up-and-in' and price_tree[j, i] < barrier: value_tree[j] = torch.tensor(0.0) elif barrier_type == 'down-and-in' and price_tree[j, i] > barrier: value_tree[j] = torch.tensor(0.0) return value_tree[0]
[docs] def chooser_option(spot, strike, expiry, volatility, rate, dividend): """ Calculate the price of a chooser option. A chooser option gives the holder the right to choose whether the option is a call or put at some point before expiration. Args: spot (torch.Tensor): The spot price of the underlying asset strike (torch.Tensor): The strike price of the option expiry (torch.Tensor): The time to expiry in years volatility (torch.Tensor): The volatility of the underlying asset rate (torch.Tensor): The risk-free interest rate dividend (torch.Tensor): The dividend yield Returns: torch.Tensor: The price of the chooser option """ # Convert tensor inputs to Python floats for compatibility with math functions spot, strike, expiry, volatility, rate, dividend = [x.item() for x in (spot, strike, expiry, volatility, rate, dividend)] # Calculate d1 and d2 parameters d1 = (math.log(spot / strike) + (rate - dividend + 0.5 * volatility ** 2) * expiry) / (volatility * math.sqrt(expiry)) d2 = d1 - volatility * math.sqrt(expiry) # Calculate call and put prices using Black-Scholes formula call_price = spot * math.exp(-dividend * expiry) * norm.cdf(d1) - strike * math.exp(-rate * expiry) * norm.cdf(d2) put_price = strike * math.exp(-rate * expiry) * norm.cdf(-d2) - spot * math.exp(-dividend * expiry) * norm.cdf(-d1) # The chooser option price is the sum of call and put prices return torch.tensor(call_price + put_price)
[docs] def compound_option(spot, strike1, strike2, expiry1, expiry2, volatility, rate, dividend): """ Calculate the price of a compound option (an option on an option). Args: spot (torch.Tensor): The spot price of the underlying asset strike1 (torch.Tensor): The strike price of the underlying option strike2 (torch.Tensor): The strike price of the compound option expiry1 (torch.Tensor): The time to expiry of the underlying option in years expiry2 (torch.Tensor): The time to expiry of the compound option in years volatility (torch.Tensor): The volatility of the underlying asset rate (torch.Tensor): The risk-free interest rate dividend (torch.Tensor): The dividend yield Returns: torch.Tensor: The price of the compound option """ # Convert tensor inputs to Python floats spot, strike1, strike2, expiry1, expiry2, volatility, rate, dividend = [x.item() for x in (spot, strike1, strike2, expiry1, expiry2, volatility, rate, dividend)] # Calculate d parameters d1 = (math.log(spot / strike1) + (rate - dividend + 0.5 * volatility ** 2) * expiry1) / (volatility * math.sqrt(expiry1)) d2 = d1 - volatility * math.sqrt(expiry1) d3 = (math.log(spot / strike2) + (rate - dividend + 0.5 * volatility ** 2) * expiry2) / (volatility * math.sqrt(expiry2)) d4 = d3 - volatility * math.sqrt(expiry2) # Calculate the price of the compound option price = math.exp(-rate * expiry2) * (spot * math.exp((rate - dividend) * expiry2) * norm.cdf(d3) - strike1 * norm.cdf(d4)) return torch.tensor(price)
[docs] def shout_option(spot, strike, expiry, volatility, rate, dividend): """ Calculate the price of a shout option. A shout option allows the holder to "shout" once during the life of the option to lock in a minimum payoff. Args: spot (torch.Tensor): The spot price of the underlying asset strike (torch.Tensor): The strike price of the option expiry (torch.Tensor): The time to expiry in years volatility (torch.Tensor): The volatility of the underlying asset rate (torch.Tensor): The risk-free interest rate dividend (torch.Tensor): The dividend yield Returns: torch.Tensor: The price of the shout option """ # Convert tensor inputs to Python floats spot, strike, expiry, volatility, rate, dividend = [x.item() for x in (spot, strike, expiry, volatility, rate, dividend)] # Calculate d1 and d2 parameters d1 = (math.log(spot / strike) + (rate - dividend + 0.5 * volatility ** 2) * expiry) / (volatility * math.sqrt(expiry)) d2 = d1 - volatility * math.sqrt(expiry) # Calculate the price of a standard call option call_price = spot * math.exp(-dividend * expiry) * norm.cdf(d1) - strike * math.exp(-rate * expiry) * norm.cdf(d2) # Calculate the additional value from the shout feature shout_value = spot * math.exp(-dividend * expiry) * (1 - norm.cdf(d1)) # The shout option price is the sum of the call price and the shout value return torch.tensor(call_price + shout_value)
[docs] def lookback_option(option_type: str, spot: Tensor, strike: Tensor, expiry: Tensor, volatility: Tensor, rate: Tensor, steps: int) -> Tensor: """ Price a lookback option using a binomial tree model. A lookback option's payoff depends on the optimal (maximum for call, minimum for put) price of the underlying asset during the option's life. Args: option_type (str): Type of option - either 'call' or 'put'. spot (Tensor): Current price of the underlying asset. strike (Tensor): Strike price of the option. expiry (Tensor): Time to expiration in years. volatility (Tensor): Volatility of the underlying asset. rate (Tensor): Risk-free interest rate (annualized). steps (int): Number of time steps in the binomial tree. Returns: Tensor: The price of the lookback option. """ # Calculate parameters for the binomial model dt = expiry / steps u = torch.exp(volatility * torch.sqrt(dt)) d = 1 / u p = (torch.exp(rate * dt) - d) / (u - d) # Initialize the price tree price_tree = torch.zeros((steps + 1, steps + 1)) for i in range(steps + 1): for j in range(i + 1): price_tree[j, i] = spot * (u ** (i - j)) * (d ** j) # Initialize and calculate the minimum/maximum price tree min_max_price_tree = price_tree.clone() for i in range(steps - 1, -1, -1): for j in range(i + 1): min_max_price_tree[j, i] = torch.minimum(min_max_price_tree[j, i], torch.minimum(min_max_price_tree[j, i + 1], min_max_price_tree[j + 1, i + 1])) # Initialize the option value at expiration if option_type == 'call': value_tree = torch.maximum(min_max_price_tree[:, steps] - strike, torch.tensor(0.0)) elif option_type == 'put': value_tree = torch.maximum(strike - min_max_price_tree[:, steps], torch.tensor(0.0)) # Backward induction through the tree for i in range(steps - 1, -1, -1): for j in range(i + 1): value_tree[j] = (p * value_tree[j] + (1 - p) * value_tree[j + 1]) * torch.exp(-rate * dt) return value_tree[0]