Jest for trading strategies. Golden-pin your strategy's backtest report, catch regressions in CI, and property-test it against fuzzed market data — in ten languages, plus a reusable composite GitHub Action.
▶ Live demos: the backtester compiled to WebAssembly, an equity curve building bar by bar — backtest-live.wickra.org; one StrategySpec side by side in Python, Rust, JS and Go — playground.wickra.org; all 514 indicators of the core over a real Binance feed — live.wickra.org. Zero backend, all of them.
Part of the Wickra ecosystem. Strategy-CI
is the test harness for the deterministic
wickra-backtest engine: it runs
a strategy through the engine, pins the resulting BacktestReport, and fails
the build when the numbers drift.
# Run a directory of strategy tests against a directory of OHLCV data.
# Exits non-zero the moment a report drifts, so CI fails on it.
wickra-strategy-ci run tests/ --data data/
# Re-pin the goldens after a change you meant to make.
wickra-strategy-ci bless tests/ --data data/
Status
0.1.6 — the current release. The core, the CLI, all ten language bindings, the byte-exact golden corpus, property + fuzz tests, benchmarks and one runnable example per language are in place and green across the full CI matrix (10 languages × 3 OS). ROADMAP.md has what is done, what is open and what is not planned.
Documentation
See docs/ — TESTS.md for the test model,
TOLERANCES.md for the golden diff,
PROPERTIES.md for the invariants,
FUZZING.md for the perturbations,
GITHUB_ACTION.md for the action, and
Cookbook.md for task-shaped recipes.
Why
A backtest is only trustworthy if it is reproducible. Wickra's engine is deterministic, so a strategy's report is a stable artifact you can pin — like a snapshot test. Strategy-CI turns that into a workflow:
- Golden tests — pin a strategy's
BacktestReportand fail when it changes beyond a tolerance you set — absolute or relative, per field, with exact equality as the default when you set none. - Property tests — assert invariants that must hold for any run: no field is NaN or infinite, drawdown stays inside a bound, the trade count clears a floor, Sharpe or PnL clears a threshold, a named field stays in range.
- Fuzz tests — perturb the input data with a seeded PRNG and re-run, catching strategies that only work on one specific history.
Quickstart
Install the CLI, point it at a directory of test files and a directory of candle data, and pin the reports once:
cargo install wickra-strategy-ci-cli # the crate; the binary is wickra-strategy-ci
wickra-strategy-ci list golden/tests/ # the test ids found under a path
wickra-strategy-ci bless golden/tests/ --data golden/data/ # pin every report (first run)
wickra-strategy-ci run golden/tests/ --data golden/data/ # fail when a report moves
run exits non-zero when any pinned report drifts further than its tolerances
allow, when a property check fails, or when a fuzzed re-run breaks — which is
what makes it a CI gate. The next section is what a test file looks like.
A test is a file, not a function
There is no test API to learn. A test is JSON: which strategy, over which dataset, pinned to which report, under which tolerances.
{
"id": "crossover",
"dataset_ref": "sym-04", // resolves to <data>/sym-04.csv
"strategy": { /* opaque StrategySpec, forwarded to wickra-backtest */ },
"expected": { /* the pinned BacktestReport — written by `bless` */ },
"tolerances": {
"*": { "kind": "rel", "value": 0.0001 }, // default for every field
"metrics.sharpe": { "kind": "abs", "value": 0.01 }
},
"property_checks": [
{ "kind": "no_nan" },
{ "kind": "max_drawdown_le", "value": 1.0 }
],
"fuzz": { "seed": 42, "runs": 8,
"perturbation": { "kind": "jitter", "amount": 0.001 } }
}
Write the strategy, run bless once to pin the report, and commit the file. From
then on run fails the build whenever the numbers move further than you allowed.
Working examples live in golden/tests/.
wickra-strategy-ci list tests/ # the test ids found under a path
wickra-strategy-ci run tests/ --data data/ # --format json for machine output
wickra-strategy-ci bless tests/ --data data/ # re-pin after an intended change
As a GitHub Action
Run your strategy tests on every push — a failing test fails the workflow:
- uses: wickra-lib/wickra-strategy-ci@v0.1.6
with:
tests: tests/
data: data/
Pin the exact release. Pre-1.0 the moving major tag is @v0, and it floats
across minor versions, which may break.
See docs/GITHUB_ACTION.md for the full inputs/outputs.
How it works
A StrategyTest is data, not code: a serde model carrying an opaque
StrategySpec sub-JSON. Strategy-CI forwards that spec verbatim to
wickra-backtest::run, takes the returned BacktestReport, and asserts it
against the test's expectations and properties. Because the engine is
deterministic and every binding forwards the core's response string unchanged,
results are reproducible byte-for-byte across languages and between the parallel
(rayon) and sequential (WASM) execution paths.
The diff works on numeric leaves. Both reports are flattened to a sorted map
of numbers — metrics.sharpe, equity[3].equity — rounded to eight decimals and
compared field by field, reporting mismatches, fields that vanished and fields
that appeared. Strings, booleans and nulls are not pinned, so a report field that
is text is outside what a golden can catch.
Use in any language
The core is exposed as a JSON-over-C-ABI data API in ten languages: Rust, Python,
Node.js and WASM natively, plus C, C++, C#, Go, Java and R over the C ABI hub. A
Session handle plus command(json) -> json and version is the whole surface;
the same test JSON produces a byte-identical result in every binding.
cargo add wickra-strategy-ci-core # Rust
pip install wickra-strategy-ci # Python
npm install wickra-strategy-ci # Node.js
dotnet add package Wickra.StrategyCi # C#
go get github.com/wickra-lib/wickra-strategy-ci-go # Go
Java ships to Maven Central (org.wickra:wickra-strategy-ci), R to r-universe
(wickrastrategyci), and the C ABI ships as a per-platform library with a
vendored header. See each binding's README.md under bindings/.
Project layout
crates/wickra-strategy-ci-core the runner: model, tolerances, properties, fuzz, session
crates/wickra-strategy-ci-cli the reference CLI (run / bless / list / version)
crates/strategy-ci-bench criterion benchmarks
bindings/c the C ABI hub — every non-native binding goes through it
bindings/python PyO3 native binding
bindings/node napi-rs native binding
bindings/wasm wasm-bindgen binding (sequential path)
bindings/{csharp,go,java,r} thin clients over the C ABI
golden/ cross-language fixtures: tests, data, expected reports
examples/ one runnable example per binding
fuzz/ cargo-fuzz targets (its own detached workspace)
action.yml the composite GitHub Action
Building everything from source
cargo build --workspace --all-features # core, CLI, bench, native bindings
cargo build -p wickra-strategy-ci-c --release # the C ABI library + header
( cd bindings/python && maturin develop --release )
( cd bindings/node && npm ci && npm run build )
( cd bindings/wasm && wasm-pack build --target web )
( cd bindings/java && mvn -q package )
( cd bindings/csharp && dotnet build )
The C ABI library is the prerequisite for the C, C++, C#, Go, Java and R
bindings: build it first, then point WKSTRATEGYCI_LIB and WKSTRATEGYCI_INC at
the resulting library and bindings/c/include.
Testing
cargo test --workspace --all-features # core, integration, C ABI
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo fmt --all --check
( cd bindings/python && pytest tests -q )
( cd bindings/node && npm test )
( cd bindings/go && go test ./... )
Every binding runs the same golden/ fixtures through its own
command(json) -> json surface, so a passing suite is evidence that the
languages agree byte-for-byte — not just that each one runs. The CLI is covered
end-to-end against golden/tests in CI.
Requirements
| To use | You need |
|---|---|
| The CLI or the GitHub Action | Nothing — the action installs a prebuilt binary, or builds from git as a fallback. |
| Rust | 1.86 or newer (workspace MSRV). |
| Python | 3.9 or newer. |
| Node.js | 22 or newer. |
| Go | 1.23 or newer. |
| Java | 22 or newer. |
| R | 4.1 or newer. |
| C / C++ / C# / R | The C ABI library plus its vendored header; see each binding's README.md. |
Building from source additionally needs a Rust toolchain; the polyglot bindings
need their own toolchain (maturin, napi, wasm-pack, dotnet, go, Maven,
R CMD) only for the binding you are building.
Benchmarks
A suite is cheap enough to gate every pull request. Median wall-clock for
run_suite, parallel path, from the strategy-ci-bench criterion suite:
| Dataset | Tests | Suite | Per test |
|---|---|---|---|
| small (200 bars) | 100 | 13.4 ms | ~134 µs |
| small (200 bars) | 1000 | 143 ms | ~143 µs |
| large (2000 bars) | 100 | 156 ms | ~1.56 ms |
| large (2000 bars) | 1000 | 1.24 s | ~1.24 ms |
Per-test cost is dominated by the engine walking the price history — roughly
linear in bar count, near-flat in test count once the rayon pool is saturated.
The golden diff and property checks are O(fields) on top. A fuzz axis
multiplies a test's cost by its runs. Full method and caveats in
BENCHMARKS.md; reproduce with cargo bench -p strategy-ci-bench.
Ecosystem
Strategy-CI is one repo in the Wickra family:
| Repo | What it does |
|---|---|
| wickra | The indicator core — 514 streaming indicators, O(1) per tick, in ten languages. |
| wickra-backtest | The deterministic engine whose BacktestReport this repo pins. |
| wickra-data | Candle types and CSV/exchange loading. |
| wickra-proof | Verifiable report hashes — a roadmap item for this repo. |
| wickra-synth | Deterministic synthetic market data, useful as fuzz input. |
| wickra-exchange | Live and historical exchange connectivity. |
Contributing
See CONTRIBUTING.md and the Code of Conduct.
Security
Report vulnerabilities per SECURITY.md.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option. Use it, fork it, modify it, redistribute it — commercially or not — file issues, send pull requests; all welcome.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Disclaimer
wickra-strategy-ci is research and engineering tooling, not financial advice. A
passing test attests only that a strategy's backtest report matches its pinned
expectation under the given data — it makes no claim about the quality,
profitability or future performance of any strategy. Trading carries risk; you
are responsible for your own decisions.
Built on Wickra. If it saved you time, the cheapest way to say thanks is to ⭐ the repo.
Metadata
Release files for wickra-strategy-ci 0.1.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| wickra_strategy_ci-0.1.6.tar.gz | 57.3 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| wickra_strategy_ci-0.1.6-cp39-abi3-win_arm64.whl | CPython 3.9 | abi3 | Windows ARM64 | Details |
| wickra_strategy_ci-0.1.6-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| wickra_strategy_ci-0.1.6-cp39-abi3-musllinux_1_2_x86_64.whl | CPython 3.9 | abi3 | Linux musl 1.2+ x86-64 | Details |
| wickra_strategy_ci-0.1.6-cp39-abi3-musllinux_1_2_aarch64.whl | CPython 3.9 | abi3 | Linux musl 1.2+ ARM64 | Details |
| wickra_strategy_ci-0.1.6-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| wickra_strategy_ci-0.1.6-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| wickra_strategy_ci-0.1.6-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
| wickra_strategy_ci-0.1.6-cp39-abi3-macosx_10_12_x86_64.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 6.8 MB
Release files / wickra_strategy_ci-0.1.6.tar.gz
| Download URL | wickra_strategy_ci-0.1.6.tar.gz |
|---|---|
| Size | 57.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9343a1d42c118850f74854fda0272c34e1ba003bf0bc0d9a84d8f7a08e0cfe04
|
|
BLAKE2b-256 checksum How to use checksums |
053f492285fba0ad1ea37fd05c876b02fdfc18fe7857c2bf26b53a417d510c1f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / wickra_strategy_ci-0.1.6-cp39-abi3-win_arm64.whl
| Download URL | wickra_strategy_ci-0.1.6-cp39-abi3-win_arm64.whl |
|---|---|
| Size | 656.1 kB |
| Tags | CPython 3.9 Windows ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
40335b55499f134768b9c2ea9e5fa3928ec2e4935cf7e45268f083c27320ea54
|
|
BLAKE2b-256 checksum How to use checksums |
4ff1cd21635e4bc5949ee7447746948fac0760d635463c4139d6d604fbd27a4f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / wickra_strategy_ci-0.1.6-cp39-abi3-win_amd64.whl
| Download URL | wickra_strategy_ci-0.1.6-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 744.6 kB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
bd52122862969e5669fff9bad07342691eeeed0ce8d07442432b5d49065dec5c
|
|
BLAKE2b-256 checksum How to use checksums |
12fb200debce4c7790b230b433f4306a6914e3df96c166508448336900583521
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / wickra_strategy_ci-0.1.6-cp39-abi3-musllinux_1_2_x86_64.whl
| Download URL | wickra_strategy_ci-0.1.6-cp39-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 1.1 MB |
| Tags | CPython 3.9 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
2e239118db2d0bbe48187b4376b918ceb12b5386f8d062b75b1f4efe26877dd7
|
|
BLAKE2b-256 checksum How to use checksums |
e4d7bee8a6b0092bb801004d85bc5ee3a1beb803d1736998258edd99b91fd1ba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / wickra_strategy_ci-0.1.6-cp39-abi3-musllinux_1_2_aarch64.whl
| Download URL | wickra_strategy_ci-0.1.6-cp39-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 974.0 kB |
| Tags | CPython 3.9 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
46f40a40004c6b1982fd0a41c292be2ee58267d4238e3ef449719f0635d12d07
|
|
BLAKE2b-256 checksum How to use checksums |
c1fb5c13d77fc414c9ad9213959c90a434ed622f8b5af902a71cac1404a5060c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / wickra_strategy_ci-0.1.6-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | wickra_strategy_ci-0.1.6-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 886.5 kB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
ee6aabd12ab36ec35e2f66793c5670f7c1ee1101b7a32d00516bb861af529954
|
|
BLAKE2b-256 checksum How to use checksums |
f4ac0ab48eaf1a1f52d1bb78b09d4a810c7d6098306b3993a80803d3b4d3517e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / wickra_strategy_ci-0.1.6-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | wickra_strategy_ci-0.1.6-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 795.5 kB |
| Tags | CPython 3.9 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
8484485645530953fc653727e027439ffd1be6607e0da0d752375bfbb959126c
|
|
BLAKE2b-256 checksum How to use checksums |
9d6bfa17cab38988d47d6a8f8351895bf974bc518bc0d31a97e7d84dc5952956
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / wickra_strategy_ci-0.1.6-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | wickra_strategy_ci-0.1.6-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 723.0 kB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
fdfd775b36638b16516c0da1e57db1b66bab7fc28796d029d54d9be780f68584
|
|
BLAKE2b-256 checksum How to use checksums |
2cd534d370855f61e1913b2276116c572e382609ce522ab466d471724a40f31f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / wickra_strategy_ci-0.1.6-cp39-abi3-macosx_10_12_x86_64.whl
| Download URL | wickra_strategy_ci-0.1.6-cp39-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 836.7 kB |
| Tags | CPython 3.9 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
6ab8da2f9dee8de021510b6a7788ded5ff218a52b10621aeb98bce38c3f2cfee
|
|
BLAKE2b-256 checksum How to use checksums |
479ef39c49529f39eb739a417d969cd06a9c1e0a4a05972b1f4bec0232593639
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|