walkforward
Volatility forecasts, position sizing and purged walk-forward splits that never see the future. A C11 library with a numpy front door.
pip install walkforward
Most backtests that look good are quietly peeking one bar ahead. The estimator that sizes a position knows the return it is about to earn, or a training label was computed from prices inside the test window. This library is built so those two things cannot happen by accident, and it is checked on every push against pandas, numpy, scikit-learn, the arch package and exact rational arithmetic.
- One timing convention. Every forecast at index
tis built from data beforet.ewma_voland the GARCH filters are predictive; the rolling statistics and range estimators are contemporaneous and say so in their own docstrings, withlagto fix them. - Purging that asks the right question.
PurgedWalkForwardtakes a label horizon, not a purge count, becausepurge = h - 1is the part people get wrong. Drops straight intocross_val_score. - GARCH(1,1) by maximum likelihood, checked against
archon identical samples and identical likelihoods, and invariant to the units of the returns. - Sizing that fails closed. A bad price gives a zero position, not a NaN one. A leverage cap that could overflow is refused rather than silently ignored.
- Ridge by Householder QR, so a feature at a price level near 1e9 keeps its slope precision.
Research tooling, not investment advice. It is a set of building blocks, not a backtester: it knows nothing about costs, borrow, calendars or corporate actions.
The loop
import numpy as np
import walkforward as wf
# The fit assumes mean-zero returns. Demean with the TRAINING mean; the
# full-sample mean would put the future into the fit.
train = returns[:1000] - returns[:1000].mean()
model = wf.garch_fit(train)
# sigma[t] forecasts period t from returns before t. filter_from continues
# from the variance state the fit ended on, which is what makes the
# out-of-sample path the same as filtering everything together.
sigma = model.filter_from(returns[1000:])
# A position held over period t is entered at the close of t-1, so it is
# sized against the previous close, and earns position * price * return.
entry = close[999:-1]
position = wf.vol_target_position(
sigma, target_vol=0.01, equity=100_000.0, price=entry, max_leverage=2.0
)
pnl = position * entry * returns[1000:]
model.persistence, model.half_life and model.unconditional_vol are there so you do not have to recompute them.
Cross-validation
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import cross_val_score
import walkforward as wf
# Target is a 21-period forward return, so labels span 21 periods and the
# last 20 training rows before each test window are dropped.
cv = wf.PurgedWalkForward(train_size=756, test_size=252, label_horizon=21)
scores = cross_val_score(GradientBoostingRegressor(), X, y, cv=cv)
Without the purge, the twenty training rows before each test window carry labels that were partly computed from test-window prices. The library counts that leak in its own test suite: at a horizon of 21, a purge of 20 leaves zero leaking training rows and a purge of 19 leaves ten.
There is no embargo argument, on purpose. An embargo protects training data that sits after a test window, and a walk-forward never trains on anything after the window it is testing. walk_forward_splits(..., include_post_train=True) exposes that variant with the warning it deserves.
Conventions
Volatility is per period. Annualised converts as annual / sqrt(periods_per_year), so a 10% annual target on daily data is 0.10 / sqrt(252), about 0.0063.
rolling_std uses the population convention and divides by window. Pandas rolling().std() defaults to the sample convention, so the two differ by sqrt(window / (window - 1)).
A pandas Series in gives a pandas Series out, on the same index. Realigning a bare array by hand is one of the ways lookahead gets in.
Bad arguments raise. Bad elements are handled per function and documented on each: a non-finite return is skipped by the recursions, makes a rolling window NaN, and gives a zero position in sizing. DomainError (a ValueError) means the arguments were fine but the computation has no answer: a singular design, a sample with no variance, an overflowing recursion.
What it is checked against
The C library underneath is compared on every push to independent implementations, and the numbers are reproducible from tests/reference/ in the repository.
| Check | Reference | Result |
|---|---|---|
| Rolling mean and std, with gaps | pandas | 3.1e-11 |
| Rolling std at a price level of 1e9 | exact rational arithmetic | 1.7e-15, where a two-pass computation is off by 4.9e-12 |
| EWMA, predictive alignment | pandas ewm shifted one period |
3.5e-18 |
| GARCH filter and forecast | arch |
4.4e-16 relative |
| GARCH fit over 20 samples | arch, same likelihood |
5.0e-7 max parameter difference |
| Ridge, condition number 1e4 to 1e10 | SVD least squares | within 2x condition times epsilon |
| Ridge with a feature at level 1e6 to 1e15 | exact rational OLS | under 1e-14 |
| Walk-forward splits, 1620 parameter sets | independent generator | 0 mismatches |
| No lookahead, 40 trials | bitwise prefix comparison | 0 violations |
The C also runs under AddressSanitizer and UndefinedBehaviorSanitizer, on 32-bit and 64-bit, and gives bit-identical answers under GCC and Clang.
License
MIT. Source and the C library: https://github.com/haeganm/walkforward
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file walkforward-3.4.1.tar.gz.
File metadata
- Download URL: walkforward-3.4.1.tar.gz
- Upload date:
- Size: 77.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac2b2ee3f87478415eea2e495cc3505736501e05a3477ad39c2aeb873aeb532d
|
|
| MD5 |
25c5defdbfd3248d76c8f24215db7745
|
|
| BLAKE2b-256 |
30dae5cd3c14ccc92779487fa4dab2b3b67d8e1f6dc5b5d8206c31822ba7a4b9
|
Provenance
The following attestation bundles were made for walkforward-3.4.1.tar.gz:
Publisher:
release.yml on haeganm/walkforward
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
walkforward-3.4.1.tar.gz -
Subject digest:
ac2b2ee3f87478415eea2e495cc3505736501e05a3477ad39c2aeb873aeb532d - Sigstore transparency entry: 2729542449
- Sigstore integration time:
-
Permalink:
haeganm/walkforward@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Branch / Tag:
refs/tags/v3.4.1 - Owner: https://github.com/haeganm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Trigger Event:
push
-
Statement type:
File details
Details for the file walkforward-3.4.1-py3-none-win_amd64.whl.
File metadata
- Download URL: walkforward-3.4.1-py3-none-win_amd64.whl
- Upload date:
- Size: 38.6 kB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d4a9a7d6c385499320558b7e83a523bfc8815ef0f2a5a221a6f31f12ff87571
|
|
| MD5 |
9128a8d8419b9284c401824e09723f95
|
|
| BLAKE2b-256 |
303818460b0d2e94acda952cb75e7cc7428af2a7f588fbd5c26b8cb90b94fdb8
|
Provenance
The following attestation bundles were made for walkforward-3.4.1-py3-none-win_amd64.whl:
Publisher:
release.yml on haeganm/walkforward
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
walkforward-3.4.1-py3-none-win_amd64.whl -
Subject digest:
0d4a9a7d6c385499320558b7e83a523bfc8815ef0f2a5a221a6f31f12ff87571 - Sigstore transparency entry: 2729542504
- Sigstore integration time:
-
Permalink:
haeganm/walkforward@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Branch / Tag:
refs/tags/v3.4.1 - Owner: https://github.com/haeganm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Trigger Event:
push
-
Statement type:
File details
Details for the file walkforward-3.4.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: walkforward-3.4.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 35.5 kB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
552d2d6437ef2f0964f85077dba52fb3c80a20bd4ed7aa2c4a2208966c9935f6
|
|
| MD5 |
3695bc5cb5e73663eef73a50659b654c
|
|
| BLAKE2b-256 |
3343919d4020641d04349950ce86e8f2ea5db6678cd54694cb221e72b61fa213
|
Provenance
The following attestation bundles were made for walkforward-3.4.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
release.yml on haeganm/walkforward
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
walkforward-3.4.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
552d2d6437ef2f0964f85077dba52fb3c80a20bd4ed7aa2c4a2208966c9935f6 - Sigstore transparency entry: 2729542493
- Sigstore integration time:
-
Permalink:
haeganm/walkforward@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Branch / Tag:
refs/tags/v3.4.1 - Owner: https://github.com/haeganm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Trigger Event:
push
-
Statement type:
File details
Details for the file walkforward-3.4.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl.
File metadata
- Download URL: walkforward-3.4.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl
- Upload date:
- Size: 34.1 kB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f3737a0c4094114d9d52fa042c3ca23607f23daa86d22eb107aacff617bf70e
|
|
| MD5 |
e7d803c47a566e2bad1b8b26b9ae82c7
|
|
| BLAKE2b-256 |
21071099e9569d52562855fa071d276921631686f8541c17b285598cd84db6f4
|
Provenance
The following attestation bundles were made for walkforward-3.4.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl:
Publisher:
release.yml on haeganm/walkforward
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
walkforward-3.4.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl -
Subject digest:
7f3737a0c4094114d9d52fa042c3ca23607f23daa86d22eb107aacff617bf70e - Sigstore transparency entry: 2729542478
- Sigstore integration time:
-
Permalink:
haeganm/walkforward@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Branch / Tag:
refs/tags/v3.4.1 - Owner: https://github.com/haeganm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Trigger Event:
push
-
Statement type:
File details
Details for the file walkforward-3.4.1-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: walkforward-3.4.1-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 33.9 kB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
03f12b6bdefa7a5f3c554a674ac2da41f2bd564467af3f8ab0860abe46919d2a
|
|
| MD5 |
bec54e165797682736d5c4fd14d95706
|
|
| BLAKE2b-256 |
d53304da3e5aac405a828aabda8e00bc5cdc99b2955d59ec9734296c8f946d0d
|
Provenance
The following attestation bundles were made for walkforward-3.4.1-py3-none-macosx_11_0_arm64.whl:
Publisher:
release.yml on haeganm/walkforward
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
walkforward-3.4.1-py3-none-macosx_11_0_arm64.whl -
Subject digest:
03f12b6bdefa7a5f3c554a674ac2da41f2bd564467af3f8ab0860abe46919d2a - Sigstore transparency entry: 2729542513
- Sigstore integration time:
-
Permalink:
haeganm/walkforward@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Branch / Tag:
refs/tags/v3.4.1 - Owner: https://github.com/haeganm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Trigger Event:
push
-
Statement type:
File details
Details for the file walkforward-3.4.1-py3-none-macosx_10_13_x86_64.whl.
File metadata
- Download URL: walkforward-3.4.1-py3-none-macosx_10_13_x86_64.whl
- Upload date:
- Size: 36.2 kB
- Tags: Python 3, macOS 10.13+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59498bbcbc89c3e26bb2e8945425332a24765de343eb7b2a0a341e7fbf2e4762
|
|
| MD5 |
31da3d0cf8e160ecba368c2701be638a
|
|
| BLAKE2b-256 |
ba7967cd8ed48619cdec4c84b929e16b60c680a21a92ab06efa2432a2e0930f1
|
Provenance
The following attestation bundles were made for walkforward-3.4.1-py3-none-macosx_10_13_x86_64.whl:
Publisher:
release.yml on haeganm/walkforward
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
walkforward-3.4.1-py3-none-macosx_10_13_x86_64.whl -
Subject digest:
59498bbcbc89c3e26bb2e8945425332a24765de343eb7b2a0a341e7fbf2e4762 - Sigstore transparency entry: 2729542468
- Sigstore integration time:
-
Permalink:
haeganm/walkforward@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Branch / Tag:
refs/tags/v3.4.1 - Owner: https://github.com/haeganm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5c6dd84ffebef3fc609a365db971dacfc5a8dc9e -
Trigger Event:
push
-
Statement type: