Skip to content

Release 0.8.0 — Data layer, execution realism, and three breaking correctness fixes

Date: 2026-09-20 Install: pip install quantwave or pip install "quantwave[polars]"


Headline

QuantWave 0.8.0 ships the bundled sample dataset (quantwave.datasets.load_sample()) that the getting-started guide has referenced since 0.7.0 — this release closes that gap. Alongside it: a full execution-realism pass on the backtest engine (order types, risk overlays, rebalance policies, portfolio optimization), new alternative bar types and harmonic pattern detection, a first-class TA-Lib abstract-API registry, two new research modules (conditional-outcome queries, prop-firm challenge simulation), and three breaking correctness fixes that change default behavior for anyone re-running an existing backtest.


Breaking changes — read this before upgrading

  • portfolio_backtest's signal_type now defaults to "weight" (fraction of total equity) instead of "shares" (a literal share count). A boolean 0/1 signal used to buy one share and deploy almost none of the book's capital, silently. Backtests re-run without passing signal_type="shares" explicitly will report much larger, and much more correct, numbers. See the Backtest Quickstart.
  • execution_delay now defaults to "next_bar" (T+1) instead of "same_bar" (T+0), across every .bt entry point. The old default filled on information that only existed once the bar had already closed — a look-ahead a live strategy never has. Existing backtests will report different, generally worse, numbers; that's the look-ahead being removed.
  • ta_beta / ta_correl now default to TA-Lib's real periods (5 and 30) instead of a blanket 14 — they previously disagreed with their own non-prefixed siblings on the same data. ta_atr / ta_natr / ta_trange now take close in the receiver, matching their siblings' shape; the old positional order silently permuted high/low/close and returned a plausible-but-wrong ATR with no error.
  • Rust callers only: talib_rs::MaType is replaced by a native quantwave_core::MaType. Python callers are unaffected — matype= still takes the same integers/strings.

Full detail on all of the above, including before/after code, is in the Changelog.


What's new

Data layer

  • quantwave.datasets.load_sample() / synthetic() — the bundled sample OHLCV dataset and synthetic-series generator the getting-started guide already documented. load_sample()'s data is synthetic, not real market history.
  • qw.trim_warmup() / qw.warmup_rows() and a quantwave.WarmupWarning on every .bt entry point — indicator warmup is NaN, never null, so drop_nulls() was a silent no-op on it; these give you a correct, metadata-driven way to trim it.

Backtest execution realism

  • First-class order types: limit / stop / stop-limit / bracket / OCO, wired into the order-driven run loop.
  • Risk overlays (vol-target, inverse-vol, position-limit, pre-trade checks) and portfolio rebalance policies.
  • Optional Bayesian (TPE) walk-forward optimization, benchmark-relative reporting (alpha/beta/Calmar/VaR/CVaR), and a Rust/nalgebra portfolio optimizer (mean-variance, risk parity, HRP) — all preserving batch↔streaming parity.

New research modules

  • quantwave.research.conditional_outcome() — "given these conditions on my data, how often did this outcome occur", with a 95% Wilson confidence interval, a minimum-sample-size guard, and a chronological stability split, so a bare hit rate never gets reported without its own trustworthiness attached.
  • quantwave.propfirm.simulate_prop_firm_challenge() — block-bootstrap Monte Carlo simulation of a backtest's trade history against prop-firm-style drawdown/daily-loss/profit-target rules, reporting pass probability with a Wilson CI and a breach-reason breakdown.

Indicators and patterns

  • Alternative bar types: Renko, Kagi, constant-range bars, Point & Figure.
  • Harmonic pattern detection: AB=CD, Alternate AB=CD, 5-0, and XABCD patterns (Gartley, Bat, Butterfly, Crab, Alternate Bat).
  • ML feature namespace: bulk feature extraction, labeling, and leakage-safe CV splits.
  • All 60 remaining candlestick patterns ported to native Rust — talib-rs has left the shipped dependency graph entirely; it's now solely a #[cfg(test)] parity oracle. The "221 native indicators" claim is now literally true.

API introspection

  • TA-Lib abstract-API registry (get_functions() / get_function_groups()-equivalent) and df.ta.all() bulk compute, matching pandas-ta's strategy("all").
  • Multi-timeframe (MTF) helpers — lookahead-safe resample/apply/broadcast.

WASM

  • quantwave-wasm crate and an experimental in-browser indicator playground.

Notable fixes

  • mama panicked on a single NaN in its input stream — a bad tick from a data vendor could crash a long-running streaming session. It now returns (NaN, NaN) for that one bar without touching any internal state.
  • 68 indicators silently resolved to a streaming class instead of their batch function — qw.supertrend was a class while qw.rsi was a function, with no error. _resolve_ta_binding now raises ImportError on a build-time mismatch instead of silently substituting the wrong calling convention.
  • _metadata_generated.py's data_inputs/outputs silently defaulted to ['close']/[slug] for any indicator without a hand-curated overlay entry — corrected for every indicator whose true arity differs.
  • calmar_ratio returned inf on a zero-drawdown run while the rest of the metrics bundle returned NaN for the same undefined-ratio condition — this could make walk-forward/sweep selection pick a degenerate variant that never lost. Now returns NaN consistently.
  • Four candlestick patterns (CDLGAPSIDESIDEWHITE, CDLCONCEALBABYSWALL, CDL3STARSINSOUTH, CDLBREAKAWAY) diverged from the TA-Lib reference in ways the random-walk parity tests never reached; fixtures rebuilt to discriminate.
  • MaStream silently substituted SMA for every matype except EMA (affecting APO, PPO, MACDEXT, STOCH, STOCHF, STOCHRSI) — all nine families now dispatch to their real streaming implementation.
  • BBANDS with a non-SMA matype was O(n²) over a series with unbounded memory — now genuinely O(1) per bar.

Full changelog: docs/changelog.md#0.8.0.


Upgrade notes

  • If you have an existing portfolio_backtest call relying on share-count sizing, pass signal_type="shares" explicitly to keep old behavior.
  • If you have an existing backtest relying on same-bar fills (you genuinely trade the closing auction, or your signal only uses data through bar t-1), pass execution_delay="same_bar" explicitly.
  • If you called .ta.ta_beta / .ta.ta_correl without an explicit timeperiod, pass timeperiod=14 to keep the previous (TA-Lib-incorrect) numbers, or accept the new TA-Lib-correct defaults.
  • If you called .ta.ta_atr / .ta.ta_natr positionally with high as the receiver, switch the receiver to close to match the corrected signature.
pip install --upgrade quantwave

Quality gates

./scripts/quantwave_verify.sh

What's next

See Roadmap.