Source code for aleatory.processes.euler_maruyama.cev_process

"""
Constant Elasticity Variance Process
"""
from aleatory.processes.base import SPEulerMaruyama
import numpy as np
from aleatory.utils.utils import draw_paths


[docs]class CEVProcess(SPEulerMaruyama): r""" CEV or constant elasticity of variance process .. image:: _static/cev_process_drawn.png A CEV process :math:`X = \{X : t \geq 0\}` is characterised by the following Stochastic Differential Equation .. math:: dX_t = \mu X_t dt + \sigma X_t^{\gamma} dW_t, \ \ \ \ \forall t\in (0,T], with initial condition :math:`X_0 = x_0`, where - :math:`\mu` is the drift - :math:`\sigma>0` is the scale of the volatility - :math:`\gamma\geq 0` is the elasticity term - :math:`W_t` is a standard Brownian Motion. :param float mu: the parameter :math:`\mu` in the above SDE :param float sigma: the parameter :math:`\sigma>0` in the above SDE :param float gamma: the parameter :math:`\gamma` in the above SDE :param float initial: the initial condition :math:`x_0` in the above SDE :param float T: the right hand endpoint of the time interval :math:`[0,T]` for the process :param numpy.random.Generator rng: a custom random number generator """ def __init__(self, gamma=1.5, mu=0.5, sigma=0.1, initial=1.0, T=1.0, rng=None): super().__init__(T=T, rng=rng) self.gamma = gamma self.mu = mu self.sigma = sigma self.initial = initial self.n = 1.0 self.dt = 1.0 * self.T / self.n self.times = np.arange(0.0, self.T + self.dt, self.dt) self.name = "CEV Process" self.paths = None self._marginals = None def f(x, _): return self.mu - 0.5 * (self.sigma ** 2) * np.exp(2.0 * (self.gamma - 1.0) * x) # return self.mu * x def g(x, _): return self.sigma * np.exp((self.gamma - 1.0) * x) # return self.sigma * (x ** self.gamma) self.f = f self.g = g @property def sigma(self): return self._sigma @sigma.setter def sigma(self, value): if value < 0: raise ValueError("sigma cannot be negative") self._sigma = value @property def gamma(self): return self._gamma @gamma.setter def gamma(self, value): if value < 0: raise ValueError("gamma cannot be negative") self._gamma = value def __str__(self): return "CEV process with parameters {gamma}, {drift}, and {volatility} on [0, {T}].".format( T=str(self.T), gamma=str(self.gamma), drift=str(self.mu), volatility=str(self.sigma)) def _get_empirical_marginal_samples(self): if self.paths is None: self.simulate(self.n, self.N) empirical_marginal_samples = np.array(self.paths).transpose() return empirical_marginal_samples def get_marginal(self, t): pass def estimate_expectations(self): if self._marginals is None: self._marginals = self._get_empirical_marginal_samples() empirical_means = [np.mean(m) for m in self._marginals] return empirical_means def estimate_variances(self): if self._marginals is None: self._marginals = self._get_empirical_marginal_samples() empirical_vars = [np.var(m) for m in self._marginals] return empirical_vars def estimate_stds(self): variances = self.estimate_variances() stds = [np.sqrt(var) for var in variances] return stds def estimate_quantiles(self, q): if self._marginals is None: self._marginals = self._get_empirical_marginal_samples() empirical_quantiles = [np.quantile(m, q) for m in self._marginals] return empirical_quantiles def _process_expectation(self): return self.estimate_expectations() def process_expectation(self): return self._process_expectation() def _process_variance(self): return self.estimate_variances() def process_variance(self): return self._process_variance() def _process_stds(self): stds = np.sqrt(self.process_variance()) return stds def process_stds(self): stds = self._process_stds() return stds
[docs] def draw(self, n, N, marginal=True, envelope=False, title=None, **fig_kw): self.simulate(n, N) expectations = self.estimate_expectations() if envelope: lower = self.estimate_quantiles(0.005) upper = self.estimate_quantiles(0.995) else: lower = None upper = None chart_title = title if title else self.name if marginal: fig = draw_paths(times=self.times, paths=self.paths, N=N, title=chart_title, KDE=True, marginal=marginal, expectations=expectations, envelope=envelope, lower=lower, upper=upper, **fig_kw) else: fig = draw_paths(times=self.times, paths=self.paths, N=N, title=chart_title, expectations=expectations, marginal=marginal, **fig_kw) return fig
def sample(self, n): return self._sample_em_process(n, log=True)