Thanks for taking the time to contribute. QuantWave is a Polars-native TA/backtesting
library with a single Rust math core (Next<T>) that powers batch, expression-plugin,
and streaming surfaces — the biggest rule in this repo is that those three never drift
apart. Everything below exists to protect that invariant.
For the day-to-day dev workflow (build cache, style guidelines) and the full
"Adding a New Indicator" walkthrough, see the canonical doc that ships with the
docs site: docs/contributing.md (published at
lavs9.github.io/quantwave/reference/contributing).
This file is the GitHub-surfaced entry point — read it first, then follow the link for
the details.
- Questions, ideas, "is this possible?" → GitHub Discussions (link added once enabled — see repo README).
- Bugs and numeric-parity mismatches → open an issue.
- Feature requests → open an issue.
- Silent footguns that are not bugs (documented, intentional behavior) → check
.claude/skills/quantwave/PITFALLS.mdbefore filing.
git clone https://github.com/lavs9/quantwave
cd quantwave
python -m venv .venv && source .venv/bin/activate
pip install -r requirements-docs.txt
cargo buildInstall the pre-push quality gate — this runs the same checks CI runs, before you push:
./scripts/install-git-hooks.shThis symlinks scripts/git-hooks/pre-push into .git/hooks/pre-push, which invokes
./scripts/quantwave_verify.sh (metadata/doc drift checks → cargo fmt/clippy/
nextest → wheel build + smoke test → pytest) before every git push. Useful knobs:
SKIP_PRE_PUSH_VERIFY=1 git push— bypass once (emergency only)VERIFY_NO_CACHE=1 git push— force a full rebuild instead of the cached resultpython3 scripts/verify_cache.py status— check what's cached./scripts/quantwave_verify.sh --skip-rust --skip-python --skip-wheel— run a subset manually (see the script for all flags)
This is the highest-scrutiny change type in the repo because a wrong indicator is silently wrong — it returns a plausible number, not an error. The checklist:
- Core logic — implement the
Next<T>trait inquantwave-core/src/indicators/. This is the single source of truth; batch and streaming both call it. - Metadata — register the indicator (name, params, category,
warmup_bars) inmetadata.rs, then regenerate the registry:python scripts/regenerate_metadata_registry.py
- Gold fixture — add a reference-vector fixture under
gold_standard/(seequantwave-core/tests/gold_standard/andquantwave-backtest/tests/gold_standard/for the existing layout) so the indicator has an independently-verified numeric answer, not just an internal self-consistency check. - Parity proptest — add a
proptest!case asserting streaming output matches the TA-Lib oracle (talib-rs, test-only dependency) across randomized inputs. Seequantwave-core/tests/test_all_talib_parity.rsfor the pattern used for every TA-Lib-lineage indicator. - Python parity test — if the indicator ships a gold JSON fixture, add/extend a
case in
tests/python/gold_parity_registry.pysotests/python/test_gold_parity.py::test_streaming_matches_gold_vectorcovers it (or add it toGOLD_PARITY_DEFERREDwith a reason —test_gold_fixture_inventoryfails on any fixture that's neither covered nor explicitly deferred). - Docs page — generate the skeleton, then hand-enrich it:
Follow
python scripts/generate_native_docs.py # emits into docs/guides/indicators/native/ python docs/generate_all_previews.py --sync-docsdocs/DOCUMENTATION_STANDARDS.mdfor the content template, then lint before landing:python docs/upgrade_to_standards.py --lint python docs/upgrade_to_standards.py --depth-lint
If the indicator has a footgun worth warning users about (units, ddof, sign
convention, a surface where the same slug means different math) — add it to
.claude/skills/quantwave/PITFALLS.md, not
just the docs page. That file is what the project's agent skill and PR reviewers both
read first.
This repo follows Conventional Commits:
<type>(<scope>): <short summary>
Types actually in use in this history: feat, fix, docs, style, chore, test,
refactor, perf, ci. Scope is usually a crate, module, or subsystem
(indicators, backtest, ci, getting-started, clippy, …). Examples straight
from git log:
fix(clippy): avoid drain-into-same-type in geometric_patterns::promote_pending_poles
style: fix rustfmt violations in recently-added backtest/hmm test files
docs(getting-started): linear zero-to-RSI path with verified output blocks
fix(indicators): hmm_bull_bear raises on price-scale input instead of silent constant Bull
feat(backtest): robustness tearsheet report (DSR, PSR, Monte Carlo MDD, slippage breakeven)
Keep the summary in the imperative mood and under ~72 characters; put "why", not just "what", in the body when the change isn't self-explanatory.
- Rust: idiomatic,
cargo clippy -- -D warningsclean, no unnecessary allocations on hot paths. - Public functions carry docstrings.
- Match existing parameter-naming convention exactly — TA-Lib-lineage indicators use
timeperiod, native ones useperiod. Don't guess; checkqw.metadata("<slug>")or the sibling indicators in the same file.
Before tagging, promote the changelog: run
python3 scripts/release_changelog.py --apply X.Y.Z, which renames
## [Unreleased] to ## [X.Y.Z] - <today> and leaves a fresh empty
[Unreleased] above it, then commit docs/changelog.md. docs/changelog.md
follows Keep a Changelog 1.1: only the
Added / Changed / Deprecated / Removed / Fixed / Security
subheadings, categorized by what the entry actually describes, and dates that
match the release tag exactly. scripts/check_changelog.py (run in CI's
"Doc & metadata sanity" job) enforces this structure and cross-checks each
version's date against its vX.Y.Z git tag.
Releases are cut by pushing a v* tag, which triggers
.github/workflows/release.yml: a gate step
first runs scripts/release_changelog.py --check, which fails the release if
docs/changelog.md doesn't already have a ## [X.Y.Z] - date section for the
pushed tag with [Unreleased] empty (i.e. you forgot the --apply step
above). Rust crates then publish to crates.io first (core → backtest/plugins
→ polars → quantwave), then Python wheels build and smoke-test across
platforms/versions, then publish to PyPI via OIDC. See
.github/workflows/README.md for the full CI/release
pipeline diagram, and scripts/check_release_invariants.py for the pre-release
invariant checks. Release notes live under docs/releases/ and the
running docs/changelog.md.
Use the GitHub issue tracker — pick the Bug report template for a broken build/API,
the Parity mismatch section of that same template if QuantWave returns a
plausibly-wrong number (include the reproducing input series and expected vs. actual
output), or the Feature request template for anything else. See
.github/ISSUE_TEMPLATE/.