assert_causal

Defined in fynance.core

assert_causal(func, *, T=256, t0=None, n_probes=3, seed=0, atol=0.0)[source]

Assert func never uses future information (no lookahead bias).

Executable form of the repository’s “no lookahead” house rule: generates a seeded synthetic price level series x, computes the baseline y0 = func(x), then at one or more probe indices t0 perturbs x strictly from t0 onward – once with additive noise, once with a 1.5x rescale – and requires func of the perturbed input to agree with y0 everywhere before t0. NaN is treated as equal to NaN (so a warmup region that is legitimately NaN does not trip the check).

Parameters:
funccallable

Maps a 1-D array of length T to a 1-D or 2-D array aligned with it on axis 0 (e.g. a rolling feature). Extra keyword arguments should be bound with a lambda or functools.partial before passing func in.

Tint, optional

Length of the synthetic input series. Default is 256.

t0int, optional

Single probe index to test. If None (default), n_probes probe points spread over [T // 4, 3 * T // 4] are used instead.

n_probesint, optional

Number of default probe points when t0 is None. Default is 3 (T // 4, T // 2, 3 * T // 4).

seedint, optional

Seed of the synthetic input and perturbation noise. Default is 0.

atolfloat, optional

Absolute tolerance for the pre-t0 comparison. Default is 0.0 (exact, NaN-safe equality).

Returns:
None

Nothing on success.

Raises:
ValueError

If T is too small to host a probe, or t0 does not lie strictly inside (0, T - 1).

AssertionError

If perturbing the input from some probe t0 onward changes the output before t0. The message names func, the probe t0, the perturbation used, and the earliest leaking index.

Examples

A trailing (causal) rolling mean passes:

>>> import numpy as np
>>> from fynance.core.checks import assert_causal
>>> def trailing_mean(x, w=5):
...     out = np.empty_like(x)
...     for t in range(len(x)):
...         lo = max(0, t - w + 1)
...         out[t] = x[lo:t + 1].mean()
...     return out
>>> assert_causal(trailing_mean, T=64, seed=0)

A centered rolling mean (mode='same' convolution) leaks future values into the past and raises, naming the earliest leaking index:

>>> def centered_mean(x, w=9):
...     return np.convolve(x, np.ones(w) / w, mode='same')
>>> try:
...     assert_causal(centered_mean, T=64, seed=0)
... except AssertionError as e:
...     print(str(e)[:40])
assert_causal: lookahead detected in cen