Files
wickra/CHANGELOG.md
T

997 lines
58 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
All notable changes to Wickra are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- **Microstructure family — price impact & depth (part 3).** Indicators over a
trade paired with the prevailing mid (`TradeQuote`) and over the order-book
depth profile, exposed in Rust, Python, Node and WASM:
- **Effective Spread** — `2 · D · (tradePrice mid) / mid · 10_000` bps, the
realised round-trip cost of a single trade against the mid.
- **Realized Spread** — `2 · D · (tradePrice mid_{t+horizon}) / mid_t ·
10_000` bps, the share of the effective spread a liquidity provider keeps
once the mid has moved over a configurable horizon.
- **Kyle's Lambda** — the rolling OLS slope of mid changes on signed volume
(`cov(Δmid, q) / var(q)`), the canonical price-impact / market-depth proxy.
- **Depth Slope** — the mean per-side OLS slope of cumulative resting size
against distance from the mid, measuring how fast the book thickens away
from the touch.
- **Microstructure family — footprint (part 4).** **Footprint** decomposes the
volume traded in a bar across price buckets (`round(price / tick_size)`),
splitting each bucket into buy-initiated (ask) and sell-initiated (bid)
volume. A multi-output, variable-length indicator: every `update` returns the
full footprint accumulated since the last `reset`, exposed in Rust, Python
(`(k, 3)` arrays), Node (`{ price, bidVol, askVol }` rows) and WASM.
## [0.4.2] - 2026-06-01
### Added
- **Microstructure family — order book (part 1).** A new family of indicators
that consume an order-book depth snapshot (`OrderBook` of sorted, uncrossed
bid/ask `Level`s) rather than OHLCV, exposed in Rust, Python, Node and WASM:
- **Order-Book Imbalance** — `OrderBookImbalanceTop1`, `OrderBookImbalanceTopN`
(configurable depth) and `OrderBookImbalanceFull` measure signed depth
pressure `(bidDepth askDepth) / (bidDepth + askDepth)` over the top level,
the top-N levels, or the full book.
- **Microprice** — the size-weighted fair value
`(bidPx·askSz + askPx·bidSz) / (bidSz + askSz)`, tilting the mid toward the
side more likely to be hit.
- **Quoted Spread** — the top-of-book spread in basis points of the mid.
- **Microstructure family — trade flow (part 2).** Indicators over a trade tape
(`Trade` with an aggressor `Side`), exposed in Rust, Python, Node and WASM:
- **Signed Volume** — per-trade size signed by aggressor side (`+size` buy,
`size` sell).
- **Cumulative Volume Delta** — the running total of signed volume; reset to
re-anchor per session.
- **Trade Imbalance** — the rolling `(buyVol sellVol)/(buyVol + sellVol)`
over a configurable window of trades.
New public value types `Level`, `OrderBook`, `Side`, `Trade` and `TradeQuote`
back this and the upcoming trade-flow and price-impact indicators. Python and
Node accept a batch over a list of snapshots; WASM exposes per-snapshot
`update`.
- **Signed Doji encoding.** `Doji` gains an opt-in `.signed()` mode
(`Doji(signed=True)` in Python, `new Doji(true)` in Node and WASM) that
classifies a detected Doji by the position of its body within the bar range —
a dragonfly (long lower shadow) emits `+1.0` (bullish), a gravestone (long
upper shadow) emits `1.0` (bearish), and a long-legged / standard Doji emits
`0.0` (neutral). The default construction is unchanged — a direction-less
`+1.0` / `0.0` detection flag — so existing callers are unaffected. This
completes the uniform `+1` bull / `1` bear / `0` none sign convention across
every candlestick pattern, making the family a drop-in machine-learning
feature where bullish and bearish instances share a single dimension.
### Fixed
- **README banner now self-updates.** The top README banner points at the org
profile image that `.github/banner.yml` regenerates from the indicator count,
and `sync-about.yml` bumps a `?v=<count>` cache-buster so GitHub's Camo proxy
refetches it immediately. Also fixes the webpage indicator-count sync, which
silently crashed on a removed `public/hero.svg` and left the marketing site's
count (and its OG banner) stale.
### Security
- **CI dependency installs are pinned by hash.** The Node binding now installs
with `npm ci` (strict `package-lock.json`), and the Python CI/bench tooling is
installed from hash-locked `--require-hashes` requirements under
`.github/requirements/` (OpenSSF Scorecard PinnedDependencies). The `ci-dev`
tooling is locked twice — for Python 3.9 and for 3.10+ — because numpy ships no
single release with wheels for both cp39 and cp313. A new
`scripts/update-lockfiles.sh` regenerates every workspace lockfile (Rust, Node
and the hash-pinned Python requirements) via `uv`, and Dependabot keeps the
pinned requirements current.
## [0.4.1] - 2026-06-01
### Added
- **Cross-asset pairwise indicators.** A new two-series family of
`Indicator<Input = (f64, f64)>` implementations that relate two distinct
assets rather than a single OHLCV stream. Each is exposed in Rust, Python,
Node, and WASM:
- **Pairwise Beta** (`PairwiseBeta`) — rolling OLS slope of one asset's
**log-returns** on another's. Unlike `Beta`, which regresses the raw inputs
it is fed, `PairwiseBeta` differences consecutive prices into log-returns
internally — the conventional way to measure cross-asset beta, where a beta
on price levels would be dominated by the shared trend.
- **Pair Spread Z-Score** (`PairSpreadZScore`) — the standardised log-spread
`ln(a) β·ln(b)` of a pair, where `β` is a rolling-OLS hedge ratio and the
spread is z-scored over its own look-back. The canonical mean-reversion /
statistical-arbitrage entry signal, with independent `beta_period` and
`z_period` windows.
- **LeadLag Cross-Correlation** (`LeadLagCrossCorrelation`) — the integer
offset `k ∈ [max_lag, max_lag]` that maximises `|corr(a[t], b[t+k])|`,
answering which of two assets leads the other and by how many bars. Emits
`{ lag, correlation }`; a positive lag means `a` leads `b`.
- **Cointegration** (`Cointegration`) — the EngleGranger two-step screen for
pairs trading: a rolling OLS hedge ratio `β`, the spread (residual)
`a (α + β·b)`, and an augmented DickeyFuller `t`-statistic on the spread
(configurable `adf_lags`). A strongly negative statistic flags a
mean-reverting, tradeable spread. Emits `{ hedge_ratio, spread, adf_stat }`.
- **Relative Strength A-vs-B** (`RelativeStrengthAB`) — the comparative
relative strength of two assets: the ratio line `a / b` together with its
moving average and its RSI, the classic asset-vs-asset / asset-vs-index
rotation screen. Emits `{ ratio, ratio_ma, ratio_rsi }`.
## [0.4.0] - 2026-06-01
### Added
- **Build-provenance attestations for release artifacts.** The release workflow
now emits signed SLSA build-provenance attestations for the published crates
and Python wheels/sdist (`actions/attest-build-provenance`); npm packages
carry inline Sigstore provenance from `npm publish --provenance`. Every
published artifact is cryptographically traceable to this repository's release
workflow run.
### Security
- **CodeQL static analysis and OpenSSF Scorecard run in CI.** CodeQL (Rust,
Python, JavaScript) and the OpenSSF Scorecard workflow now run on every push;
results appear under Security → Code scanning and a public Scorecard badge is
shown in the README.
- **CI workflows hardened against script injection.** Untrusted event contexts
(PR branch names, `workflow_dispatch` inputs) are passed through the step
environment instead of being interpolated directly into shell commands.
### Changed
- **Node binding: invalid indicator periods now throw instead of being silently
clamped.** The scalar-indicator constructors previously clamped `period = 0`
to `1`; every Node constructor now propagates the core's validation error
(e.g. `period must be greater than zero`), matching the Python and WASM
bindings and the Rust core. Constructing with a valid period is unaffected.
- **Binding package READMEs are now per-ecosystem.** The Python, Node.js, and
WebAssembly READMEs were byte-identical 314-line copies of the workspace
README and had drifted out of sync (stale indicator count, Python snippets
shown on the Node and WASM package pages). Each is now a focused landing page
with the correct install command, a language-correct quick-start snippet, and
links to the canonical documentation — removing the manual three-way sync
burden. No code or API changes.
- **CONTRIBUTING now states the correct MSRV (1.86 workspace / 1.88
`bindings/node`)** and documents that these are the dependency-forced floors,
kept minimal on purpose. The previous text claimed 1.75 / 1.77, which the
`msrv` CI job has enforced against since the criterion and napi-build bumps.
## [0.3.1] - 2026-05-30
### Fixed
- **Release pipeline — CycloneDX SBOM generation.** `cargo-cyclonedx` has no
`-p`/`--package` selector; it walks the whole workspace in a single pass.
The `release.yml` SBOM step invoked it as `cargo cyclonedx … -p <crate>` and
aborted with `error: unexpected argument '-p' found`, which failed the
crates.io publish job *after* the crates were already published and skipped
the GitHub Release attach-assets job (no release page, no SBOM artefacts).
The step now runs a single workspace pass and collects the three crates.io
crate SBOMs. No library changes relative to 0.3.0 — this patch republishes
the same code with a working release pipeline.
## [0.3.0] - 2026-05-30
### Added
- **Family 15 — Risk / Performance metrics (17 new indicators).** Implemented
pragmatically as standard `Indicator`s rather than a separate
`wickra-metrics` crate; the input is a scalar `f64` per bar (period return,
equity sample, or trade P&L depending on the metric).
- **Scalar `Indicator<f64>` — 14 metrics:** Sharpe Ratio, Sortino Ratio,
Calmar Ratio, Omega Ratio, Max Drawdown (rolling), Average Drawdown,
Drawdown Duration (time-under-water), Pain Index, Value at Risk
(historical, linear-interpolated percentile), Conditional Value at Risk
(Expected Shortfall), Profit Factor, Gain/Loss Ratio, Recovery Factor,
Kelly Criterion.
- **Two-series `Indicator<(f64, f64)>` — 3 metrics on `(asset_return,
benchmark_return)` pairs:** Treynor Ratio, Information Ratio,
Jensen's Alpha (CAPM).
- **Candlestick patterns family (15 indicators).** A new "Candlestick
Patterns" family covers the standard 1- to 3-bar reversal and
continuation shapes: `Doji`, `Hammer`, `InvertedHammer`, `HangingMan`,
`ShootingStar`, `Engulfing`, `Harami`, `MorningEveningStar`,
`ThreeSoldiersOrCrows`, `PiercingDarkCloud`, `Marubozu`, `Tweezer`,
`SpinningTop`, `ThreeInside` and `ThreeOutside`. Every detector takes a
`Candle` and emits a signed `f64` (`+1.0` bullish, `-1.0` bearish, `0.0`
no pattern; `Doji` is direction-less and emits `+1.0`/`0.0`). The MVP is
a pattern-shape check only — no trend filter is applied. Available
across Rust, Python, Node and WASM bindings. Harmonic and chart
patterns remain out of scope and will follow once the pattern-detection
framework (pivot detector + multi-bar state machines) lands.
- **Market Profile family** (3 new indicators, opens family #9 across the
catalogue):
- `ValueArea(period, bin_count, value_area_pct)` — rolling
bin-approximation volume profile over the last `period` candles.
Outputs `{poc, vah, val}`: Point of Control is the bin with the highest
cumulative volume; the Value Area expands symmetrically from POC and
always absorbs the higher-volume neighbour next, until the configured
percentage of total volume (default 70%) is enclosed. Each candle's
volume is spread uniformly across its `[low, high]` range; single-print
bars (`low == high`) drop their entire volume into one bin.
- `InitialBalance(period)` — first-N-bar session high / low, frozen
once `period` bars have been ingested. Outputs `{high, low}`. Default
`period = 12` (one-hour IB on 5-minute bars for US equities). Callers
MUST invoke `reset()` at every session boundary, otherwise the IB
locks and stays fixed for the lifetime of the instance.
- `OpeningRange(period)` — same lock-after-N-bars semantics as IB but
with a smaller default window (`period = 6`, 30 min on 5-minute
bars) and a third output `breakout_distance` = `close - or_mid`,
signed (positive above the range, negative below).
- Histogram-output Market Profile variants (Volume Profile / VPVR /
Composite Profile) and tick-data-only variants (TPO / Single Print /
Cumulative Delta / Order Flow Delta / Volume-Weighted Open) are
deliberately out of scope of this PR: the former need a new
histogram-output API layer, the latter need tick / L2 data which
`wickra-data` does not yet expose.
- **Family 12 — Statistik / Regression (13 indicators).** A complete
statistical toolkit for analysing rolling price distributions and
cross-series relationships. Every indicator ships in the Rust core
plus all three bindings (Python, Node, WASM), with full streaming +
batch parity, fuzz coverage, and benches against the BTCUSDT
dataset:
- **Variance** — rolling population variance (`StdDev` squared).
- **CoefficientOfVariation** — `StdDev / Mean`, dimensionless dispersion.
- **Skewness** — rolling third standardised moment (Pearson skewness).
- **Kurtosis** — rolling excess kurtosis (fourth moment minus `3`).
- **StandardError** — standard error of estimate for the rolling OLS
fit, with `n 2` residual degrees of freedom.
- **DetrendedStdDev** — population standard deviation of OLS
residuals (the StdDev that remains after subtracting the linear
trend).
- **RSquared** — coefficient of determination of the rolling OLS
fit; the trend-quality filter.
- **MedianAbsoluteDeviation** — robust dispersion measure that
survives outliers (median of absolute deviations from the median).
- **Autocorrelation** — rolling lag-`k` Pearson autocorrelation;
detects periodicity and tests for white-noise behaviour.
- **HurstExponent** — R/S-analysis estimator of trend-persistence
vs. mean-reversion regime (`0.5` is random walk).
- **PearsonCorrelation** — rolling correlation between two
synchronised series; takes `(x, y)` pairs.
- **Beta** — rolling OLS slope of an asset on a benchmark; the CAPM
sensitivity coefficient.
- **SpearmanCorrelation** — rolling rank correlation (monotone,
outlier-robust analogue of Pearson).
Indicator count: 71 → 84.
- **Family 13 — Ichimoku & alternative charts.** Two new indicators:
- `Ichimoku` (Ichimoku Kinko Hyo) — the full five-line cloud system
(Tenkan-sen, Kijun-sen, Senkou Span A/B, Chikou Span) with the
classic `(9, 26, 52, 26)` defaults and configurable periods. Forward
displacement is handled in a streaming ring buffer so the
currently-visible Senkou A/B at bar *n* are the values computed
from bar *n displacement*.
- `HeikinAshi` — the candle smoothing transform that recursively
averages OHLC into a four-component output (`ha_open`, `ha_high`,
`ha_low`, `ha_close`). Seeds `ha_open` from the first bar's
`(open + close) / 2`.
Exposed in all four bindings (Rust, Python, Node, WASM). Renko,
Kagi, and Point & Figure from the family ideas list are deferred:
they are custom bar generators rather than indicators and belong in
`wickra-data`.
- **Family 10 — Ehlers / Cycle (DSP) indicators.** 16 new
streaming-first indicators implementing John Ehlers'
digital-signal-processing school of cycle analytics — a strong
differentiation feature versus TA-Lib and pandas-ta, which only
ship fragments of this catalogue:
- **MAMA / FAMA** (MESA Adaptive Moving Average + Following
Adaptive Moving Average) — phase-rate-adaptive smoothing pair
from the 2001 MESA paper, exposed both jointly via `Mama` (multi-
output) and as a scalar `Fama` wrapper.
- **Fisher Transform** and **Inverse Fisher Transform** — Gaussian
normalisation of price (Ehlers 2002) and its tanh-based bounded
counterpart for oscillators.
- **SuperSmoother**, **Roofing Filter**, **Decycler** and **Decycler
Oscillator** — 2-pole Butterworth lowpass, bandpass and
high-pass complement building blocks from *Cycle Analytics for
Traders* (2013).
- **Hilbert Dominant Cycle**, **Sine Wave** and **Adaptive Cycle**
— Hilbert-transform-based period estimation from *Rocket Science
for Traders* (2001).
- **Center of Gravity**, **Cybernetic Cycle Component**,
**Instantaneous Trendline**, **Ehlers Stochastic** and
**Empirical Mode Decomposition** — EasyLanguage classics from
Ehlers' published catalogue.
- All sixteen are exposed across Rust, Python, Node.js and WASM
bindings, fuzz-tested, benchmarked against real BTCUSDT
1-minute data, and pass `batch == streaming` equivalence.
- Indicator count rises from 71 to **87** across **nine** families.
- **DeMark family (family 11) — 12 new indicators.** TD Setup (9-bar
buy/sell setup counter with parameterised lookback and target), TD
Sequential (Setup + Countdown phase machine emitting setup count,
countdown count and active countdown direction), TD DeMarker
(bounded [0, 1] range oscillator built from high/low expansions),
TD REI (Range Expansion Index — bounded ±100 oscillator with the
classic 5-bar default), TD Pressure (volume-weighted buying /
selling pressure normalised to ±100), TD Combo (aggressive
countdown variant with extra monotone-low / monotone-close
strictness conditions on top of the classic countdown rule), TD
Countdown (standalone 13-bar countdown phase machine emitting
only the signed countdown count and direction — smaller streaming
payload than the full TD Sequential), TD Lines (TDST horizontal
support / resistance levels derived from the highs and lows of
the most-recently-completed setup), TD Range Projection (next-bar
high / low projection from the current bar's OHLC via DeMark's
open-vs-close-weighted pivot), TD Differential (2-bar
buying-pressure-vs-selling-pressure reversal pattern emitting
+1 / -1 / 0), TD Open (gap-and-fade reversal pattern emitting
+1 / -1 / 0 when the open prints outside the prior bar's range
but the subsequent action recovers back into it), and TD Risk
Level (protective stop levels derived from the lowest-low / highest-
high setup bar's true range). All twelve are exposed through the
Rust, Python, Node, and WASM bindings with `batch == streaming`
equivalence tests, candle-stream fuzz coverage, and benchmark
entries on the BTCUSDT 1-minute dataset.
- **Family 08 — Pivots & Support/Resistance.** Seven new indicators land
the previously empty pivot family: Classic (Floor-Trader) Pivot Points
with three resistance and support tiers, Fibonacci Pivots spaced by
0.382 / 0.618 / 1.000 of the prior range, Camarilla Pivots
(Nick Stott's four-tier `(H L) · 1.1 / {12, 6, 4, 2}` levels),
Woodie Pivots with the close-weighted `PP = (H + L + 2·C) / 4`,
DeMark Pivots whose conditional `X` depends on whether the bar closed
up, down or flat, Williams Fractals as a five-bar swing detector and
ZigZag as a percent-threshold swing tracker. Every level/swing is
exposed across Rust, Python, Node and WASM with the standard
`update` / `batch` / `reset` / `is_ready` / `warmup_period` surface
and matching streaming-vs-batch and reference-value tests. The fuzz
candle target now covers all seven.
- **Family 09 — Trailing Stops, seven new indicators.** Rounds out the
trailing-stop family from 5 to 12: `HiLoActivator` (Crabel's
SMA-of-high / SMA-of-low trail), `VoltyStop` (Cynthia Kase's
extreme-anchor ATR stop), `YoyoExit` (long-only ATR trail with a
re-entry trigger), `DonchianStop` (the original Turtle exit, lowest
low / highest high), `PercentageTrailingStop` (fixed-percent trail),
`StepTrailingStop` (round-number grid trail) and `RenkoTrailingStop`
(block-anchored Renko-style trail). All wired into the four bindings
(Rust, Python, Node, WASM), the streaming + batch fuzz targets, and
the bench harness.
- **Klinger Volume Oscillator (KVO).** Stephen J. Klinger's trend-aware
volume-force oscillator: `EMA(vf, fast) EMA(vf, slow)` over a daily
volume force scaled by cumulative-measurement ratio. Classic
`(fast, slow) = (34, 55)` exposed via `Kvo::classic()`.
- **Volume Oscillator (VO).** Percent difference between a fast and a
slow SMA of bar volume: `100 · (SMA(vol, fast) SMA(vol, slow)) /
SMA(vol, slow)`. Default `(14, 28)`.
- **Negative Volume Index (NVI).** Paul Dysart's cumulative index that
only updates on volume-contraction bars (`volume_t < volume_{t1}`),
absorbing the percent close change on those quiet days. Fosback
baseline `1000.0`, configurable via `Nvi::with_baseline`.
- **Positive Volume Index (PVI).** The complementary index that
updates on volume-expansion bars (`volume_t > volume_{t1}`).
- **Williams Accumulation/Distribution.** Larry Williams' volume-less
cumulative flow that anchors to the previous close (true high/low) and
classifies each bar as accumulation, distribution, or neutral by the
sign of the close-to-close change.
- **Anchored VWAP.** A cumulative VWAP whose accumulation begins at a
user-chosen anchor bar rather than the session open. Re-anchor at
runtime via `AnchoredVwap::set_anchor` for click-to-anchor trader
workflows.
- **Demand Index (Sibbet).** James Sibbet's smoothed buying-vs-selling
pressure ratio in the streaming-friendly textbook form
`EMA(volume · close-return · (1 + range/close), period)`.
- **Time Segmented Volume (TSV).** Don Worden's rolling sum of signed
volume weighted by the close-to-close move: a window-sum measure of
net accumulation/distribution.
- **Volume Zone Oscillator (VZO).** Walid Khalil's normalised
volume-flow oscillator bounded in `[100, 100]`, defined as
`100 · EMA(signed_volume) / EMA(volume)`.
- **Market Facilitation Index (Bill Williams).** Per-bar
`(high low) / volume` — how much price movement the market produces
per unit of volume.
- **ADXR (Average Directional Movement Index Rating)** in the Trend &
Directional family. Wilder's directional-strength smoother: the
average of the current `ADX` and the `ADX` from `period - 1` bars
ago. Warmup is `3 * period - 1` (e.g. 41 for the default `period =
14`). Shipped across all four bindings (Rust core, Python, Node,
WASM) plus fuzz/test/bench coverage.
- **Random Walk Index (RWI)** in the Trend & Directional family. Mike
Poulos' trend-vs.-random-walk gauge: for each lookback `i ∈ [2,
period]` the ratio of actual displacement to the random-walk
expectation `ATR_i * sqrt(i)` is taken; the per-bar output is the
maximum across lookbacks for both the high (`RWI_High`) and low
(`RWI_Low`) directions. Multi-output `(high, low)` across all four
bindings; warmup `= period`.
- **Trend Intensity Index (TII)** in the Trend & Directional family.
M.H. Pee's `[0, 100]` oscillator: the share of the most recent
`dev_period` SMA-deviations that are positive, scaled to
`[0, 100]`. Saturates at 100 on a pure uptrend, at 0 on a pure
downtrend, and returns the neutral 50 on a perfectly flat market.
Canonical Python defaults `(sma_period=60, dev_period=30)`; warmup
`= sma_period + dev_period 1`.
- **Wave Trend Oscillator (LazyBear)** in the Trend & Directional
family. Two-line mean-reverting momentum gauge built from the
typical price and three cascaded EMAs:
`esa = EMA(ap, channel)`, `d = EMA(|ap esa|, channel)`,
`ci = (ap esa) / (0.015 · d)`, `wt1 = EMA(ci, average)`,
`wt2 = SMA(wt1, signal)`. `WaveTrend::classic()` exposes the
LazyBear defaults `(channel = 10, average = 21, signal = 4)`;
warmup `= 2 · channel + average + signal 3` (42 for the classic
defaults). Includes a sub-ULP flat-tolerance guard on `ci` so a
perfectly flat market reports `(0, 0)` instead of the
mathematically indeterminate `1 / 0.015 = 66.67`. Multi-output
`(wt1, wt2)` across all four bindings.
- **Family 05 — Bands & Channels (11 new indicators).** Eleven additional
price-envelope overlays organised into the new "Bands & Channels"
family, exposed across all four bindings (Rust, Python, Node, WASM):
- `MaEnvelope` — SMA centerline with fixed-percent envelope (the oldest
band overlay still in use).
- `AccelerationBands` — Price Headley's momentum-biased bands that widen
with the bar's relative range `(H L) / (H + L)`.
- `StarcBands` — Stoller Average Range Channel: SMA(close) ± k·ATR
(Keltner's SMA-centerline sibling).
- `AtrBands` — Close-anchored envelope of width `k · ATR`, the standard
volatility-targeting stop/target band.
- `HurstChannel` — SMA centerline wrapped by the rolling high-low range
(Brian Millard / Hurst-cycle channel).
- `LinRegChannel` — Linear-regression endpoint ± k·σ of the residuals,
measuring dispersion about the *trend* rather than the mean.
- `StandardErrorBands` — Linear regression with the OLS standard error
(denominator `n 2`) for prediction-interval bands.
- `DoubleBollinger` — Kathy Lien's `±1σ` plus `±2σ` zone-partition setup.
- `TtmSqueeze` — John Carter's BB-inside-KC squeeze flag paired with a
detrended-close momentum reading.
- `FractalChaosBands` — Bill Williams 5-bar fractal high/low envelope.
- `VwapStdDevBands` — Cumulative VWAP with volume-weighted standard
deviation bands.
Indicator count rises from 71 to 82 across nine families; the README
family table and the wiki overview/sidebar/warmup pages were updated to
match.
- **Yang-Zhang Volatility.** Yang & Zhang (2000) gold-standard OHLC
estimator: a convex blend of overnight (close-to-open), open-to-close
and Rogers-Satchell variances. The blending factor
`k = 0.34 / (1.34 + (n+1)/(n-1))` is the one that minimises
estimator variance under driftless GBM with overnight gaps. The
overnight and open-to-close pieces use sample variance (Bessel's
correction, divisor `n1`), so the indicator needs `period + 1` bars
to emit. Output annualised to a percent. Defaults: `period = 20`,
`trading_periods = 252`. The recommended OHLC estimator for equities,
futures, and any asset with material close-to-open gaps.
- **Rogers-Satchell Volatility.** Drift-free OHLC realised-volatility
estimator from Rogers, Satchell & Yoon (1994). Per-bar sample is
`ln(H/C)·ln(H/O) + ln(L/C)·ln(L/O)`; every term is non-negative by
construction (high >= open, close; low <= open, close), so the
rolling mean is exact, not biased, under arbitrary drift. The
algebraic drift-cancellation is what differentiates it from
Garman-Klass. Output annualised to a percent. Defaults:
`period = 20`, `trading_periods = 252`.
- **Garman-Klass Volatility.** Garman & Klass (1980) OHLC realised
volatility estimator: per-bar sample is
`0.5·(ln H/L)² (2·ln2 1)·(ln C/O)²`, then take the annualised
square root of the rolling mean. Roughly 7.4× more statistically
efficient than close-to-close stddev under driftless GBM. Output
annualised to a percent. Defaults: `period = 20`,
`trading_periods = 252`.
- **Parkinson Volatility.** Michael Parkinson's (1980) high-low realised
volatility estimator: `sigma² = (1 / (4n·ln2)) · Σ (ln(H/L))²`. Output
annualised to a percent in the same style as `HistoricalVolatility`
(pass `trading_periods = 1` for the raw per-bar `sigma·100` figure).
Roughly 5× more statistically efficient than close-to-close stddev
under a driftless-GBM assumption. Defaults: `period = 20`,
`trading_periods = 252`.
- **RVIVolatility (Relative Volatility Index).** Donald Dorsey's
RSI-shaped volatility gauge: partition the rolling standard
deviation of close into "up" (close rose) and "down" (close fell)
samples, Wilder-smooth each side, and compute
`100 · AvgUp / (AvgUp + AvgDown)`. Bounded on `[0, 100]`; saturates
at `100` in pure uptrends, `0` in pure downtrends, and falls back to
`50` on a completely flat series (same undefined-RS convention as
`RSI`). Single `period` parameter (default `10`) drives both the
stddev window and the Wilder smoothing. Named `RVIVolatility` rather
than plain `RVI` to disambiguate from Relative Vigor Index, which
ships in Family 02 under the shorter `RVI` name.
- **Family 03 — MACD & Price Oscillators.** `Stc` (Schaff Trend Cycle,
Doug Schaff): doubly-`Stochastic`-smoothed MACD producing a bounded
`[0, 100]` reading that reacts faster than `MACD` itself. Four
parameters `(fast = 23, slow = 50, schaff_period = 10, factor = 0.5)`.
Output is clamped to `[0, 100]` to absorb floating-point rounding.
Exposed in all four bindings.
- **Family 03 — MACD & Price Oscillators.** `ElderImpulse` (Alexander
Elder's Impulse System): tri-state momentum gauge combining `EMA`
trend slope with `MACD` histogram slope. Returns `+1` (green/buy)
when both rise, `1` (red/sell) when both fall, `0` (blue/neutral)
on disagreement. Four parameters
`(ema_period, macd_fast, macd_slow, macd_signal)`; defaults
`(13, 12, 26, 9)` track *Come Into My Trading Room*. Exposed in all
four bindings.
- **Family 03 — MACD & Price Oscillators.** `ZeroLagMacd`: classic
MACD topology with `ZLEMA` substituted for `EMA` everywhere — faster
reaction to trend changes at the cost of slightly noisier readings.
Multi-output `ZeroLagMacdOutput { macd, signal, histogram }`. Three
parameters `(fast = 12, slow = 26, signal = 9)`; `fast` must be
strictly less than `slow`. Exposed in all four bindings.
- **Family 03 — MACD & Price Oscillators.** `CFO` (Chande Forecast
Oscillator): `100 · (close LinReg(close, period)) / close`. Positive
when the close overshoots the linear forecast, negative when it
undershoots. Holds the previous value if the close is zero. Default
period 14. Exposed in all four bindings.
- **Family 03 — MACD & Price Oscillators.** `AwesomeOscillatorHistogram`:
`AO SMA(AO, sma_period)`. A configurable variant of the existing
`AcceleratorOscillator` (which fixes `(fast, slow, sma) = (5, 34, 5)`).
Three parameters; defaults match Bill Williams' Accelerator. Exposed
in all four bindings.
- **Family 03 — MACD & Price Oscillators.** `APO` (Absolute Price
Oscillator): `EMA(close, fast) EMA(close, slow)`. Like MACD's line
without the signal EMA. Default `(fast = 12, slow = 26)`. `fast` must
be strictly less than `slow`. Exposed in all four bindings.
- **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](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](https://rustsec.org/advisories/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.
[Unreleased]: https://github.com/wickra-lib/wickra/compare/v0.4.2...HEAD
[0.4.2]: https://github.com/wickra-lib/wickra/compare/v0.4.1...v0.4.2
[0.4.1]: https://github.com/wickra-lib/wickra/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/wickra-lib/wickra/compare/v0.3.1...v0.4.0
[0.3.1]: https://github.com/wickra-lib/wickra/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/wickra-lib/wickra/compare/v0.2.7...v0.3.0
[0.2.7]: https://github.com/wickra-lib/wickra/compare/v0.2.6...v0.2.7
[0.2.6]: https://github.com/wickra-lib/wickra/compare/v0.2.5...v0.2.6
[0.2.5]: https://github.com/wickra-lib/wickra/compare/v0.2.1...v0.2.5
[0.2.1]: https://github.com/wickra-lib/wickra/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/wickra-lib/wickra/compare/v0.1.4...v0.2.0
[0.1.4]: https://github.com/wickra-lib/wickra/compare/v0.1.3...v0.1.4
[0.1.3]: https://github.com/wickra-lib/wickra/compare/v0.1.2...v0.1.3
[0.1.2]: https://github.com/wickra-lib/wickra/compare/v0.1.1...v0.1.2
[0.1.1]: https://github.com/wickra-lib/wickra/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/wickra-lib/wickra/releases/tag/v0.1.0