Source code for pfhedge.instruments.primary.vasicek

from math import ceil
from typing import Optional
from typing import Tuple

import torch

from pfhedge._utils.doc import _set_attr_and_docstring
from pfhedge._utils.doc import _set_docstring
from pfhedge._utils.str import _format_float
from pfhedge._utils.typing import TensorOrScalar
from pfhedge.stochastic import generate_vasicek

from .base import BasePrimary


[docs]class VasicekRate(BasePrimary): r"""A rate which follow the Vasicek model. .. seealso:: - :func:`pfhedge.stochastic.generate_vasicek`: The stochastic process. Args: kappa (float, default=1.0): The parameter :math:`\kappa`. theta (float, default=0.04): The parameter :math:`\theta`. sigma (float, default=0.04): The parameter :math:`\sigma`. cost (float, default=0.0): The transaction cost rate. dt (float, default=1/250): The intervals of the time steps. dtype (torch.device, optional): Desired device of returned tensor. Default: If None, uses a global default (see :func:`torch.set_default_tensor_type()`). device (torch.device, optional): Desired device of returned tensor. Default: if None, uses the current device for the default tensor type (see :func:`torch.set_default_tensor_type()`). ``device`` will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types. Buffers: - spot (:class:`torch.Tensor`): The spot rate of the instrument. This attribute is set by a method :meth:`simulate()`. The shape is :math:`(N, T)` where :math:`N` is the number of simulated paths and :math:`T` is the number of time steps. Examples: >>> from pfhedge.instruments import VasicekRate ... >>> _ = torch.manual_seed(42) >>> rate = VasicekRate() >>> rate.simulate(n_paths=2, time_horizon=5/250) >>> rate.spot tensor([[0.0400, 0.0409, 0.0412, 0.0418, 0.0423, 0.0395], [0.0400, 0.0456, 0.0439, 0.0451, 0.0457, 0.0471]]) """ def __init__( self, kappa: float = 1.0, theta: float = 0.04, sigma: float = 0.04, cost: float = 0.0, dt: float = 1 / 250, dtype: Optional[torch.dtype] = None, device: Optional[torch.device] = None, ) -> None: super().__init__() self.kappa = kappa self.theta = theta self.sigma = sigma self.cost = cost self.dt = dt self.to(dtype=dtype, device=device) @property def default_init_state(self) -> Tuple[float, ...]: return (self.theta,) def simulate( self, n_paths: int = 1, time_horizon: float = 20 / 250, init_state: Optional[Tuple[TensorOrScalar, ...]] = None, ) -> None: if init_state is None: init_state = self.default_init_state spot = generate_vasicek( n_paths=n_paths, n_steps=ceil(time_horizon / self.dt + 1), init_state=init_state, kappa=self.kappa, theta=self.theta, sigma=self.sigma, dt=self.dt, dtype=self.dtype, device=self.device, ) self.register_buffer("spot", spot) def extra_repr(self) -> str: params = [ "kappa=" + _format_float(self.kappa), "theta=" + _format_float(self.theta), "sigma=" + _format_float(self.sigma), ] if self.cost != 0.0: params.append("cost=" + _format_float(self.cost)) params.append("dt=" + _format_float(self.dt)) return ", ".join(params)
# Assign docstrings so they appear in Sphinx documentation _set_docstring(VasicekRate, "default_init_state", BasePrimary.default_init_state) _set_attr_and_docstring(VasicekRate, "to", BasePrimary.to)