Source code for torchquantlib.models.stochastic_volatility.sabr

import torch
from models.stochastic_model import StochasticModel

[docs] class SABR(StochasticModel): """ SABR (Stochastic Alpha, Beta, Rho) model. This model describes the evolution of a forward rate F and its volatility α using two coupled stochastic differential equations: dF = α * F^β * dW_1 dα = ν * α * dW_2 where: F is the forward rate α is the stochastic volatility β is the elasticity parameter (0 ≤ β ≤ 1) ν is the volatility of volatility ρ is the correlation between W_1 and W_2 (the two Wiener processes) """
[docs] def __init__(self, alpha_init=0.2, beta_init=0.5, rho_init=0.0, nu_init=0.3, F0=100.0): """ Initialize the SABR model. Args: alpha_init (float): Initial value for volatility. beta_init (float): Initial value for elasticity parameter (0 ≤ β ≤ 1). rho_init (float): Initial value for correlation between F and α processes. nu_init (float): Initial value for volatility of volatility. F0 (float): Initial forward rate. """ params = { 'alpha': torch.tensor(alpha_init, requires_grad=True), 'beta': torch.tensor(beta_init, requires_grad=True), 'rho': torch.tensor(rho_init, requires_grad=True), 'nu': torch.tensor(nu_init, requires_grad=True), } super().__init__(params) self.F0 = torch.tensor(F0, device=self.device)
[docs] def simulate(self, S0, T, N, steps=100): """ Simulate forward rate paths using the SABR model. Args: S0 (float): Initial asset price (not used, included for consistency with other models). T (float): Time horizon for simulation. N (int): Number of simulation paths. steps (int): Number of time steps in each path. Returns: torch.Tensor: Simulated forward rates at time T. """ dt = T / steps alpha = self.params['alpha'] beta = torch.clamp(self.params['beta'], min=0.0, max=1.0) rho = torch.clamp(self.params['rho'], min=-0.999, max=0.999) nu = self.params['nu'] dt = torch.tensor(dt, device=self.device) N = int(N) steps = int(steps) # Initialize forward rate and volatility paths F = torch.zeros(N, steps, device=self.device) alpha_t = torch.zeros(N, steps, device=self.device) F[:, 0] = self.F0 alpha_t[:, 0] = alpha for t in range(1, steps): # Generate correlated random numbers Z1 = torch.randn(N, device=self.device) Z2 = torch.randn(N, device=self.device) W_F = Z1 * torch.sqrt(dt) W_alpha = (rho * Z1 + torch.sqrt(1 - rho ** 2) * Z2) * torch.sqrt(dt) F_t_minus = F[:, t - 1] alpha_t_minus = alpha_t[:, t - 1] # Update forward rate dF = alpha_t_minus * (F_t_minus ** beta) * W_F F[:, t] = F_t_minus + dF # Update volatility dalpha = nu * alpha_t_minus * W_alpha alpha_t[:, t] = alpha_t_minus + dalpha return F[:, -1]
[docs] def option_price(self, K, T, r, option_type='call', N=10000, steps=100): """ Price a European option using Monte Carlo simulation under the SABR model. Args: K (float): Strike price. T (float): Time to maturity. r (float): Risk-free interest rate. option_type (str): 'call' or 'put'. N (int): Number of simulation paths. steps (int): Number of time steps in simulation. Returns: float: Option price. """ F_T = self.simulate(S0=self.F0, T=T, N=N, steps=steps) if option_type.lower() == 'call': payoff = torch.relu(F_T - K) elif option_type.lower() == 'put': payoff = torch.relu(K - F_T) else: raise ValueError("option_type must be 'call' or 'put'") price = torch.exp(-r * T) * torch.mean(payoff) return price.item()
[docs] def _apply_constraints(self): """ Apply constraints to model parameters to ensure they remain in valid ranges. """ self.params['alpha'].data.clamp_(min=1e-6) self.params['beta'].data.clamp_(min=0.0, max=1.0) self.params['nu'].data.clamp_(min=1e-6) self.params['rho'].data.clamp_(min=-0.999, max=0.999)