Files
wickra/CHANGELOG.md
T
kingchenc 24e723fa7d feat: Family 02 Momentum Oscillators — RVI / PGO / KST / SMI / Laguerre / Connors / Inertia (#40)
* feat(rvi): add Relative Vigor Index

Dorsey's RVI = SMA(close - open, period) / SMA(high - low, period) over
a rolling window of period candles. Candle input, single parameter
period (default 10). Positive on average-bullish windows, negative on
average-bearish. Holds the previous value if the entire window has
zero range (denominator undefined).

Reference: Donald Dorsey, also pandas-ta rvi.

Touchpoints: rvi.rs + mod.rs + lib.rs re-export, PyRvi + __init__.py
+ test_new_indicators CANDLE_SCALAR + test_known_values reference,
RviNode (4-column OHLC batch) + index.d.ts/index.js + indicators.test
.js factory + reference, WasmRvi + make_candle_ohlc helper, candle-fuzz
target + criterion bench, README + CHANGELOG.

* feat(pgo): add Pretty Good Oscillator

Mark Johnson's PGO = (close - SMA(close, period)) / EMA(TR, period).
Counts roughly how many ATR-equivalents the close sits from its
period-bar mean. Candle input, single parameter period (default 14).
Johnson's heuristic uses +3/-3 crossings as entry signals.

Touchpoints: pgo.rs + mod.rs + lib.rs re-export, PyPgo + __init__.py
+ test_new_indicators CANDLE_SCALAR + test_known_values flat-close
reference, PgoNode (h/l/c) + index.d.ts/index.js + indicators.test.js
factory + reference, WasmPgo, candle-fuzz target + bench, README +
CHANGELOG.

* feat(kst): add Know Sure Thing (Pring)

Pring's long-horizon momentum oscillator: weighted sum of four
SMA-smoothed ROC series with fixed weights 1, 2, 3, 4, plus an SMA
signal line. Nine parameters (four ROC periods, four SMA periods, one
signal period); classic() applies Pring's recommended defaults.
Multi-output indicator emitting KstOutput { kst, signal }.

Touchpoints: kst.rs + mod.rs + lib.rs re-export, PyKst + __init__.py
+ test_new_indicators MULTI + test_known_values flat-input reference,
KstNode + KstValue + index.d.ts/index.js + indicators.test.js multi
factory + reference, WasmKst (manual JsValue object), scalar-fuzz
target (handled outside the f64-output drive helper), README +
CHANGELOG.

* feat(smi): add Stochastic Momentum Index (Blau)

Blau's doubly-EMA-smoothed bounded oscillator: measures the close's
displacement from the centre of the recent high-low range, scaled by
the smoothed range. Candle input, three parameters (period, d_period,
d2_period) with defaults 5 / 3 / 3.

Internally feeds both the displacement-EMA stack and the range-EMA
stack on every candle so they warm up in parallel (gating either
behind the other starves the second by one input).

Touchpoints: smi.rs + mod.rs + lib.rs re-export, PySmi + __init__.py
+ test_new_indicators CANDLE_SCALAR + test_known_values flat-input
reference, SmiNode + index.d.ts/index.js + indicators.test.js factory
+ reference, WasmSmi, candle-fuzz target, README + CHANGELOG.

* feat(laguerre-rsi): add Ehlers Laguerre RSI

Four-stage Laguerre polynomial filter wrapped in an RSI-style up/down
accumulator. Single gamma in [0, 1] (default 0.5) trades lag for
smoothness. State is seeded by setting all four L_i to the first input
so a constant series stays at the neutral 50. Output clamped to
[0, 100] to absorb floating-point rounding.

Reference: Ehlers, Time Warp - Without Space Travel, 2002.

Touchpoints: laguerre_rsi.rs + mod.rs + lib.rs re-export, PyLaguerreRsi
+ __init__.py + test_new_indicators SCALAR + test_known_values neutral
reference, LaguerreRsiNode + index.d.ts/index.js + indicators.test.js
factory + reference, WasmLaguerreRsi via scalar macro, scalar-fuzz
target, README + CHANGELOG.

* feat(connors-rsi): add Connors RSI (CRSI)

Larry Connors' 3-component aggregate: RSI(close), RSI(streak), and
PercentRank of the 1-period return over the last period_rank returns.
Each component is bounded in [0, 100] so the aggregate is too.
Three parameters (period_rsi, period_streak, period_rank) with
defaults 3 / 2 / 100. Streak tracks consecutive up/down runs (resets
to 0 on unchanged close).

Touchpoints: connors_rsi.rs + mod.rs + lib.rs re-export, PyConnorsRsi
+ __init__.py + test_new_indicators SCALAR + test_known_values bounded
reference, ConnorsRsiNode + index.d.ts/index.js + indicators.test.js
factory + reference, WasmConnorsRsi via scalar macro, scalar-fuzz
target, README + CHANGELOG.

* feat(inertia): add Dorsey Inertia (RVI + LinReg)

Donald Dorsey's Inertia — a LinearRegression smoothing of the RVI
series. Endpoint of an n-bar least-squares fit of RVI is the indicator
reading. Preserves trend direction while damping the ratio. Candle
input, two parameters (rvi_period, linreg_period) with defaults 14 / 20.

Touchpoints: inertia.rs + mod.rs + lib.rs re-export, PyInertia +
__init__.py + test_new_indicators CANDLE_SCALAR + test_known_values
constant reference, InertiaNode (4-column OHLC batch) + index.d.ts /
index.js + indicators.test.js factory + reference, WasmInertia,
candle-fuzz target, README + CHANGELOG.

* test(kst): Move KST out of MULTI dict (it is scalar-input)

KST sits in the MULTI dict (candle-input, multi-output) but its
update() takes a single f64, not a candle tuple. The shared streaming
loop in test_multi_streaming_matches_batch fed the OHLCV tuple in,
which crashed with `TypeError: argument 'value': must be real number,
not tuple` on every Python matrix entry.

Split into a new MULTI_SCALAR_INPUT dict with its own test function
that feeds the close-price stream as floats. KST is currently the
only such indicator; structure is ready for future scalar-input
multi-output additions (e.g. some MACD-shaped indicators).

* test(coverage): Cover SMI zero-range and ConnorsRsi zero-prev cold paths

codecov/patch on PR 40 flagged two uncovered defensive branches:
- SMI returns self.current early when the smoothed range collapses to
  zero (`r2 <= 0.0`) so the formula stays defined. Exercised by feeding
  bars where high == low.
- ConnorsRsi skips the ROC ring-buffer update when the previous price
  is exactly zero so the divide-by-zero in `(input - prev) / prev` is
  impossible. Exercised by seeding the first bar at 0.0.
2026-05-25 15:28:56 +02:00

27 KiB
Raw Blame History

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

Added

  • Family 02 — Momentum Oscillators. Inertia (Dorsey): a LinearRegression smoothing of the RVI series — preserves trend direction while damping the underlying ratio. Candle input, two parameters (rvi_period, linreg_period) (defaults 14 / 20). Exposed in all four bindings.
  • Family 02 — Momentum Oscillators. ConnorsRsi: Larry Connors' 3-component aggregate — RSI(close), RSI(streak), and the percentile rank of the 1-bar return over the recent period_rank returns. Bounded in [0, 100]. Three parameters (period_rsi, period_streak, period_rank) (defaults 3 / 2 / 100). Exposed in all four bindings.
  • Family 02 — Momentum Oscillators. LaguerreRsi (Ehlers): four-stage Laguerre polynomial filter wrapped in an RSI-style up/down accumulator. Single parameter gamma in [0, 1] (default 0.5) trades lag for smoothness. State is seeded to the first input so a constant series stays at the neutral 50. Output clamped to [0, 100]. Exposed in all four bindings.
  • Family 02 — Momentum Oscillators. SMI (Stochastic Momentum Index, Blau): doubly-EMA-smoothed bounded oscillator measuring the close's displacement from the centre of the recent high-low range, scaled by the smoothed range. Candle input, three parameters (period, d_period, d2_period) (defaults 5 / 3 / 3). Exposed in all four bindings.
  • Family 02 — Momentum Oscillators. KST (Know Sure Thing, Pring): weighted sum of four SMA-smoothed ROC series with Pring's fixed weights 1, 2, 3, 4, plus an SMA signal line. Nine parameters (four ROC periods, four SMA periods, signal period); Kst::classic() uses Pring's recommended defaults. Multi-output indicator emitting KstOutput { kst, signal }. Exposed in all four bindings.
  • Family 02 — Momentum Oscillators. PGO (Pretty Good Oscillator, Mark Johnson): (close SMA(close, period)) / EMA(TR, period). Candle input, single parameter period (default 14). Roughly counts how many ATR-equivalents the close is from its mean. Exposed in all four bindings.
  • Family 02 — Momentum Oscillators. RVI (Relative Vigor Index, Dorsey): per-bar ratio SMA(close - open, period) / SMA(high - low, period). Candle input, single parameter period (default 10). Positive on average-bullish windows, negative on average-bearish. Holds previous value if the entire window has zero range. Exposed in all four bindings.
  • Family 01 — Moving Averages. ALMA (Arnaud Legoux Moving Average): Gaussian-weighted moving average with configurable centre (offset in [0, 1]) and kernel width (sigma > 0). Community-standard defaults (period = 9, offset = 0.85, sigma = 6.0) available via Alma::classic(). Exposed in all four bindings (Rust, Python, Node, WASM).
  • Family 01 — Moving Averages. EVWMA (Elastic Volume-Weighted Moving Average, Fries 2001): an "elastic" recurrence whose smoothing weight is the bar's volume relative to the running window-volume. Candle input (uses close + volume), single parameter period (default 20). Holds its previous value if the entire window has zero volume. Exposed in all four bindings.
  • Family 01 — Moving Averages. Alligator (Bill Williams): three SMMA lines (Jaw / Teeth / Lips) of the median price (high + low) / 2 with default periods 13 / 8 / 5. Multi-output indicator emitting AlligatorOutput { jaw, teeth, lips }. Visual chart shift is left to the consumer. Exposed in all four bindings.
  • Family 01 — Moving Averages. JMA (Jurik Moving Average): three-stage filter reconstruction of Mark Jurik's adaptive MA. Three parameters: period (14), phase in [-100, 100] (0), power in 1..=4 (2). State is seeded to the first input so a constant series is reproduced exactly. Exposed in all four bindings.
  • Family 01 — Moving Averages. VIDYA (Variable Index Dynamic Average, Chande 1992): EMA whose smoothing factor is scaled by the absolute Chande Momentum Oscillator. Two parameters period and cmo_period (defaults 14 / 9). Exposed in all four bindings.
  • Family 01 — Moving Averages. FRAMA (Fractal Adaptive Moving Average, Ehlers 2005): adapts its smoothing constant to the fractal dimension of the recent window — fast in trends, slow in chop. Single parameter period (must be even, default 16). Exposed in all four bindings.
  • Family 01 — Moving Averages. McGinleyDynamic: John McGinley's self-adjusting MA. Single parameter period; the recurrence MD + (price - MD) / (0.6 * period * (price / MD)^4) speeds up when price falls below the indicator and damps when price runs above. Seeded with the simple average of the first period inputs. Exposed in all four bindings.

0.2.7 - 2026-05-24

Added

  • Windows ARM64 is back. npm Support unblocked the wickra-win32-arm64-msvc sub-package name (same path wickra-win32-x64-msvc took through 0.1.4) and transferred write access to @kingchenc. 0.2.7 ships the binding for aarch64-pc-windows-msvc alongside the existing five platforms: the napi.triples.additional entry, the optionalDependencies pin, the bindings/node/npm/win32-arm64-msvc/ sub-package and the windows-11-arm row of the release.yml node-build matrix are all restored from 8aa74cb. npm install wickra on Windows ARM64 now resolves to a native build instead of failing the loader's optional-dep lookup. PyPI's win_arm64 wheel was unaffected and carries through as before.

Changed

  • Benchmark CPU renamed. The "Reproduced on" line in every README listed an AMD Ryzen 9 7950X3D; the canonical machine is actually a Ryzen 9 9950X. Speedup ratios in the tables are unchanged (they're relative across libraries on the same machine), only the labelling is corrected. The performance-regression issue template's CPU example was updated for consistency.

0.2.6 - 2026-05-24

Fixed

  • docs.rs build. Rust 1.92 removed the doc_auto_cfg feature gate and folded it back into doc_cfg (rust-lang/rust#138907). docs.rs builds against the latest nightly and sets --cfg docsrs, so every published 0.2.x failed with E0557 on the #![cfg_attr(docsrs, feature(doc_auto_cfg))] line at the top of wickra, wickra-core, and wickra-data. GitHub CI didn't see this — stable rustc never enables the docsrs cfg. The three library crates now gate on doc_cfg (same intent, same rendered output on docs.rs, builds again on nightly).

Changed

  • README — Wickra is now the top row of every comparison table. The "Why Wickra exists" library matrix and the per-indicator benchmark tables previously placed Wickra at the bottom; a reader landing on the README is here to compare against Wickra, so the pivot row belongs at the top with a ★ marker. Same column data, same winner annotations — only row order changed. Mirrored across the umbrella README and every binding README so crates.io / PyPI / npm landing pages stay in sync.

0.2.5 - 2026-05-24

Added

  • BinanceConfig plus BinanceKlineStream::connect_with_config(symbols, interval, config) in wickra-data's live::binance module. connect() keeps its previous signature and now forwards to the new entry-point with the defaults, so the public API is backwards-compatible. The config lets callers point the stream at Binance Testnet (wss://testnet.binance.vision) or tune the read timeout, reconnect attempt count, initial / capped backoff and frame size limits without rewriting the connector.
  • README Disclaimer section clarifying that Wickra is an indicator toolkit (not a trading system) and that any production-trading use is at the caller's own risk. The legal terms in LICENSE are unchanged.

Changed

  • BinanceKlineStream::next_event now writes the Pong reply to a server Ping on a best-effort basis. A failed write means the connection is already dead, so the existing timeout / read-error reconnect arm one loop iteration later picks it up — the previous explicit reconnect on Pong-write failure is gone. Observable behaviour is unchanged for every healthy connection.

0.2.1 - 2026-05-23

Changed

  • MSRV bumped. Workspace minimum supported Rust version is now 1.86 (was 1.75) and the Node binding (wickra-node) is now 1.88 (was 1.77). The bumps are driven by transitive-dependency floors that were lifted in recent updates: criterion 0.8.2 (the bench dev-dep) requires Rust 1.86, and napi-build >= 2.3.2 requires Rust 1.88. Pinning those deps to the older versions would have frozen us out of future security fixes from those upstreams, so lifting the MSRV is the cleaner path for a young 0.x library. Downstream consumers on older Rust toolchains can stay on Wickra 0.2.0.
  • Bumped the bench dev-dep criterion from 0.5 to 0.8 and migrated bindings/wickra/benches/indicators.rs from the deprecated criterion::black_box re-export to the stable std::hint::black_box.
  • Bumped tokio-tungstenite from 0.24 to 0.29. WebSocketConfig became #[non_exhaustive] upstream, so the struct-literal construction in crates/wickra-data/src/live/binance.rs is rewritten to the builder-style WebSocketConfig::default().max_message_size(..).max_frame_size(..). Same caps, same semantics, same default carry-over.
  • Bumped every committed CI/release GitHub Action to its latest pinned SHA: actions/checkout 4 → 6, actions/setup-node 4 → 6, actions/setup-python 5 → 6, actions/upload-artifact 4 → 7, actions/download-artifact 4 → 8, softprops/action-gh-release 2 → 3, codecov/codecov-action 5 → 6, taiki-e/install-action patch.

Fixed

  • tick_aggregator gap-fill no longer allocates an unbounded number of placeholder candles. The new MAX_GAP_FILL_CANDLES = 1_000_000 cap surfaces an adversarial timestamp jump (e.g. a clock-glitch tick years in the future) as Error::Malformed instead of an OOM panic. Found by the new tick_aggregator fuzz target.
  • HistoricalVolatility::geometric_series_yields_zero now uses an 1e-6 tolerance instead of 1e-9. The mathematical result on a perfectly geometric price series is exactly zero, but the underlying 1.01_f64.powi(i) + log-return + std-dev cascade accumulates platform-sensitive FP drift on the order of 1e-7 on x86_64 Linux and macOS. The widened tolerance stays four decimal places below any realistic annualised volatility value while absorbing the drift across every supported platform.
  • Replaced every (high + low) / 2.0 test-helper and three real call sites (Ohlcv::median_price, Donchian.middle, EaseOfMovement.mid, SuperTrend.hl2) with f64::midpoint(high, low). The change satisfies clippy 1.95's new manual_midpoint lint without affecting values (f64::midpoint matches the naive average to better than 1 ULP for the inputs used here).
  • Replaced i.is_multiple_of(2) (unstable on Rust 1.85) with i % 2 == 0 in the SMA / Bollinger long-stream-drift tests so the workspace MSRV job builds cleanly on Rust 1.86.
  • The Compile examples CI step now invokes cargo build -p wickra-examples --bins instead of the now-deleted cargo build -p wickra --example backtest / -p wickra-data --example live_binance (the Z5 reorganisation moved every runnable example into the dedicated wickra-examples crate, but the CI step had not been updated).
  • The Fuzz (smoke) CI job installs cargo-fuzz from a prebuilt binary via taiki-e/install-action instead of cargo install cargo-fuzz. The source install resolved against rustix 0.36.5, which uses internal #[rustc_*] attributes the current nightly compiler rejects.
  • The fuzz targets now build with an explicit --target x86_64-unknown-linux-gnu; cargo-fuzz was defaulting to x86_64-unknown-linux-musl, which is not installed on the standard GitHub-hosted Ubuntu runner.

Removed

  • wickra-win32-arm64-msvc is temporarily omitted from this release. The npm spam-detection filter blocks the first publish of this brand-new package name (same situation that affected wickra-win32-x64-msvc through 0.1.4 until npm Support unblocked it). A support ticket is open; once the new name is unblocked the aarch64-pc-windows-msvc triple will be restored in bindings/node/package.json (napi.triples.additional + optionalDependencies), in the release.yml node-build matrix, and as a fresh bindings/node/npm/win32-arm64-msvc/ template. Until then, npm install wickra@0.2.1 on Windows ARM64 will surface the loader's standard Cannot find module 'wickra-win32-arm64-msvc' error; every other platform (Linux x64 / Linux ARM64 / macOS x64 / macOS ARM64 / Windows x64) ships normally. The PyPI wheel for Windows ARM64 is unaffected and still published.

0.2.0 - 2026-05-23

Fixed

  • HistoricalVolatility::update no longer substitutes a 0.0 log-return on non-positive prices (audit finding R13). Negative or zero prices are semantically invalid for a log-return calculation; silently treating them as "no movement" underreported realised volatility. They are now skipped — the previous valid value is returned and the indicator's state (prev_price, window, sums) is left untouched — matching how every other indicator handles invalid inputs.
  • Tick::new now returns the new Error::InvalidTick variant for negative volume instead of Error::InvalidCandle (audit finding R14). A tick is not a candle, and downstream tick-stream pipelines should be able to match on a semantically-correct error. The Python binding's map_err was extended to forward the new variant as a ValueError; the Node and WASM bindings format via Error::to_string() and pick the new variant up automatically.
  • Psar::is_ready now matches the convention shared by every other indicator: is_ready() == true iff a real value has been produced (audit finding R6). The previous implementation returned self.initialised, which flipped to true after the seed candle even though the seed candle itself returns None. A streaming consumer that wrote if ind.is_ready() { use(ind.update(c)?) } would hit an unexpected None on the first post-seed update. The fix introduces a has_emitted gate set when the first Some value is returned.
  • Psar::reset now restores the compute fields (prev_high, prev_low, sar, ep) to f64::NAN sentinels instead of 0.0 (audit Opus-Bonus 1). 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.

Changed

  • Sma and BollingerBands now reseed their incremental sum (and sum_sq for Bollinger) from the live window every 16 · period finite updates, capping floating-point drift on long-running streams (audit findings R7 and L2-Rust). Previously the incremental single-subtract sum -= old could accumulate catastrophic-cancellation error on streams with alternating large/small magnitudes; the misleading sma.rs comment that claimed the drift was already bounded "by recomputing the sum after each pop" is replaced with an accurate description of the new reseed strategy. Amortised cost stays at O(1) (O(period) work amortised over O(period) updates), values are bit-identical on inputs that did not drift to begin with, and two new long_stream_drift_stays_bounded tests stress the recompute by alternating 1e9 / 1.0 (SMA) and 1e6 / 1.0 (Bollinger) for several recompute cycles and verify the reported values track a fresh from-scratch computation over the live window.
  • LinearRegression, LinRegSlope and LinRegAngle (via composition over LinRegSlope) now run their rolling ordinary-least-squares fit incrementally in O(1) per update (audit finding R2). Previously every tick refit the line from scratch in O(period). The OLS denominators (Σx and Σxx) depend only on period, so they were already precomputed; this release adds running Σy and Σxy accumulators and slides them in closed form via the identity new_Σxy = old_Σxy old_Σy + popped_y₀ (then Σxy += (n 1) · new_value and Σy += new_value). New per-bar equivalence tests compare the O(1) output against a fresh O(n) refit on noisy ramps, step functions, and constants — values agree to within 1e-9.
  • Fuzz suite expanded from 2 indicators to the full catalogue (audit finding R9). The existing indicator_update target now exercises every scalar-input indicator (~33 classes including MACD and Bollinger Bands); a new indicator_update_candle target exercises every candle-input indicator (~37 classes, including ATR, ADX, Stochastic, PSAR, Keltner, SuperTrend, ChandelierExit, AwesomeOscillator, OBV, MFI, VWAP, RollingVWAP, and the rest of the volume / volatility / trailing-stop / price-statistics families). Each iteration sweeps every indicator through both the streaming update loop and a full batch call so any state-mutation bug surfaces on either path. CI gains a fuzz-smoke job that runs each of the five targets for 30 s on every push and pull-request.
  • UlcerIndex::update now 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 the Indicator trait'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 on warmup_period() is also corrected: the two windows overlap by one bar, so the formula is 2 * period - 1.

Added

  • RollingVWAP is 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 advertised VWAP (cumulative + rolling)). All four bindings now ship the same cumulative VWAP plus the finite-window RollingVWAP(period). The wiki page Indicator-Vwap.md adds 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, and Obv. 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, or null during warmup — matching the existing SuperTrend convention. Each class also gains reset(), isReady() and warmupPeriod(), bringing the WASM surface to full parity with Python and Node so browser-side streaming code no longer has to replay batch() on every tick. WasmKama gains the previously missing warmupPeriod().
  • New wasm-bindgen integration test exercises update == batch plus 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) and numpy (0.22 → 0.28) in the Python binding. Fixes RUSTSEC-2025-0020 — a buffer overflow in PyString::from_object that affected the published Python wheels. The cargo-deny ignore entry that previously suppressed the advisory has been removed; cargo deny check is now clean without suppression. Migrated into_pyarray_bound to into_pyarray, downcast::<PyDict> to cast::<PyDict>, and opted every #[pyclass] out of the deprecated automatic FromPyObject derive via skip_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.
  • 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 msrv job 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 the fetch_btcusdt example that regenerates them from the Binance REST API.
  • Timeframe::minutes, Timeframe::hours and Timeframe::days convenience 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.md and the README indicator table follow the same eight families.
  • TickAggregator::push returns Result<Vec<Candle>> (was Result<Option<Candle>>) so a single tick can yield a closed bar plus gap fillers.
  • Resampler::push returns Result<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 release environment.
  • 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 new examples/rust/ workspace member crate (wickra-examples, run via cargo run -p wickra-examples --bin <name>), Node into examples/node/ with its own package.json linking wickra via file:../../bindings/node, and the WASM browser demos into examples/wasm/. The bundled BTCUSDT datasets move alongside them at examples/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) and examples/node/fetch_btcusdt.js (Node 18+ built-in fetch) mirror the Rust fetch_btcusdt binary — byte-for-byte identical CSV output on the same Binance snapshot.
  • Four additional WebAssembly browser demos under examples/wasm/ alongside the original index.html: backtest.html (fetch + basket of indicators), live_trading.html (browser-native WebSocket to Binance), multi_timeframe.html (in-page resample) and parallel_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 from talib.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) and FAQ.md. All three linked from Home.md.

Fixed

  • Timeframe::floor no longer overflows for timestamps near i64::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.py skips non-kline frames and validates the symbol/interval; backtest.py and multi_timeframe.py report 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 .crate files) to the tag's release page.

0.1.3 - 2026-05-21

Fixed

  • npm package ships the napi-generated loader and is built with --platform so 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.