r"""Weiner process.
Weiner process :math:`W_t` such that,
#. :math:`W_0 = 0`
#. :math:`\forall t>0, u \ge 0, s<t \Rightarrow W_{t+u}-W_{t} \text{ independent of } W_s`
#. :math:`W_{t+u}-W_{t}\sim {\mathcal {N}}(0,u)`
#. :math:`W_t` is continuous
"""
import numpy as np
[docs]def weiner_simulate_paths(
n_steps: int,
n_sims: int = 1,
stdev: float | np.ndarray = 1,
R: np.ndarray = np.array([[1]]),
rng=None,
):
"""Generate simulated Weiner paths.
`stdev` is the increment size, `R` a correlation matrix, `n_steps`
is how many time steps, `n_sims` the number of simulations and `rng`
a numpy random number generator (optional). If `stdev` is a scalar
it will be broadcasted to the size of `n_sims`.
"""
R = np.atleast_2d(R)
stdev = np.resize(stdev, n_sims).reshape(n_sims, 1)
if rng is None:
rng = np.random.default_rng()
k = R.shape[0]
# simulate 'n_sims' price paths of `k` sized asset groups with 'n_steps' timesteps
dws = rng.multivariate_normal(
mean=np.zeros(k), cov=R, size=(n_steps - 1, n_sims), check_valid="raise"
)
# use cumsum as speed up
ws = np.concatenate([np.zeros((1, n_sims, k)), np.cumsum(dws, axis=0)])
return stdev * ws