Files
wickra/bindings/node
kingchenc 53941b7b07 feat: add Market Breadth family with CrossSection input (#153)
## What

Adds a new indicator input type and family for **market-breadth** analysis — indicators that aggregate the state of an entire universe of symbols at each tick, rather than a single instrument's price. This is the last open input-type on the expansion roadmap (S10) and unblocks the remaining breadth indicators (McClellan, TRIN, High-Low Index, ...).

## Core

- **`CrossSection` input type** (`crates/wickra-core/src/cross_section.rs`) — one tick carrying the per-symbol state of the whole universe as a `Vec<Member>` + `timestamp`. Each `Member` precomputes a signed `change` (sign classifies advancing / declining / unchanged), a `volume`, and `new_high` / `new_low` extreme flags, so the breadth indicators stay stateless per tick. Both `Member` and `CrossSection` are `#[non_exhaustive]` for additive field growth. `CrossSection::new` validates the universe (non-empty, finite changes, finite non-negative volumes); `new_unchecked` skips validation for hot paths. `advancers()` / `decliners()` count by sign.
- **`Error::InvalidCrossSection`** variant for the validation failures.
- **`AdvanceDecline`** (`advance_decline.rs`) — the Advance/Decline Line: the running cumulative sum of net advancing-minus-declining issues. `Input = CrossSection`, `Output = f64`, ready after the first tick.
- New **"Market Breadth"** `FAMILIES` group; indicator count **314 → 315**, family count nineteen → twenty.

## Bindings

All custom (CrossSection is non-scalar, so no macros apply). The universe crosses each boundary as parallel arrays (`change`, `volume`, `new_high`, `new_low`):
- **Python / Node** expose `update` + `batch` (one array group per tick). Node satisfies the completeness contract (`update`/`batch`/`reset`/`isReady`/`warmupPeriod`).
- **WASM** exposes only `update` (the universe is ragged across ticks, matching the other multi-input wasm indicators) with numeric high/low flags.
- Python `map_err` gains the new error arm; `__init__.py` gets a `# Market Breadth` section in both the import and `__all__` blocks. `index.d.ts` / `index.js` regenerated.

## Tests / Fuzz

- Dedicated **streaming-vs-batch + reference-value + ragged-rejection** tests in Python (`test_new_indicators.py`) and Node (`indicators.test.js`) — kept out of the scalar/candle parametrize lists.
- Rust unit tests cover every reject branch (empty / non-finite change / negative & non-finite volume) and every indicator branch.
- New fuzz target `indicator_update_crosssection` drives `AdvanceDecline` over bounded ragged universes built with `new_unchecked`.

## Verify

- `cargo fmt --all` clean
- `cargo test -p wickra-core --lib` → 2593 passed; `--doc` → 298 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` clean
- `cd bindings/node && npm run build && npm test` → 398 passed
- `maturin develop --release` + `pytest bindings/python/tests` → all passed
- counter check: mod-count 315 == lib-block 315
2026-06-03 04:11:10 +02:00
..
2026-06-03 02:56:00 +02:00

Wickra — Node.js

CI codecov npm License: PolyForm-NC

Streaming-first technical indicators for Node.js. npm install wickra — prebuilt native binary, no system dependencies.

Wickra is a multi-language technical-analysis library with a Rust core and bindings for Python, Node.js, and WebAssembly. Every indicator is an O(1) streaming state machine, so live trading bots and historical backtests share the exact same implementation. This package is the Node.js binding (napi-rs); it exposes 200+ streaming-first indicators across sixteen families.

Install

npm install wickra

The native addon ships as a prebuilt binary per platform (Linux, macOS, Windows — x64 and arm64), selected automatically through optional dependencies. There is nothing to compile.

Quick start

const wickra = require('wickra');

// Batch: run an indicator over a whole array.
const prices = Array.from({ length: 1000 }, (_, i) => 100 + i * 0.1);
const values = new wickra.RSI(14).batch(prices); // null during warmup

// Streaming: the same indicator, fed tick by tick in O(1).
const rsi = new wickra.RSI(14);
for (const price of liveFeed) {
  const value = rsi.update(price); // no recomputation over history
  if (value !== null && value > 70) {
    console.log('overbought');
  }
}

batch(prices) and feeding the same prices through update() produce identical values — the equivalence is enforced by the test suite.

Documentation

The full indicator catalogue, guides, quickstarts, and API reference live in the main repository and documentation site:

Wickra ships four bindings — Python, Node.js, WebAssembly, and Rust — that all expose the same indicators from the shared, unsafe-forbidden Rust core.

Disclaimer

Wickra is an indicator toolkit, not a trading system. The values it computes are deterministic transforms of the input data — they are not financial advice and do not predict the market. Any use in a live trading context is at your own risk. The library is provided as is, without warranty of any kind.

License

Licensed under the PolyForm Noncommercial License 1.0.0. Personal projects, research, education, non-profits, and hobby trading bots are all fine; the one thing not allowed is commercial sale of the software or of services built around it. See LICENSE.