Commit Graph
100 Commits
Author SHA1 Message Date
kingchencandGitHub 025d453293 deps(node): migrate the Node binding to napi-rs 3 (#347)
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.
2026-06-28 00:45:38 +02:00
kingchencandGitHub 9368c6655a docs: add banner + self-made badges to binding READMEs, move title under badges, add a rule (#342)
Each binding README now opens with the shared Wickra banner, a row of self-made
SVG badges (CI, codecov, the language registry, License), then the language
title and a horizontal rule separating the header from the body. The main README
gets the same rule under its badge row (no title).
2026-06-24 05:14:17 +02:00
kingchencandGitHub 063a1b5192 docs: move the Live demo badge one position forward in the README badge row (#341) 2026-06-24 03:33:19 +02:00
kingchencandGitHub fdb4c1e86b docs: link the live in-browser demo (live.wickra.org) (#340)
* docs: link the live in-browser demo (live.wickra.org)

Adds a Live-demo badge + callout to the main README and a callout to all eight
binding READMEs, pointing at the wickra-live in-browser demo of all 514
indicators. DRAFT until live.wickra.org is deployed.

* ci: exclude live.wickra.org from lychee until the wickra-live site is deployed

* Revert "ci: exclude live.wickra.org from lychee until the wickra-live site is deployed"

This reverts commit 3809d4a3b3.
2026-06-24 03:27:01 +02:00
kingchencandGitHub 57e52c67b9 ci: add external link check (lychee, hybrid non-blocking) (#339)
* ci: add external link check (lychee, scheduled + on-demand)

Adds a non-blocking external-link checker mirroring wickra-docs: an
authoritative weekly + on-demand lychee run (this workflow), kept off the
PR/release path so a flaky third-party site can never block a merge or an
irreversible publish. Fixes the one redirecting link it would flag
(docs.rs/wickra -> docs.rs/wickra/latest/wickra/ in SUPPORT.md).

* ci: run lychee as a non-blocking PR job (continue-on-error)

Hybrid link-check: the authoritative run is the scheduled links.yml; this
adds an early heads-up on PRs that never gates the merge. release.yml stays
ungated so a flaky third-party site can never block an irreversible publish.
2026-06-22 15:59:14 +02:00
kingchencandGitHub 96cd9b3f02 release: bump 0.9.6 -> 0.9.7 (#338) 2026-06-21 16:07:53 +02:00
kingchencandGitHub 96ae63799d fix(r): credit kingchenc as R package author/maintainer (#332)
Every other binding (Python, Node, Rust core) credits kingchenc; the R
DESCRIPTION was an outlier with "Wickra contributors" as the sole
aut/cre, introduced when the binding was added (#230). Use kingchenc to
match, regenerate the package .Rd, and align the LICENSE copyright holder
with the root LICENSE-MIT ("kingchenc and the Wickra contributors").
2026-06-18 04:49:24 +02:00
kingchencandGitHub d5f5e14dda release: bump 0.9.5 -> 0.9.6 (#331)
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).
2026-06-18 02:10:03 +02:00
kingchencandGitHub 7e5d394f2a fix(r): document the data-layer exports + add an R CMD check gate (#330)
Clears the R CMD check **WARNING**s r-universe surfaced when it finally built
0.9.5 (it was stuck on 0.9.2, before the data layer existed): 11/13 platform
builds reported the same two warnings.

### Warnings fixed
1. **Undocumented code objects** — the data layer added in 0.9.3 (`BinanceFeed`,
   `CandleReader`, `Resampler`, `TickAggregator`, `fetch_binance_klines` and the
   generics `name` / `is_ready` / `warmup_period` / `push` / `read`). The roxygen
   blocks existed in `methods.R`, but the `man/*.Rd` were never regenerated, and
   the constructors had only a title.
2. **Codoc mismatch** — `AwesomeOscillatorHistogram.Rd` still documented
   `sma_period` after the argument was renamed to `lookback`.

### Changes
- Full `@param`/`@return` roxygen on the seven data-layer constructors.
- Regenerated `man/*.Rd` + `NAMESPACE` with roxygen2: 13 new pages, the stale
  AwesomeOscillator usage refreshed, and `flush` registered on `base::flush`.
- Fixed the `push()` example (`TickAggregator(1000)` was missing the required
  `gap_fill`) — it only ran once `push()` got a generated `.Rd`.
- **New CI gate:** a ubuntu-only `R CMD check` in the `r` job that fails on doc
  WARNING/ERROR, so stale docs fail the PR instead of reaching r-universe.

Verified locally with `R CMD check` (R 4.6.0 + Rtools45): *missing documentation
entries* and *Rd \usage sections* now **OK**, examples **OK**, tests **OK**. This
PR's own CI exercises the new gate.

Follow-up: a separate `0.9.6` release ships these doc fixes to r-universe.
2026-06-18 01:38:44 +02:00
kingchencandGitHub ff268500ef release: bump 0.9.4 -> 0.9.5 (#329)
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).
2026-06-17 23:55:18 +02:00
kingchencandGitHub 929fc17127 ci: harden cache, timeouts and retries across CI and release (#328)
Hardens both workflows after the R-on-ubuntu job repeatedly hit the 20-minute
job cap and was cancelled (no R-package cache + no retry + a slow source build).
Each item below maps to the requested checklist.

### CI (`ci.yml`)
- **Timeouts 20 → 30 min** on every job (backstop only; real jobs finish well under).
- **R dependencies cached**: replace the manual `install.packages(testthat/knitr)`
  with `r-lib/actions/setup-r-dependencies` — restores a cached R library and pulls
  **RSPM binaries** instead of compiling from source (the slow/flaky path that blew
  the cap). This is the actual root-cause fix.
- **NuGet cache** for the C# job (`~/.nuget/packages`, keyed on the project files).
- **Retry** the network installs that had none — `npm ci`, `dotnet test`,
  `mvn install` — via `nick-fields/retry` (2–3 attempts, backoff). On top of the
  existing env-level retries (`CARGO_NET_RETRY`, `npm_config_fetch_retries`,
  `PIP_RETRIES`) and the setup-* CDN-flake retries.
- **Go stays `cache: false`** on purpose: the module has no `go.sum` / external
  deps, so there is nothing to cache (enabling it would only warn).

### Release (`release.yml`)
- **Per-job timeouts** added (there were none — only GitHub's 6h default): **45 min**,
  higher than CI's 30 because the wheel/build jobs compile from source incl.
  **vendored OpenSSL** and must not be killed mid-build.
- **wasm-publish** gets a `Swatinem/rust-cache` like the other Rust-build jobs.
- **Retry** the no-retry network installs (`npm ci` ×2, `dotnet pack`). The actual
  publish/deploy steps are left alone — they are already idempotent
  (skip-existing / skip-duplicate), so re-running the job is the safe recovery.

### R binding download (`bindings/r/configure[.win]`)
- A freshly cut release can 404 for 1–2 min while assets propagate, which broke
  the C ABI download (`cannot open URL … 404`). Add a `wickra_download` retry
  helper (6 × 20s ≈ 2 min backoff) for both the release-asset and wasm-source
  downloads. Note: the CI R job builds the C ABI **locally** (`WICKRA_*_DIR`), so
  it never downloads — this fix covers the real-world r-universe / end-user build.

This PR's own CI exercises the CI changes (the reworked R job, caches, retries,
timeouts) before merge; the release-only changes are validated on the next tag.
2026-06-17 23:42:06 +02:00
kingchencandGitHub 41d5a7dd25 fix: build Linux Python wheels with vendored OpenSSL (manylinux + musllinux) (#327)
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).
2026-06-17 22:26:21 +02:00
kingchencandGitHub e595ea8bfe release: bump 0.9.3 -> 0.9.4 (#326)
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`).
2026-06-17 21:35:14 +02:00
kingchencandGitHub a5fe2e71c4 release: bump 0.9.2 -> 0.9.3 (#325)
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).
2026-06-17 21:14:22 +02:00
kingchencandGitHub 75eefbbd08 examples: fix and harmonize the strategy backtests across all languages (#324)
The strategy_* examples were only syntax-smoked in CI, never run, which hid two
classes of problem:

1. Python strategy_macd_adx / strategy_bollinger_squeeze passed three separate
   arguments to the candle indicators ADX/ATR, whose .update() takes a single
   candle — a TypeError at runtime — and read the ADX tuple at index 0 (plus_di)
   instead of 2 (adx). Both fixed.

2. The Go / C# / R / Java strategies defaulted to synthetic data and used a
   different (annualised) one-line summary, so they printed wildly different
   numbers from the Rust/Python/Node/C/WASM suite. Rewrite them to the shared
   per-trade backtest (load the bundled BTCUSDT CSV by default, same entry/exit
   logic, same print_summary output).

All nine runnable bindings now print byte-identical backtest summaries on the
same data (MACD+ADX 246 trades / -47.19%, RSI 37 / -17.84%, Bollinger 1 / -7.82%),
verified by diffing each language's output against the Python reference. WASM
shares the same logic and bundled dataset (browser-rendered).
2026-06-17 17:56:22 +02:00
kingchencandGitHub 2e07c07a40 docs(examples): drop stale third-party install hints (#323)
live_binance.py uses the native BinanceFeed and the node examples no longer
depend on ws, so the examples README no longer tells readers to pip install
websockets or that npm install pulls ws. The whole examples set runs on
Wickra alone.
2026-06-17 17:56:17 +02:00
kingchencandGitHub ec937b8281 docs: re-measure per-binding throughput on the 9950X (#322)
Refresh the section 3 / README per-binding throughput table with a fresh
SMA(20) run on the reference machine. The Python batch figure now reflects
the stdlib array.array output path (NumPy is optional since the zero-dep
change), so batch is no longer near-core for Python and Node; the prose is
updated to match.
2026-06-17 15:30:20 +02:00
kingchencandGitHub 1f5eb90b0d docs: PyPI README reflects the zero-dependency Python binding (#321)
The package README shown on **PyPI** still advertised a NumPy quick-start. Now that Python dropped its NumPy runtime dependency (#317), this updates it:

- Tagline + install note: `pip install wickra` pulls **zero** third-party packages (not even NumPy); NumPy is an optional extra (`wickra[numpy]`).
- Quick start imports no NumPy; `batch` returns `array.array('d')` (with a note that `np.asarray` wraps it zero-copy if you use NumPy).

Ships with the data-layer release.
2026-06-17 03:40:18 +02:00
kingchencandGitHub bbda70f75b docs: lead with the zero-dependency native data layer (README) (#320)
Updates the README now that the data layer is native in all ten languages and Python no longer needs NumPy (#317): the docs lead with **zero third-party deps for data I/O, in every language**.

- **'Batteries included'** bullet now covers the full data layer — CSV reader, tick aggregator, resampler, live WebSocket feed, historical REST fetcher — as zero third-party deps in every language. Drops the stale 'indicator chaining' wording (chaining stays documented under Reference).
- **Hero Python example** no longer imports NumPy; `batch` returns `array.array('d')` (with a note that `np.asarray` wraps it zero-copy if you do use NumPy).
- **'Live data sources'** lists the native `fetch_binance_klines` and the `BinanceFeed` naming, and drops the stale reference to the third-party `websockets` package.
- Intro tagline notes 'zero third-party packages'.

Docs-only; ships with the data-layer release. The matching webpage / wickra-docs / wickra-go / org-profile updates follow with the release.
2026-06-17 03:27:19 +02:00
kingchencandGitHub 2d2d5970b4 bench: native data-layer throughput benchmark (#318)
Adds a measured benchmark for the native data layer (Task 6).

## What
- New criterion bench `crates/wickra/benches/data_layer.rs` exercising the data layer on **50 000 real BTCUSDT 1m candles** (`examples/data/btcusdt-1m.csv`):
  - **CSV parse** (`CandleReader`)
  - **Tick aggregation** → 1m candles (`TickAggregator`)
  - **Resample** 1m → 5m (`Resampler`)
- New **"4. Data layer"** section in `BENCHMARKS.md` with the measured numbers and the same "one core, FFI boundary" framing as the per-binding table.

## Measured (Rust core, Ryzen 9 9950X, median of 100 samples)
| Operation | Throughput | Per element |
|---|--:|--:|
| CSV parse | 3.0 M candles/s | 329 ns |
| Tick aggregate → 1m | 44 M ticks/s | 22.6 ns |
| Resample 1m → 5m | 234 M candles/s | 4.3 ns |

This is the same native code every binding rides through the FFI boundary characterised in §3 — the data I/O that replaces pandas / csv-parse / manual bucketing / pandas.resample, with zero third-party packages.

Run: `cargo bench -p wickra --bench data_layer`.

Verified locally: `cargo clippy -p wickra --benches --all-features -- -D warnings` clean; bench runs green.
2026-06-17 03:27:14 +02:00
kingchencandGitHub 8228be7069 python: drop the NumPy runtime dependency (zero third-party deps) (#317)
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).
2026-06-17 03:19:50 +02:00
kingchencandGitHub 677ea37402 examples: migrate to the native data layer (drop ws/coder-websocket/jackson/jsonlite) (#316)
Stacked on #315 (the native Binance REST fetcher). Retarget to `main` once #315 merges.

Migrates the runnable examples off third-party data-I/O packages onto Wickra's
native data layer (`CandleReader`, `Resampler`, `BinanceFeed`, `fetch_*klines`).

## Third-party packages removed (the zero-dep selling point)
- **Node**: `ws` (live feed → BinanceFeed) — dropped from package.json + lockfile
- **Go**: `github.com/coder/websocket` — dropped from go.mod / go.sum (`go mod tidy`)
- **Java**: `jackson-databind` (live feed + REST fetch) — dropped from pom.xml
- **R**: `jsonlite` + `websocket` + `later` — dropped from the README notes

Each language's CSV loading now goes through `CandleReader`, manual resampling
through `Resampler`, the live feed through `BinanceFeed`, and (Java/R) the REST
download through the native fetcher.

## Verification
Ran the offline examples per language against the bundled data — backtest and
multi_timeframe produce identical output across Python / Node / Go / Java / R
(e.g. ATR(14) last 345.1010; 1h→5m resamples to 240 bars, →15m to 80 bars).

C# / C / WASM (stdlib-only, no third-party deps to remove) follow in this branch.

Note: the streaming `strategy_*` examples have pre-existing candle-indicator
runtime bugs (CI only syntax-smokes them); the CSV migration preserves their
shape and leaves those bugs for a separate fix.
2026-06-17 01:49:11 +02:00
kingchencandGitHub 2ae76bb90e feat(data): native Binance REST kline fetcher in 9 languages (#315)
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.
2026-06-17 01:48:24 +02:00
kingchencandGitHub b92ad32037 docs(crates): point wickra documentation link to docs.wickra.org (#314)
## What

Change the `documentation` field of the main `wickra` crate from the auto-generated rustdoc URL to the project's documentation site:

```toml
# crates/wickra/Cargo.toml
documentation = "https://docs.wickra.org"   # was: https://docs.rs/wickra
```

## Why

On crates.io the "Documentation" link defaulted to `docs.rs/wickra`. The richer landing page for someone arriving from crates.io is the prose docs site — it carries the Rust quickstart, the API guide, and the full indicator catalogue. The auto-built docs.rs API reference stays reachable at `docs.rs/wickra`; only the crates.io link target changes.

## Scope

- Only the user-facing `wickra` crate is changed.
- `wickra-core` and `wickra-data` keep their `docs.rs` links — those low-level crates have no dedicated guide page on the docs site, so docs.rs remains the correct target for them.
- Metadata only; no code, build, or runtime change. Takes effect on the next crates.io publish.
2026-06-17 01:06:01 +02:00
kingchencandGitHub 3a709d9a66 feat(data): native live Binance kline feed in 9 languages (+ 3d/1M intervals) (#313)
* 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.
2026-06-16 02:01:37 +02:00
kingchencandGitHub baf4d0ff47 fix(data): add missing 3d and 1M Binance kline intervals (#312)
Binance supports 16 kline intervals (1s,1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,
12h,1d,3d,1w,1M); the live-binance `Interval` enum listed only 14, missing
three-day (`3d`) and one-month (`1M`). Add both variants in Binance order
with their wire-format strings, and extend the exhaustive as_str test.
2026-06-16 00:51:52 +02:00
kingchencandGitHub d362ae26a3 feat(data): expose CandleReader (CSV) natively in all 10 languages (#311)
Add the data-layer CSV candle reader to every binding so loading OHLCV
candles from a CSV no longer needs a per-language CSV/dataframe dependency.

- C ABI: wickra_candle_reader_new(bytes, len) / _count / _read / _free over
  an opaque CandleReader handle (parse the whole buffer up front, then drain).
- Native: Node/WASM CandleReader.read() -> Candle[], Python read() -> list[tuple].
- C-ABI languages: Go Read() []Candle, C# Candle[] Read(), Java Candle[] read(),
  R read() S3 generic (n x 6 matrix); C / C++ call the C ABI directly.
- Cross-language golden testdata/golden/data_csv*.csv pins the parsed candles
  bit-for-bit across every binding.

Verified locally across Rust (test+clippy+fmt), Node, WASM, Python, C#, Go,
Java, R, and the C/C++ cmake parity suite.
2026-06-16 00:10:58 +02:00
kingchencandGitHub cb6da4d737 feat(data-layer): Resampler (candle resampling) in all 10 languages (#310)
* feat(data-layer): Resampler (candle resampling) in all 10 languages

Second data-layer feature (F3): resample candles into a higher timeframe.

- Native (Node.js/WASM): new Resampler(timeframe) -> update(o,h,l,c,v,ts):
  Candle|null + flush(): Candle|null. Python the same -> tuple|None.
- C ABI: wickra_resampler_new/update/flush/free (update has the multi-output
  shape so the generators auto-emit it; flush is bespoke). Go Update -> (Candle,
  bool) + Flush; C# Candle? Update/Flush; Java Candle update/flush; R update()
  generic + a flush() S3 method (extends base::flush); C/C++ direct.
- Cross-language golden (testdata/golden/data_resampled.csv): the shared input
  candles resampled into 5-unit buckets, the final partial bucket via flush,
  pinned bit-for-bit across every binding.

Verified locally in all 10 (3 candles for the 5-unit smoke; 16 for the golden).
The WickraCandle output record is shared with the tick aggregator (deduped).

* test(node): exclude data-layer types from the indicator completeness contract

The Resampler exposes update(), so the completeness test flagged it as an
indicator and required batch/reset/isReady/warmupPeriod, which a data-layer type
does not have. Exclude TickAggregator and Resampler like the bar builders.
2026-06-15 22:36:16 +02:00
kingchencandGitHub 8a103ef920 feat(data-layer): TickAggregator (tick-to-candle) in all 10 languages (#309)
* 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).
2026-06-15 21:24:33 +02:00
kingchencandGitHub fd9f4c8bc6 feat(bindings): expose name() on every indicator in all 10 languages (#308)
* feat(bindings): expose name() on every indicator in Node, WASM, and Python

Surface the core Indicator::name() / BarBuilder::name() accessor through the
three native bindings so every indicator reports its canonical name at runtime,
matching the existing reset/isReady/warmupPeriod surface.

- Node (napi): name(): string on all 514 classes (regenerated index.d.ts)
- WASM (wasm-bindgen): name(): string on all 514 classes
- Python (pyo3): name() -> str on all classes

* feat(bindings): expose name() across the C ABI and C/C++/Go/C#/Java/R

Regenerate the C ABI and the four generated language bindings from the updated
ScriptHelpers generators so every indicator and bar builder reports its
canonical name at runtime, completing name() coverage across all 10 languages.

- C ABI (bindings/c): wickra_<ind>_name() -> *const c_char for all 514, cached
  in a per-function OnceLock<CString> with ind.name() as the source of truth;
  cbindgen header regenerated and vendored into bindings/go/include.
- Go: Name() string; C#: string Name(); Java: String name(); R: name() S3
  generic over the wk_<ind>_name C glue (methods.R + NAMESPACE).

The Java regeneration also restores two fixes that had drifted out of the
generator (bool* arrays via boolSegment; uint8_t ctor args cast to byte) and C#
re-emits '#nullable enable'; these are no-op vs the previous committed output
apart from the new name() accessors.

* test(golden): pin canonical name() across all 10 language bindings

Add a cross-language name() consistency check: every indicator must report the
exact core Indicator::name() (which can differ from the registered class name,
e.g. ChaikinMoneyFlow -> "CMF", Donchian -> "DonchianChannels"). The 514 core
names are committed as testdata/golden/names.json (keyed by Rust canonical) and
asserted by each binding's golden replay, which already reconstructs the whole
catalogue:

- node / wasm: assert against names.json in the existing golden test
- python: new test_golden_names.py over the shared node manifest
- go / csharp / java / c+c++ / r: the golden-test generators load names.json and
  emit a name assertion per indicator (regenerated test artifacts committed)

All 10 bindings return identical names by construction (each delegates to core),
so this pins that contract and guards against a future binding breaking the
passthrough.

* docs(changelog): record name() across all 10 bindings under Unreleased

* fix(r): restore bool* flag marshalling in the regenerated C glue

The name() regeneration had reverted the cross-section bool fix: the R glue
emitted (bool *)REAL(x) for const bool* inputs, reinterpreting 8-byte doubles as
1-byte bools so every flag read as false (PercentAboveMa, NewHighsNewLows,
HighLowIndex, BullishPercentIndex returned 0 instead of the breadth value). The
wk_bool_vec() helper is restored in the generator and the glue routes bool arrays
through it again.
2026-06-15 17:19:24 +02:00
kingchencandGitHub 59acefa1ea ci: retry-harden the Rust toolchain install across all jobs (#307) 2026-06-15 07:09:39 +02:00
kingchencandGitHub 5f0677d62d release: bump 0.9.1 -> 0.9.2 (#306) 2026-06-15 06:02:05 +02:00
kingchencandGitHub ce792cf8b8 fix: clear the last two build warnings (C# CA2255, WASM license files) (#304)
- C#: the [ModuleInitializer] that registers the DllImport resolver is the
  one legitimate library use of the attribute (a static ctor would run too
  late), so suppress CA2255 with a documented justification — the dotnet
  build is now warning-clean.
- WASM: add LICENSE-MIT + LICENSE-APACHE to bindings/wasm so wasm-pack stops
  warning about missing license files and the published npm package ships
  its license texts.

Full warnings audit: Rust, C, C++, Go, Java, Node, Python, C#, WASM all
build clean; R installs clean.
2026-06-15 05:26:06 +02:00
kingchencandGitHub 120b6ac265 docs+ci: surface 10-language golden verification (#303)
* docs+ci: surface 10-language golden verification; add WASM golden CI

- README: add C++ to the Quickstart list and state prominently that all 514
  indicators are replayed through all 10 languages and checked bit-for-bit
  against the Rust reference.
- CHANGELOG: document the cross-language golden suite, the Java and R C-ABI
  bool-marshalling fixes, the C# nullable directive and the live_binance rename.
- ci.yml: run the WASM golden suite (nodejs-target build + node --test); the
  C/C++ golden tests already run via the C-ABI job's ctest and the other
  bindings pick up their golden runners in their existing test suites.

* readme: add verified badge + prominent per-language throughput table

- Add the 'verified across 10 languages' badge to the badge row, linking to
  the FAQ that explains the cross-language golden parity.
- Surface a per-binding throughput table (the cost of each language's FFI
  boundary) so readers can pick a binding that keeps up with streaming hot
  loops — the cross-library benchmarks stay in BENCHMARKS.md.

* changelog: note the verified badge and per-binding throughput table

* docs: fix cross-language consistency (audit)

- docs/README.md: add the missing C++ quickstart link.
- README testing section: the golden parity now covers all 10 languages and
  all 514 indicators (was 'four C-ABI bindings, 7 archetype indicators').
- CHANGELOG: the live_binance rename also covers the C examples.
2026-06-15 05:07:52 +02:00
kingchencandGitHub 4f708d410d test: golden-pin the four de-duplicated indicators across all bindings (#305)
* test: golden-pin the four de-duplicated indicators across all C-ABI bindings

Extend gen_golden to emit reference fixtures for AdOscillator (ADOSC),
IntradayIntensity, AwesomeOscillatorHistogram and AverageDrawdown, and replay
them through the Go / C# / Java / R golden harnesses so their corrected
definitions stay bit-identical to the Rust core in every binding. Go suite
verified locally (gcc 13 + cgo): all 9 golden tests pass; C#/Java/R use the
same fixtures and harness pattern (CI-verified). First step of extending the
golden coverage beyond the seven archetype representatives.

* test: golden-pin the scalar-output tranche (308 indicators) against Rust

Extend gen_golden with a generated emit_scalar that writes reference fixtures
for every single-f64-output indicator (scalar / candle / pairwise input) using
valid constructor params, and add a manifest-driven generic Python golden
replay that reconstructs each by its native name and checks it bit-for-bit
against the Rust output. 308 indicators now value-tied to the Rust core in
Python (pytest: 308/308). Takes golden coverage from the 7 archetype
representatives to 308+ of the catalogue.

22 scalar indicators with non-default constructor constraints are skipped by
gen_golden for now (logged), as are non-f64-output ones; multi-output, exotic
inputs and the per-indicator arg arities of the C-ABI/Node replays follow.
Generated + verified locally with the full toolchain.

* test: golden-pin the multi-output tranche (70 indicators) in Python

Add a generated emit_multi to gen_golden (per-indicator Output-field access,
one CSV column per field) and a manifest-driven generic Python replay that
checks every field of each multi-output indicator against the Rust reference.
70 multi-output indicators now value-tied to Rust in Python; combined with the
scalar tranche, 378 indicators are golden-pinned. 8 multi with non-default
param constraints and 5 with non-f64 Output fields (Option/Vec/i64) are
deferred. pytest green.

* test(golden): add 30 constraint-tuned indicators to scalar/multi golden suite

Emit golden fixtures for 22 scalar-output and 8 multi-output indicators
whose constructors need non-default parameters (Alma, Jma, Psar, T3, Mama,
DoubleBollinger, ZigZag, ...). All 408 fixtures replay bit-for-bit through
the Python binding.

* test(golden): cover 36 missed scalar/multi indicators

Add 26 single-output (LinearRegression family, HT cycle, Candle
volatility estimators, DrawdownDuration) and 10 multi-output
(BollingerBands, MACD/MACDEXT/MACDFIX, Camarilla, VWAP bands, ...)
indicators to the golden suite. 444 fixtures replay bit-for-bit
through the Python binding.

* test(golden): cover 50 exotic-input indicators

Add deterministic synthetic feeders for the DerivativesTick (17),
CrossSection (15), Trade (8), TradeQuote (3) and OrderBook (7)
families, derived from the shared OHLCV input series in both
gen_golden and a new Python replay harness (test_golden_exotic).
All 494 fixtures replay bit-for-bit through the Python binding.

* test(golden): complete 514-indicator golden coverage

Add the final tranches: 3 mixed multi-output indicators (Ichimoku,
WilliamsFractals, LeadLagCrossCorrelation), 6 histogram profiles
(time/volume seasonality + TPO/volume price profiles), 10 alt-chart
bar builders and the footprint. Every one of the 514 distinct
indicators now has a Rust-generated g_<Canonical>.csv fixture and a
generic Python replay (scalar/multi/exotic/profile/bars), all passing
bit-for-bit.

* test(golden): add generic Node replay for all 514 indicators

A manifest-driven node:test harness reconstructs every indicator by its
native class, feeds the same synthetic stream derived from the shared
golden input, and checks output bit-for-bit against the Rust reference
fixtures (scalar/multi/exotic/profile/bars). node_manifest.json is
generated from index.d.ts plus the Python-side manifests. 514/514 pass.

* test(golden): add generated Go replay for all 514 indicators

golden_all_test.go (generated by gen_golden_test.py) reconstructs every
Go indicator, feeds the shared synthetic stream and checks output
bit-for-bit against the Rust reference fixtures. A reflection-based
comparator flattens multi-output structs, profiles and bar slices so one
path covers all archetypes. This is the first C-ABI binding verified
across the full catalogue. 514/514 pass.

* test(golden): add generated C# replay for all 514 indicators

GoldenAllTests.g.cs (generated by gen_golden_test.py) reconstructs every
C# indicator, feeds the shared synthetic stream and checks output
bit-for-bit against the Rust reference fixtures via a reflection-based
flatten covering scalar/multi/profile/bar archetypes. 514/514 pass.

Also add the '#nullable enable' directive the compiler requires to the
generated Indicators.g.cs, clearing the four CS8669 warnings on the
nullable double[] profile return types.

* fix(java): marshal C ABI bool params correctly; add 514 golden replay

The Java FFM binding marshalled the cross-section state flags (newHigh,
newLow, aboveMa, onBuySignal) as JAVA_DOUBLE arrays, but the C ABI takes
them as const bool* (one byte each), so the native side read the low byte
of each 8-byte double and saw every flag as false. Add WickraNative.
boolSegment and use it across the 15 cross-section indicators. Also pass
the MacdExt MaType arguments as byte to match the uint8_t downcall
descriptor (was int, throwing WrongMethodTypeException).

Add GoldenAllTest.java (generated by gen_golden_test.py): a reflection
runner replaying all 514 indicators against the Rust reference fixtures.
The bugs above were found by this test; 514/514 now pass.

* fix(r): marshal C ABI bool flags correctly; add 514 golden replay

The R wrapper passed the cross-section state flags as (bool *)REAL(x),
reinterpreting the 8-byte doubles as 1-byte bools so the native side read
every flag as false. Add wk_bool_vec to convert each flag vector into a
real C bool buffer and use it for all 15 cross-section update wrappers.

Add test-golden-all.R + generated golden_specs.R: a reflective runner
replaying all 514 indicators against the Rust reference fixtures. The bug
above was found by this test; verified 514/514 pass locally.

* test(golden): add WASM replay for all 514 indicators

A manifest-driven node:test harness loads the nodejs-target wasm-pack
build, reconstructs every indicator by its JS class, feeds the shared
synthetic stream and checks output bit-for-bit against the Rust
reference fixtures. wasm_manifest.json is generated from the wasm .d.ts
plus the shared manifests; a recursive flattener covers scalar, multi
(Reflect objects), profile and bar shapes. 514/514 pass locally
(wasm-pack build --target nodejs, then node --test).

* test(golden): add C and C++ replay for all 514 indicators

golden_test.c (generated by gen_golden_test.py) drives every indicator
through the C ABI (wickra.h) and checks output bit-for-bit against the
Rust reference fixtures. golden_test.cpp #includes the same source so the
identical runner is compiled and run under both gcc (C) and g++ (C++) via
the CMake targets golden_test / golden_test_cpp — proving the extern "C"
header is consumable from each language. Both 514/514 (verified via ctest).

* test(golden): gofmt the generated Go golden replay

* test(golden): make the Node fixture reader CRLF-safe and pin fixtures to LF
2026-06-15 04:48:51 +02:00
kingchencandGitHub de1112ea91 chore(examples): rename live_trading examples to live_binance (#301)
The examples stream a live Binance feed into the indicators and print signals;
they place no orders, so 'live_trading' overstated them and was inconsistent
with the C/Go/R examples already named live_binance. Rename the Python/Node/WASM
files to live_binance.* and update every reference, run command, header, and the
project-tree listings. Accurate use-case wording ('suitable for live trading
bots') and the risk disclaimers are left unchanged.
2026-06-15 03:41:19 +02:00
kingchencandGitHub 82d7479011 fix: de-duplicate four indicators by correcting their definitions (#300)
* fix(core): de-duplicate 3 indicators by correcting their definitions

Behavioral audit found these computed identically to another indicator:

- AverageDrawdown was the mean per-bar under-water fraction = PainIndex.
  Now the conventional average drawdown: mean of the maximum depths of the
  distinct drawdown episodes in the window.
- IntradayIntensity was a cumulative line = the A/D Line (Adl); its normalized
  form is the Chaikin Money Flow (Cmf). Now the raw per-bar Bostian intensity
  volume*(2c-h-l)/(h-l), distinct from both.
- AwesomeOscillatorHistogram was AO - SMA(AO, n) = AcceleratorOscillator. Now
  the AO momentum AO[t] - AO[t-lookback] (the histogram delta); the 3rd
  parameter is reinterpreted from sma_period to lookback (default 1).

Constructor signatures are unchanged, so the bindings keep their API. Core
unit tests rewritten with the new reference values; workspace tests + clippy
green. Binding value-tests and deep-dive docs are updated separately.

* fix(core): redefine AdOscillator as the A/D Oscillator (was a Wad duplicate)

AdOscillator computed the cumulative volume-free Williams A/D line, identical
to the Wad indicator. Redefine it as the Williams A/D *Oscillator*: the same
line minus its 13-bar SMA, so it oscillates around zero (mean-reverting) while
Wad stays the drifting cumulative line for divergence analysis. The canonical
name AdOscillator is now accurate; the trait name() becomes "ADOSC".

Constructor stays no-arg (internal 13-bar signal). Unit tests rewritten and
cross-checked against Wad - SMA(Wad, 13). The native bindings' "WilliamsAD"
alias is renamed to "ADOSC" separately.

* fix(bindings): rename WilliamsAD alias to ADOSC and update value tests

Follows the core de-duplication: the native bindings exposed the Williams A/D
line as 'WilliamsAD', which is now the A/D Oscillator. Rename the Python /
Node.js / WASM alias to 'ADOSC' (regenerated node index.js / index.d.ts) and
update the binding value-tests for the four redefined indicators
(AverageDrawdown episode mean, AwesomeOscillatorHistogram momentum warmup,
the Wad-line reference test now uses ta.Wad()). Python suite and node suite
both pass (pytest all green, node 584/584).

* docs: record indicator de-duplication in README and CHANGELOG

README volume family: 'Williams A/D' -> 'Williams A/D Oscillator', 'Intraday
Intensity Index' -> 'Intraday Intensity'. CHANGELOG [Unreleased] documents the
four redefinitions and the native WilliamsAD -> ADOSC rename as breaking.

* test(core): cover Default impl and drop dead match arm

Codecov flagged AdOscillator::default() (never exercised) and the unreachable
_ => panic!() arm in the AwesomeOscillatorHistogram test. Exercise Default in
the accessors test and rewrite the histogram check as an if-let, removing the
dead arm.
2026-06-15 03:41:15 +02:00
kingchencandGitHub a3950bf31b release: bump 0.9.0 -> 0.9.1 (#299) 2026-06-14 02:42:45 +02:00
kingchencandGitHub a0a7bc4e62 docs(readme): add a Requirements section with per-language supported versions (#298)
There was no single place listing the minimum supported version per language.
Add a Requirements table after Languages (Rust 1.86, Python 3.9, Node 20, WASM,
C99, C++14, .NET 8, Go 1.23, Java 22, R >= 2.10) linking to the full
Requirements page in the docs.
2026-06-14 01:09:02 +02:00
kingchencandGitHub 0c925aa9d5 feat(c-abi): expose warmup_period / is_ready across the C ABI bindings (#297)
* feat(c-abi): expose warmup_period / is_ready across the C ABI bindings

The C ABI hub exposed new/update/batch/reset/free per indicator but not the
Indicator::warmup_period / is_ready queries that the native (Python/Node/WASM)
bindings already had, so C/C#/Go/Java/R callers could not ask an indicator
whether it was warmed up without feeding it and watching for NaN.

Regenerated from the ScriptHelpers capi + language generators:
- bindings/c: wickra_<ind>_warmup_period (size_t) and wickra_<ind>_is_ready
  (bool) for every indicator (504; the 10 alt-chart bar builders are excluded
  by design). wickra.h regenerated via cbindgen (additive only).
- bindings/csharp: int WarmupPeriod() / bool IsReady() on each wrapper.
- bindings/go: WarmupPeriod() int / IsReady() bool.
- bindings/java: int warmupPeriod() / boolean isReady().
- bindings/r: C glue + registration; hand-written warmup_period() / is_ready()
  S3 generics in methods.R, plus NAMESPACE exports.

Tests: C-ABI Rust unit tests, the C examples/archetypes.c suite, and the C#,
Go, Java and R archetype suites all gain a warmup/is_ready transition check.

* build(go): sync vendored wickra.h with the C ABI header

The Go binding vendors bindings/c/include/wickra.h; refresh it with the new
warmup_period / is_ready declarations so the CI sync check passes.
2026-06-14 01:04:06 +02:00
kingchencandGitHub d9e6807ca0 fix: correct RelativeStrengthAB binding wrapper casing (#296)
The WASM and Node binding wrappers were named with the lowercase-b acronym
(WasmRelativeStrengthAb, RelativeStrengthAbNode) while the core type and every
other surface use RelativeStrengthAB. The published JS/WASM class name was
already correct via js_name/js_class, so this only aligns the internal Rust
identifiers and the auto-generated TypeScript type alias
(RelativeStrengthAbNode -> RelativeStrengthABNode). Runtime API unchanged.
2026-06-14 00:50:03 +02:00
kingchencandGitHub eb50ae4e90 deps(python): upgrade pyo3 + rust-numpy to 0.29, clear 2 advisories (#295)
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).
2026-06-14 00:36:52 +02:00
kingchencandGitHub 4a12f60a88 ci: test the Node binding on the 22/24 LTS (18/20 are EOL) (#294)
* ci: test the Node binding on the 22/24 LTS (18/20 are EOL)

Node 18 is EOL and 20 reaches EOL; move the binding CI matrix from [18, 20]
to [22, 24] (both LTS), bump the fixed Node setups in ci.yml/release.yml to 22,
and raise package.json engines to >= 20.

The N-API 8 binary is ABI-stable across Node versions, so no per-version build
is needed. The `npm test` script (`node --test __tests__/`) breaks on Node 22
— it resolves `__tests__` as a module — so switch to `node --test` (auto-
discovery). Verified locally on Node 22: build + 584 tests green.

* build(node): sync package-lock engines.node to >= 20

* ci(node): drop the __tests__/ path arg so node --test auto-discovers

On Node 22+, `node --test __tests__/` resolves the directory as a single
module and fails with one unrunnable subtest. `node --test` (no path)
auto-discovers every *.test.js under the package, matching the package.json
test script. Verified locally: 584 tests pass.
2026-06-14 00:23:36 +02:00
kingchencandGitHub 8003572d92 docs(core): add runnable rustdoc examples to 23 indicators (#293)
The fib_* / auto_fib / golden_pocket Fibonacci indicators, the harami_cross /
tristar / tower_top_bottom candlestick patterns and the td_* DeMark family
lacked the `/// # Example` runnable doctest that ARCHITECTURE.md requires of
every indicator. Add a minimal construct-and-feed example to each. The
swing-tracker helper `pattern_swing` is not an `Indicator` and is left out.
All 23 pass `cargo test --doc` (487 doctests green).
2026-06-14 00:09:07 +02:00
kingchencandGitHub db5bd5e028 ci: build the Java binding on JDK 25 LTS (22 is EOL) (#292)
Java 22 is a non-LTS that has reached end of life; bump the build JDK in
ci.yml and release.yml to the 25 LTS. The pom pins `maven.compiler.release`
to 22, so the emitted bytecode and the Java 22+ runtime floor (FFM API, final
since 22) are unchanged. Not 21 — FFM was only preview there.
2026-06-13 23:55:49 +02:00
kingchencandGitHub 2ef60874b9 fix(examples): satisfy clippy -D warnings in gen_golden (#291)
Replace `i as i64` with `i64::try_from(i)` (cast_possible_wrap) and rename
the OHLCV destructure to descriptive names (many_single_char_names) so
`cargo clippy --all-targets --all-features -- -D warnings` passes on the 1.95
toolchain. Dev-tool only; no library change.
2026-06-13 23:35:29 +02:00
kingchencandGitHub d59cd44043 docs: standardise language naming + binding security sections (#290)
* docs: standardise language naming and add binding security sections

Canonical binding list everywhere: Rust, Python, Node.js, WASM, C, C++, C#,
Go, Java, R. Use C# (not .NET) as the language label, WASM (not WebAssembly)
in prose, and frame the C ABI as a hub rather than a list item.

- Bump stale indicator counts (200+ -> 514) and family count (sixteen ->
  twenty-four) in the Node/Python/WASM and docs READMEs.
- Add a short Security section to all eight binding READMEs.
- Relabel benchmark rows (C -> C / C++, C# / .NET -> C#).
- Fix the 'language stecker' wording in the C#/Go/R API intros.
- Documentation only; no code or public API changes.

* release.yml: extend install snippets and expose version output

Add the missing registry installs to the release body (dotnet, go, Gradle/
Maven Central, r-universe) alongside cargo/pip/npm, and expose a v-stripped
'version' output from the tag step for the Gradle coordinate. Also fix the
C-ABI language order in the assets note (C# before Go).

* release.yml: correct the release body (10 languages, all registries)

Reframe the tagline to '10 languages' (native Rust/Python/Node.js/WASM + a C
ABI hub for C, C++, C#, Go, Java, R) instead of '4 language registries', note
that C#/Java/Go/R publish to NuGet/Maven/Go/r-universe via their own jobs, and
tidy the Node.js label and the C-ABI hub list.
2026-06-13 23:34:24 +02:00
kingchencandGitHub ee5ee6980e release: bump 0.8.9 -> 0.9.0 (#289) 2026-06-13 03:21:33 +02:00
kingchencandGitHub dd6a4affb2 ci(dependabot): group updates per ecosystem into a single PR (#288)
All nine Dependabot ecosystems now batch their updates into one grouped PR each
(`groups: { <name>: { patterns: ["*"] } }`) instead of one PR per dependency.

A routine refresh previously fanned out into a dozen-plus PRs (the Maven batch
alone opened 12), each running the full nine-language CI matrix (~55 checks) and
clogging the runner queue ahead of release and feature runs. Grouping collapses
that to one PR per ecosystem.

Trade-off: a single broken update blocks its whole group until excluded — fine
for routine maintenance bumps. Security updates are unaffected (they are not
grouped and continue to arrive individually).
2026-06-12 23:39:26 +02:00
kingchencandGitHub e5e094d370 chore(supply-chain): suppress jackson false positive in the Maven sub-scans (#287)
Follow-up to #272. The root `osv-scanner.toml` cleared the OpenSSF Scorecard
Vulnerabilities check from score 5 to 9 (the two pyo3 advisories, which live in
`Cargo.lock` at the root). One finding remained — `GHSA-72hv-8253-57qq`
(jackson-core 3.x async-parser DoS) — because OSV-Scanner resolves its config
relative to each manifest, and the repo-root config does not cover the Maven
sub-directory scans.

This adds the same not-affected suppression next to the two Maven manifests
(`bindings/java`, `examples/java`).

`tools.jackson.core:jackson-core` 3.x is not a dependency of this project: full
Maven resolution (publishing-plugin tree + project trees) resolves only jackson
`2.16.1` / `2.17.1`; tools.jackson 3.x appears nowhere. OSV-Scanner's own
resolver flags it as a false positive.
2026-06-12 23:31:21 +02:00
kingchencandGitHub 97940046d1 ci(dependabot): track NuGet (C#) and the Node/Go examples (#286)
Closes the remaining Dependabot coverage gaps after the Maven ecosystem was
added. Audited every binding for external dependency manifests:

| Language | External deps | Coverage |
|----------|---------------|----------|
| Rust (core/wasm) | yes | already `cargo` |
| Python | yes | already `pip` |
| Node (binding) | yes | already `npm` |
| Java | yes | already `maven` |
| **C# / .NET** | **yes** (xunit, Microsoft.NET.Test.Sdk, BenchmarkDotNet) | **added `nuget`** |
| **examples/node** | **yes** (`ws`) | **added `npm`** |
| **examples/go** | **yes** (`coder/websocket`) | **added `gomod`** |
| C (C ABI) | no | nothing to track |
| Go (binding) | no (empty go.mod) | nothing to track |
| R | only `Depends: R` | Dependabot has no R/CRAN ecosystem |

The published `Wickra.csproj` itself has no external NuGet packages (thin C-ABI
wrapper), so only the test and benchmark projects are tracked.
2026-06-12 23:23:08 +02:00
kingchencandGitHub f7f0bfbc48 release: bump 0.8.8 -> 0.8.9 (#285)
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.
2026-06-12 23:21:39 +02:00
kingchencandGitHub 8ccfd638b2 chore(supply-chain): osv-scanner suppressions, bump publishing plugin, track Maven in Dependabot (#272)
Follow-up to the Dependabot action-bump merges and the cargo-deny ignore (#271).
Three low-risk supply-chain housekeeping changes — config/docs only, no library
code, no runtime change.

## 1. `osv-scanner.toml` (new)

The OpenSSF Scorecard *Vulnerabilities* check runs OSV-Scanner over the repo and
was flagging five advisory IDs (`score is 5`). These reduce to three findings,
all assessed as not affecting Wickra:

| Advisory | Assessment |
|----------|------------|
| RUSTSEC-2026-0176 / GHSA-36hh-v3qg-5jq4 (pyo3) | Vulnerable API unused; fix is pyo3 0.29 but rust-numpy 0.28 pins pyo3 `^0.28` → upstream-blocked. Already in `deny.toml`. |
| RUSTSEC-2026-0177 / GHSA-chgr-c6px-7xpp (pyo3) | Same — `PyCFunction::new_closure` not called. Already in `deny.toml`. |
| GHSA-72hv-8253-57qq (jackson-core 3.x) | **Not a dependency of this project.** No manifest, Maven plugin, or the GitHub dependency-graph SBOM references `tools.jackson` 3.x; the only jackson present is `com.fasterxml.jackson.core:jackson-databind` 2.17.1. |

`osv-scanner.toml` records these as ignored-with-reason at the OSV layer,
mirroring `deny.toml` and the SECURITY.md VEX section. The Scorecard finding
also flip-flopped (fixed → reappeared) across unrelated release-bump commits,
confirming it is not a stable real exposure.

## 2. Bump `central-publishing-maven-plugin` 0.5.0 → 0.10.0

The Java binding pinned a publishing plugin five versions behind. Validated
locally with the JDK 22 toolchain (`mvn -Prelease validate`): the extension
loads, the existing `publishingServerId`/`autoPublish` config is compatible, and
all 14 binding tests pass. The actual `mvn deploy` upload path is only exercised
at release time (needs the Central token + GPG key), so it will be confirmed at
the next release.

## 3. Add a Maven ecosystem to Dependabot

The Java binding had no Dependabot coverage, which is why the stale 0.5.0 plugin
went unnoticed. Adds `package-ecosystem: maven` over `/bindings/java`,
`/bindings/java/benchmarks`, and `/examples/java` so plugin and dependency
updates (incl. the examples' jackson) are tracked going forward.
2026-06-12 23:15:56 +02:00
kingchencandGitHub c3029f2548 ci(supply-chain): ignore RUSTSEC-2026-0176/0177 until rust-numpy 0.29 (#271)
## Why

`Supply-chain (cargo-deny)` started failing on every CI run from 2026-06-11
onward, including all five open Dependabot action-bump PRs (#266–#270), which
touch no Rust code. The cause is two PyO3 advisories published 2026-06-11:

| Advisory | Issue | Affected | Patched |
|----------|-------|----------|---------|
| RUSTSEC-2026-0176 | OOB read in `PyList`/`PyTuple` `nth`/`nth_back` | `>=0.24.0, <0.29.0` | `>= 0.29.0` |
| RUSTSEC-2026-0177 | Missing `Sync` bound on `PyCFunction::new_closure` | `>=0.15.0, <0.29.0` | `>= 0.29.0` |

We are on pyo3 0.28.3, so both apply.

## Why not just bump pyo3 to 0.29

The clean fix is blocked upstream: `rust-numpy` 0.28 (its latest release) hard-pins
`pyo3 ^0.28.0`, so the resolver rejects 0.29 (`failed to select a version for the
requirement pyo3 = "^0.28.0"`). rust-numpy's "Updated to PyO3 version 0.29.0" PR is
open but not yet published to crates.io.

## Why the ignore is safe

Neither vulnerable code path is reachable from our binding — verified by grep over
`bindings/python/src`: no `BoundListIterator::nth`/`nth_back` or `PyTuple`
equivalents (0176), no `PyCFunction::new_closure` (0177), zero `PyList`/`PyTuple`
references at all.

## Removal trigger

Drop both ignores once rust-numpy 0.29 lands and pyo3 is bumped to 0.29.

Verified locally: `cargo deny check` → `advisories ok, bans ok, licenses ok, sources ok`.
2026-06-12 21:38:10 +02:00
kingchencandGitHub 806ae22abe release: bump 0.8.7 -> 0.8.8 (#265)
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.
2026-06-11 22:44:47 +02:00
kingchencandGitHub fb9c39d4cd fix(r): declare Depends: R (>= 2.10) for the bundled dataset (#264)
The `sample_ohlcv` dataset added in #262 is lazy-loaded (`LazyData: true`), which makes `R CMD check` on r-universe / CRAN warn:

```
* checking data for ASCII and uncompressed saves ... WARNING
  Warning: package needs dependence on R (>= 2.10)
```

Lazy-loading of package data requires R ≥ 2.10, so the package must declare it. This adds `Depends: R (>= 2.10)` to `bindings/r/DESCRIPTION`.

**Verified locally** (R 4.6.0, `R CMD build` + `R CMD check`): the `checking data for ASCII and uncompressed saves` step now reports **OK**. (The repo CI only runs `R CMD INSTALL` + testthat, not full `R CMD check`, so this surfaces only on r-universe — same asymmetry as the golden-test skip.)
2026-06-11 22:41:05 +02:00
kingchencandGitHub b1653e2107 release: bump 0.8.6 -> 0.8.7 (#263)
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.
2026-06-11 21:47:50 +02:00
kingchencandGitHub 32e18cb3a3 feat(r): getting-started vignette + sample_ohlcv dataset (#262)
Fills the two empty r-universe tabs (**Articles**, **Datasets**) for the R package and gives R users a runnable onboarding path.

## What
- **`vignettes/getting-started.Rmd`** — Articles tab. Walks through batch vs streaming (and that they're equivalent), multi-output MACD, candle ATR, and `reset()`, all over the bundled sample series. Built strictly from the already-proven README quick-start + golden-test API (`Sma`/`Ema`/`Rsi`/`Atr`/`MacdIndicator`, `batch`/`update`/`reset`) — no new indicator logic.
- **`data/sample_ohlcv.rda`** (+ `data-raw/sample_ohlcv.R` generator) — Datasets tab. A deterministic, seeded synthetic daily OHLCV series (250 rows × `date/open/high/low/close/volume`); documented via `R/data.R` + `man/sample_ohlcv.Rd`. `LazyData: true` → available right after `library(wickra)`.
- **`DESCRIPTION`** — `Suggests: knitr, rmarkdown`, `VignetteBuilder: knitr`, `LazyData: true`.
- **`ci.yml`** — the R job now knits the vignette (executes its R chunks, no pandoc needed) so a broken example is caught **in CI** before r-universe / CRAN `R CMD check`. The main job otherwise only `R CMD INSTALL`s.

## Verified locally (R 4.6.0 + Rtools45)
- Package installs with the dev C-ABI override; dataset moves to the lazyload DB.
- Vignette **knits cleanly** — every chunk runs, `batch == streaming` holds, MACD/ATR/RSI produce sensible values.

## Notes
- No version bump — metadata/docs only; rides the next release. Merging triggers an r-universe rebuild → Articles + Datasets populate **and** (now that `support@wickra.org` is verified) the maintainer avatar resolves.
- `data-raw/` is `.Rbuildignore`d (generator, not shipped). The `.rda` is XZ-compressed (~3 KB).

Not merging — for review.
2026-06-11 21:44:56 +02:00
kingchencandGitHub dc0c3d1736 docs(readme): serve footer social badges + star-history from snapshots (#261)
Pairs with **wickra-lib/.github#32**. The README footer (GitHub stars / forks / issues) hot-linked `img.shields.io` directly, so it showed shields' transient **"unable to select next github token from pool"** error live; the star-history chart hot-linked `star-history.com` and **froze** behind GitHub's Camo image cache (the embedded `<img>` is proxied + cached, while the linked page renders fresh).

All four now point at the committed snapshots in the `.github` repo (`raw.githubusercontent.com/wickra-lib/.github/main/profile/badges/{stars,forks,issues,star-history}.svg`), refreshed hourly by the new `refresh-social.yml` — so a broken upstream never reaches the page and the last good SVG is always served. The `<a>` links and the visual style are unchanged.

## Merge order
Merge **.github#32 first** (it creates the snapshot SVGs), then this one — otherwise the footer would briefly point at missing raw URLs.

Not merging yet — for review.
2026-06-11 19:28:04 +02:00
kingchencandGitHub ad1231cde1 release: bump 0.8.5 -> 0.8.6 (#260)
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.
2026-06-11 18:17:05 +02:00
kingchencandGitHub 7ebb60b60a chore: improve package registry metadata (R-universe, PyPI, NuGet) (#259)
Closes the package-page discoverability gaps found while auditing the public registry listings. No code or version change — metadata only; takes effect on the next registry build/publish.

## R — R-universe (`bindings/r`)
- `DESCRIPTION`: add the R-universe URL to `URL:` and a CRAN-permitted `X-schema.org-keywords` field (feeds R-universe's search/ranking).
- Add a package logo at `man/figures/logo.png` (pkgdown convention → shown in the R-universe packages tab) and reference it in the R `README.md`.
- Maintainer email is **unchanged** (`support@wickra.org`).

## Python — PyPI (`bindings/python`)
- `pyproject.toml`: add a `Documentation` project URL (`https://docs.wickra.org`) so it appears in the PyPI sidebar.

## C# — NuGet (`bindings/csharp`)
- Add `icon.png` (brand mark) and `<PackageIcon>` so nuget.org shows the logo instead of the default placeholder.

## Notes
- Rust/crates.io, Node/npm, Go/pkg.go.dev and Java/Maven Central were audited and are already complete for their respective metadata models (no icon/keyword concept on some).
- The repository topic `rstats` was added (replacing the redundant `webassembly`, since `wasm` already covers it) so R is represented alongside the other language tags.
2026-06-11 18:11:54 +02:00
kingchencandGitHub d7cb771a28 release: bump 0.8.4 -> 0.8.5 (#258)
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.
2026-06-11 17:13:52 +02:00
kingchencandGitHub 3e5a19c94a fix(r): skip golden-fixture test in standalone package builds + document parity (#257)
## Problem
The r-universe build of `wickra` 0.8.4 fails (`R CMD check` ERROR): the golden-fixture parity test added in #255 walks up from the working directory looking for `testdata/golden` and `stop()`s when it cannot find it. Standalone package builds (r-universe / CRAN) package only `bindings/r`, so the repo-root `testdata/golden` fixtures are unreachable there — whereas the monorepo CI checks out the full repo, so the walk-up succeeds and the test passes.

Run: https://github.com/r-universe/wickra-lib/actions/runs/27354800361

## Fix
- **`bindings/r/tests/testthat/test-golden.R`** — the fixture-directory lookup now returns `NULL` instead of erroring when the fixtures are absent, and each test starts with `skip_if(is.null(golden_dir), ...)`. The repository CI (full repo present) still runs the parity checks; standalone builds skip them. The per-test `golden_input` read moved inside the (post-skip) test bodies so nothing runs at source time when the fixtures are missing.
- **`CHANGELOG.md`** — `[Unreleased]` Fixed entry.

## Docs (B6)
- **`README.md` `## Testing`** — the four C-ABI bindings (C#, Go, Java, R) were described as covering one indicator per FFI archetype; document that they additionally replay the shared golden fixture and assert exact parity with the Rust reference outputs.
2026-06-11 17:08:33 +02:00
kingchencandGitHub 8bfe24ac1d test(golden): language-neutral fixtures + per-binding parity runners (#255)
**Task 5 — golden-fixture parity for the C-ABI bindings.** Lifts the C#/Go/Java/R tests from *one indicator per archetype* toward reference-value parity, catching FFI wiring bugs (swapped params, wrong multi-output field) the math-only core tests cannot see.

## What's here
- **`examples/rust/src/bin/gen_golden.rs`** + **`testdata/golden/*.csv`** — a Rust generator computing a deterministic OHLCV series plus the core's reference outputs for a curated archetype-spanning set: scalar (`Sma`/`Ema`/`Rsi`), candle (`Atr`), scalar multi-output (`MACD`), candle multi-output (`ADX`), pairwise (`Beta`). `nan` marks warmup. Regenerate with `cargo run -p wickra-examples --bin gen_golden`.
- **Parity runners** replaying the identical fixtures through each FFI (rel-tol 1e-6), each a standard test in the binding's existing suite (no `ci.yml` change — rides `dotnet test` / `go test` / `mvn install` / `R CMD`+testthat). A walk-up search locates `testdata/golden` regardless of run dir.
  - **C#** (`bindings/csharp/.../GoldenTests.cs`) —  validated locally, 7/7 pass.
  - **Go** (`bindings/go/golden_test.go`) —  validated locally, pass.
  - **Java** (`bindings/java/.../GoldenTests.java`) — modeled on the archetype API; validated by CI (no local mvn).
  - **R** (`bindings/r/tests/testthat/test-golden.R`) — modeled on the archetype API; validated by CI (no local Rscript).

## Notes
- The curated set spans every marshalling archetype; extending the indicator list is mechanical (add to the generator + regenerate). Bars/profile archetypes can be added next.
- No new CI jobs.
2026-06-11 15:22:29 +02:00
kingchencandGitHub 395a2289f4 release: bump 0.8.3 -> 0.8.4 (#256)
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.
2026-06-11 14:57:15 +02:00
kingchencandGitHub 9973d1a6bf test(core): catalogue-wide invariant harness + guard non-finite input in 38 indicators (#254)
Adds `crates/wickra-core/tests/invariants.rs` — a property-based (`proptest`) harness asserting three invariants for **every** indicator and bar-builder in the catalogue (every module entry implementing `Indicator` or `BarBuilder`; the lone non-indicator helper `pattern_swing`/`SwingTracker` is excluded by design) — and fixes the non-finite bugs the harness surfaced.

## Invariants
1. **batch == streaming** — `batch()` must replay `update()` exactly.
2. **reset == fresh** — after `reset()`, re-feeding the same data matches a fresh instance.
3. **non-finite rejected without poisoning** (`f64` / `(f64, f64)` families) — a NaN/inf tick returns `None` and leaves state identical to never having seen it.

## How it works
- A single generic `check_seq<I: Indicator>` covers **all** input families — `f64`, `Candle`, `(f64, f64)`, and the exotic `CrossSection`, `Trade`, `DerivativesTick`, `OrderBook`, `TradeQuote` — via per-family `proptest` generators that produce *valid* inputs (e.g. order books are strictly monotonic and uncrossed).
- `check_bars<B: BarBuilder>` covers the bar-builder trait.
- Outputs are compared by `Debug` string so bit-identical `NaN` outputs count as equal — these properties test **determinism**, not NaN-freeness.

## The 38 non-finite fixes
The harness surfaced **38 more** scalar/pairwise indicators that let a NaN/inf tick poison their state — the same class as the 16 pairwise indicators fixed earlier (#251), but missed by the grep-based audit (`kalman_hedge_ratio` and `spread_bollinger_bands` carried `is_finite` on their **constructor** params, not the update input). All 38 now reject non-finite input via the established first-statement guard; signatures unchanged, so every binding inherits the fix. With these in, the non-finite invariant is enforced for every `f64`/`(f64,f64)` indicator going forward — the permanent regression net that would have caught #251.

Warmup-exactness was evaluated and **left out** (not universal — many multi-component/candlestick indicators emit before `warmup_period` by design).

## Verification
- `cargo test -p wickra-core`: 4225 unit + harness + 464 doc/integration, all green.
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`: clean.
- Coverage runs integration tests, so the new guard branches are exercised by the harness's NaN feed.

Also documents the harness in README's Testing section and adds the pending 0.8.4 entries to CHANGELOG `[Unreleased]`.
2026-06-11 14:51:46 +02:00
kingchencandGitHub cb216668ee docs: fix accuracy drift in SECURITY, ARCHITECTURE, THREAT_MODEL (#253)
Documentation-only accuracy fixes from the codebase audit (no code changes).

## SECURITY.md
- Supported-version policy was stale at `0.5.x` while `0.8.3` is published. Bump to the exact `0.8.3` (prose + table `0.8.3 (latest)` / `< 0.8.3`). The exact `x.y.z` form lets `bump_version.py` keep it current automatically (now wired as a touchpoint).

## ARCHITECTURE.md
- `three` → **four** binding crates (the C ABI crate was added).
- workspace diagram `214` → **514** indicators (matches the `mod`-count and `lib.rs` public-type count; now wired into the indicator-wiring automation so it self-heals).
- WASM "does not have automated tests yet" → corrected: `bindings/wasm/src/lib.rs` carries **21** `wasm-bindgen-test` cases.
- **Numerical-stability notes rewritten to match the code:** the sliding-window variance family (`StdDev`, `Variance`, `ZScore`, `Bollinger`) uses running `Σx²−mean²` with clamping (and periodic reseed for `Bollinger`), **not** Welford. True Welford is used only by `IntradayVolatilityProfile` and `SeasonalZScore` (it does not transfer cleanly to a sliding window). The **Kahan-summation** bullet is removed — no Kahan summation exists in the crate.

## THREAT_MODEL.md
- The C ABI is built with `panic = "abort"` and has no `catch_unwind`. Replace the false "catches panics so none cross the boundary" claim with the honest abort strategy (terminates deterministically instead of unwinding across the FFI boundary, which would be UB).
2026-06-11 03:28:19 +02:00
kingchencandGitHub 20c0002f8e ci: cap job runtimes and auto-retry the flaky Node test step (#252)
## Problem

A Node test job wedged on a macOS runner: the **Run Node tests** step (`node --test`) hung for **over an hour** while the identical tests passed in ~1.5 min on every other runner (Node 20 macOS same run: 1m46s; Node 18 macOS on a sibling PR: 1m34s). It is a one-off stuck process — the macOS analogue of the documented `setup-node` Windows CDN flake — not a real Node 18 vs 20 difference.

No job in `ci.yml` set any `timeout-minutes`, so a hung step runs toward GitHub's **6h default** before the platform kills it.

## Changes

- **Job-level `timeout-minutes: 20` backstop on all 15 jobs.** The slowest real job is ~5 min, so 20 min is generous headroom (survives cold-cache spikes) while turning a 6h hang into a 20-min fail. The clock counts execution time only — queued/waiting time does not count against it.
- **`Run Node tests` wrapped in `nick-fields/retry` (SHA-pinned, v4.0.0):** a hung attempt is killed after 6 min and retried once (`timeout_minutes: 6`, `max_attempts: 2`), so a one-off wedge self-heals without a manual re-run. Normal run is well under a minute; worst case 2×6 min stays under the 20-min job backstop.

## Notes

- New SHA-pinned third-party action (`nick-fields/retry@ad98453…` = v4.0.0) — satisfies the SHA-pin convention; `zizmor` runs on this PR.
- `ci.yml` validated as well-formed YAML; all 15 jobs confirmed to carry a job-level timeout.
2026-06-11 03:27:05 +02:00
kingchencandGitHub 99497eb062 fix(core): reject non-finite input in 16 pairwise indicators (#251)
## Problem

16 of the 24 pairwise indicators (`type Input = (f64, f64)`) accepted non-finite input unchecked. A single NaN/Inf tick produced a NaN reading, contradicting the streaming-robustness guarantee that *a single bad tick cannot silently poison state* (README, `ARCHITECTURE.md`).

The other 8 pairwise indicators (`Alpha`, `InformationRatio`, `KalmanHedgeRatio`, `PairSpreadZScore`, `PairwiseBeta`, `RelativeStrengthAb`, `SpreadBollingerBands`, `TreynorRatio`) already guard finite input and are untouched. Scalar (169 files) and candle (`Candle::new`) inputs were already protected.

## Severity split

- **7 running-sum indicators** \(permanent corruption — NaN entered `Σ` and stayed until `reset()`\): `Beta`, `BetaNeutralSpread`, `Cointegration`, `HasbrouckInformationShare`, `PearsonCorrelation`, `RollingCorrelation`, `RollingCovariance`.
- **9 buffer-recompute indicators** \(transient — NaN cleared once evicted, but still surfaced in the reading\): `DistanceSsd`, `GrangerCausality`, `KendallTau`, `LeadLagCrossCorrelation`, `OuHalfLife`, `SpearmanCorrelation`, `SpreadAr1Coefficient`, `SpreadHurst`, `VarianceRatio`.

## Fix

Add the established finite-input guard (the same pattern `Alpha` already uses) as the first step of `update()`, before any window or sum mutation. Signature unchanged → bindings re-export unchanged, no binding regen needed; core-only.

Each indicator gains a `non_finite_input_returns_none` test that exercises the guard (NaN and Inf, covering both `||` operands) and proves a clean warmup is unaffected by the rejected ticks.

Split into two commits by severity class.

## Verification

- `cargo fmt --all` clean
- `cargo test --workspace --all-features` — 4225 core tests pass (+16 new)
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` clean
2026-06-11 03:01:24 +02:00
kingchencandGitHub 7e51f31f02 sync-about: propagate published version into Java install snippets (#250)
## Problem

Java is the only target whose install snippet pins a version (the Maven coordinate). Every other language exposes its version through an api/*.md `Latest:` line or the docs published-versions table, both of which `sync-about.yml` already rewrites on each `v*` tag. The Java `<version>` tag and the `org.wickra:wickra:<v>` Gradle coordinate were never wired in, so they drifted:

- `webpage/index.md` install tab → stuck on `0.8.0`
- `webpage/api/java.md` → stuck on `0.7.9`
- `wickra-docs/Quickstart-Java.md` (Maven `<version>` + Gradle coord) → stuck on `0.7.9`

while the published version is already `0.8.3`.

## Fix

Extend the webpage and docs version-sync steps to rewrite the Maven `<version>` tag and the Gradle coordinate to the released `${version}`, and add `index.md` / `Quickstart-Java.md` to the respective commits. Java publishes on every release, so it tracks the same version as the rest — future releases self-heal these snippets.

Patterns dry-run-verified against the live files (all three resolve to `0.8.3`). The current drift is corrected directly in companion PRs on the webpage and wickra-docs repos.
2026-06-11 01:38:54 +02:00
kingchencandGitHub 3ee3fb67ec release: bump 0.8.2 -> 0.8.3 (#249)
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.
2026-06-10 03:55:39 +02:00
kingchencandGitHub 3ebcb3f758 Per-binding throughput benchmarks + test-coverage gaps (#246)
Adds a `throughput` benchmark to every target and closes two small
test-coverage documentation/QA gaps. One PR, no merge of binding code beyond
the additive benchmarks and one C test.

## 1. Per-binding throughput benchmarks (all 9 targets)

Each benchmark feeds a deterministic synthetic OHLCV series through three
indicators chosen by **FFI call-signature archetype** (not algorithm — the same
Rust core runs underneath all bindings):

- `SMA(20)` — 1-in → 1-out (baseline boundary cost)
- `ATR(14)` — multi-in → 1-out (input marshalling)
- `MACD(12,26,9)` — 1-in → multi-out (output marshalling)

Streaming is timed for all three; batch for the single-output SMA and ATR
(median of 3 runs, after a warmup pass).

New: Python (PyO3), WASM, C (CMake), C# (Stopwatch), Go, Java (FFM), R, and the
Rust core baseline (`examples/rust/.../throughput.rs`, **no FFI** — the ceiling
the bindings are measured against and the value their batch paths converge
towards). Node already had `throughput.js`.

**Not a speed claim:** there is no comparable streaming TA library for C, C#,
Go, Java, R or WASM to compare against, so these are raw per-binding throughput
numbers documenting each language's FFI overhead — see BENCHMARKS.md §3. The
"Wickra is fast" claim still lives in §1/§2 (Rust core + the Python/Rust
cross-library runs).

## 2. README `## Testing`: C# and C bullets

The section listed every layer except C# and C, even though both have suites.
Adds the two missing bullets.

## 3. C archetype ctest

`examples/c/archetypes.c` drives one indicator per FFI archetype through the
real C boundary (scalar + batch==streaming, multi-output, bars, profile, array
input) plus reset, invalid-parameter and NULL-safety — the C counterpart of the
Go/R/Java archetype suites. Runs on three OSes via the existing CMake/ctest.

## Notes

- Benchmarks are not CI-gated (manual-run scripts, like the existing
  `throughput.js`); no `ci.yml`/`release.yml` changes.
- Docs: BENCHMARKS.md §3, a `## Benchmark` section in every binding README, a
  CHANGELOG entry.
- Verified locally by running: Rust, Python, C, C#, Go, Java (real numbers); the
  C archetype ctest with `-Wall -Wextra -Wpedantic -Werror`. WASM and R are
  API-correct and syntax-checked but need their own toolchains to run.
2026-06-10 03:46:38 +02:00
kingchencandGitHub b31a9a3624 release: bump 0.8.1 -> 0.8.2 (#245)
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).
2026-06-10 01:57:10 +02:00
kingchencandGitHub 6433c9b3de bindings/r: build the C ABI from source for the WebAssembly target (#244)
## Problem
r-universe builds every package to WebAssembly (webR) in addition to the native platforms, calling `./configure --host=wasm32-unknown-emscripten`. `bindings/r/configure` only knew Linux/macOS/Windows and exited with `unsupported OS 'Emscripten'`, so the **WASM job was the single red check** on an otherwise fully-published r-universe build (all native platforms + deploy are green; the package installs fine everywhere).

## Fix
The r-universe wasm build image ships **cargo** (`/usr/local/cargo/bin`) and **emscripten** (`EMSDK` on PATH). So instead of needing a prebuilt wasm lib (which would risk an emscripten ABI/version mismatch), `configure` now detects the Emscripten host and **builds the C ABI staticlib from source** for `wasm32-unknown-emscripten` in-place — compiled with the image's own emscripten, so the ABI always matches. The static `libwickra.a` is linked into the package object (no shared lib, no rpath).

`wickra-core`'s rayon batch needs threads (absent on wasm), so the wasm build drops it via `--no-default-features`. `wickra-c` now takes `wickra-core` as a direct path dep with `default-features = false` plus a default `parallel` feature that re-enables it for native builds (a member-level `default-features = false` is ignored when inheriting a workspace dep — that was the trap). 

## Validated locally
- `cargo build -p wickra-c` (default) → rayon present, builds.
- `cargo build -p wickra-c --no-default-features` (the wasm feature path) → rayon **gone**, builds.
- `cargo build --workspace`, clippy, fmt all clean; `configure` passes `sh -n`.

## Cannot be validated locally
No Rust/emscripten wasm toolchain here. The wasm build only runs in r-universe, and `configure` downloads the matching `v${version}` source tag — so this takes effect from the **first release that includes it** (the tagged source must contain the feature toggle). Open risks: whether the image has the `wasm32-unknown-emscripten` Rust target pre-installed (configure runs `rustup target add` best-effort) and the wasm build time. Worth one r-universe rebuild to confirm.

Not merging — review first.
2026-06-10 01:44:48 +02:00
kingchencandGitHub 447bbc8220 bindings/csharp: sync the static csproj version to 0.8.1 (#243)
The static `<Version>` in `bindings/csharp/Wickra/Wickra.csproj` had drifted to 0.7.9 — it was missed in the 0.8.0/0.8.1 bumps (wrongly assumed to be purely tag-injected). The published NuGet package was correct (the release packs with `-p:Version=`), but the static field lagged. Resync to 0.8.1; bump_version.py now covers it.
2026-06-10 01:08:37 +02:00
kingchencandGitHub c9ef2fc037 bindings/r: bump DESCRIPTION version to 0.8.1 (#242)
The R package DESCRIPTION was missed in the 0.8.0/0.8.1 version bumps (it stayed at 0.7.9), so r-universe published wickra 0.7.9 while everything else is 0.8.1. Resync it; r-universe rebuilds from the main HEAD on its next ~hourly sync.
2026-06-10 00:47:13 +02:00
kingchencandGitHub 49c2872ad4 sync-about: sync the version for every registry row (#241)
The published-versions table sync only matched crates.io/PyPI/npm rows, so the NuGet, Maven Central, Go and r-universe rows added to the docs would stay stale on each release. Extend the regex to all seven registries and match the registry name whether plain or wrapped in a markdown link.
2026-06-10 00:26:49 +02:00
kingchencandGitHub 0c4bbaf314 release: bump 0.8.0 -> 0.8.1 (#240)
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.
2026-06-10 00:22:29 +02:00
kingchencandGitHub 99ad2ab107 release.yml: ship the dual license in the wickra-go mirror (#239)
The go-mirror job copied the source, header, README and libraries into wickra-go but not the license, so pkg.go.dev reports `License: None detected` (not redistributable). Copy the root `LICENSE-MIT` and `LICENSE-APACHE` into the assembled module. Takes effect on the next release; the already-published `v0.8.0` tag is immutable on the Go proxy and keeps its current state.
2026-06-10 00:20:34 +02:00
kingchencandGitHub 8e225a5872 docs: add Maven Central, Go and r-universe badges to the README (#238)
Adds the three remaining package badges to the README badge row — Maven Central (`org.wickra:wickra`), the Go module (`wickra-go`) and the r-universe R package — mirroring the org profile badge row. They are served from the `.github` badge snapshots like the existing badges.
2026-06-10 00:12:52 +02:00
kingchencandGitHub 87bb008aa1 release: bump 0.7.9 -> 0.8.0 + Go module mirror (#237)
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.
2026-06-09 23:50:48 +02:00
kingchencandGitHub b06ce2678a Restructure the Go binding for self-contained distribution (#236)
## Problem
Plain `go get` + `go build` of the Go binding never worked **as a dependency**:
- cgo `CFLAGS` pointed at `${SRCDIR}/../c/include` — the parent dir is **outside** the Go module, so a proxy-fetched module has no header.
- the library under `./lib` was git-ignored and never shipped; the README told users to `cargo build` it from the workspace, which only works inside a clone, not from the read-only module cache.

cgo has no build hook and Go has no registry, so the only way a consumer's `go get`+build can find the lib is for it to be committed **inside the module the consumer pulls**. Per the design decision, that module is a separate **`wickra-go`** repo (keeps this repo free of committed binaries), populated by the release pipeline — this PR is the in-repo restructure that makes that possible.

## Changes
- **Vendor the header** at `bindings/go/include/wickra.h` (committed copy of `bindings/c/include/wickra.h`); `CFLAGS` → `-I${SRCDIR}/include`. A **CI drift check** fails if the copy goes stale.
- **Per-platform libraries**: cgo `LDFLAGS` become per `GOOS`/`GOARCH`, linking `${SRCDIR}/lib/<goos>_<goarch>/`.
- **CI** stages the host library into `lib/<goos>_<goarch>/` (`RUNNER_OS`/`RUNNER_ARCH` — note `macos-latest` is arm64) and exports `WICKRA_GO_LIBDIR` for the Windows PATH; libraries stay git-ignored here.
- **README**: install via the `wickra-go` module + a contributor build section.

## Validation
- Local **windows/amd64**: `gofmt` clean, `go vet`, `go build`, `go test` all green against the staged library + vendored header.
- Linux/macOS arches → the 3-OS `Go on …` CI job.

Follow-up (Stage 2, separate): create `wickra-lib/wickra-go` + a `release.yml` mirror job (source + 6 platform libs → `lib/<goos>_<goarch>/`, commit + tag), and point the docs at the new import path. Part of the self-contained gap (`todo-11`).
2026-06-09 23:06:16 +02:00
kingchencandGitHub 5b7523265c Make the R binding self-contained (fetch the C ABI at install time) (#235)
## Problem
The R package built **only** inside the dev/CI workspace. `src/Makevars` and `configure.win` hard-required `WICKRA_INCLUDE_DIR` / `WICKRA_LIB_DIR` pointing at a pre-built `libwickra` (`configure.win` literally `: "${WICKRA_LIB_DIR:?...}"`), and there was no Unix `configure`. So **r-universe** and any plain `install.packages` / `install_github` failed — R had no working end-user install path.

## Fix
Fetch the prebuilt `wickra-c-<triple>.tar.gz` release asset matching the package version at install time and bundle the library into the package (same outcome as the C# / Java bindings, which bundle per-platform native libs):

- **New POSIX `configure`** (the Unix hook R lacked): detect OS/arch → triple, download + `untar` via **base R** (no curl/wget system dep), stage `wickra.h` + `libwickra.{so,dylib}` into `src/`, generate `src/Makevars` from `Makevars.in` with an rpath (`$ORIGIN` Linux, `@loader_path` + `install_name_tool` macOS) so the bundled lib resolves post-install.
- **`configure.win`**: drop the hard `WICKRA_LIB_DIR` requirement; download the Windows triple when unset, then keep the existing `wickra_abi.dll` rename + `objdump`/`dlltool` import-lib dance (sourcing the dll/header from the asset).
- **`install.libs.R`** also bundles `libwickra.dylib` (macOS); the `*.so`/`*.dll` globs already covered Linux/Windows.
- `WICKRA_INCLUDE_DIR`/`WICKRA_LIB_DIR` stay as an optional **dev override**.
- **CI (3 OS)** keeps building against the locally built C ABI (version-independent — avoids the chicken-egg of downloading the in-flight version) but **no longer exports `LD_LIBRARY_PATH`/`DYLD_LIBRARY_PATH`**, so it now verifies the bundled rpath — the real self-contained path users and r-universe get.

`Makevars` is now generated from `Makevars.in`; `SystemRequirements` + ignore/attributes files updated.

## Validation
- The Windows `objdump`/`dlltool` import-lib dance was smoke-tested locally against the real v0.7.9 asset (2412 `wickra_` exports → import lib built).
- Linux/macOS rpath bundling can't be tested on this Windows host → **the 3-OS `r` CI job is the gate** (now without the loader-path mask).
- Follow-up (separate, after release): set up `wickra-lib/wickra-lib.r-universe.dev` (`packages.json` → `bindings/r`).

Closes the R half of the self-contained-distribution gap (`todo-10`).
2026-06-09 22:11:50 +02:00
kingchencandGitHub d188959aab release: bump 0.7.8 -> 0.7.9 (#234)
Version bump 0.7.8 -> 0.7.9 for the Java binding release.
2026-06-09 20:33:17 +02:00
kingchencandGitHub 167f7b3ffe Add the Java binding over the C ABI hub (Panama/FFM) (#233)
Adds a Java binding (`bindings/java`) over the C ABI hub — the fourth language stecker after C#, Go and R, reaching the hub through the Java Foreign Function & Memory API (Panama, `java.lang.foreign`, final in Java 22) rather than JNI or jextract.

## What's here
- **`bindings/java`** — a Maven module (`org.wickra:wickra`) exposing all 514 indicators as idiomatic `AutoCloseable` classes. The downcall handles (`internal/NativeMethods.java`), the per-indicator wrappers and the output records are generated from `bindings/c/include/wickra.h` (same eight-archetype taxonomy as the C#/Go/R generators: scalar/batch, multi-output, bars, profile, values-profile, array-input). The opaque handle is a `MemorySegment` freed by a registered `java.lang.ref.Cleaner` action; multi-output returns a `record` (`null` at warmup), bars a `record[]`, profiles a record with a trailing `double[]`. The hand-written `WickraNative` resolves the native library (a bundled per-platform copy, or a `target/release` fallback for local development) and validates it against a sentinel symbol. repr(C) struct offsets are computed in the generator so the FFM reads land on the exact bytes.
- **`examples/java`** — the full example suite mirroring C/C#/Go/R: streaming, backtest, multi_timeframe, parallel_assets (parallel streams), three strategies, and `FetchBtcusdt`/`LiveBinance`.
- **CI** — a `java` job builds the C ABI library, sets up JDK 22 (Temurin, with a CDN-flake retry), runs the archetype test suite and the seven offline examples on Linux, macOS and Windows.
- **Release** — a gated `java-publish` job (skipped until the `JAVA_PUBLISH_ENABLED` repository variable is set) stages the native libraries from the `wickra-c-<triple>.tar.gz` assets into the binding's resources and deploys to Maven Central with GPG signing. Independent of the GitHub-release job, like the NuGet job.
- **Docs** — Java added to the README languages table, project layout, building/testing and comparison table, CONTRIBUTING, ARCHITECTURE, the examples index, the issue/PR templates, the About-description template, and the other binding READMEs.

## Requirements
Java 22+ (the FFM API is final since Java 22). The binding requires `--enable-native-access=ALL-UNNAMED` at runtime; the test and example runners pass it automatically.

No Rust crate or `Cargo.toml` change — the Java binding is standalone and additive. The generated `*.java` are committed (like the node `index.js`/`index.d.ts`); the generator stays private.
2026-06-09 20:30:29 +02:00
kingchencandGitHub 8659b42bef release: bump 0.7.7 -> 0.7.8 (#232)
Version bump 0.7.7 -> 0.7.8 for the R binding release.
2026-06-09 19:22:11 +02:00
kingchencandGitHub b7ef63400d Add the R binding over the C ABI hub (#230)
Adds an R binding (`bindings/r`) over the C ABI hub — the third language stecker after C# and Go, reaching the hub through R's native `.Call` interface (not extendr).

## What's here
- **`bindings/r`** — an R package exposing all 514 indicators as constructors that return a `wickra_indicator` object with generic `update`/`batch`/`reset` methods. The C glue (`src/wickra.c`) and R wrappers (`R/indicators.R`) are generated from `bindings/c/include/wickra.h` (same archetype taxonomy as the C#/Go generators: scalar/batch, multi-output, bars, profile, profile-values, array-input). The opaque handle is an R external pointer freed by a registered finalizer; multi-output returns a named vector (`NA` at warmup), bars a matrix, profiles a list.
- **`examples/r`** — the full example suite mirroring C/C#/Go: streaming, backtest, multi_timeframe, parallel_assets (`mclapply`), three strategies, and `fetch_btcusdt`/`live_binance`.
- **CI** — an `r` job builds the C ABI library, installs the package, runs the `testthat` suite and the offline examples on Linux, macOS and Windows (`R CMD check` is clean: 0 warnings, 0 notes).
- **Docs** — R added to the README languages table, project layout, building/testing, CONTRIBUTING binding table + regenerate note, ARCHITECTURE, examples index, issue/PR templates, the About-description template, and the other binding READMEs.

## Linking / distribution
The package compiles a thin `.Call` glue layer against the prebuilt C ABI library (header via `WICKRA_INCLUDE_DIR`, library via `WICKRA_LIB_DIR`). On Windows the package's own `wickra.dll` would collide with the C ABI's `wickra.dll`, so `configure.win` stages a renamed copy (`wickra_abi.dll`) and builds an import library referencing it; `install.libs.R` bundles the DLL and `.onLoad` puts it on the load path. On Linux/macOS the rpath locates the shared library. No `release.yml` change — R is distributed via r-universe / source install (gated).

No Rust crate or `Cargo.toml` change — the R package is standalone and additive.
2026-06-09 19:18:40 +02:00
kingchencandGitHub 8225e1ab91 Check out the PR head by SHA in the count-sync workflow (#231)
## Problem
The **Sync indicator count** check has failed on every release-bump PR since 0.7.5 (`release/0.7.6`, `release/0.7.7`, …). It is a race, not a counter mismatch.

The checkout step used the PR head **branch name**:
\`\`\`yaml
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }}
\`\`\`
Release PRs are merged with \`gh pr merge --squash --delete-branch\`, which deletes the head branch the instant the PR merges — usually before this queued read-only check reaches its checkout. Fetching the now-gone \`refs/heads/release/X.Y.Z\` then fails with exit 1 (3 retries, then error). Push-to-main, tag and slower feature PRs stayed green because their head branch still existed when the check ran.

## Fix
Check out \`github.event.pull_request.head.sha\` instead. The head SHA stays reachable via \`refs/pull/N/head\` after the branch is deleted, so an instant merge no longer red-Xes the run. It is still the author's head commit (not the merge ref), so the counter validates exactly what will land — the existing design intent is preserved.

Because \`pull_request\` runs the workflow definition from the merge commit, the fix already applies to this PR itself.
2026-06-09 18:59:09 +02:00
kingchencandGitHub d0061c73b8 release: bump 0.7.6 -> 0.7.7 (#229)
Bumps the workspace to 0.7.7 to ship the Go binding.
2026-06-09 17:35:25 +02:00
kingchencandGitHub 23d636fd97 Add the Go binding over the C ABI hub (#228)
Adds a Go binding (`bindings/go`) over the C ABI hub — the second language stecker after C#.

## What's here
- **`bindings/go`** — a cgo binding exposing all 514 indicators as idiomatic Go types with `New<Indicator>` constructors and `Update`/`Batch`/`Reset`/`Close` methods. The wrappers in `indicators_gen.go` are generated from `bindings/c/include/wickra.h` (same archetype taxonomy as the C# generator: scalar/batch, multi-output, bars, profile, profile-values, array-input). Opaque handles are freed by `Close()` with a `runtime.SetFinalizer` backstop; pointer arguments are caller-owned, panics never cross the boundary.
- **`examples/go`** — the full example suite mirroring C/C#: streaming, backtest, multi_timeframe, parallel_assets (goroutine fan-out), three strategies, and `fetch_btcusdt`/`live_binance`.
- **CI** — a `go` job builds the C ABI library, stages it, and runs `gofmt`/`go vet`/`go test` plus the offline examples on Linux, macOS and Windows.
- **Docs** — Go added to the README languages table, project layout, building/testing, CONTRIBUTING binding table + regenerate note, ARCHITECTURE, examples index, issue/PR templates, the About-description template, and the other binding READMEs.

## Linking / distribution
The binding links the prebuilt C ABI library via cgo (`libwickra.so`/`.dylib`/`wickra.dll` staged under `bindings/go/lib`, gitignored). The native libraries are already shipped per target triple by the existing `c-abi-build` release job; distribution is via the subdirectory module tag `bindings/go/vX.Y.Z` (gated), so `release.yml` needs no new publish job.

No Rust crate or `Cargo.toml` change — the Go module is standalone and additive.

Not for merge yet (gated, per request).
2026-06-09 17:33:37 +02:00
kingchencandGitHub fce26cf881 release: bump 0.7.5 -> 0.7.6 (#227)
Bumps the workspace to 0.7.6 to ship the C# (.NET) binding to NuGet.
2026-06-09 14:34:18 +02:00
kingchencandGitHub 91f6f67257 Add C# (.NET) binding over the C ABI hub (#226)
The first language stecker on the C ABI hub: a .NET binding exposing all 514
indicators as idiomatic `IDisposable` classes, generated from `wickra.h`.

## What's here

- **`bindings/csharp/`** — the `Wickra` .NET 8 package. `[LibraryImport]`
  source-generated P/Invoke (`NativeMethods.g.cs`) plus idiomatic wrappers
  (`Indicators.g.cs`), both generated from the committed `bindings/c/include/wickra.h`.
  The binding owns no indicator maths — it only marshals types across the C ABI.
- **Marshalling, verified end-to-end against the native library.** Opaque handles
  cross as `nint` kept alive per call via a `SafeHandle`; `bool` as
  `[MarshalAs(U1)]` (Rust `bool` is one byte); a self-correcting
  `DllImportResolver` validates the loaded library actually exports the Wickra
  ABI. Tests cover one representative per FFI archetype (scalar, candle, pairwise,
  multi-output, bars, profile, values-profile, order-book / array-input) plus
  exact Sma reference values.
- **NuGet packaging** — `dotnet pack` produces `Wickra.<version>.nupkg`; the
  release pipeline stages prebuilt native libraries under `runtimes/<rid>/native/`
  for six target triples (win/linux/osx × x64/arm64).
- **`examples/csharp/`** — nine examples mirroring `examples/c/`: streaming,
  backtest, multi_timeframe, parallel_assets, three strategies, and
  fetch_btcusdt + live_binance.
- **CI** — a `csharp` job on the three OSes builds the C ABI, tests the binding,
  and runs the offline examples. **Release** — a gated `csharp-publish` job packs
  and pushes to NuGet (gated on `NUGET_API_KEY`, independent of the GitHub-release
  job so a C# hiccup never blocks the C/C++ asset release).
- **Docs consistency wave** — README, CONTRIBUTING, CHANGELOG, examples/README,
  the issue / PR templates, `sync-about.yml`, and `.gitattributes`.

The native Python / Node / WASM bindings and the C ABI are untouched; this is
additive. Publishing to NuGet stays gated behind the release tag and the secret.
2026-06-09 14:32:05 +02:00
kingchencandGitHub 4caaa1db97 release: bump 0.7.4 -> 0.7.5 (#225)
Version bump for the C ABI hub release (0.7.4 -> 0.7.5). See #222 + #224.
2026-06-09 02:26:27 +02:00
kingchencandGitHub 12681e4b1b C ABI: full example suite + docs & About coverage (#224)
Stacked on #222 (base `feat/c-abi-hub`), so the diff is just the additions on top of the hub foundation — no merge of #222 required.

## What this adds

**Examples — full parity with rust/python/node (`examples/c/`)**
- `streaming.c` upgraded to the multi-indicator (SMA/EMA/RSI/MACD + signals) demo
- `backtest.c`, `multi_timeframe.c` (manual time-bucket resampling), `parallel_assets.c` (serial vs OpenMP fan-out, one handle per asset)
- three educational strategies: `strategy_rsi_mean_reversion.c`, `strategy_macd_adx.c`, `strategy_bollinger_squeeze.c`
- two network examples shelling out to `curl`: `fetch_btcusdt.c`, `live_binance.c` (REST poll)
- two header-only helpers (`wickra_csv.h`, `wickra_strategy.h`) since the C ABI ships no IO layer
- CMake builds all 11; the 9 offline ones run under `ctest` on 3 OS; the network two are built-only

**Docs & metadata — surface the C ABI everywhere it was missing**
- ARCHITECTURE diagram + crate table, SECURITY + THREAT_MODEL (the C ABI as the sole `unsafe` FFI surface), the three binding package READMEs, issue/PR templates, CHANGELOG, and the GitHub About template (live About + org description updated too)

**Cleanup**
- removed all references to the private generator tooling from public files (`bindings/c/src/lib.rs` header, `CONTRIBUTING.md`, `sync-about.yml`)

Verified locally: `cargo build -p wickra-c --release`, `cmake + ctest` (9/9 pass), and `-Wall -Wextra -Wpedantic` clean on gcc 13.
2026-06-09 02:14:28 +02:00
kingchencandGitHub 91e05e3c26 C ABI hub crate (bindings/c) foundation (#222)
## 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.
2026-06-09 02:07:03 +02:00
kingchencandGitHub 9d0983b666 ci(sync-about): sync indicator count into the webpage About page (#223)
The `wickra.org/about` page carries the indicator count in the bot-syncable `<N> indicators` token, but `sync-about.yml` only rewrote `index.md` + `.vitepress/config.ts` on the webpage. Add `about.md` to the webpage count step's `sed` file list and its `git add`, so the About page's count self-heals on every push-to-main / tag like the rest of the marketing site.

No-op for the Rust build — workflow file only.
2026-06-08 21:38:42 +02:00
kingchencandGitHub 13c8250488 release: bump 0.7.3 -> 0.7.4 (#221)
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).
2026-06-08 14:33:43 +02:00
kingchencandGitHub e5305ffa94 feat: add 7 alt-chart bar builders (B19) (#220)
Adds seven information-driven bar builders to the **Alt-Chart Bars** family, the final batch of the family-deepening run. Indicator count **507 → 514**.

## Builders
All implement the `BarBuilder` trait (`update(Candle) -> Vec<Bar>`), emitting a data-dependent number of completed bars per candle.

| Builder | Driver | Bar fields |
|---------|--------|-----------|
| `RangeBars` | close | open, close, direction |
| `TickBars` | OHLCV | open, high, low, close, volume |
| `VolumeBars` | OHLCV | open, high, low, close, volume |
| `DollarBars` (Lopez de Prado) | OHLCV | + dollar |
| `ImbalanceBars` | OHLC | + imbalance, direction |
| `RunBars` | OHLC | + length, direction |
| `ThreeLineBreakBars` | close | open, close, direction |

## Touchpoints
Seven core modules (each with full unit tests), `mod.rs`/`lib.rs` (builders counted, bar element types on their own re-export lines), README family rows, Python/Node/WASM hand-written bindings for the variable-length output (Python tuples + `(k, N)` ndarray; Node `Vec<object>`; WASM array of objects), the `bar_builder_update_candle` fuzz target, dedicated Python + Node tests, the `BAR_BUILDERS` completeness exclusion, and CHANGELOG.

## Verification
- `cargo test -p wickra-core --lib` — 4207 passed
- `cargo test -p wickra-core --doc` — 464 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` — clean
- `npm test` (node) — 584 passed
- `pytest` (python) — 957 passed
2026-06-08 14:32:40 +02:00
kingchencandGitHub 46be7a54ea release: bump 0.7.2 -> 0.7.3 (#219)
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).
2026-06-08 13:29:39 +02:00
kingchencandGitHub bca61322b5 feat: add 9 Risk / Performance indicators (B18) (#218)
Adds nine risk/performance metrics to the existing **Risk / Performance** family, all consuming a per-period return series (`f64` in, `f64` out). Indicator count **498 → 507**.

## Indicators

Single-param (`new(period)`, macro bindings):
- **SterlingRatio** — mean return over average drawdown of the equity curve.
- **BurkeRatio** — return over root-sum-squared drawdowns.
- **MartinRatio** — Ulcer Performance Index; return over RMS percentage drawdown.
- **TailRatio** — 95th percentile over the absolute 5th percentile return.
- **KRatio** — Kestner; equity-curve OLS slope over the standard error of that slope.
- **CommonSenseRatio** — tail ratio times gain-to-pain.
- **GainToPainRatio** — sum of returns over the sum of absolute losses.

Multi-param (hand-written Python/Node bindings, variadic WASM macro):
- **UpsidePotentialRatio** — `new(period, mar)`; upside mean over downside deviation (Sortino philosophy).
- **M2Measure** — `new(period, risk_free, benchmark_stddev)`; Modigliani M², Sharpe rescaled into benchmark return units.

## Touchpoints
Core modules + unit tests, `mod.rs`/`lib.rs` wiring, Python/Node/WASM bindings (`index.d.ts`/`index.js` regenerated), fuzz drive lines, Python `SCALAR` registry + Node factories, CHANGELOG, and the indicator counters.

## Verification
- `cargo test -p wickra-core --lib` — 4149 passed
- `cargo test -p wickra-core --doc` — 457 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` — clean
- `npm test` (node) — 577 passed
- `pytest` (python) — 947 passed
2026-06-08 13:23:01 +02:00