0995f8d66a
`Psar::is_ready` previously returned `self.initialised`, which flips to
`true` *after* the seed candle — but the seed candle itself returns
`None`. The contract every other indicator honours is
`is_ready() == true` ↔ "the most recent update produced (or could
produce) a real value". Streaming consumers writing
`if ind.is_ready() { use(ind.update(c)?) }` would hit an unexpected
`None` on the first post-seed update.
Fix: add a `has_emitted: bool` field that flips on the first
`Some(sar)` return; `is_ready` now reads that. New test
`is_ready_only_after_first_some_value` pins the contract.
While in the same file, `reset()` is corrected to restore the compute
fields (`prev_high`, `prev_low`, `sar`, `ep`) to `f64::NAN` sentinels
instead of `0.0` (Opus bonus finding). The fields are gated by
`initialised` today, so the `0.0` sentinel never leaked into output —
but a future refactor that read them pre-init would have silently
treated `0.0` as a real price. A `debug_assert!` at the read site makes
the invariant explicit and catches a re-introduction of the bug in
debug builds.
Bit-equivalence with the previous behaviour is preserved
(`reset_allows_clean_reuse` and `batch_equals_streaming` continue to
pass unchanged).
11 KiB
11 KiB
Changelog
All notable changes to Wickra are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
Fixed
Psar::is_readynow matches the convention shared by every other indicator:is_ready() == trueiff a real value has been produced (audit finding R6). The previous implementation returnedself.initialised, which flipped totrueafter the seed candle even though the seed candle itself returnsNone. A streaming consumer that wroteif ind.is_ready() { use(ind.update(c)?) }would hit an unexpectedNoneon the first post-seed update. The fix introduces ahas_emittedgate set when the firstSomevalue is returned.Psar::resetnow restores the compute fields (prev_high,prev_low,sar,ep) tof64::NANsentinels instead of0.0(audit Opus-Bonus 1). The fields are gated byinitialisedtoday, so the0.0sentinel never leaked into output — but a future refactor that read them pre-init would have silently treated0.0as a real price. Adebug_assert!at the read site makes the invariant explicit.
Changed
UlcerIndex::updatenow tracks the trailing maximum with a monotonically- decreasing deque of(index, price)pairs instead of scanning the whole trailing window on every tick. The indicator now honours theIndicatortrait's O(1)-per-tick contract; values and warmup semantics are unchanged (verified by a new adversarial-input test that compares the deque output bar-by-bar against a naive O(n) trailing-max scan on strictly increasing, strictly decreasing, constant, and sawtooth inputs). The doc comment onwarmup_period()is also corrected: the two windows overlap by one bar, so the formula is2 * period - 1.
Added
RollingVWAPis now exposed in Python, Node and WASM under that name (previously the rolling-window VWAP existed only in the Rust core, even though the README's volume-family table already advertisedVWAP (cumulative + rolling)). All four bindings now ship the same cumulativeVWAPplus the finite-windowRollingVWAP(period). The wiki pageIndicator-Vwap.mdadds Python, Node and WASM examples and drops the "Rust-only" caveat.- WASM binding now exposes the streaming
update()method on every candle-input indicator:Adx,WilliamsR,Cci,Mfi,Psar,Keltner,Donchian,Vwap,AwesomeOscillator,Aroon,Stochastic, andObv. Multi-output indicators (Adx,Keltner,Donchian,Aroon,Stochastic) return a named JS object ({ plusDi, minusDi, adx },{ upper, middle, lower },{ up, down },{ k, d }) once warm, ornullduring warmup — matching the existingSuperTrendconvention. Each class also gainsreset(),isReady()andwarmupPeriod(), bringing the WASM surface to full parity with Python and Node so browser-side streaming code no longer has to replaybatch()on every tick.WasmKamagains the previously missingwarmupPeriod(). - New
wasm-bindgenintegration test exercisesupdate == batchplus the full lifecycle (reset/isReady/warmupPeriod) for all twelve newly wired classes against a deterministic 40-bar synthetic OHLCV stream.
Security
- Upgrade
pyo3(0.22 → 0.28) andnumpy(0.22 → 0.28) in the Python binding. Fixes RUSTSEC-2025-0020 — a buffer overflow inPyString::from_objectthat affected the published Python wheels. Thecargo-denyignore entry that previously suppressed the advisory has been removed;cargo deny checkis now clean without suppression. Migratedinto_pyarray_boundtointo_pyarray,downcast::<PyDict>tocast::<PyDict>, and opted every#[pyclass]out of the deprecated automaticFromPyObjectderive viaskip_from_py_object.
Added
- 46 new technical indicators, taking the library from 25 to 71 and
reorganising the catalogue into eight families, each with at least five
members. Every indicator is implemented once in the Rust core and wired
through the Python, Node and WASM bindings, with reference-value tests and a
dedicated wiki page:
- Moving Averages:
Smma,Trima,Zlema,T3,Vwma. - Momentum Oscillators:
Mom,Cmo,Tsi,Pmo,StochRsi,UltimateOscillator. - Trend & Directional:
AroonOscillator,Vortex,MassIndex,ChoppinessIndex,VerticalHorizontalFilter. - Price Oscillators:
Ppo,Dpo,Coppock,AcceleratorOscillator,BalanceOfPower. - Volatility & Bands:
Natr,StdDev,UlcerIndex,HistoricalVolatility,BollingerBandwidth,PercentB,TrueRange,ChaikinVolatility. - Trailing Stops:
SuperTrend,ChandelierExit,ChandeKrollStop,AtrTrailingStop. - Volume:
Adl,VolumePriceTrend,ChaikinMoneyFlow,ChaikinOscillator,ForceIndex,EaseOfMovement. - Price Statistics:
TypicalPrice,MedianPrice,WeightedClose,LinearRegression,LinRegSlope,ZScore,LinRegAngle.
- Moving Averages:
TickAggregator::with_gap_fill— opt-in mode that emits a flat placeholder candle for every empty bucket between two ticks, keeping the candle series evenly spaced for downstream indicators.- CSV reader: a leading UTF-8 byte-order mark is stripped, fields are trimmed, and the header is validated against the required OHLCV columns.
- CI: an
msrvjob that builds and tests the workspace on Rust 1.75 and the node binding on Rust 1.77. - Community health files:
CONTRIBUTING.md,SECURITY.md,CODE_OF_CONDUCT.md, issue / pull-request templates,CODEOWNERS, and a Dependabot configuration. - Seven example OHLCV datasets under
examples/data/, one per timeframe (1m / 5m / 15m / 1h / 12h / 1d / 1month), holding real BTCUSDT spot klines, alongside thefetch_btcusdtexample that regenerates them from the Binance REST API. Timeframe::minutes,Timeframe::hoursandTimeframe::daysconvenience constructors, each building on seconds with a checked-multiplication overflow guard.
Changed
- The indicator wiki is reorganised into eight family folders under
docs/wiki/indicators/(moving-averages/,momentum-oscillators/,trend-directional/,price-oscillators/,volatility-bands/,trailing-stops/,volume/,price-statistics/);Indicators-Overview.md,Home.mdand the README indicator table follow the same eight families. TickAggregator::pushreturnsResult<Vec<Candle>>(wasResult<Option<Candle>>) so a single tick can yield a closed bar plus gap fillers.Resampler::pushreturnsResult<Option<Candle>>: a candle in a bucket earlier than the open bar is now rejected as out of order.- Aggregated candles are finalised through the validating
Candle::new, so a volume that overflows to a non-finite value is surfaced as an error instead of producing a poisoned candle. - All GitHub Actions are pinned to commit SHAs; the four publish jobs run in a
protected
releaseenvironment. - The indicator benchmarks (
crates/wickra/benches/indicators.rs) now run against the checked-in real BTCUSDT 1-minute dataset instead of a synthetic price series. - Every language's examples now live under a uniform
examples/<lang>/tree: Rust moved into a newexamples/rust/workspace member crate (wickra-examples, run viacargo run -p wickra-examples --bin <name>), Node intoexamples/node/with its ownpackage.jsonlinkingwickraviafile:../../bindings/node, and the WASM browser demos intoexamples/wasm/. The bundled BTCUSDT datasets move alongside them atexamples/data/. Six new examples close the cross-language parity matrix: streaming demos for Python and Rust; multi-timeframe and parallel-assets demos for both Rust and Node. - Cross-language data-generator parity:
examples/python/fetch_btcusdt.py(stdlib only:urllib+json+csv) andexamples/node/fetch_btcusdt.js(Node 18+ built-infetch) mirror the Rustfetch_btcusdtbinary — byte-for-byte identical CSV output on the same Binance snapshot. - Four additional WebAssembly browser demos under
examples/wasm/alongside the originalindex.html:backtest.html(fetch + basket of indicators),live_trading.html(browser-nativeWebSocketto Binance),multi_timeframe.html(in-page resample) andparallel_assets.html+parallel_worker.js(module-Worker pool with serial-vs-parallel speedup). The cross-language matrix is now closed for every cell where the pattern makes sense. - Three new wiki pages:
TA-Lib-Migration.md(full mapping table fromtalib.X(...)calls to Wickra),Cookbook.md(seven concrete strategy recipes — RSI mean reversion, MACD crossover, Bollinger breakout, ADX-gated trend, multi-timeframe confirmation, SuperTrend, chained indicators) andFAQ.md. All three linked fromHome.md.
Fixed
Timeframe::floorno longer overflows for timestamps neari64::MIN.- The aggregator rejects same-bucket ticks that arrive out of order instead of silently overwriting the bar's close with a stale price.
- The Binance live stream reconnects with exponential backoff, skips non-kline frames, applies a read timeout and message-size limits, and tracks a closed flag.
- Example scripts:
live_trading.pyskips non-kline frames and validates the symbol/interval;backtest.pyandmulti_timeframe.pyreport clear errors for malformed CSV input.
0.1.4 - 2026-05-21
Added
- GitHub Release runs now attach every built artefact (wheels, sdist, native
Node binaries, npm-pack tarballs, cargo
.cratefiles) to the tag's release page.
0.1.3 - 2026-05-21
Fixed
- npm package ships the napi-generated loader and is built with
--platformso the per-platform binary is resolved correctly.
0.1.2 - 2026-05-21
Fixed
- Release pipeline: per-platform idempotent npm publishing with a spam-filter
retry, and committed
npm/<platform>/package templates.
0.1.1 - 2026-05-21
Fixed
- Node publish step and coordinated version bump across all bindings.
0.1.0 - 2026-05-21
Added
- Initial release: a streaming-first technical-analysis library with 25 indicators (SMA, EMA, WMA, DEMA, TEMA, HMA, KAMA, RSI, MACD, ROC, Stochastic, CCI, Williams %R, ADX, MFI, TRIX, Aroon, Awesome Oscillator, Bollinger Bands, ATR, Keltner Channels, Donchian Channels, Parabolic SAR, OBV, VWAP).
- Rust core (
wickra-core), umbrella crate (wickra), and a data layer (wickra-data) with a CSV reader, tick aggregator, resampler, and an optional Binance live feed. - Bindings for Python, Node.js, and WebAssembly.