block_permutation_test

Defined in fynance.research

block_permutation_test(strategy_returns, *, n_perm=1000, block=21, method='stationary', seed=0)[source]

Dependence-preserving analogue of permutation_test.

guards.permutation_test reruns a Strategy on i.i.d.-shuffled price paths – an appropriate null when the question is “does this strategy exploit any real structure”, but it also destroys genuine autocorrelation, which would bias the null toward rejecting a real, dependence-driven edge as noise.

block_permutation_test instead takes an already-computed strategy return series (no strategy rerun) and tests whether its mean is significantly positive against a null that preserves autocorrelation: the observed series is demeaned, then block-bootstrap resampled via resample_paths (so within-block dependence survives), giving the distribution of the mean statistic expected under no drift. The p-value uses the same smoothed convention as permutation_test: (#{null >= observed} + 1) / (n_perm + 1).

Parameters:
strategy_returnsarray-like

Realized return series of the strategy under test, shape (T,).

n_permint

Number of block-bootstrap replicates forming the null distribution.

blockint

Block length ('circular') or mean block length ('stationary'), forwarded to resample_paths.

method{‘circular’, ‘stationary’}

Resampling scheme, forwarded to resample_paths.

seedint

Master seed, forwarded to resample_paths (reproducible for a fixed seed).

Returns:
float

Smoothed p-value for the one-sided test “the observed mean return is larger than chance would produce, given the series’ own autocorrelation structure”. Low values indicate a genuine (not merely autocorrelation-driven) positive edge.

Examples

>>> import numpy as np
>>> from fynance.research import block_permutation_test
>>> rng = np.random.default_rng(0)
>>> noise = rng.standard_normal(300) * 0.01
>>> p = block_permutation_test(noise, n_perm=200, block=10, seed=1)
>>> 0.0 < p <= 1.0
True