Bumps napi 2.16 -> 3.9 and napi-derive 2.16 -> 3.5 (cargo) and @napi-rs/cli
2.18 -> 3.7 (npm). napi 3's derive macros emit #[allow(unsafe_code)], which the
workspace-wide forbid(unsafe_code) cannot permit, so the Node crate gets its own
[lints] block mirroring the workspace with unsafe_code relaxed to deny — forbid
stays in force for every other crate. The CLI-3-regenerated index.d.ts / index.js
keep the identical 626-symbol public API (only the codegen format changed). MSRV
stays 1.88 (napi 3.9 requires exactly that); ureq is intentionally left at 2.x.
Verified locally: cargo clippy clean, 1108/1108 Node tests pass.
Supersedes the napi half of the cargo group bump and the @napi-rs/cli npm bump.
Documentation release for the R binding. The library API and every indicator are
unchanged from `0.9.5`; only the R package's help pages change.
### What's in 0.9.6
- **R package documentation** (landed in #330): the twelve undocumented data-layer
exports now have full man pages, the stale `AwesomeOscillatorHistogram` codoc is
fixed, and a broken `push()` example is corrected — clearing the two `R CMD check`
warnings r-universe reported for `0.9.5`. CI now runs `R CMD check` so doc drift
fails the PR instead of reaching r-universe.
Pure version-string bump on top — `bump_version.py` touched 19 files. `cargo fmt`
clean.
Tag/publish waits for explicit GO (irreversible publish to crates.io / PyPI / npm
/ NuGet / Maven / Go / r-universe).
Maintenance release. The library API and every indicator are unchanged from
`0.9.4`; the only change that ships to users is the R package's build script.
### What's in 0.9.5
- **R package: retry the C ABI download** (`bindings/r/configure[.win]`). A freshly
cut release can briefly 404 while assets propagate; the download is now retried
with a ~2 min backoff instead of failing with `cannot open URL … 404`. Landed in
#328; ships to r-universe / source installs with this release.
- The rest of #328 — CI/release-pipeline hardening (R/NuGet dependency caching,
job timeouts 20→30 / release 45, network-install retries, wasm-publish cache) —
is infrastructure and does not affect the published artifacts, but makes this
release's own pipeline more robust.
Pure version-string bump on top — `bump_version.py` touched 19 files (Cargo +
Lock, pyproject, node package.json/locks + 6 platform stubs, pom + csproj +
DESCRIPTION, SECURITY, CHANGELOG `[0.9.5]` + compare URLs). `cargo fmt` clean.
Tag/publish waits for explicit GO (irreversible publish to crates.io / PyPI /
npm / NuGet / Maven / Go / r-universe).
Fixes the Linux Python wheel build that broke the `0.9.3` release (and would
have broken `0.9.4`), and adds a CI guard so it cannot regress silently.
### Root cause
The `live-binance` data layer links `native-tls` -> `openssl-sys`, which needs
OpenSSL at build time. Neither wheel container provides it:
- **manylinux** ships no OpenSSL headers, and
- **musllinux** cross-compiles against a musl sysroot that has no OpenSSL at all,
so installing a host package (`yum`/`apk`) cannot reach the cross target.
The 3-OS Python CI jobs build natively on the runner, which already has system
OpenSSL, so CI stayed green while the release container build failed.
### Fix
- New opt-in **`vendored-tls`** feature on `wickra-data` and the Python binding:
enables `native-tls/vendored`, compiling OpenSSL from source and linking it
statically. No system OpenSSL needed on either libc. No-op on macOS/Windows
(Security.framework / SChannel — `openssl-sys` is never in the graph there).
- `release.yml` builds the Linux wheels with `--features vendored-tls` (replaces
the manylinux-only `before-script-linux` header install, which could not fix
the musllinux cross build).
- CI gains a **`manylinux` + `musllinux` container build-smoke** matrix job, so
both container builds run on every PR. This PR's own CI is the proof the fix
works before any release re-attempt.
### Notes
- No version bump: `0.9.4` published nowhere (the release run was cancelled
before any publish job ran), so this lands on `0.9.4` and the tag is re-pointed
at the fixed commit.
- Adds checks to `ci.yml` (the smoke job is now a 2-entry matrix).
Packaging fix for the `0.9.3` data layer.
`0.9.3` published to crates.io, Maven Central, NuGet, npm, and the Go mirror, but
the **Linux Python wheels failed to build**, so `Publish to PyPI` was skipped and
no GitHub Release was attached. Root cause: the `live-binance` data layer links
`native-tls` -> `openssl-sys`, and the `manylinux` / `musllinux` wheel-build
containers do not ship the system OpenSSL headers. The native macOS and Windows
wheels were unaffected (system TLS), which is why CI — running Python natively on
the runners, where OpenSSL is present — stayed green.
### Changes
- **`ci`**: install the OpenSSL headers inside the wheel container via
maturin-action's `before-script-linux` (`openssl-devel` on `manylinux`,
`openssl-dev` on `musllinux`) before maturin compiles. No library code changed.
- **`release: bump 0.9.3 -> 0.9.4`**: version-string bump across the manual
touchpoints + `CHANGELOG` `[0.9.4]`.
`0.9.4` is functionally identical to `0.9.3`; it exists only because the already-
published registries cannot re-release `0.9.3`. PyPI publishes for the first time
starting at `0.9.4` (it skips `0.9.3`).
Release the native data-layer bundle.
`0.9.3` ships everything merged since `0.9.2`:
- **Data layer in all 10 languages** — `CandleReader` (CSV), `TickAggregator`, `Resampler`, live `BinanceFeed` (WebSocket) and the historical `fetch_binance_klines` (REST), each cross-language golden-pinned.
- **`name()` on every indicator** in all 10 languages (514 indicators, golden-pinned).
- **Python is now zero third-party deps** — NumPy is optional (`pip install wickra[numpy]`); `batch` returns a stdlib `array.array` / buffer-protocol `Matrix` (breaking; results numerically identical, batch throughput tradeoff noted in the CHANGELOG).
- **Binance feed: missing `3d` / `1M` intervals** fixed.
Pure version-string bump on top — `bump_version.py` touched 19 files (Cargo + Lock, pyproject, all node package.json/locks + 6 platform stubs, pom + csproj + DESCRIPTION, SECURITY, CHANGELOG `[0.9.3]` + compare URLs). `cargo fmt`/`clippy -D warnings`/`test --workspace --all-features` all green locally (4225+ tests, 0 failed).
The tag/publish is **not** part of this PR — it waits for explicit GO (irreversible publish to crates.io / PyPI / npm / NuGet / Maven / Go / r-universe).
Makes the Python binding truly dependency-free (Task 5b). `pip install wickra` now pulls **zero** third-party packages, and `import wickra` never imports NumPy (verified: `"numpy" not in sys.modules` after a batch call).
## What changed
- **Inputs** accept any sequence or buffer of numbers — `array.array`, `memoryview`, a NumPy `ndarray`, or a plain `list` — via `Vec` extraction (newtypes `Buf1`/`BufI64`). `PyBuffer` is unavailable under `abi3-py39`, and `unsafe_code = forbid` rules out a zero-copy slice, so inputs are copied once (negligible vs. compute).
- **Single-output `batch(...)`** returns a stdlib `array.array('d')` (native buffer protocol → `numpy.asarray` zero-copy).
- **Multi-output `batch(...)`** returns a buffer-protocol `Matrix` preserving `.shape`, integer-row and `[i, j]` access, and `.tolist()`.
- **NumPy** moves to an optional extra (`pip install wickra[numpy]`); it is never required.
- Streaming `update(...)` is unchanged; results are numerically identical.
## Implementation
- Trait `IntoPyData` + a `matrix()`/`f64_array()` helper collapse the ~400 batch call sites; `array.array` is built via `bytemuck` (Zlib/MIT/Apache — `cargo deny check licenses` ok).
- Tests migrated to `array.array`/`Matrix` (1-D `.shape`→`len()`, `.dtype`→`.typecode`; numeric comparisons normalize through a `_to_np` helper). The non-contiguous-input test now asserts acceptance instead of rejection.
## Verification (local)
- `pytest bindings/python/tests` — **1991 passed**.
- `cargo fmt --all`, `cargo clippy --workspace --all-targets --all-features -- -D warnings`, `cargo test --workspace --all-features`, `cargo deny check licenses` — all green.
**BREAKING for Python**: batch return types change from NumPy arrays to `array.array`/`Matrix`. Documented in CHANGELOG; ships with the data-layer release bundle (no separate tag).
Adds `BinanceRest::fetch_klines` to `wickra-data`: a blocking historical kline
downloader (`GET /api/v3/klines`) and the historical counterpart to the F4 live
`BinanceFeed`. It is the last native data-layer primitive needed to drop
third-party HTTP/JSON download helpers (`jackson`, `jsonlite`, `urllib`, …) from
the examples.
## What
- **Core** (`wickra-data`): `fetch_klines(symbol, interval, limit, start?, end?)`
built on `ureq` with native-tls — sharing the exact same TLS backend
(native-tls 0.2 / SChannel) as the existing tokio-tungstenite live feed, so the
two pull one TLS stack, not two. Parses Binance's 12-element array rows via the
existing serde infrastructure into validated `Candle`s. Blocking by design (a
one-shot request needs no async runtime; the FFI boundary is synchronous
anyway). Nine mock-HTTP-server tests cover parse / empty / limit / transport /
JSON / invariant-violation paths.
- **C ABI**: `wickra_binance_fetch_klines(...)` (blocking drain into a caller
buffer, `-1` on error) + regenerated cbindgen header and its vendored Go copy.
- **Bindings**: native Node `fetchBinanceKlines` / Python `fetch_binance_klines`;
generated Go `FetchBinanceKlines` / C# `BinanceFeed.FetchKlines` / Java
`BinanceFeed.fetchKlines` / R `fetch_binance_klines`. C / C++ call the C ABI
directly. **WASM is excluded** (browsers use the host `fetch`).
The four C-ABI bindings are regenerated from the ScriptHelpers generators (not
hand-edited); the regen diff is exactly the new wrapper in each.
## Verification
All ten toolchains green locally: Rust (`cargo test`/`clippy`/`fmt`), Node, Python,
Go, C#, Java, R (`R CMD INSTALL` + smoke), WASM (`cargo check`, confirmed `ureq`
is not pulled). Each binding has an error-path smoke test; the parse/HTTP success
path is covered by the Rust mock-server tests.
No release in this PR — ships with the data-layer + numpy bundle later.
* feat(data): C-ABI Binance feed + Go binding (F4 wip)
C ABI exposes the existing async BinanceKlineStream (tokio + TLS, auto-reconnect,
mock-server-tested in wickra-data) through a blocking poll: wickra_binance_connect
/ _next(out, timeout_ms) -> {1 event, 0 timeout, -1 closed} / _close / _free over
an opaque BinanceStream that owns a current-thread runtime. WickraKlineEvent
carries OHLCV + open_time + is_closed + a 16-byte symbol buffer. `live-binance`
is now a default feature of wickra-c (the published DLL ships the feed; the wasm
build drops it via --no-default-features).
Go: NewBinanceFeed(symbols, interval, baseURL) + Next(timeout) + Close, with a
deterministic error-path smoke (the connect->event pipeline is covered by the
Rust mock-WS-server tests).
* feat(data): Binance feed C# + Java bindings (F4 wip)
C#: BinanceFeed(symbols, interval, baseUrl?) + Next(timeout) -> KlineEvent? +
Dispose; bespoke WickraKlineEvent native struct (fixed symbol buffer + byte
is_closed) since the scalar struct parser can't model it. `char` maps to `byte`
for the const char* params.
Java: BinanceFeed + KlineEvent record + BinanceInterval enum over Panama FFM;
the event is read at hand-computed offsets (symbol@0, doubles@16..48,
open_time@56, is_closed@64; 72-byte struct). Both with deterministic error-path
smokes (pipeline covered by the Rust mock-WS-server tests).
* feat(data): Binance feed R binding (F4 wip)
R: BinanceFeed(symbols, interval, base_url) + binance_next(feed, timeout_ms) ->
named list | NULL + binance_close, via bespoke .Call glue (wk_binance_*). The
glue + its registration entries are gated out of the Emscripten/wasm build
(#ifndef __EMSCRIPTEN__) since r-universe/webR has no raw sockets. NAMESPACE
exports added by hand (roxygen2 not installed locally). Deterministic error-path
smoke; pipeline covered by the Rust mock-WS-server tests.
* feat(data): native Binance feed for Node + Python; CHANGELOG (F4 complete)
Node (napi) BinanceFeed: new(symbols, interval, baseUrl?) + next(timeoutMs) ->
KlineEvent | null + close. Python (pyo3) BinanceFeed: same, with next releasing
the GIL (py.detach) while it waits. Both drive the mock-server-tested async
BinanceKlineStream on a single-thread tokio runtime (blocking poll); wickra-data
gains the live-binance feature + tokio in each binding.
Completes F4: the live Binance kline feed is now native in all 9 languages
(WASM excluded), with no third-party WebSocket client in any of them.
* feat(data-layer): TickAggregator in Node, WASM, Python + C ABI hub
First data-layer feature (F2): roll trade ticks up into fixed-timeframe OHLCV
candles, exposed natively and over the C ABI.
- wickra-data wired as a binding dependency (workspace dep; its wickra-core dep
is default-features=false so it never forces rayon into the rayon-free WASM
build — native bindings re-enable parallel through their own dependency).
- Node `TickAggregator(bucket, gapFill?)` -> `push(price, size, ts): Candle[]`;
WASM the same (array of objects); Python `push(...) -> list[tuple]`.
- C ABI: `WickraCandle` struct + `wickra_tick_aggregator_new/push/free` (push
writes candles into a caller buffer and returns the count), generated via the
capi generator's new DATA_LAYER section; cbindgen now parses wickra-data so
`TickAggregator` is a forward-declared opaque; header vendored to bindings/go.
Verified bit-identical across Node/WASM/Python/C/C++ (o=100 h=101 l=100 c=101
v=3 ts=0 for the shared 3-tick probe). WIP: Go/C#/Java/R generated bindings and
the cross-language golden are still pending.
* feat(data-layer): TickAggregator in Go, C#, Java, R (lossless push/drain)
Complete F2 across all 10 languages: the C-ABI tick aggregator now uses a
two-step push/drain so gap-fill candles are never lost, and the four generated
bindings expose it idiomatically.
- C ABI redesigned: opaque TickAggregator handle (inner aggregator + pending
buffer); push consumes a tick and returns the closed-candle count, drain copies
them into a count-sized caller buffer.
- Go: NewTickAggregator + Push(price,size,ts) []Candle; C#: TickAggregator +
Candle[] Push(...); Java: TickAggregator + Candle[] push(...); R: TickAggregator
constructor + push() S3 generic returning an (n x 6) numeric matrix.
- Candle output record generated per language from WickraCandle.
Verified bit-identical to the native bindings (o=100 h=101 l=100 c=101 v=3 ts=0)
in Go, C#, Java, and R at runtime; R passes R CMD check (pre-existing doc
warnings only). WIP: cross-language data-layer golden + CHANGELOG still pending.
* test(data-layer): cross-language golden for the tick aggregator + CHANGELOG
gen_golden emits a deterministic tick stream (testdata/golden/data_ticks.csv) and
the reference candle streams with and without gap filling (data_candles.csv,
data_candles_gap.csv). Every binding replays the shared ticks through its
TickAggregator and checks the candles bit-for-bit (fp tolerance) against the Rust
reference:
- Node / WASM / Python / Go / C# / Java / R: a dedicated parity test each.
- C / C++: data_layer_test.c (compiled as both, run as ctest).
The gap-fill fixture closes several candles from a single push, exercising the
lossless push/drain path. Records the feature under CHANGELOG [Unreleased].
* fix(examples): rename the CSV-loader candle to WickraBar
The example CSV helper (wickra_csv.h) defined its own struct WickraCandle, which
now collides with the public C ABI WickraCandle (the tick aggregator output) in
any example that includes both headers (backtest, multi_timeframe, the strategy
examples). The public type owns the name; rename the example loader's bar to
WickraBar. The generated golden_test.c is untouched (its only match was the
unrelated WickraCandleVolumeOutput).
rust-numpy 0.29 lifted its pyo3 ^0.28 pin, so the resolver can now select
pyo3 0.29. Bump both (kept as a pair) and drop the temporary not-affected
exceptions for RUSTSEC-2026-0176 and RUSTSEC-2026-0177 from deny.toml and
osv-scanner.toml — pyo3 0.29 fixes both advisories.
No public API change; the Python test suite passes unchanged (957 tests).
Maintenance release — supply-chain and CI housekeeping only. No library code or
public API changes.
### Security
- Triaged the pyo3 advisories RUSTSEC-2026-0176 / RUSTSEC-2026-0177 as not
affecting Wickra (vulnerable APIs unreachable from the binding; fix blocked
upstream by rust-numpy pinning pyo3 `^0.28`). Recorded in `deny.toml` and
`osv-scanner.toml`.
### Changed
- Java binding: `central-publishing-maven-plugin` 0.5.0 → 0.10.0.
- CI GitHub Actions bumped to latest (checkout, setup-go, setup-java,
codeql-action, taiki-e/install-action).
- Added a Maven ecosystem to Dependabot.
Version bumped across all manifests/lockfiles via `bump_version.py`; Cargo.lock
refreshed. CHANGELOG `[0.8.9]` filled. Tag/publish to follow on explicit GO.
Version bump `0.8.7` → `0.8.8`.
### Fixed
- R binding: declare `Depends: R (>= 2.10)`, clearing the `R CMD check` "package needs dependence on R (>= 2.10)" warning that the bundled, lazy-loaded `sample_ohlcv` dataset triggers on r-universe / CRAN. (#264)
Bump touches the manual release touchpoints only; docs/webpage version strings are left to `sync-about.yml` on the tag.
Version bump `0.8.6` → `0.8.7`.
### Added
- R binding: a *Getting started* vignette and a synthetic `sample_ohlcv` example dataset, giving new users a runnable, self-contained walkthrough and populating the R-universe Articles and Datasets tabs. The vignette's code is exercised in CI so a broken example is caught before the published build. (#262)
Bump touches the manual release touchpoints only; docs/webpage version strings are left to `sync-about.yml` on the tag.
Version bump `0.8.5` → `0.8.6`.
### Changed
- Package registry metadata for better discoverability (#259):
- R (R-universe): R-universe URL + `X-schema.org-keywords` in `DESCRIPTION`, package logo at `bindings/r/man/figures/logo.png`.
- Python (PyPI): `Documentation` project URL.
- C# (NuGet): package icon via `PackageIcon`.
Ships the metadata so the next PyPI/NuGet publish carries the new fields (R-universe rebuilds from main independently). Bump touches the manual release touchpoints only; docs/webpage version strings are left to `sync-about.yml` on the tag.
Version bump `0.8.4` → `0.8.5`.
### Fixed
- The R binding's golden-fixture parity test now skips gracefully when the shared `testdata/golden` fixtures are not bundled with the package — standalone r-universe / CRAN builds package only `bindings/r`, so the repo-root fixtures are unreachable there (this was failing the r-universe build of 0.8.4). The parity stays enforced by the repository CI, where the fixtures are present. (#257)
Bump touches the manual release touchpoints only (`Cargo.toml`/`Cargo.lock`, Python/Node/Java/C#/R manifests, lockfiles, `SECURITY.md`, `CHANGELOG.md`). docs/webpage version strings are left to `sync-about.yml` on the tag.
Version bump `0.8.3` → `0.8.4`.
Ships the work merged via #254:
### Fixed
- A single non-finite (NaN/inf) tick no longer poisons indicator state — 38 more scalar/pairwise indicators (linear-regression family, rolling quantiles/IQR, `Variance`/`StdDev`-derived stats, `Kurtosis`/`Skewness`, trailing stops, `KalmanHedgeRatio`, `SpreadBollingerBands`, …) now reject non-finite input and return `None`, joining the 16 pairwise indicators fixed earlier.
### Added
- Catalogue-wide property-based invariant harness (`crates/wickra-core/tests/invariants.rs`) asserting `batch == streaming`, `reset == fresh`, and non-finite-input rejection for every indicator and bar-builder.
### Changed
- CI: every job now has a runtime cap and the flaky Node test step auto-retries.
- Documentation accuracy fixes in `SECURITY.md`, `ARCHITECTURE.md`, and `THREAT_MODEL.md`.
Bump touches the manual release touchpoints only (`Cargo.toml`/`Cargo.lock`, Python/Node/Java/C#/R manifests, lockfiles, `SECURITY.md`, `CHANGELOG.md`). docs/webpage version strings are left to `sync-about.yml` on the tag.
Version bump 0.8.2 → 0.8.3, releasing the per-binding throughput benchmark work
(merged in #246).
Bumped via `ScriptHelpers/bump_version.py` across all manual touchpoints —
`Cargo.toml`, `pyproject.toml`, the Node `package.json` + 6 platform packages +
both lockfiles, the Java `pom.xml`s + README, the C# `.csproj`, the R
`DESCRIPTION` — plus `Cargo.lock` (via `cargo build`) and the `CHANGELOG.md`
`[0.8.3]` section and compare URLs.
CHANGELOG `[0.8.3]`:
- Per-binding throughput benchmarks for all 9 targets (BENCHMARKS.md §3).
- C ABI archetype test (`examples/c/archetypes.c`).
`cargo fmt`, `cargo test --workspace --all-features` (all green) and
`cargo clippy --workspace --all-targets --all-features -D warnings` pass. The
docs/webpage version strings are bumped by `sync-about.yml` on the `v*` tag —
not touched here.
Patch release shipping the R WebAssembly build fix (#244): `bindings/r/configure` builds the C ABI from source for `wasm32-unknown-emscripten`, so r-universe's webR build stops failing. Also carries the shared Go badge in `bindings/go/README.md`. Version bumped across all manual touchpoints via `bump_version.py` (incl. DESCRIPTION + csproj, which had drifted before).
Patch release shipping the wickra-go mirror license fix (#239). The release-time Go mirror now includes the dual MIT OR Apache-2.0 license files, so the next published Go module version resolves with a redistributable license on pkg.go.dev. No code changes; all other packages are republished unchanged at 0.8.1.
Bumps the workspace from 0.7.9 to 0.8.0 and adds the release-time mirror of the Go module to a standalone `wickra-go` repository.
## release.yml: `go-mirror` job
Adds a `go-mirror` job (independent of `github-release`, `needs: c-abi-build`) that on every `v*` tag:
- assembles the standalone Go module from `bindings/go` (single-package source + the vendored `include/wickra.h`),
- stages the six prebuilt C ABI libraries from the `c-abi-build` artifacts under `lib/<goos>_<goarch>/` (committed in the mirror, unlike the in-repo `lib/.gitignore`),
- rewrites `go.mod` to `module github.com/wickra-lib/wickra-go`,
- commits and tags the result in `wickra-lib/wickra-go` using the `WICKRA_GO_MIRROR_TOKEN` fine-grained PAT.
This makes `go get github.com/wickra-lib/wickra-go` build with no extra steps, closing the gap where the in-repo `bindings/go` module only built inside a full repository checkout. The mirror is a derived artifact, so its bot commit is intentionally unsigned.
## Version bump 0.7.9 -> 0.8.0
Patch never reaches double digits (`0.7.9` -> `0.8.0`). Touchpoints: `Cargo.toml` (+ `Cargo.lock` via build), `bindings/python/pyproject.toml`, `bindings/node/package.json` + 6 platform `npm/*/package.json` + both `package-lock.json` files, `bindings/java/pom.xml` + `examples/java/pom.xml` + Maven/Gradle snippets in `bindings/java/README.md`, and `CHANGELOG.md`. The C# `Wickra.csproj` (version set per tag) and `bindings/c` (inherits the workspace version) are intentionally untouched.
Tagging `v0.8.0` (which triggers the publish + the new mirror) stays gated on explicit confirmation.
## What
Introduces `wickra-c` — a `cdylib` + `staticlib` that exposes the Rust core over a **C ABI**. This is the hub every C-capable language (C, C++, Go, C#, Java, R) links against, instead of re-wiring each indicator natively. The native Python/Node/WASM bindings are untouched; this is purely additive, for ecosystems without first-class Rust tooling.
## Scope (foundation slice)
This PR deliberately validates the **whole pipeline end to end with one indicator (SMA)** before scaling to all 514, so the CI / cross-OS / header-drift mechanics are proven green first.
- Opaque `*mut T` handles; `wickra_<ind>_{new,update,batch,reset,free}`.
- NaN sentinel for warmup / NULL handles; caller-owned batch buffers; every function NULL-safe.
- cbindgen generates and commits `bindings/c/include/wickra.h` with opaque handle typedefs.
- A C smoke example (`examples/c/`) links the header + compiled library and runs (CMake + ctest).
- A `c-abi` CI job builds the library and runs the smoke test on **Linux, macOS and Windows**, plus a header drift check on Linux.
## Notes
- The per-indicator FFI blocks are plain `#[no_mangle]` functions, **not** a macro: cbindgen cannot see macro-generated functions on stable Rust (macro expansion needs nightly), so the blocks are written literally and will be generated mechanically by the ScriptHelpers `capi` wrapper in a follow-up (same model as the committed-but-generated Node `index.js`).
- `bindings/c` cannot inherit the workspace `forbid(unsafe_code)` lint (the C boundary needs raw pointers), so it mirrors every workspace lint and only relaxes `unsafe_code`. The Rust core stays `unsafe`-forbidden.
## Follow-ups (separate PRs)
- ScriptHelpers `capi` generator + wire the scalar family (~235).
- Hand-written blocks for multi-output / custom-input / bars (~279).
- Docs consistency wave (README / docs / webpage: Python·Node·WASM·Rust → +C).
- Release wiring (native-lib matrix + header/lib GH-release assets) — gated.
Version bump **0.7.3 → 0.7.4** for the B19 Alt-Chart Bars batch (7 new bar builders, 507 → 514).
Bumps `Cargo.toml` (+ `wickra-core` dep), `Cargo.lock`, `pyproject.toml`, the Node `package.json` and its six platform packages, both `package-lock.json` files, and the CHANGELOG (`[Unreleased]` → `[0.7.4]` with compare URLs).
Version bump **0.7.2 → 0.7.3** for the B18 Risk / Performance batch (9 new indicators, 498 → 507).
Bumps `Cargo.toml` (+ `wickra-core` dep), `Cargo.lock`, `pyproject.toml`, the Node `package.json` and its six platform packages, both `package-lock.json` files, and the CHANGELOG (`[Unreleased]` → `[0.7.3]` with compare URLs).
Version bump for the **v0.6.5** release shipping the **B10 Ehlers / Cycle** family (#199): 452 -> 462 indicators. Bumps workspace + Python/Node/WASM package versions, lockfiles and CHANGELOG. No code changes.
Version bump for the **v0.6.4** release shipping the **B9 Price Statistics** family (#197): 447 -> 452 indicators. Bumps workspace + Python/Node/WASM package versions, lockfiles and CHANGELOG. No code changes.
Version bump for the **v0.6.3** release shipping the **B8 Volume** family (#195): 440 -> 447 indicators. Bumps workspace + Python/Node/WASM package versions, lockfiles and CHANGELOG. No code changes.
Version bump for the **v0.6.2** release shipping the **B7 Trailing Stops** family (#193): 434 -> 440 indicators.
Bumps workspace + Python/Node/WASM package versions, lockfiles and CHANGELOG (cuts the `[0.6.2]` section). No code changes.
Version bump for the **v0.6.0** release (ships the B5 Volatility & Bands batch, #189 — 423 -> 429 indicators).
Bumps version strings across Cargo workspace, pyproject, node package.json + 6 platform packages, both package-lock.json files, and Cargo.lock; CHANGELOG `[Unreleased]` -> `[0.6.0]`. No code changes.
Versioning note: patch never reaches two digits — `0.5.9` rolls to the next minor `0.6.0` (not 0.5.10).
Patch release: streaming/batch perf (SMA, Bollinger, RSI, EMA, ATR; outputs unchanged), cross-library benchmark harness, honest tiered README. No new indicators, no API changes.
## Summary
An honest, tiered cross-library benchmark — and the optimization pass it triggered.
### Performance (wickra-core, outputs unchanged)
Profiling against the other Rust TA crates exposed real inefficiencies. Each
benchmarked indicator is now **5–79% faster** in both streaming and batch:
- **SMA, Bollinger**: flat `Box<[f64]>` ring buffers replace `VecDeque` (−69…79%).
- **RSI**: `100·ag/(ag+al)` collapses three divisions into one; Wilder smoothing
hoists `1/period` out of the hot path (−46%).
- **ATR**: reciprocal hoisted (−42%).
- **EMA/RSI/ATR**: per-tick `Option<f64>` hot state → bare `f64` + ready flag.
Net result vs `kand`: Wickra now wins **RSI, Bollinger and ATR** (streaming), and
ties `ta-rs` on SMA — up from losing every indicator 1.5–6× before.
### Benchmark harness
New `crates/wickra-bench` (publish=false): a Criterion benchmark comparing Wickra
against `kand`, `ta-rs` and `yata` on an identical BTCUSDT candle series, in
streaming and batch modes. Peer APIs were verified against their source, not
guessed. Wired into the nightly `cross-library-bench` workflow as a separate job.
### Honest README
The benchmark section is rewritten into three layered tables (Rust core vs Rust
crates; Python vs the Python ecosystem) that **show the losses as well as the
wins**. The "only library that combines…" claim is gone; the new framing is
breadth + multi-language reach + the deliberate safety trade-off that costs raw
speed. Added an origin/why-slower rationale and a star CTA.
### Python benchmark
Added `tulipy` runners and expanded per-tick streaming coverage to SMA/EMA/RSI/
MACD/Bollinger. `bench.in`/`bench.txt` now lock `TA-Lib` + `tulipy` (hash-pinned);
`pandas-ta` stays out (it requires Python ≥ 3.12, the bench runs on 3.11).
### Notes
- TA-Lib/tulipy numbers in the README Python table are marked ⧗ — they are
produced by the CI Linux job (C extensions don't build cleanly on every
desktop), not measured locally.
- The matching `wickra-docs` prose update is committed separately and will be
pushed with the release, per the docs-don't-lead-the-registries rule.
Verified locally: `cargo fmt`, `cargo test --workspace --all-features` (3413 core
+ bindings), `cargo clippy --workspace --all-targets --all-features -D warnings`,
Node build + 498 tests, and pytest all green.
Version bump 0.5.6 → 0.5.7 for the **B3 — Trend & Directional** batch (#181):
seven new indicators (`Qstick`, `TtmTrend`, `TrendStrengthIndex`,
`PolarizedFractalEfficiency`, `WavePm`, `GatorOscillator`,
`KasePermissionStochastic`), catalog 413 → 420.
Bumps: workspace `Cargo.toml` + `Cargo.lock`, `bindings/python/pyproject.toml`,
`bindings/node/package.json` + the six `npm/*/package.json` platform manifests,
both `package-lock.json` files, and the `CHANGELOG.md` `[Unreleased]` → `[0.5.7]`
roll with compare URLs.