resample_paths

Defined in fynance.research

resample_paths(returns, *, n_paths=1000, block=21, method='stationary', seed=0)[source]

Block-bootstrap resampled paths of a (possibly autocorrelated) series.

Unlike an i.i.d. shuffle, resampling whole blocks preserves the short-range dependence within a block (e.g. volatility clustering, slow trends), which makes the resampled paths a more honest stand-in for “another draw from the same data-generating process” than a fully shuffled series.

Two block schemes are supported:

  • 'circular': fixed-length blocks of block observations, each starting at a uniformly-drawn index and wrapping circularly around the series (Politis & Romano, 1992). ceil(T / block) blocks are drawn and concatenated, then truncated to length T.

  • 'stationary': blocks of random, geometrically-distributed length with mean block (Politis & Romano, 1994): at each step, with probability 1 / block a new block starts at a fresh uniformly-drawn index, otherwise the current block continues (wrapping circularly). This scheme yields a strictly stationary resampled process, unlike the fixed-length circular scheme (which has a seam at every block boundary).

All randomness (block starts, and for 'stationary' the per-step continue/restart draws) is generated once via numpy.random.default_rng(seed) before entering the Numba kernel, so the result is fully reproducible for a fixed seed.

Parameters:
returnsarray-like

Return series to resample, shape (T,).

n_pathsint

Number of resampled paths to draw.

blockint

Block length ('circular') or mean block length ('stationary').

method{‘circular’, ‘stationary’}

Resampling scheme, see above.

seedint

Seed for reproducibility.

Returns:
numpy.ndarray, shape (n_paths, T)

Resampled paths (each row is one bootstrap replicate of returns).

Raises:
ValueError

If returns has fewer than 2 observations, n_paths or block is not a positive integer, or method is not recognized.

Examples

>>> import numpy as np
>>> from fynance.research import resample_paths
>>> r = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
>>> paths = resample_paths(r, n_paths=3, block=2, method='circular', seed=0)
>>> paths.shape
(3, 5)
>>> a = resample_paths(r, n_paths=2, seed=1)
>>> b = resample_paths(r, n_paths=2, seed=1)
>>> bool(np.array_equal(a, b))
True