Commit Graph

123 Commits

Author SHA1 Message Date
kingchenc 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
kingchenc 063a1b5192 docs: move the Live demo badge one position forward in the README badge row (#341) 2026-06-24 03:33:19 +02:00
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 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
kingchenc 91aa6fffbf feat(market-profile): naked POC, single prints, profile shape, HVN/LVN, composite profile (B17) (#216)
## B17 Market Profile — five new indicators (493 → 498)

| Indicator | Output | Notes |
|-----------|--------|-------|
| `NakedPoc` | `f64` | most recent untouched point-of-control level |
| `SinglePrints` | `f64` | count of single-print price levels |
| `ProfileShape` | `f64` | b/P/D shape classification as a numeric code |
| `HighLowVolumeNodes` | struct `{hvn, lvn}` | highest/lowest volume nodes |
| `CompositeProfile` | struct `{poc, vah, val}` | multi-session composite volume profile |

### Wiring
- Core structs + full unit tests; all join the existing **Market Profile** family.
- Hand-written Python/Node/WASM bindings (f64 via candle helpers; struct via PyArray2 / `#[napi(object)]` / `Object`+`Reflect::set`).
- Fuzz drives in `indicator_update_candle.rs`; CANDLE_SCALAR + MULTI registry tests + reference tests.
- README counter + `docs/README.md` + `FAMILIES` assert bumped to 498.

### Verify (local, all green)
- `cargo test -p wickra-core --lib`: 4066 · `--doc`: 448
- clippy workspace: clean
- node: 568 · pytest: 938
2026-06-08 04:17:06 +02:00
kingchenc ff5a047078 feat(derivatives): leverage, OI/volume, perpetual premium, funding APR, OI momentum (B16) (#214)
## B16 Derivatives — five new indicators (488 → 493)

All consume a `DerivativesTick` and emit `f64`:

| Indicator | Reads | Formula |
|-----------|-------|---------|
| `EstimatedLeverageRatio` | open_interest, long_size, short_size | `OI / (long + short)` |
| `OiToVolumeRatio` | open_interest, taker_buy_volume, taker_sell_volume | `OI / (buy + sell)` |
| `PerpetualPremiumIndex` | mark_price, index_price | `(mark − index) / index` |
| `FundingImpliedApr` | funding_rate | `rate × intervals_per_year` |
| `OpenInterestMomentum` | open_interest | `100 · (OI_t − OI_{t−period}) / OI_{t−period}` |

### Wiring
- Core structs + full unit tests (incl. zero-denominator branches).
- Hand-written Python/Node/WASM tick bindings; two new tick helpers (`deriv_oi_long_short`, `deriv_oi_taker`).
- Fuzz drives in `indicator_update_derivatives.rs`; dedicated reference + streaming-vs-batch tests (Python + Node).
- README counter + `docs/README.md` + `FAMILIES` assert bumped to 493.

### Verify (local, all green)
- `cargo test -p wickra-core --lib`: 4028 · `--doc`: 443
- clippy workspace: clean
- node: 563 · pytest: 928
2026-06-08 03:33:59 +02:00
kingchenc e385734275 feat(microstructure): trade-sign autocorrelation, PIN, Hasbrouck information share (B15) (#212)
## B15 Microstructure — three new indicators (485 → 488)

| Indicator | Input | Output | Notes |
|-----------|-------|--------|-------|
| `TradeSignAutocorrelation` | `Trade` | `f64` ∈ [-1,1] | lag-1 autocorrelation of the signed aggressor (order-flow persistence) |
| `Pin` | `Trade` | `f64` ∈ [0,1] | probability of informed trading from rolling buy/sell imbalance (EKOP single-window estimator); `name()` = `"PIN"` |
| `HasbrouckInformationShare` | `(f64, f64)` | `f64` ∈ [0,1] | variance-ratio proxy for each venue's share of price discovery |

### Wiring
- Core structs + full unit tests (every branch).
- Hand-written Python/Node/WASM bindings for the two `Trade`-input indicators (precedent `TradeImbalance`); `node_pair_indicator!` / `wasm_pair_indicator!` macro bindings + hand Python pyclass for the pairwise Hasbrouck (precedent `RollingCorrelation`).
- Fuzz drives added to `indicator_update_trade.rs` and `indicator_update_pair.rs`.
- Dedicated Python + Node streaming-vs-batch and reference tests; Hasbrouck in the `PAIR` registry.
- README counter (3 spots) + `docs/README.md` + `FAMILIES` assert bumped to 488.

### Verify (all green, local)
- `cargo test -p wickra-core --lib`: 3991 passed
- `cargo test -p wickra-core --doc`: 438 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`: clean
- node: 561 passed · pytest: 926 passed
2026-06-08 03:07:50 +02:00
kingchenc 943825d6a0 feat: add Candlestick Patterns deepening (B14, 6 indicators) (#209)
B14 of the family-deepening roadmap — six candlestick patterns (479 -> 485), all in the **Candlestick Patterns** family.

**Fixed-lookback (candle-pattern macro bindings, neutral 0.0 during warmup):**
- **Tristar** — three-doji star reversal.
- **Harami Cross** — Harami whose second candle is a contained doji.
- **Tower Top/Bottom** — tall bar, small pause, tall opposite bar.

**Windowed / parameterized (hand-bound, `candle -> f64`):**
- **Frying Pan Bottom** — rounded U-shaped accumulation base, recovery-confirmed.
- **Dumpling Top** — rounded dome-shaped distribution top, breakdown-confirmed.
- **New Price Lines** — run of N consecutive new closing highs (+1) / lows (-1).

Window/Gap (Rising-Falling) dropped (SKIP — existing gap coverage). Wiring complete across core, Python, Node, WASM, fuzz, tests, README + docs counter (485) and CHANGELOG. Verified: core 3966 + doc 435, clippy clean, node 560, python 922.
2026-06-08 02:29:24 +02:00
kingchenc 34c097aee2 docs: refresh Python benchmark figures from a fresh measured run (#206)
The published Python benchmark tables (README/BENCHMARKS.md) were a stale, incoherent run. Re-measured locally with the current build (wickra 0.6.5, post batch fast-paths) via `compare_libraries.py` on the same 9950X.

- **Streaming vs talipp:** 11-56x (was 9-58x).
- **Batch:** real per-indicator numbers; MACD and ATR were notably off in the old table.
- **Prose:** Wickra beats TA-Lib on RSI and ATR (no longer MACD, which now trails 130 vs 111 us).

Rust tables unchanged. Numbers are a single coherent run; absolute us still depend on machine state (caveat already in the doc).
2026-06-08 02:13:13 +02:00
kingchenc ceaeb90a22 feat: add Ichimoku & Charts deepening (B13, 5 indicators) (#207)
B13 of the family-deepening roadmap — five alternative-chart indicators (474 -> 479), all in the **Ichimoku & Charts** family.

- **Smoothed Heikin-Ashi** (`candle -> struct {open, high, low, close}`) — a Heikin-Ashi candle computed from EMA-smoothed OHLC.
- **Heikin-Ashi Oscillator** (`candle -> f64`) — the HA body (`ha_close - ha_open`), optionally EMA-smoothed, as a zero-line oscillator.
- **Three Line Break** (`candle -> f64`) — line-break ("kakushi") chart trend direction; reverses only when the close breaks the extreme of the last N lines. Distinct from the candlestick `ThreeLineStrike`.
- **Equivolume** (`candle -> struct {height, width}`) — a box whose height is the bar range and width is volume-relative.
- **CandleVolume** (`candle -> struct {body, width}`) — a candle whose body is close-minus-open and width is volume-relative.

All bindings hand-written (3 struct-output + 2 candle-input-with-open / non-period-ctor). Wiring complete across core, Python, Node, WASM, fuzz, tests, README + docs counter (479) and CHANGELOG. Verified: core 3915 + doc 432, clippy clean, node 554, python 913.
2026-06-08 01:49:03 +02:00
kingchenc 8431b1400c feat: add DeMark deepening (B12, 7 indicators) (#204)
B12 of the family-deepening roadmap — seven Tom DeMark indicators (467 -> 474).

**Candle -> +1/0 qualifier patterns (candlestick macro bindings):**
- **TD Camouflage** — hidden intrabar strength/weakness against the prior close.
- **TD Clop** — two-bar open/close engulfing reversal.
- **TD Clopwin** — the inside-body cousin of TD Clop (compression bar).
- **TD Propulsion** — continuation thrust closing beyond the prior extreme.
- **TD Trap** — inside ("trap") bar followed by a range breakout.

**Hand-bound:**
- **TD D-Wave** — streaming Elliott-style 1-5 / A-C swing-wave counter (candle -> f64, `strength` param).
- **TD Moving Averages** — ST1/ST2 median-price trend ribbon (candle -> struct {st1, st2}).

All seven join the existing **DeMark** family. Patterns follow the house-style
+1/0 candle-pattern convention (neutral 0.0 during warmup). Public binding names
use the family-consistent `TD...` casing.

Wiring complete across core, Python, Node, WASM, fuzz, tests, README + docs
counter (474) and CHANGELOG. Verified: core 3874 + doc 427, clippy clean,
node 549, python 903.
2026-06-08 01:12:46 +02:00
kingchenc 05fe7ffa90 perf: bit-exact batch fast paths + streaming-first benchmark docs (#202)
## Summary
- Dedicated batch fast paths for **EMA, RSI, Bollinger, MACD and ATR** (used by the Python bindings): one allocation filled in a single pass, warmup encoded as `NaN`, no per-element `Option` or input re-validation. Each is **bit-for-bit equal** to replaying `update` — SMA/Bollinger keep the drift-reseed cadence, the EMA-family keep the seed division and `mul_add` recurrences. Adds the `BatchNanExt` extension trait.
- **Cross-library benchmark refresh**: `compare_libraries.py` reports the median across timing rounds (`--rounds` / `--streaming-rounds`), gains `--skip-batch` / `--skip-streaming`, and runs every peer through the streaming arena (recompute for batch-only libraries). `wickra-bench` drives the batch fast paths against `kand`.
- **README** benchmark section reordered streaming-first (the order-of-magnitude result), with measured TA-Lib/tulipy/pandas-ta numbers in place of the CI-only placeholders.

## Impact
- Python batch ~2× faster on EMA/RSI/MACD/ATR; streaming path unchanged.
- The `batch == streaming` equivalence stays bit-exact.

## Verification
- `cargo fmt` · `cargo clippy --workspace --all-targets --all-features -- -D warnings` (clean)
- `cargo test --workspace --all-features` — 3782 unit + 420 doc tests pass
- Python `pytest` — streaming-vs-batch, known-values, input-validation, smoke pass

## Notes
- Node/WASM bindings keep their existing batch; the fast paths are Python-only for now.
2026-06-08 00:17:58 +02:00
kingchenc e97c3389fe feat: add Pivots & S/R indicators (B11) (#201)
Adds five support/resistance and pivot indicators, growing the catalog 462 -> 467.

## Indicators
- **CentralPivotRange** (Candle -> struct) — the classic pivot `(H+L+C)/3` flanked by two central levels (TC/BC); range width gauges trending vs balanced days.
- **MurreyMathLines** (Candle -> struct) — T. H. Murrey's eighths grid over a rolling high-low frame; nine levels (0/8 .. 8/8) acting as support/resistance.
- **AndrewsPitchfork** (Candle -> struct) — median line and two parallels projected forward from the last three auto-detected swing pivots (symmetric fractal of half-width `strength`).
- **VolumeWeightedSr** (Candle -> struct) — a band whose edges are the volume-weighted average of recent highs (resistance) and lows (support); falls back to equal weighting when window volume is zero.
- **PivotReversal** (Candle -> f64) — a `+1`/`-1` breakout signal fired on the bar where price closes through the most recently confirmed swing pivot.

## Wiring
Core structs with branch-complete unit tests, Python/Node/WASM bindings, fuzz drives, reference + streaming-vs-batch tests, README + docs counter sync (FAMILIES "Pivots & S/R"), and CHANGELOG entries.

Verified locally: `cargo fmt`, `cargo test -p wickra-core` (3798 lib + 425 doc), `cargo clippy --workspace --all-targets --all-features -D warnings`, `npm run build && npm test` (542), `maturin develop` + `pytest` (891).
2026-06-08 00:13:42 +02:00
kingchenc 80850c81f7 Add B10 Ehlers / Cycle deepening (10 indicators) (#199)
Deepens the **Ehlers / Cycle (DSP)** family (B10) with ten indicators (452 -> 462):

- **HighpassFilter**, **Reflex**, **Trendflex**, **CorrelationTrendIndicator**, **AdaptiveRsi**, **UniversalOscillator** — scalar (f64) Ehlers filters/oscillators.
- **AdaptiveCci** — efficiency-ratio-adaptive CCI on typical price (Candle input).
- **BandpassFilter**, **EvenBetterSinewave**, **AutocorrelationPeriodogram** — multi-arg scalar (hand-written bindings; the wasm variadic scalar macro covers wasm).

Verified locally: 3755 core lib + 420 doc tests, clippy clean, 537 node tests, 881 pytest, counter 462.
2026-06-07 04:25:16 +02:00
kingchenc 389200f855 Add B9 Price Statistics deepening (5 indicators) (#197)
Deepens the **Price Statistics** family (B9) with five rolling-statistics indicators (447 -> 452):

- **ShannonEntropy** — Shannon entropy of a binned rolling value distribution.
- **SampleEntropy** — Richman-Moorman sample entropy (regularity/complexity of a window).
- **KendallTau** — Kendall rank correlation (tau-b) over paired observations (pairwise; distinct from Pearson/Spearman).
- **JarqueBera** — Jarque-Bera normality test statistic over a rolling window.
- **RollingMinMaxScaler** — maps the latest value to 0..1 over a rolling window.

All scalar f64 input except KendallTau (pairwise). Multi-arg scalars (Shannon/Sample entropy) use hand-written Python/Node bindings + the variadic wasm macro; KendallTau uses the pair macros. Verified locally: 3668 core lib + 410 doc tests, clippy clean, 527 node tests, 871 pytest, counter 452.
2026-06-07 03:08:53 +02:00
kingchenc c78b84e186 Add B8 Volume family deepening (7 indicators) (#195)
Deepens the **Volume** family (B8) with seven indicators (440 -> 447):

- **VolumeRsi** — Wilder RSI computed on signed volume flow.
- **WilliamsAd** — Williams Accumulation/Distribution cumulative line (distinct from Chaikin A/D).
- **TwiggsMoneyFlow** — true-range volume accumulation with Wilder smoothing (distinct from CMF).
- **TradeVolumeIndex** — tick-direction volume accumulation past a min-tick threshold (distinct from TSV).
- **IntradayIntensity** — volume weighted by close position within the bar range.
- **BetterVolume** — VSA volume-vs-spread effort/result classifier.
- **VolumeWeightedMacd** — MACD computed on VWMA with signal line and histogram (struct output).

("Up/Down Volume Ratio" already ships from A2.) All Candle input; the six scalar stops emit f64, VolumeWeightedMacd a {macd, signal, histogram} struct. Hand-written Python/Node/WASM bindings for the volume signature. Verified locally: 3620 core lib + 405 doc tests, clippy clean, 522 node tests, 865 pytest, counter 447.
2026-06-07 02:30:56 +02:00
kingchenc 2991ba411d Add B7 Trailing Stops family (6 indicators) (#193)
Adds the **Trailing Stops** family deepening (B7), six new indicators (434 -> 440):

- **KaseDevStop** — Cynthia Kase's volatility stop on the standard deviation of the two-bar true range.
- **ElderSafeZone** — Alexander Elder's stop offset by a multiple of average market noise.
- **AtrRatchet** — Kaufman ATR ratchet that tightens its multiple by a per-bar increment.
- **Nrtr** — Nick Rypock Trailing Reverse (percentage band).
- **TimeBasedStop** — exits after a fixed number of bars (scalar fraction of elapsed life).
- **ModifiedMaStop** — moving-average based trailing stop.

("Wilder Volatility System" is intentionally skipped — it overlaps the existing VoltyStop/Psar/SarExt.)

Each takes Candle input; the five band/structure stops emit a {value, direction} struct, TimeBasedStop a scalar. Wired across core, Python/Node/WASM bindings, fuzz target and tests. Verified locally: 3560 core lib + 398 doc tests, clippy clean, 515 node tests, 852 pytest, counter 440.
2026-06-07 01:32:15 +02:00
kingchenc 67feec598a feat(indicators): add B6 Bands & Channels family (429 -> 434) (#191)
Adds the **B6 Bands & Channels** batch — five band/channel indicators, taking the catalogue from 429 to 434.

| Indicator | Input → Output | Summary |
|-----------|----------------|---------|
| `ProjectionBands` | `Candle` → `{upper,middle,lower}` | Widner forward-projected high/low regression envelope |
| `ProjectionOscillator` | `Candle` → `f64` | Close position inside the projection bands, scaled 0..100 |
| `QuartileBands` | `f64` → `{upper,middle,lower}` | Rolling 25th/50th/75th-percentile (Q1/median/Q3) envelope |
| `BomarBands` | `f64` → `{upper,middle,lower}` | Adaptive percentage bands containing a target coverage fraction of recent closes |
| `MedianChannel` | `f64` → `{upper,middle,lower}` | Robust median ± multiplier·MAD envelope |

All five are distinct from existing indicators (verified against the core: `LinRegChannel`, `StandardErrorBands`, `Donchian`, `RollingQuantile`, `HurstChannel`). SKIPped from the roadmap: Price Channel (= `Donchian`) and Moving-Average Channel (≈ `MaEnvelope`/`Keltner`).

Each ships:
- Core indicator with per-branch unit tests (Codecov-strict 100%).
- python / node / wasm bindings (struct outputs are hand-written; `ProjectionOscillator` uses the generated candle→f64 path).
- Fuzz drives, python (`MULTI`/`SCALAR_MULTI`/`CANDLE_SCALAR`) + node test registries, README + CHANGELOG counter bump to 434.

Verified locally: `cargo fmt`, `clippy --workspace --all-targets --all-features -D warnings` (clean), `wickra-core` 3511 lib + 392 doc tests, node 509 tests, pytest 840.
2026-06-07 00:03:02 +02:00
kingchenc 6b8c6a0e7f B5 volatility & bands batch (423 -> 429) (#189)
Adds six **Volatility & Bands** indicators (Part B5 of the expansion roadmap), 423 → 429.

| Indicator | Input → Output | Summary |
|-----------|----------------|---------|
| `EwmaVolatility` | `f64` → `f64` | RiskMetrics exponentially-weighted volatility (λ decay) |
| `Garch11` | `f64` → `f64` | GARCH(1,1) conditional volatility with a long-run-variance anchor |
| `BipowerVariation` | `f64` → `f64` | jump-robust realized bipower variation (π/2 · Σ\|rₜ\|\|rₜ₋₁\|) |
| `VolatilityRatio` | `Candle` → `f64` | Schwager's true range over the EMA of prior true ranges (>2 = wide-ranging day) |
| `VolatilityCone` | `Candle` → `VolatilityConeOutput` | current realized volatility within its min/median/max envelope + percentile |
| `VolatilityOfVolatility` | `f64` → `f64` | sample stddev of a rolling realized-volatility series |

### Notes
- Two B5 roadmap items were dropped as duplicates/by-construction: `RealizedVolatility` already ships (v0.5.4); `Downside Semi-Deviation` is internal to Sortino. `Bipower Variation` confirmed distinct from `JumpIndicator` (a ±1 flag, not a variance measure).
- `VolatilityRatio` implements the widely-charted EMA-of-true-range convention (denominator excludes the current bar so the 2.0 threshold means "twice typical"), distinct from the existing pairwise `variance_ratio`.
- `Garch11` mean-reverts to `ω/(1−β)` on a flat series (does not decay to 0 like EWMA) — pinned by a dedicated test.

### Coverage / verification
- Full core + Python/Node/WASM bindings, fuzz drivers (scalar + candle), registries, CHANGELOG, README + docs counter sync.
- 100% unit-test coverage per indicator (every branch).
- Green locally: `cargo clippy --workspace --all-targets --all-features -D warnings`, core lib (3479) + doc (387), node (504), python (830).

Deep-dive docs for all six are staged for `wickra-docs` and pushed after release (gated).
2026-06-06 22:38:34 +02:00
kingchenc db186b18d3 docs(readme): star-history chart + ci(sync-about): sync docs config count (#188)
Add a dark-mode star-history chart under the README footer thank-you line (all existing badges kept), and make sync-about also patch the indicator count into wickra-docs .vitepress/config.ts.
2026-06-06 21:32:35 +02:00
kingchenc aacb9280f1 Honest tiered cross-library benchmark + streaming/batch perf (#186)
## Summary

An honest, tiered cross-library benchmark — and the optimization pass it triggered.

### Performance (wickra-core, outputs unchanged)
Profiling against the other Rust TA crates exposed real inefficiencies. Each
benchmarked indicator is now **5–79% faster** in both streaming and batch:

- **SMA, Bollinger**: flat `Box<[f64]>` ring buffers replace `VecDeque` (−69…79%).
- **RSI**: `100·ag/(ag+al)` collapses three divisions into one; Wilder smoothing
  hoists `1/period` out of the hot path (−46%).
- **ATR**: reciprocal hoisted (−42%).
- **EMA/RSI/ATR**: per-tick `Option<f64>` hot state → bare `f64` + ready flag.

Net result vs `kand`: Wickra now wins **RSI, Bollinger and ATR** (streaming), and
ties `ta-rs` on SMA — up from losing every indicator 1.5–6× before.

### Benchmark harness
New `crates/wickra-bench` (publish=false): a Criterion benchmark comparing Wickra
against `kand`, `ta-rs` and `yata` on an identical BTCUSDT candle series, in
streaming and batch modes. Peer APIs were verified against their source, not
guessed. Wired into the nightly `cross-library-bench` workflow as a separate job.

### Honest README
The benchmark section is rewritten into three layered tables (Rust core vs Rust
crates; Python vs the Python ecosystem) that **show the losses as well as the
wins**. The "only library that combines…" claim is gone; the new framing is
breadth + multi-language reach + the deliberate safety trade-off that costs raw
speed. Added an origin/why-slower rationale and a star CTA.

### Python benchmark
Added `tulipy` runners and expanded per-tick streaming coverage to SMA/EMA/RSI/
MACD/Bollinger. `bench.in`/`bench.txt` now lock `TA-Lib` + `tulipy` (hash-pinned);
`pandas-ta` stays out (it requires Python ≥ 3.12, the bench runs on 3.11).

### Notes
- TA-Lib/tulipy numbers in the README Python table are marked ⧗ — they are
  produced by the CI Linux job (C extensions don't build cleanly on every
  desktop), not measured locally.
- The matching `wickra-docs` prose update is committed separately and will be
  pushed with the release, per the docs-don't-lead-the-registries rule.

Verified locally: `cargo fmt`, `cargo test --workspace --all-features` (3413 core
+ bindings), `cargo clippy --workspace --all-targets --all-features -D warnings`,
Node build + 498 tests, and pytest all green.
2026-06-06 20:57:31 +02:00
kingchenc 1f4bf9e3a6 feat(core): B4 price oscillators (TsfOscillator, MacdHistogram, PpoHistogram) (#184)
Adds three **Price Oscillators** family indicators (420 → 423).

## Indicators

- **TsfOscillator** — `100·(close − TSF)/close`, the percentage gap of the close to the **one-bar-ahead** time-series forecast. Close-relative companion to `Cfo`, which measures the same gap against the regression value at the *current* bar; the two differ by exactly the slope term `100·b/close`.
- **MacdHistogram** — the standalone `macd − signal` bar of MACD exposed as a plain `f64` series.
- **PpoHistogram** — the Percentage Price Oscillator with its 9-period signal EMA and the resulting scale-free, zero-centered histogram (PPO itself only emits the line).

All three are scalar `f64` indicators wrapping existing, already-tested building blocks (`MacdIndicator`, `Ppo` + `Ema`, `Tsf`).

## Scope notes (VORAB-CHECK)

The B4 roadmap listed six items; three were dropped to avoid duplicates:
- *Forecast Oscillator* already ships as `Cfo`.
- *Derivative Oscillator* already ships (`DerivativeOscillator`, B2).
- *Detrended Synthetic Price* deferred — no citable formula distinct from the existing `Apo`/`Dpo`.

## Touchpoints

Core (`tsf_oscillator.rs`, `macd_histogram.rs`, `ppo_histogram.rs`) with full per-branch unit tests, `mod.rs`/`lib.rs`, python/node/wasm bindings (wasm via typed-arg macro, python/node hand-written for the multi-arg histograms), fuzz drivers, python reference + streaming-vs-batch tests, node factories, README family row + counter, CHANGELOG.

Local verify: `cargo test --workspace` green, `clippy -D warnings` clean, node 498 tests, full python suite green.
2026-06-04 19:36:43 +02:00
kingchenc 13bc801f89 feat(indicators): B3 Trend & Directional batch (413 -> 420) (#181)
Adds the **B3 — Trend & Directional** batch: seven new indicators, taking the
catalog from 413 to 420 (Trend & Directional family).

| Indicator | Input → Output | Summary |
|-----------|----------------|---------|
| `Qstick` | candle → f64 | Chande's SMA of the candle body (close − open) |
| `TtmTrend` | candle → f64 (±1) | John Carter close-vs-median-SMA trend filter |
| `TrendStrengthIndex` | f64 → f64 | signed r² of an OLS regression of price vs time |
| `PolarizedFractalEfficiency` | f64 → f64 | Hannula directional trend efficiency |
| `WavePm` | f64 → f64 | Kase variance-normalised peak-momentum statistic (reconstruction) |
| `GatorOscillator` | candle → struct | Bill Williams Alligator convergence/divergence histogram |
| `KasePermissionStochastic` | candle → struct | double-smoothed stochastic permission filter |

Note: the roadmap's "Directional Indicator +DI/−DI" item is already covered by
the existing standalone `PlusDi` / `MinusDi` / `Dx`, so it is intentionally not
re-added.

All touchpoints wired: core (every-branch unit tests), Python/Node/WASM
bindings, fuzz drivers, Python test registries + reference tests, Node
factories, README/CHANGELOG counters.

Local verify: `cargo test -p wickra-core` (lib 3389 + doc 378), `cargo clippy
--workspace --all-targets --all-features -- -D warnings`, node build + 495
tests, maturin + 815 pytest, counter 420 == 420.
2026-06-04 17:57:24 +02:00
kingchenc 4f81222aed Deepen Momentum Oscillators family with ten additions (#179)
Deepens the **Momentum Oscillators** family with ten widely-used oscillators
(403 → 413 indicators), the second batch of Part B (family deepening).

| Indicator | Binding | Input → Output |
|-----------|---------|----------------|
| `DisparityIndex` | `DisparityIndex` | scalar → scalar |
| `FisherRsi` | `FisherRSI` | scalar → scalar |
| `Rmi` | `RMI` | scalar (period, momentum) → scalar |
| `DerivativeOscillator` | `DerivativeOscillator` | scalar (4 periods) → scalar |
| `Rsx` | `RSX` | scalar → scalar |
| `DynamicMomentumIndex` | `DynamicMomentumIndex` | scalar → scalar |
| `IntradayMomentumIndex` | `IMI` | candle (open+close) → scalar |
| `StochasticCci` | `StochasticCCI` | candle → scalar |
| `ElderRay` | `ElderRay` | candle → struct (bull/bear) |
| `Qqe` | `QQE` | scalar → struct (rsi_ma/trailing) |

LSMA was dropped from the planned set: it already ships as `LinearRegression`.

The single-period scalars use generated macro bindings; `Rmi` /
`DerivativeOscillator` use hand node/python bindings with the typed wasm macro;
`ElderRay`/`Qqe` use custom struct bindings; `IntradayMomentumIndex` uses custom
candle bindings carrying the open. Full coverage: core modules with per-branch
unit tests, mod/lib catalogue, FAMILIES + assert, README + docs counters,
CHANGELOG, all three bindings (regenerated `index.d.ts`/`index.js`), fuzz
drivers, and the python/node test registries.

Local verification: `cargo test -p wickra-core` (lib 3335 + doc 371),
`cargo clippy --workspace --all-targets --all-features -D warnings` clean,
node `npm run build && npm test` (488), python `pytest` (802).
2026-06-04 15:26:17 +02:00
kingchenc b228a70d7d Deepen Moving Averages family with seven additions (#177)
Deepens the **Moving Averages** family with seven widely-used variants
(396 → 403 indicators), the first batch of Part B (family deepening).

All are scalar `f64 → f64`:

| Indicator | Binding | Notes |
|-----------|---------|-------|
| `SineWeightedMa` | `SWMA` | symmetric half-cycle sine-weighted window |
| `GeometricMa` | `GMA` | rolling geometric mean (log-space average) |
| `Ehma` | `EHMA` | exponential Hull MA (Hull construction over EMAs) |
| `MedianMa` | `MedianMA` | rolling median, robust to single outliers |
| `AdaptiveLaguerreFilter` | `AdaptiveLaguerre` | Ehlers' adaptive Laguerre filter (median-of-normalised-error γ) |
| `GeneralizedDema` | `GD` | Tillson's volume-factor double EMA; `v=1` is DEMA, `v=0` is EMA |
| `HoltWinters` | `HoltWinters` | Holt's linear double exponential smoothing (level + trend) |

LSMA was dropped from the planned set: it already ships as `LinearRegression`
(TA-Lib `LINEARREG`, the rolling least-squares endpoint).

The five single-period filters use the generated scalar macro bindings;
`GeneralizedDema` (period, v) and `HoltWinters` (alpha, beta) use hand-written
node/python bindings with the typed wasm macro (precedent `T3` / `Alma`).

Full coverage: core modules with per-branch unit tests (100% intent), mod/lib
catalogue, FAMILIES group + assert, README + docs counters, CHANGELOG, all three
bindings (regenerated `index.d.ts` / `index.js`), fuzz drivers, and the
python/node test registries.

Local verification: `cargo test -p wickra-core` (lib 3255 + doc 361),
`cargo clippy --workspace --all-targets --all-features -D warnings` clean,
node `npm run build && npm test` (478), python `pytest` (791).
2026-06-04 13:44:51 +02:00
kingchenc fcb221ec03 feat: add 19 indicators for external feature-extractor coverage (377 -> 396) (#175)
Adds 19 streaming indicators so an external trading-bot feature extractor can replace its hand-built features with native, batch/streaming-equivalent ones. Each is a real gap (verified against the existing catalogue), production-only, with full Python/Node/WASM bindings, fuzz drivers, and tests. Five commits, one per family group; counter 377 -> 396.

## What's added

**Price Statistics (6)** — `LogReturn`, `RealizedVolatility` (raw quadratic variation, the un-annualised counterpart to `HistoricalVolatility`), `RollingQuantile`, `RollingIqr`, `RollingPercentileRank`, `SpreadAr1Coefficient` (pairwise AR(1) rho of the spread; complements `OuHalfLife`).

**Price Action (4)** — `CloseVsOpen`, `BodySizePct`, `WickRatio`, `HighLowRange` (stateless per-bar OHLC transforms).

**Regime / Trend / Jump labels (3)** — `TrendLabel` (sign of the rolling OLS slope), `JumpIndicator` (return outliers vs trailing volatility, measured as deviation from the trailing mean so steady drift is not flagged), `RegimeLabel` (volatility-quantile regime split).

**Risk / Performance (2)** — `WinRate`, `Expectancy` (R-multiple).

**Microstructure (4)** — `OrderFlowImbalance` (Cont-Kukanov-Stoikov OFI), `Vpin`, `AmihudIlliquidity`, `RollMeasure`. These reuse the existing `OrderBook` / `Trade` inputs (no new input type).

## Intentionally NOT added (already present, would be duplicates)

- **Population skew / kurtosis** — `skewness.rs` / `kurtosis.rs` are already population moments (divisor n).
- **Hurst R/S** — `hurst_exponent.rs` already uses rescaled-range (R/S) analysis.
- **Queue Imbalance** — exactly `OrderBookImbalanceTop1` ((bidSize - askSize) / (bidSize + askSize)).

## Verification

`cargo test -p wickra-core` (lib 3187 + doc 354), `cargo clippy --workspace --all-targets --all-features -D warnings` clean, node `npm run build && npm test` (471), python `pytest` (784). Counter consistent across `mod.rs`, lib block, README, and docs/README at 396.
2026-06-04 12:00:35 +02:00
kingchenc 5a1d607807 feat(indicators): A5b Fibonacci tools (geometric) (#172)
Completes the **Fibonacci** family with the four geometric/time tools (catalogue 373 -> 377). All extend the internal `pattern_swing` ZigZag tracker with a per-pivot bar index and a current-bar counter (additive — the chart/harmonic detectors are unaffected), and emit `Candle -> struct` outputs via custom Python/Node/WASM bindings.

| Tool | Output |
|------|--------|
| `FibFan` | three trendlines fanning from a swing start through its 38.2/50/61.8% retracement levels, extended to the current bar |
| `FibArcs` | semicircular retracement levels centred on the swing end, normalised by the leg's bar-width (chart-scale-free) |
| `FibChannel` | a sloped base trendline plus parallel lines at Fibonacci multiples of the channel width |
| `FibTimeZones` | markers at Fibonacci bar-distances (1/2/3/5/8/...) from the latest swing pivot |

The geometric tools are novel as streaming indicators; each normalises its geometry to the swing leg's bar-width so the output is chart-scale-free. Formulas are documented in each module and deep-dive.

Fully wired: core (100% unit-tested branches incl. the new `pattern_swing` bar tracking), Python/Node/WASM struct bindings, fuzz, reference + streaming-vs-batch tests.

Verification: `cargo test --workspace` green, clippy `-D warnings` clean, node 454 tests, python 768 tests.
2026-06-04 01:12:09 +02:00
kingchenc 716eb40206 feat(indicators): A5a Fibonacci tools (price-level) (#171)
Adds the six price-level Fibonacci tools as a new **Fibonacci** family (catalogue 367 -> 373, twenty-four families). All build on the internal `pattern_swing` ZigZag tracker, are parameter-free (baked 5% swing threshold), and emit `Candle -> struct` outputs via custom Python/Node/WASM bindings.

| Tool | Output |
|------|--------|
| `FibRetracement` | seven levels (0/23.6/38.2/50/61.8/78.6/100%) of the last swing leg |
| `FibExtension` | five extension ratios (127.2/141.4/161.8/200/261.8%) projected beyond the leg |
| `FibProjection` | A-B-C measured-move target zone (61.8/100/161.8/261.8%) |
| `AutoFib` | retracement anchored on the dominant (largest-magnitude) recent leg |
| `GoldenPocket` | the 0.618-0.65 optimal-trade-entry band (low/mid/high) |
| `FibConfluence` | densest cluster of retracement levels across recent legs (price + strength) |

Fully wired: core (100% unit-tested branches), Python/Node/WASM struct bindings, fuzz driver, reference + streaming-vs-batch tests, README/docs counter. The four geometric/time tools (Fan, Arcs, Channel, Time Zones) follow in A5b.

Verification: `cargo test --workspace` green, clippy `-D warnings` clean, node 450 tests, python 760 tests.
2026-06-04 00:47:00 +02:00
kingchenc 4250ed99f4 feat(patterns): add the Harmonic Patterns family (8 XABCD detectors) (#169)
## Summary

Adds a new **Harmonic Patterns** indicator family (counter 359 → 367, families 22 → 23) — the second half of the A4 roadmap item, following the Chart Patterns family in #166.

Eight Fibonacci-ratio detectors built on the shared swing-pivot tracker (`indicators::pattern_swing`) plus two new helpers there — `xabcd` (reads the last five pivots as X-A-B-C-D) and `ratios_in` (checks a list of `(value, low, high)` Fibonacci windows in one expression, no multi-line `&&` coverage gaps). Each consumes candles and emits the uniform pattern sign convention — `+1.0` bullish (terminal point D a swing low), `-1.0` bearish (D a swing high), `0.0` otherwise, never `None`. Parameter-free, with the Fibonacci windows documented as constants per detector.

## Detectors

| Indicator | Defining ratio |
|-----------|----------------|
| `Abcd` | four-point AB=CD (BC retraces AB, CD ≈ AB) |
| `Gartley` | AD/XA ≈ 0.786 |
| `Butterfly` | AD/XA ∈ 1.27–1.618 (extended D) |
| `Bat` | AD/XA ≈ 0.886, shallow B |
| `Crab` | AD/XA ≈ 1.618 (deepest D) |
| `Shark` | expansion AB, AD/XA 0.886–1.13 |
| `Cypher` | BC on XA, CD/XC ≈ 0.786 |
| `ThreeDrives` | two symmetric extension drives |

## Touchpoints

Core modules + `FAMILIES` group/assert, crate root re-exports, Python/Node/WASM bindings via the candle-pattern macros (Node `index.d.ts`/`index.js` regenerated), the candle fuzz target (`// --- Harmonic Patterns ---` section), Python reference + `CANDLE_SCALAR` registry tests and the Node candle-scalar factory, README catalogue counter + banner cache-buster + family table row + family-count word, `docs/README.md` counter, and the changelog.

## Verification

- `cargo test -p wickra-core --lib` — 2966 passed
- `cargo test -p wickra-core --doc` — 335 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` — clean
- Node `npm run build && npm test` — 444 passed
- Python `maturin develop --release` + `pytest` — 748 passed

Every detector branch is unit-tested, including a bullish and a bearish match per pattern to cover both output arms, plus an out-of-ratio non-match. Fibonacci windows use standard harmonic-trading ranges with documented tolerance bands.
2026-06-03 23:24:25 +02:00
kingchenc 995f119010 feat(patterns): add the Chart Patterns family (8 swing-based detectors) (#166)
## Summary

Adds a new **Chart Patterns** indicator family (counter 351 → 359, families 21 → 22), the first half of the A4 roadmap item (the harmonic patterns follow in a second PR).

All eight detectors are built on a shared, non-repainting swing-pivot tracker — the internal, **uncounted** `indicators::pattern_swing` module (declared `pub(crate) mod`, re-exported nowhere). Each consumes candles and emits the uniform pattern sign convention already used by the candlestick family — `+1.0` bullish / `-1.0` bearish / `0.0` otherwise, never `None`. They are parameter-free, baking the swing threshold (5%) and level tolerance (3%) in as documented constants, mirroring how candlestick patterns bake in their geometric thresholds.

## Detectors

| Indicator | Signal |
|-----------|--------|
| `DoubleTopBottom` | twin-peak / twin-trough reversal |
| `TripleTopBottom` | three matching extremes (stronger reversal) |
| `HeadAndShoulders` | central head + matching shoulders + flat neckline (and inverse) |
| `Triangle` | ascending (+1) / descending (-1) / symmetrical |
| `Wedge` | rising wedge (-1) / falling wedge (+1) |
| `FlagPennant` | shallow consolidation against a pole → continuation |
| `RectangleRange` | flat support/resistance mean-reversion |
| `CupAndHandle` | rounded base + shallow handle (and inverse) |

## Touchpoints

Core modules + `FAMILIES` group and assert, crate root re-exports, Python/Node/WASM bindings via the candle-pattern macros (Node `index.d.ts`/`index.js` regenerated), the candle fuzz target, Python reference + `CANDLE_SCALAR` registry tests and the Node candle-scalar factory, README catalogue counter + banner cache-buster + family table row + family-count word, `docs/README.md` counter, and the changelog.

## Verification

- `cargo test -p wickra-core --lib` — 2915 passed
- `cargo test -p wickra-core --doc` — 335 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` — clean
- Node `npm run build && npm test` — 436 passed
- Python `maturin develop --release` + `pytest` — 732 passed

Every detector branch is unit-tested; multi-condition predicates were flattened to single-line precomputed booleans to keep patch coverage at 100%.
2026-06-03 22:55:36 +02:00
kingchenc 755f4aa0f6 docs: add OpenSSF Best Practices badge to README (#163)
Adds the OpenSSF Best Practices passing badge next to the OpenSSF Scorecard badge in the README header.

The project earned a passing badge: https://www.bestpractices.dev/projects/13094
2026-06-03 21:44:34 +02:00
kingchenc 3ab2d6ec2d feat(seasonality): add the Seasonality & Session family (12 indicators) (#161)
## Summary

Adds the **Seasonality & Session** family — the first family that reads the wall-clock fields of `Candle::timestamp`. A new private `calendar` module decomposes an epoch-millisecond instant (shifted by a per-indicator `utc_offset_minutes`) into civil fields via Howard Hinnant's branch-light `civil_from_days` algorithm. Session / day / month rollovers are detected automatically, so callers never have to invoke `reset()` at a boundary.

Indicator counter **339 → 351**; family count **20 → 21**.

## Indicators

| Shape | Indicators |
|-------|-----------|
| Scalar (`f64`) | `SessionVwap`, `AverageDailyRange`, `OvernightGap`, `TurnOfMonth`, `SeasonalZScore` |
| Struct | `SessionHighLow`, `SessionRange` (Asia/EU/US), `OvernightIntradayReturn` |
| Profile (`Vec<f64>`) | `TimeOfDayReturnProfile`, `DayOfWeekProfile`, `IntradayVolatilityProfile`, `VolumeByTimeProfile` |

## Bindings

The input is the **full** candle (`open, high, low, close, volume, timestamp`), not the `high/low/close` slice the value-indicator helper assumes, so the Python / Node / WASM bindings are custom full-candle implementations:

- **Python** — `update((o,h,l,c,v,ts))`; `batch(open, high, low, close, volume, timestamp)` → `PyArray1` (scalar) / `PyArray2` (struct & profile), warmup rows `NaN`.
- **Node** — `update(open, high, low, close, volume, timestamp)`; `batch(...)` → flat `Vec<f64>`; struct outputs as `#[napi(object)]` values.
- **WASM** — `update` only (multi-input precedent); profiles as `Float64Array`, structs as camelCase objects, `timestamp` as `BigInt`.

## Verification

- `wickra-core`: full per-branch unit tests, **100%** coverage target; 2852 lib tests + 334 doctests green.
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`: clean.
- Node: 428 tests (dedicated `seasonality.test.js` streaming-vs-batch).
- Python: full suite + dedicated `test_seasonality.py` streaming-vs-batch.
- Counter check: mod-count == counted lib block == 351.
2026-06-03 20:31:32 +02:00