Bases: StochasticProcess1D
Barndorff-Nielson & Shephard (BNS) stochastic
volatility model.
This is a stochastic volatility model where the variance process is given by a
non-Gaussian Ornstein-Uhlenbeck process driven by a pure-jump Lévy process.
The BNS model is defined by the following system of SDEs:
\[\begin{equation}
\begin{aligned}
dx_t &= \sqrt{v_t} dw_t + \rho dz_{\kappa t} \\
dv_t &= -\kappa v_t dt + dz_{\kappa t}
\end{aligned}
\end{equation}\]
The model is flexible and can capture various stylized facts of financial markets,
such as volatility clustering and leverage effects.
This implementation uses a GammaOU process
for the variance, which is a common choice in the BNS model.
Fields:
variance_process
pydantic-field
rho
pydantic-field
Correlation between variance and price processes, in (-1, 1)
create
classmethod
create(vol, kappa, decay, rho)
Convenience constructor for BNS process with parameters
of the variance process
Source code in quantflow/sp/bns.py
| @classmethod
def create(cls, vol: float, kappa: float, decay: float, rho: float) -> Self:
"""Convenience constructor for BNS process with parameters
of the variance process
"""
return cls(
variance_process=GammaOU.create(rate=vol * vol, kappa=kappa, decay=decay),
rho=rho,
)
|
characteristic_exponent
characteristic_exponent(t, u)
Source code in quantflow/sp/bns.py
| def characteristic_exponent(self, t: FloatArrayLike, u: Vector) -> Vector:
return -self._zeta(t, 0.5 * Im * u * u, self.rho * u)
|
sample
sample(n, time_horizon=1, time_steps=100)
Source code in quantflow/sp/bns.py
| def sample(self, n: int, time_horizon: float = 1, time_steps: int = 100) -> Paths:
return self.sample_from_draws(Paths.normal_draws(n, time_horizon, time_steps))
|
sample_from_draws
sample_from_draws(path_dw, *args)
Source code in quantflow/sp/bns.py
| def sample_from_draws(self, path_dw: Paths, *args: Paths) -> Paths:
if args:
path_dz = args[0]
else:
# generate the background driving process samples if not provided
path_dz = self.variance_process.bdlp.sample(
path_dw.samples, path_dw.t, path_dw.time_steps
)
dt = path_dw.dt
# sample the activity rate process
v = self.variance_process.sample_from_draws(path_dz)
# create the time-changed Brownian motion
dw = path_dw.data * np.sqrt(v.data * dt)
paths = np.zeros(dw.shape)
paths[1:] = np.cumsum(dw[:-1], axis=0) + path_dz.data
return Paths(t=path_dw.t, data=paths)
|
characteristic
Characteristic function at time t for a given input parameter u
The characteristic function represents the Fourier transform of the
probability density function
\[\begin{equation}
\phi = {\mathbb E} \left[e^{i u x_t}\right] = e^{-\psi(t, u)}
\end{equation}\]
where \(\psi\) is the characteristic exponent, which can be more easily
computed for many processes.
| PARAMETER |
DESCRIPTION |
t
|
TYPE:
FloatArrayLike
|
u
|
Characteristic function input parameter
TYPE:
Vector
|
Source code in quantflow/sp/base.py
| def characteristic(
self,
t: Annotated[FloatArrayLike, Doc("Time horizon")],
u: Annotated[Vector, Doc("Characteristic function input parameter")],
) -> Vector:
r"""Characteristic function at time `t` for a given input parameter `u`
The characteristic function represents the Fourier transform of the
probability density function
\begin{equation}
\phi = {\mathbb E} \left[e^{i u x_t}\right] = e^{-\psi(t, u)}
\end{equation}
where $\psi$ is the characteristic exponent, which can be more easily
computed for many processes.
"""
return np.exp(-self.characteristic_exponent(t, u))
|
convexity_correction
Convexity correction for the process
Source code in quantflow/sp/base.py
| def convexity_correction(self, t: FloatArrayLike) -> Vector:
"""Convexity correction for the process"""
return -self.characteristic_exponent(t, complex(0, -1)).real
|
analytical_std
Source code in quantflow/sp/base.py
| def analytical_std(self, t: FloatArrayLike) -> FloatArrayLike:
return np.sqrt(self.analytical_variance(t))
|
analytical_mean
Analytical mean of the process at time t
Implement if available
Source code in quantflow/sp/base.py
| def analytical_mean(self, t: FloatArrayLike) -> FloatArrayLike:
"""Analytical mean of the process at time `t`
Implement if available
"""
raise NotImplementedError
|
analytical_variance
Analytical variance of the process at time t
Implement if available
Source code in quantflow/sp/base.py
| def analytical_variance(self, t: FloatArrayLike) -> FloatArrayLike:
"""Analytical variance of the process at time `t`
Implement if available
"""
raise NotImplementedError
|
analytical_pdf
Analytical pdf of the process at time t
Implement if available
Source code in quantflow/sp/base.py
| def analytical_pdf(self, t: FloatArrayLike, x: FloatArrayLike) -> FloatArrayLike:
"""Analytical pdf of the process at time `t`
Implement if available
"""
raise NotImplementedError
|
analytical_cdf
Analytical cdf of the process at time t
Implement if available
Source code in quantflow/sp/base.py
| def analytical_cdf(self, t: FloatArrayLike, x: FloatArrayLike) -> FloatArrayLike:
"""Analytical cdf of the process at time `t`
Implement if available
"""
raise NotImplementedError
|
marginal
Marginal distribution of the process at time t
Source code in quantflow/sp/base.py
| def marginal(self, t: FloatArrayLike) -> StochasticProcess1DMarginal:
"""Marginal distribution of the process at time `t`"""
return StochasticProcess1DMarginal(process=self, t=t)
|
domain_range
Source code in quantflow/sp/base.py
| def domain_range(self) -> Bounds:
return default_bounds()
|
frequency_range
frequency_range(std, max_frequency=None)
Maximum frequency when calculating characteristic functions
Source code in quantflow/sp/base.py
| def frequency_range(self, std: float, max_frequency: float | None = None) -> Bounds:
"""Maximum frequency when calculating characteristic functions"""
if max_frequency is None:
max_frequency = np.sqrt(40 / std / std)
return Bounds(0, max_frequency)
|
support
support(mean, std, points)
Support of the process at time t
Source code in quantflow/sp/base.py
| def support(self, mean: float, std: float, points: int) -> FloatArray:
"""Support of the process at time `t`"""
bounds = self.domain_range()
start = float(sigfig(bound_from_any(bounds.lb, mean - std)))
end = float(sigfig(bound_from_any(bounds.ub, mean + std)))
return np.linspace(start, end, points + 1)
|