Commit Graph

66 Commits

Author SHA1 Message Date
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 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 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 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
kingchenc c096943bdf feat(breadth): complete the Market Breadth family (14 indicators) (#157)
Completes expansion-roadmap block **A2 — Market Breadth**: the 14 indicators that remained after the `AdvanceDecline` bootstrap, all built on the existing `CrossSection` input.

## Indicators (all scalar `Indicator<Input = CrossSection, Output = f64>`)

| Indicator | Reading |
|-----------|---------|
| `AdvanceDeclineRatio` | advancers / decliners |
| `AdVolumeLine` | cumulative net advancing volume |
| `McClellanOscillator` | 19/39 EMAs of ratio-adjusted net advances |
| `McClellanSummationIndex` | running total of the oscillator |
| `Trin` (Arms Index) | A/D ratio over up/down volume ratio |
| `BreadthThrust` (Zweig) | SMA of the advancing-issues share |
| `NewHighsNewLows` | new highs − new lows |
| `HighLowIndex` | SMA of the record-high percent |
| `PercentAboveMa` | % of the universe above its MA |
| `UpDownVolumeRatio` | advancing / declining volume |
| `BullishPercentIndex` | % on a point-and-figure buy signal |
| `CumulativeVolumeIndex` | volume-normalised cumulative net advancing volume |
| `AbsoluteBreadthIndex` | \|advancers − decliners\| |
| `TickIndex` | instantaneous net advancers − decliners |

## Input model

`AdVolumeLine` and `CumulativeVolumeIndex` are kept distinct (the latter normalises each tick's net advancing volume by total volume, so it stays comparable across volume regimes). `PercentAboveMa` and `BullishPercentIndex` need a per-symbol state signal that `Member` did not carry, so `Member` gains two additive flags (`above_ma`, `on_buy_signal`) via a new `Member::with_signals` constructor; the 4-arg `Member::new` leaves both cleared, so every existing caller and binding is unchanged. `CrossSection` gains volume / new-extreme / state aggregation helpers.

## Wiring

Fully wired across the Rust core, the python/node/wasm bindings, the cross-section fuzz target, the README + docs indicator counters (325 → 339), and dedicated python/node streaming-vs-batch tests. `fmt` / `test --workspace --all-features` / `clippy --workspace -D warnings` / node build+test / pytest all green locally.
2026-06-03 17:24:33 +02:00
kingchenc a3a1ae4dba Add 10 pairwise stat-arb indicators to Price Statistics (#154)
Adds ten pairwise `(f64, f64)` indicators to the **Price Statistics** family, completing the A1 stat-arb expansion block.

## Indicators

**Scalar output:**
- **RollingCorrelation** — rolling Pearson correlation of period-over-period *returns* (distinct from level-based `PearsonCorrelation`).
- **RollingCovariance** — rolling covariance of returns.
- **OuHalfLife** — Ornstein–Uhlenbeck half-life of mean reversion of the spread `a − b`.
- **SpreadHurst** — Hurst exponent of the spread (variance-of-lagged-differences fit) for regime detection.
- **DistanceSsd** — Gatev sum-of-squared-deviations between two start-normalised series.
- **BetaNeutralSpread** — rolling OLS regression residual `a − (α + β·b)`.
- **VarianceRatio** — Lo–MacKinlay variance-ratio test on the spread (two params: `period`, `q`).
- **GrangerCausality** — F-statistic for whether `b` predicts `a` (two params: `period`, `lag`).

**Struct output (custom bindings):**
- **KalmanHedgeRatio** — dynamic hedge ratio via a Kalman filter → `{ hedgeRatio, intercept, spread }`.
- **SpreadBollingerBands** — Bollinger bands on the spread → `{ middle, upper, lower, percentB }`.

## Notes
- No new traits or input families: all use the native `Indicator<Input = (f64, f64)>` (precedent `Beta`, `Cointegration`).
- Adds `Error::InvalidParameter` for floating-point constructor parameters (Kalman `delta`/`observation_var`, `num_std`).
- Full Python/Node/WASM bindings; the two struct-output indicators are hand-written, the rest use the pair macros.
- Indicator count 315 → 325; README, family rows, `__init__`, fuzz target, and CHANGELOG updated.

## Verification
- `cargo test --workspace --all-features` — green (2676 core lib + 308 doc).
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` — clean.
- Node: `npm run build && npm test` — 410 passing (`index.d.ts`/`index.js` regenerated).
- Python: `pytest` — 684 passing.
2026-06-03 15:39:55 +02:00
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
kingchenc 9eb46f144a feat: TA-Lib parity — 19 standalone indicators (DM components, price transforms, ROC/LinReg/MACD/SAR variants, Hilbert outputs) (#148)
Closes the remaining TA-Lib function-name gap by shipping each missing or
bundled-only function as a real, standalone, fully-covered indicator. 19 new
indicators across 5 families; mod-count 295 -> 314.

### Trend & Directional — Directional Movement components
- `PlusDm` (`PLUS_DM`), `MinusDm` (`MINUS_DM`) — Wilder-smoothed ±DM.
- `PlusDi` (`PLUS_DI`), `MinusDi` (`MINUS_DI`) — `100·smoothed(±DM)/ATR`.
- `Dx` (`DX`) — `100·|+DI−−DI|/(+DI+−DI)`.

### Price Statistics
- `AvgPrice` (`AVGPRICE`) — `(O+H+L+C)/4`.
- `MidPoint` (`MIDPOINT`) — `(max+min)/2` of a scalar series over N.
- `MidPrice` (`MIDPRICE`) — `(highestHigh+lowestLow)/2` over N.
- `LinRegIntercept` (`LINEARREG_INTERCEPT`) — OLS intercept.
- `Tsf` (`TSF`) — time series forecast `a + b·period`.

### Momentum Oscillators
- `Rocp` (`ROCP`), `Rocr` (`ROCR`), `Rocr100` (`ROCR100`) — ROC ratio forms.

### Trailing Stops
- `SarExt` (`SAREXT`) — Parabolic SAR with start value, reversal offset,
  separate long/short acceleration, signed output.

### Trend & Directional — MACD variants
- `MacdFix` (`MACDFIX`) — MACD fixed 12/26.
- `MacdExt` (`MACDEXT`) — MACD with a selectable moving-average type per line
  (new public `MaType` enum: SMA/EMA/WMA/DEMA/TEMA/TRIMA).

### Ehlers / Cycle (DSP) — Hilbert transform outputs
- `HtPhasor` (`HT_PHASOR`) — in-phase / quadrature components.
- `HtDcPhase` (`HT_DCPHASE`) — dominant-cycle phase (degrees).
- `HtTrendMode` (`HT_TRENDMODE`) — trend (1) vs cycle (0) classification.

Each indicator ships the full chain: core + every-branch unit tests, Python /
Node / WASM bindings, fuzz coverage, README counter + family rows, CHANGELOG.
`cargo test`, doctests, `clippy -D warnings`, `npm test` and pytest all green
locally; mod-count == lib-block == README counter (314), FAMILIES total 309.
2026-06-03 02:26:38 +02:00
kingchenc d4b3f9dbd1 feat: add Alt-Chart Bars (Renko, Kagi, Point & Figure) via a BarBuilder trait (#146)
Introduces a BarBuilder trait for price-driven chart constructors that emit a variable number of bars per candle (deliberately not Indicator). Adds Renko (box-size bricks, 2-box reversal), Kagi (reversal-amount segments) and Point & Figure (box-size X/O columns, N-box reversal) in a new Alt-Chart Bars family, with custom Python/Node/WASM bindings, a dedicated fuzz target, tests and docs. Indicator count 292 -> 295.
2026-06-02 21:56:00 +02:00
kingchenc f37eedd44e feat: add Volume Profile and TPO Profile to the market profile family (#145)
Volume Profile exposes the full per-bin volume histogram (price bounds plus raw distribution) that Value Area reduces to POC/VAH/VAL. TPO Profile is the volume-agnostic Time-Price-Opportunity letter count over a rolling window. Both candle-input, Vec-output, Market Profile family, with custom Python/Node/WASM bindings, fuzz, benches, tests and docs. Indicator count 290 -> 292.
2026-06-02 21:16:30 +02:00
kingchenc 93097db482 feat: add Anchored RSI to the momentum oscillators family (#144)
Cumulative Relative Strength Index whose averaging begins at a runtime-chosen anchor bar (set_anchor), the momentum counterpart to Anchored VWAP. Scalar f64 input, 0..=100 output; wired through core, Python, Node and WASM bindings, fuzz, benches, tests and docs. Indicator count 289 -> 290.
2026-06-02 20:50:56 +02:00
kingchenc 124efb4432 feat: TA-Lib candlestick patterns — tasuki-gap/unique-three-river/marubozu-pair/concealing-baby-swallow (part 9 of 9) (#141)
The final batch of the TA-Lib candlestick roadmap. Adds five patterns, each a streaming `Indicator<Input = Candle, Output = f64>` emitting the family's uniform `±1.0 / 0.0` sign convention, fully wired across the Rust core, Python / Node / WASM bindings, fuzz target and reference tests.

- **Tasuki Gap** (`CDLTASUKIGAP`) — a 3-bar continuation: two same-coloured candles gap in the trend direction, then an opposite candle opens within the second body and closes back into the gap without filling it; upside +1, downside -1.
- **Unique Three River** (`CDLUNIQUE3RIVER`) — a 3-bar bullish reversal: a long black candle, a black candle probing a new low with its body inside the first, then a small white candle held below it; bullish +1.
- **Closing Marubozu** (`CDLCLOSINGMARUBOZU`) — a single long-bodied candle with no shadow on the close end; +1 (white, closes at the high) or -1 (black, closes at the low).
- **Opening Marubozu** — a single long-bodied candle with no shadow on the open end; +1 (white, opens at the low) or -1 (black, opens at the high). No direct TA-Lib equivalent — completes the pair with the closing marubozu.
- **Concealing Baby Swallow** (`CDLCONCEALBABYSWALL`) — a rare 4-bar bullish capitulation: two black marubozu, a black candle gapping down with an upper shadow into the second, then a large black candle engulfing it entirely; bullish +1.

Body and shadow thresholds follow the geometric house style (fixed fractions of the bar range) rather than TA-Lib's rolling averages.

Counter 284 → 289 (mod-count == lib counted block; FAMILIES total 279 → 284).

Stacked on #140 (`feat/cdl-gap-methods`); base retargets to `main` as the stack merges down.
2026-06-02 17:27:39 +02:00
kingchenc 4d0bc08efd feat: TA-Lib candlestick patterns — gap-three-methods/stalled/stick-sandwich/takuri (part 8 of 9) (#140)
Adds five TA-Lib candlestick patterns, each a streaming `Indicator<Input = Candle, Output = f64>` emitting the family's uniform `±1.0 / 0.0` sign convention, fully wired across the Rust core, Python / Node / WASM bindings, fuzz target and reference tests.

- **Upside Gap Three Methods** (`CDLXSIDEGAP3METHODS`) — a 3-bar bullish continuation: two white candles gap up, then a black candle opens within the second body and closes within the first; bullish +1.
- **Downside Gap Three Methods** (`CDLXSIDEGAP3METHODS`) — the bearish mirror: two black candles gap down, then a white candle opens within the second body and closes within the first; bearish -1.
- **Stalled Pattern** (`CDLSTALLEDPATTERN`) — a 3-bar bearish reversal warning: two long white candles then a small white candle riding the shoulder, signalling the rally is stalling; bearish -1.
- **Stick Sandwich** (`CDLSTICKSANDWICH`) — a 3-bar bullish reversal: two black candles closing at the same level sandwich a white candle, marking a support floor; bullish +1.
- **Takuri** (`CDLTAKURI`) — a single-bar bullish reversal, a strict Dragonfly Doji with a negligible upper shadow and very long lower shadow; bullish +1.

Body and shadow thresholds follow the geometric house style (fixed fractions of the bar range) rather than TA-Lib's rolling averages. Upside / Downside Gap Three Methods share the `CDLXSIDEGAP3METHODS` code, so the second carries a manual CHANGELOG entry (as with Rising / Falling Three Methods).

Counter 279 → 284 (mod-count == lib counted block; FAMILIES total 274 → 279).

Stacked on #139 (`feat/cdl-lines`); base retargets to `main` once the predecessor merges.
2026-06-02 17:24:42 +02:00
kingchenc c2c85c7ecf feat: TA-Lib candlestick patterns — matching-low/lines/three-methods (part 7 of 9) (#139)
Adds five TA-Lib candlestick patterns, all `Input = Candle`, `Output = f64`
(`+1.0` bullish / `-1.0` bearish / `0.0` no pattern), wired across core,
Python/Node/WASM bindings, fuzz, and tests.

- **Matching Low** (`CDLMATCHINGLOW`) — 2-bar bullish reversal: two black candles in a decline share the same close, signalling selling pressure is exhausting; bullish +1.
- **Long Line** (`CDLLONGLINE`) — a candle whose range beats a rolling average of recent ranges with a body-dominated range; bullish +1 (white) / bearish -1 (black).
- **Short Line** (`CDLSHORTLINE`) — a compact candle whose range falls below the rolling average with a body-dominated range; bullish +1 (white) / bearish -1 (black).
- **Rising Three Methods** (`CDLRISEFALL3METHODS`) — 5-bar bullish continuation: a long white candle, three small bars holding within its range, then a white breakout to new highs; bullish +1.
- **Falling Three Methods** (`CDLRISEFALL3METHODS`) — the bearish mirror: a long black candle, three small bars within its range, then a black breakdown to new lows; bearish -1.

Counter 274 → 279 (mod-count == lib counted block; FAMILIES total 269 → 274).

Stacked on #138 (part 6 of 9); base retargets to `main` as the chain merges.
2026-06-02 17:16:15 +02:00
kingchenc 04ae145126 feat: TA-Lib candlestick patterns — separating/kicking/ladder/mat-hold (part 6 of 9) (#138) 2026-06-02 17:06:40 +02:00
kingchenc e4ca9c3f8f feat: TA-Lib candlestick patterns — hikkake-mod/pigeon/neck-lines (part 5 of 9) (#137)
* feat: add hikkake-modified, homing-pigeon and neck-line candlestick patterns

Five patterns, all `Input = Candle`, `Output = f64`:

- Modified Hikkake (CDLHIKKAKEMOD) — a close-confirmed Hikkake: an inside bar
  then a breakout that closes back inside the inside-bar range; bullish +1,
  bearish -1.
- Homing Pigeon (CDLHOMINGPIGEON) — two black candles, the second a small body
  inside the first, a bullish reversal; +1.
- On-Neck (CDLONNECK) — long black bar then a white bar closing at its low (the
  neckline), a bearish continuation; -1.
- In-Neck (CDLINNECK) — long black bar then a white bar closing just into its
  body, a bearish continuation; -1.
- Thrusting (CDLTHRUSTING) — long black bar then a white bar closing well into
  but below the midpoint of its body, a bearish continuation; -1.

Counter 264 -> 269 (mod-count == lib counted block; FAMILIES total 259 -> 264).

* chore: sync indicator count to 269

---------

Co-authored-by: wickra-bot <wickra-bot@users.noreply.github.com>
2026-06-02 17:03:49 +02:00
kingchenc d43bc9ddf3 feat: TA-Lib candlestick patterns — doji-star/gap/high-wave/hikkake (part 4 of 9) (#135)
* feat: add doji-star, gap, high-wave and hikkake candlestick patterns

Five patterns, all `Input = Candle`, `Output = f64`:

- Evening Doji Star (CDLEVENINGDOJISTAR) — bearish top reversal: long white bar,
  a doji gapping up, then a black bar closing deep into the first body; -1
  (penetration configurable, default 0.3).
- Morning Doji Star (CDLMORNINGDOJISTAR) — bullish bottom reversal mirror; +1.
- Gap Side-by-Side White (CDLGAPSIDESIDEWHITE) — two similar white candles
  opening side by side after a gap, a continuation; gap up +1, gap down -1.
- High-Wave (CDLHIGHWAVE) — a small body with very long shadows on both sides,
  an extreme indecision flag; +1 on detection.
- Hikkake (CDLHIKKAKE) — an inside bar followed by a failed breakout (a trap);
  bullish +1, bearish -1.

Counter 259 -> 264 (mod-count == lib counted block; FAMILIES total 254 -> 259).

* chore: sync indicator count to 264

---------

Co-authored-by: wickra-bot <wickra-bot@users.noreply.github.com>
2026-06-02 16:54:47 +02:00
kingchenc 244d754707 feat: TA-Lib candlestick patterns — Doji family (part 3 of 9) (#134)
* feat: add Doji-family candlestick patterns

Five single-/two-bar Doji patterns, all `Input = Candle`, `Output = f64`:

- Doji Star (CDLDOJISTAR) — a long body followed by a doji gapping away in the
  trend direction; bullish +1 (after a black bar), bearish -1 (after a white bar).
- Dragonfly Doji (CDLDRAGONFLYDOJI) — a doji opening and closing at the high with
  a long lower shadow; bullish +1.
- Gravestone Doji (CDLGRAVESTONEDOJI) — a doji opening and closing at the low with
  a long upper shadow; bearish -1.
- Long-Legged Doji (CDLLONGLEGGEDDOJI) — a doji with long shadows on both sides; a
  non-directional indecision flag, +1 on detection.
- Rickshaw Man (CDLRICKSHAWMAN) — a long-legged doji with the body centred in the
  range; a non-directional indecision flag, +1 on detection.

Counter 254 -> 259 (mod-count == lib counted block; FAMILIES total 249 -> 254).

* chore: sync indicator count to 259

---------

Co-authored-by: wickra-bot <wickra-bot@users.noreply.github.com>
2026-06-02 16:45:08 +02:00
kingchenc 03ceac1f3b feat: TA-Lib candlestick patterns — abandoned/advance/belt/break/counter (part 2 of 9) (#132)
* feat: add Abandoned Baby candlestick pattern (CDLABANDONEDBABY)

* feat: add Advance Block candlestick pattern (CDLADVANCEBLOCK)

* feat: add Belt Hold candlestick pattern (CDLBELTHOLD)

* feat: add Breakaway and Counterattack candlestick patterns (CDLBREAKAWAY, CDLCOUNTERATTACK)

Breakaway is a 5-bar reversal: a trend gaps away on the second bar, drifts
two more bars, then the fifth bar snaps back and closes inside the bar1/bar2
body gap (bullish +1, bearish -1). Counterattack is a 2-bar reversal where an
opposite-coloured long second bar closes level with the first (the counterattack
line; bullish +1, bearish -1).

Also suppress libtest's spanless `large_stack_arrays` false positive in
wickra-core test builds: the `#[test]` harness collects every test into a
compiler-generated array of references that crosses clippy's 16 KB threshold
once the suite passes ~2048 unit tests. The allow is scoped to `cfg(test)`, so
library code is still linted for genuinely large stack arrays.

* chore: sync indicator count to 254

---------

Co-authored-by: wickra-bot <wickra-bot@users.noreply.github.com>
2026-06-02 16:34:15 +02:00
kingchenc f09057aaf1 feat: TA-Lib candlestick patterns — crows & three-line (part 1 of 9) (#130)
* feat: add Two Crows candlestick pattern (CDL2CROWS)

* feat: add Upside Gap Two Crows candlestick pattern (CDLUPSIDEGAP2CROWS)

* feat: add Identical Three Crows candlestick pattern (CDLIDENTICAL3CROWS)

* feat: add Three Line Strike candlestick pattern (CDL3LINESTRIKE)

* feat: add Three Stars in the South candlestick pattern (CDL3STARSINSOUTH)
2026-06-01 23:20:10 +02:00
kingchenc 2d140419bb feat: derivatives basis & calendar-spread indicators (part 3 of 3) (#128)
* feat(derivatives): TermStructureBasis indicator (core)

* feat(derivatives): CalendarSpread indicator (core)

* feat(derivatives): Python, Node and WASM bindings for basis & calendar-spread indicators

* test(derivatives): Python and Node tests for basis & calendar-spread indicators

* docs(derivatives): README row + counter 242->244, CHANGELOG part 3; fuzz basis indicators
2026-06-01 22:07:35 +02:00
kingchenc 8e5bfd07ce feat: derivatives open-interest, flow & liquidation indicators (part 2 of 3) (#127)
* feat(derivatives): OIPriceDivergence indicator (core)

* feat(derivatives): OIWeighted indicator (core)

* feat(derivatives): LongShortRatio indicator (core)

* feat(derivatives): TakerBuySellRatio indicator (core)

* feat(derivatives): LiquidationFeatures multi-output indicator (core)

* feat(derivatives): Python, Node and WASM bindings for OI, flow & liquidation indicators

* test(derivatives): Python and Node tests for OI, flow & liquidation indicators

* fuzz(derivatives): drive OI, flow & liquidation indicators in derivatives target

* docs(derivatives): README row + counter 237->242, CHANGELOG part 2
2026-06-01 21:50:35 +02:00
kingchenc 5eb820a9c7 feat: derivatives funding & open-interest indicators (part 1 of 3) (#126)
* feat(derivatives): DerivativesTick input type + InvalidDerivatives error

* feat(derivatives): FundingRate indicator (core)

* feat(derivatives): FundingRateMean indicator (core)

* feat(derivatives): FundingRateZScore indicator (core)

* feat(derivatives): FundingBasis indicator (core)

* feat(derivatives): OpenInterestDelta indicator (core)

* feat(derivatives): Python, Node and WASM bindings for funding & OI-delta indicators

* test(derivatives): Python and Node tests for funding & OI-delta indicators

* bench(derivatives): synthetic-tick bench + derivatives fuzz target

* docs(derivatives): README family row + counter 232->237, CHANGELOG entry
2026-06-01 21:26:37 +02:00
kingchenc 3dd7010129 feat: footprint microstructure indicator (part 4 of 4) (#123) 2026-06-01 20:00:58 +02:00
kingchenc 4f11df0e33 feat: microstructure price-impact & depth indicators (part 3 of 4) (#122)
* feat: effective spread microstructure indicator (part 3 of 4)

* feat: realized spread microstructure indicator (part 3 of 4)

* feat: kyle's lambda microstructure indicator (part 3 of 4)

* feat: depth slope microstructure indicator (part 3 of 4)
2026-06-01 19:45:38 +02:00
kingchenc 5867f71450 feat: trade-flow microstructure indicators (part 2 of 4) (#113)
* feat(core): add 3 trade-flow microstructure indicators

SignedVolume (per-trade size signed by aggressor), CumulativeVolumeDelta
(running signed-volume total), and TradeImbalance (rolling buy/sell volume
imbalance over a trade window). All consume the Trade type, with full unit
coverage. Extends the Microstructure family.

* feat(bindings): expose trade-flow microstructure indicators

Python, Node and WASM bindings for SignedVolume, CumulativeVolumeDelta and
TradeImbalance. Each takes a trade via update(price, size, is_buy); Python and
Node expose a batch over three parallel arrays, WASM exposes per-trade update.
Regenerates node index.d.ts/.js.

* test(bindings,fuzz,bench): cover trade-flow microstructure indicators

Python and Node: reference values, streaming-vs-batch, lifecycle/repr and input
validation (zero window, negative size, non-positive price, mismatched batch
lengths). New indicator_update_trade fuzz target. Synthetic trade-tape benches
(signed_volume cheapest, trade_imbalance windowed/expensive).

* docs: add trade-flow indicators + bump counter to 227

README Microstructure family row gains signed volume / CVD / trade imbalance and
the counter goes 224 -> 227; CHANGELOG records the trade-flow indicators.
2026-06-01 16:38:48 +02:00
kingchenc 2be21df803 feat: order-book microstructure indicators (part 1 of 4) (#112)
* feat(core): add microstructure input types (OrderBook, Trade, TradeQuote)

New non-OHLCV value types for the order-book / trade-flow indicator family:
Level, OrderBook (sorted, uncrossed depth snapshot), Side, Trade (with
aggressor side), and TradeQuote (trade paired with prevailing mid). Each has a
validating constructor plus a new_unchecked hot-path constructor, with full
unit coverage. Adds InvalidOrderBook / InvalidTrade error variants.

* feat(core): add 5 order-book microstructure indicators

OrderBookImbalanceTop1/TopN/Full (signed depth imbalance), Microprice
(size-weighted fair value), and QuotedSpread (top-of-book spread in bps). All
consume the OrderBook snapshot type, emit f64, are stateless and ready after
the first snapshot, with full unit coverage. Registers a new Microstructure
family in the taxonomy.

* feat(bindings): expose order-book microstructure indicators

Python, Node, and WASM bindings for OrderBookImbalanceTop1/TopN/Full,
Microprice and QuotedSpread. Each takes a depth snapshot via four equal-length
(bid_px, bid_sz, ask_px, ask_sz) arrays. Python and Node expose a batch over a
list of snapshots; WASM exposes per-snapshot update (the streaming model that
fits a browser book feed). Regenerates node index.d.ts/.js and registers the
new InvalidOrderBook/InvalidTrade arms in the Python error mapping.

* test(bindings,fuzz): cover order-book microstructure indicators

Python: smoke, reference values, streaming-vs-batch, lifecycle/repr and input
validation (mismatched lengths, crossed book, misordered levels, zero levels)
for all five order-book indicators. Node: reference values, streaming-vs-batch,
and rejection cases. Adds an indicator_update_orderbook fuzz target driving
every order-book indicator over arbitrary (incl. degenerate) snapshots.

* bench(microstructure): synthetic order-book benchmarks

Add a bench_orderbook_input harness and synthesise a five-level book around
each candle close (no order-book dataset ships with the repo). Benches the
cheapest (top-of-book imbalance) and most-expensive (full-depth imbalance) plus
microprice, matching the curated cheapest/expensive-per-family approach.

* docs: add Microstructure family + bump indicator counter to 224

README gains the Microstructure family row (order-book imbalance, microprice,
quoted spread) and the indicator counter goes 219 -> 224 across seventeen
families; CHANGELOG records the new order-book indicators and value types.
2026-06-01 16:06:22 +02:00
kingchenc 0479191b66 feat: signed candlestick directional ±1 encoding (Doji signed mode) (#111)
* feat(core): add signed dragonfly/gravestone encoding to Doji

Doji gains an opt-in `.signed()` mode that classifies a detected Doji by the
position of its body within the bar range: dragonfly (long lower shadow) emits
+1.0 (bullish), gravestone (long upper shadow) emits -1.0 (bearish), and a
long-legged/standard Doji emits 0.0. The default detection-flag behaviour
(+1.0/0.0) is unchanged, so existing callers are unaffected.

The other 14 candlestick patterns already emit the uniform +1 bull / -1 bear /
0 none convention; document that explicitly with a "Signed +-1 encoding"
section on each so the whole family is a consistent drop-in ML feature.

* feat(bindings): expose Doji signed mode in python, node, wasm

Hand-write the Doji binding in all three language bindings (instead of the
shared candle-pattern macro) so it accepts an opt-in `signed` flag and exposes
an `is_signed`/`isSigned` accessor:

- Python: `Doji(signed=False)` keyword argument
- Node: `new Doji(signed?)` optional constructor argument (index.d.ts/.js
  regenerated via napi build)
- WASM: `new Doji(signed?)` optional constructor argument

The default construction is unchanged, so existing callers keep the
direction-less +1/0 detection flag.

* test(bindings,fuzz): cover Doji signed dragonfly/gravestone encoding

- python: dragonfly(+1)/gravestone(-1)/neutral(0) and default-flag cases in
  test_known_values
- node: equivalent signed/default assertions in indicators.test.js
- fuzz: drive a signed Doji alongside the default in indicator_update_candle

* docs: document signed candlestick convention and Doji signed mode

README gains a candlestick sign-convention note; CHANGELOG records the new
opt-in Doji signed dragonfly/gravestone encoding under [Unreleased].
2026-06-01 14:37:20 +02:00
kingchenc 0b85142ad1 feat: cross-asset / pairwise indicators (5 new) (#109)
* feat(core): add PairwiseBeta cross-asset indicator

Rolling OLS slope of one asset's log-returns on another's. Unlike Beta,
which regresses the raw inputs it is fed, PairwiseBeta differences
consecutive prices into log-returns internally -- the conventional way to
measure cross-asset beta, where a beta on price levels would be dominated
by the shared trend.

Two-series Indicator<Input = (f64, f64)>, exposed in Rust, Python, Node
and WASM, with unit/known-value/streaming tests and a pair fuzz target.

* feat(core): add PairSpreadZScore cross-asset indicator

Standardised log-spread ln(a) - beta*ln(b) of a pair, where beta is a
rolling-OLS hedge ratio and the spread is z-scored over its own look-back.
The canonical mean-reversion / statistical-arbitrage entry signal, with
independent beta_period and z_period windows.

Two-series Indicator<Input = (f64, f64)>, exposed in Rust, Python, Node
and WASM, with sign/known-value/streaming tests and a pair fuzz target.

* feat(core): add LeadLagCrossCorrelation cross-asset indicator

Reports the integer offset k in [-max_lag, max_lag] that maximises
|corr(a[t], b[t+k])|, answering which of two assets leads the other and by
how many bars. A positive lag means a leads b. Fully causal: a's window is
held centred while b's window slides across the buffered history, so every
lag is evaluated only against data already seen.

Struct output { lag, correlation }, exposed in Rust, Python, Node and WASM
with lead-detection/streaming tests and a pair fuzz driver.

* feat(core): add Cointegration (Engle-Granger + ADF) indicator

Rolling pairs-trading screen: an OLS hedge ratio of a on b, the spread
(residual) a - (alpha + beta*b), and an augmented Dickey-Fuller t-statistic
on the spread with configurable lags. A strongly negative statistic flags a
mean-reverting, tradeable spread. Includes a small Gaussian-elimination
solver for the augmented regression.

Struct output { hedge_ratio, spread, adf_stat }, exposed in Rust, Python,
Node and WASM with stationarity/hedge-ratio/streaming tests and a pair fuzz
driver.

* feat(core): add RelativeStrengthAB cross-asset indicator

Comparative relative strength of two assets: the ratio line a/b together
with its moving average and its RSI, the classic asset-vs-asset /
asset-vs-index rotation screen. Composes the existing Sma and Rsi over the
ratio; a zero denominator or non-finite price is skipped.

Struct output { ratio, ratio_ma, ratio_rsi }, exposed in Rust, Python, Node
and WASM with flat/rising-ratio/streaming tests and a pair fuzz driver.

* test(cointegration): cover ADF guard branches

The ADF helper's short-series and degrees-of-freedom guards and the
zero-dispersion (perfect AR) path are unreachable through the public
Cointegration API (period >= 2*adf_lags + 4), so exercise them with direct
unit tests on adf_no_constant. The second linear solve cannot be singular
once the coefficient solve on the same matrix has succeeded, so it now uses
expect() instead of a dead error branch.
2026-06-01 13:45:21 +02:00
kingchenc eb4454ab27 P5: track index.d.ts + reject invalid periods in the Node binding (#83)
* build(node): track the generated index.d.ts (P5.1)

index.js was committed but index.d.ts was gitignored, an inconsistency that
also contradicts CONTRIBUTING ('regenerate both .d.ts/.js when a binding API
changes'). Track index.d.ts too so the repo carries the TypeScript types as a
matched pair with index.js. Generated by napi build; ~214 indicator classes.

* fix(node): reject invalid periods instead of clamping them (P5.4)

The Node scalar-indicator macro clamped period 0 to 1 (via clamp_period + must)
and the multi-parameter constructors did the same, silently swallowing the
core's PeriodZero validation. The core rejects period 0 (Error::PeriodZero),
and the Python and WASM bindings already propagate it — Node was the outlier,
masking caller mistakes. Make the macro constructor fallible and let every
constructor propagate the core error via map_err, removing clamp_period/must.
Update the smoke test (period 0 now throws, matching core/Python/WASM).

* docs(changelog): note the Node period-validation change (P5.4)

Behavior change per CONTRIBUTING: Node constructors now reject invalid periods
instead of clamping. Add an [Unreleased] Changed entry.

* chore: drop accidentally committed scratch log
2026-05-31 05:22:56 +02:00
kingchenc 21c86f348f examples + bindings: Node/WASM strategy parity + test/benchmark parity (P2 + P3) (#81)
* examples(node): add RSI mean-reversion strategy

Node counterpart of strategy_rsi_mean_reversion.{py,rs}: RSI(14) < 30 long,
> 70 exit, 0.1% fees, hourly BTCUSDT. Output verified byte-identical to the
Rust reference (37 trades W24/L13, -17.84% return, 46.89% max drawdown).

* examples(node): add MACD + ADX trend-filter strategy

Node counterpart of strategy_macd_adx.{py,rs}: MACD(12,26,9) histogram
crossover entries gated by ADX(14) > 20, hourly BTCUSDT, 0.1% fees. Output
verified byte-identical to the Rust reference (246 trades W90/L156, -47.19%
return, 53.75% max drawdown).

* examples(node): add Bollinger-squeeze breakout strategy

Node counterpart of strategy_bollinger_squeeze.{py,rs}: enter on a fresh
180-bar Bollinger-bandwidth low + close above the upper band, exit on a
2*ATR(14) stop or upper-band collapse, daily BTCUSDT, 0.1% fees. Output
verified byte-identical to the Rust reference (1 trade, -7.82% return,
13.01% max drawdown).

* examples(wasm): add RSI mean-reversion strategy demo

Browser counterpart of strategy_rsi_mean_reversion.{py,js,rs}: RSI(14) < 30
long, > 70 exit, 0.1% fees, summary table. Same signal/fill/PnL/equity loop as
the runtime-verified Node example; loads via the established wickra_wasm.js
init + fetch-CSV pattern. (wasm32 build runs in CI.)

* examples(wasm): add MACD + ADX trend-filter strategy demo

Browser counterpart of strategy_macd_adx.{py,js,rs}: MACD(12,26,9) histogram
crossover gated by ADX(14) > 20, hourly BTCUSDT, 0.1% fees. Logic identical to
the runtime-verified Node example; standard wickra_wasm.js init + fetch-CSV
loader. (wasm32 build runs in CI.)

* examples(wasm): add Bollinger-squeeze breakout strategy demo

Browser counterpart of strategy_bollinger_squeeze.{py,js,rs}: fresh 180-bar
Bollinger-bandwidth low + upper-band breakout, 2*ATR(14) stop, daily BTCUSDT,
0.1% fees. Logic identical to the runtime-verified Node example; standard
wickra_wasm.js init + fetch-CSV loader. (wasm32 build runs in CI.)

* ci: add examples syntax-smoke job (P2.3)

The Rust examples are built by 'cargo build -p wickra-examples --bins'; the
Node, browser-WASM and Python examples had no build gate. New job parse-checks
every examples/{node,wasm}/*.js, extracts and node --checks each WASM .html
module script, and python -m py_compile's every examples/python/*.py — so a
broken example edit fails CI instead of landing silently.

* docs(examples): list the new Node + WASM strategy examples

Add the three Node strategy scripts and three WASM strategy demos to the
examples README tables, bringing Node and WASM to parity with the existing
Rust and Python strategy rows.

* chore(examples): refresh examples/node lockfile for the linked wickra binding

npm install rewrote the file: dependency snapshot of the local wickra binding
that the examples link against (version 0.1.4 -> 0.3.1, license + engines
fields), which had gone stale in the committed lockfile.

* test(node): add input-validation suite

Node counterpart of bindings/python/tests/test_input_validation.py: invalid
constructor parameters (ATR zero period, MACD non-increasing fast/slow,
BollingerBands negative multiplier, PSAR step > max, ValueArea period/pct,
InitialBalance/OpeningRange zero period, Ichimoku non-increasing periods,
Ehlers-family ordering) and unequal-length candle/ValueArea batch inputs all
throw a JS Error. Validated against the built binding.

* test(node): add indicator completeness contract

Introspects every exported indicator class and asserts the full interface
(update / batch / reset / isReady / warmupPeriod) plus the pre-warmup contract
for zero-arg indicators, and guards that the full catalogue (>= 200 classes)
is exported. Catches a new indicator wired without the standard methods, or a
stale/partial native build dropping exports, with no per-indicator boilerplate.

* test(wasm): broaden scalar streaming-vs-batch coverage

Extend the inline wasm-bindgen-test suite with a streaming==batch check across
~70 scalar indicators spanning moving averages, momentum, volatility,
statistics/regression, Ehlers/cycle and risk/performance families (previously
only EMA + the candle-input group were covered per-indicator), plus four more
invalid-constructor assertions. Constructor args mirror the CI-passing Node
factories. Host-compiles (cargo test -p wickra-wasm --no-run); executed in CI
via wasm-pack test --node.

* bench(node): add indicator throughput benchmark

Node counterpart of the Rust criterion benches / Python compare_libraries:
measures streaming (per-tick update) and batch throughput in Mupd/s across a
representative indicator set over a synthetic OHLCV series (--bars, default
200k). Dependency-free; wired as 'npm run bench'.

* docs(wasm): list strategy demos + document the benchmark story

Add the three new strategy demos to the WASM examples table and a Performance
section: parallel_assets.html is the in-browser benchmark, with raw throughput
covered by the Rust criterion / Python / Node benchmarks (the WASM engine is the
same core compiled to wasm32).
2026-05-31 05:09:26 +02:00
kingchenc 4e3c41ea80 feat(family-15): add 17 risk/performance metrics (#54)
* feat(family-15): add 17 risk/performance metrics

Implements Family 15 pragmatically as standard `Indicator`s instead of a
separate `wickra-metrics` crate. Input is scalar `f64` per bar — period
return, equity sample, or per-trade P&L depending on the metric.

Scalar `Indicator<f64>` (14):
- SharpeRatio(period, risk_free)
- SortinoRatio(period, mar)
- CalmarRatio(period)
- OmegaRatio(period, threshold)
- MaxDrawdown(period)          — rolling, peak-to-trough
- AverageDrawdown(period)
- DrawdownDuration             — cumulative, bars under water (u32 output)
- PainIndex(period)
- ValueAtRisk(period, confidence)
- ConditionalValueAtRisk(period, confidence)
- ProfitFactor(period)
- GainLossRatio(period)
- RecoveryFactor               — cumulative, net return / max drawdown
- KellyCriterion(period)

Two-series `Indicator<(f64, f64)>` for (asset, benchmark) returns (3):
- TreynorRatio(period, risk_free)
- InformationRatio(period)
- Alpha(period, risk_free)     — Jensen / CAPM

Touchpoints:
- 17 new files under `crates/wickra-core/src/indicators/`.
- `mod.rs` + `lib.rs` re-exports.
- Python bindings (`bindings/python/src/lib.rs`, `__init__.py`).
- Node bindings (`bindings/node/src/lib.rs`, `index.js`).
- WASM bindings (`bindings/wasm/src/lib.rs`).
- Fuzz: scalar metrics appended to `indicator_update.rs`; new
  `indicator_update_pair.rs` fuzz target for `(f64, f64)` indicators.
- Python tests: SCALAR + new PAIR parameter lists in `test_new_indicators.py`,
  reference-value cases in `test_known_values.py`.
- Node tests: scalar factories + new pair-factory block in
  `bindings/node/__tests__/indicators.test.js`.
- Benches: 5 Family-15 benches added in `crates/wickra/benches/indicators.rs`.
- Docs: README family-table row + counter (71 -> 88), CHANGELOG entry under
  [Unreleased].

Note: Family 12 (statistik-regression, PR #51) introduces
`node_pair_indicator!` and `wasm_pair_indicator!` macros for Pearson /
Beta / Spearman. Family 15 needs the same pair-input pattern but Family 12
is not yet in main, so the three pair wrappers below are written by hand
in this PR. When PR #51 lands, the trivial merge-conflict is resolved by
keeping the macros from Family 12 and re-using them for Treynor / IR /
Alpha (drop the three handwritten wrappers).

cargo check --workspace --all-features: green.

* fix(family-15): satisfy clippy doc_markdown / if_not_else / digit_grouping

* fix(family-15): unused TreynorRatio import, duplicate pairFactories, _eq_nan inf handling

* fix(family-15): node eq() handles matching infinities for ratio indicators

* test(family-15): cover cold paths flagged by codecov patch
2026-05-26 20:44:21 +02:00
kingchenc 55284a3042 feat(family-14): add 15 candlestick patterns (#53)
* feat(family-14): add 15 candlestick patterns

Introduces the Candlestick Patterns family (block A of the family-14 spec)
as scalar f64 indicators on Candle inputs. Each detector emits +1.0 for a
bullish reading, -1.0 for a bearish reading, and 0.0 when no pattern is
present. Doji is direction-less and emits +1.0 / 0.0 only.

New indicators (15):

- Doji
- Hammer
- InvertedHammer
- HangingMan
- ShootingStar
- Engulfing
- Harami
- MorningEveningStar (signed: +1.0 morning star, -1.0 evening star)
- ThreeSoldiersOrCrows (signed: +1.0 soldiers, -1.0 crows)
- PiercingDarkCloud (signed: +1.0 piercing, -1.0 dark cloud)
- Marubozu (signed: +1.0 bullish, -1.0 bearish, 5 percent shadow tolerance default)
- Tweezer (signed: +1.0 bottom, -1.0 top, 10 bps relative tolerance default)
- SpinningTop (direction-signed indecision)
- ThreeInside (confirmed Harami)
- ThreeOutside (confirmed Engulfing)

MVP scope notes:

- Pattern-shape check only, no trend filter applied. Caller combines with a
  trend indicator for actionable signals. Documented in every doc comment.
- Block B (Harmonic patterns) and block C (Chart patterns) remain
  out-of-scope and will follow when the pattern-detection framework (pivot
  detector, multi-bar state machines) lands.

Touched across all bindings: Python, Node, WASM. Fuzz target, Python tests
(streaming-vs-batch + reference values), Node tests (streaming-vs-batch +
reference values), and a representative bench subset (1-, 2- and 3-bar
patterns) added. README family table + indicator counter (71 -> 86, eight
-> nine families) and CHANGELOG [Unreleased] updated.

* fix(family-14): unpack MULTI values with *_ to handle 3-element tuples

* cov(family-14): cover Default impl cold paths and MorningEveningStar guard branches
2026-05-26 00:54:11 +02:00
kingchenc 9b8e1346ed feat(family-16): add ValueArea + InitialBalance + OpeningRange (#52)
* feat(family-16): add ValueArea + InitialBalance + OpeningRange

Opens family #16 (Market Profile) with the three OHLCV-compatible scalar /
multi-output indicators:

- ValueArea(period, bin_count, value_area_pct) -> {poc, vah, val}.
  Rolling bin-approximation volume profile over the last `period`
  candles. Each candle's volume is spread uniformly across [low, high];
  POC is the bin with highest cumulative volume; the value area expands
  symmetrically from POC and always absorbs the higher-volume neighbour
  next, until `value_area_pct` (default 0.70) of total volume is
  enclosed. Defaults (20, 50, 0.70).

- InitialBalance(period) -> {high, low}. Tracks session-opening high
  and low over the first `period` bars, then locks. Default period = 12
  (one-hour IB on 5-minute bars for US equities). Callers MUST invoke
  reset() at every session boundary, otherwise IB stays fixed for the
  lifetime of the instance.

- OpeningRange(period) -> {high, low, breakout_distance}. Same
  lock-after-N-bars semantics as IB with a shorter default period
  (6 = 30 min on 5-minute bars) and a third output that tracks
  close - or_mid (positive above the range mid, negative below).

Histogram-output Market Profile variants (Volume Profile, VPVR,
Composite Profile) are deferred because they need a new histogram
output API layer rather than fixed-arity scalars. Tick-data-only
variants (TPO Profile, Single Print, Order Flow Delta, Cumulative
Delta, Volume-Weighted Open) are out of scope because `wickra-data`
does not currently expose tick / L2 data.

All four bindings (Rust core, Python, Node, WASM) ship the new
indicators with parity tests; benches added; fuzz target extended.
Counter 71 -> 74 across 8 -> 9 families. cargo check --workspace
--all-features green.

* fix(family-16): cover cold paths in InitialBalance + ValueArea

InitialBalance::value() public getter had no test covering the post-update
Some(...) branch — extended accessors_and_metadata to call value() after one
update. ValueArea single-print bar path (c.high == c.low) was unreachable in
existing tests since the only single-print test used a uniform 100-price
window which exits early via the span == 0 guard; added a mixed-window test
that triggers the c.high <= c.low branch directly. The (None, None) arm of
the expansion match was by-construction unreachable (the loop condition
already requires at least one neighbour) and has been folded into an
if/else.
2026-05-26 00:14:30 +02:00
kingchenc 05fcdd9a5e feat(family-12): add 13 Statistik/Regression indicators (#51)
* feat(family-12): add 13 Statistik/Regression indicators

Brings the Price Statistics family to 20 indicators (7 → 20) and the
total catalogue to 84 (71 → 84). Every indicator ships in the Rust
core plus Python, Node, and WASM bindings with full streaming ↔ batch
parity, fuzz coverage, and benches.

Scalar (f64 → f64):
- Variance, CoefficientOfVariation: rolling population variance and
  its dimensionless ratio with the mean. O(1) updates.
- Skewness, Kurtosis: rolling Pearson skewness and excess kurtosis,
  derived from running sums of x, x², x³, x⁴ via the binomial
  identities — also O(1) per bar.
- StandardError, DetrendedStdDev: standard error of estimate (n − 2)
  and population StdDev (n) of OLS residuals, sharing the LinReg
  O(1) sliding sums.
- RSquared: coefficient of determination of the rolling OLS fit; the
  trend-quality filter, clamped to [0, 1].
- MedianAbsoluteDeviation: robust dispersion estimator; O(period log
  period) per emission via two in-place sorts of a reusable scratch
  buffer.
- Autocorrelation(period, lag): rolling lag-k Pearson autocorrelation.
- HurstExponent(period, chunks): R/S-analysis trend-persistence
  estimator clamped to [0, 1].

Pair indicators (Input = (f64, f64)):
- PearsonCorrelation: rolling cross-series Pearson, O(1).
- Beta: rolling OLS slope of asset vs. benchmark (CAPM).
- SpearmanCorrelation: rolling rank correlation with mid-rank tie
  handling; O(period log period).

Touchpoints:
- crates/wickra-core: 13 new indicator modules + mod.rs / lib.rs
  re-exports.
- bindings/python: pyclasses + add_class registration + __init__.py
  import & __all__ updates. The pair indicators expose
  update(x, y) and batch(x, y) over two equally-sized numpy arrays.
- bindings/node: scalar indicators via node_scalar_indicator! macro;
  pair indicators via new node_pair_indicator! macro; explicit
  structs for Autocorrelation and HurstExponent (two-arg ctors).
  index.js extended with the new exports.
- bindings/wasm: scalar wrappers via wasm_scalar_indicator!; pair
  wrappers via new wasm_pair_indicator! macro.
- fuzz: every scalar drove through the generic helper; pair
  indicators stress-tested by pairing adjacent samples of the fuzz
  input.
- Python tests (test_new_indicators.py): added to SCALAR
  parametrisation, plus algebraic reference values
  (variance of [2,4,6] = 8/3, MAD ignoring outlier = 0, monotone
  non-linear Spearman = 1, two-to-one Beta = 2, etc.) and a
  streaming-vs-batch test for the pair indicators.
- Node tests (indicators.test.js): extended the scalar factories
  map and added a pair-indicator section with the same algebraic
  reference values.
- crates/wickra/benches: bench_scalar entries for all 10 single-
  input new indicators.
- README: counter 71 → 84; Price Statistics family-table row
  expanded with the 13 new indicators.
- CHANGELOG: Unreleased section documents the family addition.

Wiki drafts (ghost-ignored, manual sync to wickra.wiki at release
time): indicator-ideas/families/wiki/family-12-statistik-regression/
contains 13 deep-dive pages plus _Sidebar / Indicators-Overview /
Warmup-Periods / Home fragments for the curator merge.

cargo check --workspace --all-features: clean.

* fix(family-12): remove unreachable defensive guards in hurst_exponent

The three guards (m < 2 continue, end > buf.len() break, denom == 0.0
return) are by-construction unreachable given the constructor invariant
period >= 2 * chunks: m = period / k for k in 1..=chunks always
satisfies m >= 2 and end = (c+1) * m <= k * m <= period = buf.len(),
and m_1 = period and m_2 = period / 2 are always distinct so the slope
denominator is strictly positive. Removing them brings codecov/patch
back to 100%.
2026-05-25 23:42:05 +02:00
kingchenc 5aa0949bce feat(family-13): add Ichimoku + Heikin-Ashi (#50)
Two new indicators in a brand-new "Ichimoku & alternative charts"
family:

- `Ichimoku` (Ichimoku Kinko Hyo): the full five-line cloud system
  (Tenkan-sen, Kijun-sen, Senkou Span A/B, Chikou Span). Classic
  (9, 26, 52, 26) defaults; configurable. Forward displacement is
  handled in an O(1) ring buffer so the visible Senkou A/B at bar n
  are the values computed at bar n-displacement.
- `HeikinAshi`: recursive candle smoothing transform emitting a
  four-field synthetic candle. Seeds ha_open from (open+close)/2 on
  the first bar.

Touchpoints: core + unit tests, mod.rs/lib.rs re-exports, Python +
Node + WASM bindings (multi-output via PyArray2 / interleaved Vec<f64>
/ Object+Float64Array), Python tests across smoke/new-indicators/
input-validation, Node parity tests, fuzz target (Candle), benches,
README family table + counter (71 -> 73, 8 -> 9 families), CHANGELOG.

Note: Renko, Kagi, and Point & Figure from the family-13 ideas list
are intentionally skipped. They are bar generators (the bar boundary
is defined by price moves, not by a fixed time interval) rather than
indicators that consume a candle stream, and belong in wickra-data
as candle/tick transforms alongside the existing tick-to-candle
aggregator and resampler.
2026-05-25 23:02:29 +02:00