Commit Graph

101 Commits

Author SHA1 Message Date
kingchenc 962ced0712 examples: add multi-timeframe demos for Rust and Node
Python's examples/python/multi_timeframe.py had no Rust or Node sibling.
Add both — the Rust version uses wickra-data's `Resampler` /
`resample_all` (the canonical path; no manual roll-up), the Node version
mirrors the Python one's inline aggregation because wickra-data's
resampler is currently Rust-only.

* examples/rust/src/bin/multi_timeframe.rs — reads the bundled 1m CSV via
  `CandleReader`, resamples to 5m / 15m / 1h / 4h / 1d via `resample_all`,
  prints last RSI(14), MACD(12,26,9) histogram and ADX(14) per timeframe.
* examples/node/multi_timeframe.js — same outputs from a hand-rolled
  bucket aggregator; reuses the new examples/data/ default path.
* examples/README.md gains the new rows.

Run side by side: the Rust and Node summaries are bit-identical at every
timeframe (50000 / 10000 / 3334 / 834 / 209 / 35 bars; same RSI, MACD
histogram and ADX to two decimals) — confirming both the Rust resampler
and the inline Node aggregator produce the same OHLC buckets.
2026-05-23 00:16:08 +02:00
kingchenc 5a4cf66022 examples: add streaming demos for Python and Rust
Python and Rust both lacked a standalone "streaming indicators" example
that mirrors examples/node/streaming.js — the quickstart docs cover the
pattern, but a runnable file makes the parity visible across all four
languages.

* examples/python/streaming.py — argparse-driven synthetic streaming demo
  feeding SMA(20) / EMA(20) / RSI(14) / MACD(12,26,9), tagging BUY?/SELL?
  candidates when RSI extremes and MACD-histogram direction agree.
* examples/rust/src/bin/streaming.rs — same demo as a wickra-examples
  binary, reusing the seeded LCG so its first 40 rows are bit-identical
  to the Python (and Node) sibling — a strong cross-language consistency
  signal verified by running both side by side.
* examples/README.md gains a `streaming` row in the Rust and Python tables.
2026-05-23 00:13:38 +02:00
kingchenc d87005577e examples: move the WASM browser demo into a top-level examples/wasm/
Finish the per-language `examples/<lang>/` restructure by relocating the
WASM browser demo from bindings/wasm/examples/ to examples/wasm/.

* `examples/wasm/index.html` is the moved file; its WASM module import
  becomes `../../bindings/wasm/pkg/wickra_wasm.js` so the demo still loads
  the wasm-pack output without copying it.
* bindings/wasm/README.md, Quickstart-WASM.md, examples/README.md and the
  root README "Languages" + project-layout block all point at the new
  path. The serve command in the docs now says "serve the repository root
  and open examples/wasm/index.html".

`bindings/wasm/examples/` is empty after the move; the now-empty
directory is removed.
2026-05-23 00:11:07 +02:00
kingchenc 8b9e8e30b9 examples: move Node examples into a top-level examples/node/
Continue the per-language `examples/<lang>/` restructure: move the three
Node example files (streaming.js, backtest.js, live_trading.js) out of
bindings/node/examples/ and into a top-level examples/node/ directory.

* `examples/node/package.json` is a `private` package that pulls the
  native binding via `file:../../bindings/node` and lists `ws` as a
  dev-dependency for the live-trading example. `require('..')` in each
  file becomes `require('wickra')` — exactly what a downstream user would
  write — and the file-header run instructions are updated to the new
  two-step workflow (`npm install` in bindings/node, then in
  examples/node).
* `backtest.js`'s default-CSV path becomes the much shorter
  `__dirname/../data/btcusdt-1d.csv` from the new location.
* `bindings/node/package.json` drops the now-unused `ws` devDependency.
* `.gitignore` is broadened from `bindings/node/node_modules/` to
  `**/node_modules/` so the new `examples/node/node_modules/` directory is
  not tracked.
* The README "Languages" table, project-layout block and
  `examples/README.md` Node section are updated for the new paths and run
  commands.

Verified by running `node backtest.js` (3200 BTCUSDT daily bars, matching
output), `node streaming.js`, and `node --check live_trading.js` from the
new location.
2026-05-23 00:10:06 +02:00
kingchenc 747d1a5b1b examples: move Rust examples into a top-level examples/rust/ crate
The three Rust examples (backtest, fetch_btcusdt, live_binance) used to
live each in their own crate's examples/ dir, splitting the example set
across crates and burying it inside the source tree. Move them into a new
workspace member crate at `examples/rust/` (package `wickra-examples`,
`publish = false`) so all language examples sit under one top-level
`examples/<lang>/` tree.

* `examples/rust/Cargo.toml` declares the per-binary deps (wickra,
  wickra-data with the `live-binance` feature always on, serde_json, tokio
  for the macro and current-thread runtime).
* `examples/rust/src/bin/{backtest,fetch_btcusdt,live_binance}.rs` are the
  three migrated binaries; their doc-comments and the fetch_btcusdt output
  path are updated for the new location and run command
  (`cargo run -p wickra-examples --bin <name>`).
* Workspace `Cargo.toml` lists the new member; the now-empty
  `[dev-dependencies]` extras (`wickra`, `tokio` in wickra-data and
  `serde_json` in wickra) that existed only for these examples are dropped.
* The `[[example]] live_binance` table is removed from wickra-data's
  manifest since the file moved out.
* README "Languages" + project-layout, examples/README.md, Quickstart-Rust
  and Data-Layer are pointed at the new paths and commands.

`cargo build -p wickra-examples` and `cargo run --release -p wickra-examples
--bin backtest -- examples/data/btcusdt-1d.csv` both succeed; the rest of
the workspace (core, data, wickra) builds, clippies (`--all-targets -D
warnings`) and tests (508 core + 28 data + 1 integration + 74+3+1
doctests) all stay green.
2026-05-23 00:07:07 +02:00
kingchenc a1c646ae7c examples: move the bundled BTCUSDT datasets to a top-level examples/data/
The seven BTCUSDT OHLCV datasets used to live under
crates/wickra/examples/data/, which buried them inside a Rust crate even
though the Node backtest example and the upcoming Rust/Node/WASM example
restructure need to reach them too. Move them to the workspace-level
examples/data/ so every language's examples can resolve the same path.

The bench (crates/wickra/benches/indicators.rs), the example_data
integration test, fetch_btcusdt.rs and the Node backtest example all take
the new ../../examples/data/ path; Data-Layer.md, examples/README.md and
the CHANGELOG entry are updated to match. No data file content changes.
2026-05-23 00:01:52 +02:00
kingchenc ba10898801 docs: add a cross-language examples index
The top-level examples/ directory held only python/, which made the
examples look Python-only even though Rust, Node and WASM all ship their
own. Add examples/README.md: a single index of every runnable example
across Rust, Python, Node and WASM, each with its run command, plus a note
on the bundled BTCUSDT datasets.

Point the README "Languages" table at the Node backtest example and link
the new index from both the table and the project-layout section.
2026-05-22 22:47:13 +02:00
kingchenc 25454fa89a examples(node): add a live Binance trading example
Mirror examples/python/live_trading.py for the Node binding: connect to the
public Binance kline WebSocket, stream close prices through RSI / MACD /
Bollinger Bands, and print BUY/SELL candidate signals when all three agree.
The symbol and interval are validated before being spliced into the stream
URL, and non-kline frames (acks, heartbeats) are skipped.

Uses the standard `ws` package, added as a devDependency so it installs
with `npm install` for anyone running the examples but never reaches a
consumer of the published package.
2026-05-22 22:45:52 +02:00
kingchenc 2eabda5fa3 examples(node): add an offline backtest example
The Node binding shipped only one example (a synthetic streaming demo),
while Python and Rust both have a CSV backtest. Add the Node counterpart of
examples/python/backtest.py and crates/wickra/examples/backtest.rs: it reads
an OHLCV CSV, streams every candle through a basket of indicators (SMA, EMA,
RSI, MACD, Bollinger Bands, ATR, ADX, OBV) via the O(1) update call, and
prints a per-series summary.

With no argument it runs against the bundled BTCUSDT daily dataset, so it is
runnable out of the box; pass a path to use any other OHLCV CSV.
2026-05-22 22:43:21 +02:00
kingchenc c6938e8473 docs: refresh the stale Python and Node binding READMEs
The Python binding README still advertised "63 indicators across four
families" with the pre-restructure five-group taxonomy, missing the eight
indicators added since. Update it to "71 indicators across eight families"
with the catalogue grouped to match the main README.

The Node binding README referred to the package as @wickra/wickra in its
title, install command and import example; the published package is named
wickra (per bindings/node/package.json). Correct all three.
2026-05-22 22:21:49 +02:00
kingchenc 91f24946a6 docs(changelog): record the example datasets and Timeframe constructors
Add the still-unreleased Z1/Z2 work to the [Unreleased] section: the seven
real-BTCUSDT example datasets plus the fetch_btcusdt example, the
Timeframe::minutes/hours/days constructors, and the switch of the indicator
benchmarks from a synthetic series to the checked-in BTCUSDT dataset.
2026-05-22 22:20:26 +02:00
kingchenc d5ff0a9df6 wickra-data: add minutes/hours/days Timeframe constructors
Timeframe gained new/millis/seconds/one_minute_ms; add minutes, hours and
days alongside them. Each builds on seconds (minutes(5) -> a 300-second
bucket), consistent with Timeframe::seconds, and guards the multiplication
with checked_mul so an oversized n yields Error::InvalidTimeframe instead
of an overflow panic. A non-positive n is rejected by Timeframe::new.

Each method carries a runnable doctest, and unit tests cover the known
bucket sizes, non-positive rejection and overflow rejection.
2026-05-22 21:49:21 +02:00
kingchenc 2b3a1b7384 examples: add real BTCUSDT candle datasets from Binance
Add seven OHLCV datasets under crates/wickra/examples/data/, one per
timeframe (1m/5m/15m/1h/12h/1d/1month), holding real BTCUSDT spot klines
fetched from the Binance REST API. The new fetch_btcusdt example
regenerates them: it paginates the klines endpoint through the system
curl, parses with serde_json, validates every candle via Candle::new and
keeps only fully closed buckets.

The indicator benchmarks now run against the 1m dataset instead of a
synthetic series, and a new example_data integration test checks that
every file parses and carries evenly spaced, monotonic timestamps.

The monthly file is named btcusdt-1month.csv rather than btcusdt-1M.csv
so it does not collide with btcusdt-1m.csv on case-insensitive
filesystems (Windows, default macOS).
2026-05-22 21:47:17 +02:00
kingchenc d2f99efd78 F13c: restructure the indicator catalogue into eight families
The original taxonomy was four classical families plus a statistics group,
with the F1-F12 expansion slotted in as sub-categories. This regroups the
whole 71-indicator catalogue into eight top-level families, each with at
least five members:

  Moving Averages (12), Momentum Oscillators (13), Trend & Directional (9),
  Price Oscillators (5), Volatility & Bands (12), Trailing Stops (5),
  Volume (9), Price Statistics (7).

- Wiki: docs/wiki/indicators/ reorganised into eight family folders; all 71
  indicator pages moved with `git mv`. Every internal cross-link is
  normalised to `../<family>/Indicator-X.md`, each page's `Family` field is
  set to its new family, and two pre-existing `../Indicator-Chaining.md`
  links (should have been `../../`) are corrected. A link check confirms
  every relative wiki link resolves.
- Indicators-Overview.md fully rewritten around the eight families;
  Home.md indicator reference and the README family table follow suit.
- Warmup-Periods.md gains the eight F13 indicators; CHANGELOG records the
  46-indicator expansion (25 -> 71) and the eight-family taxonomy.
- Tests: Node indicators.test.js and Python test_new_indicators.py cover
  all eight new indicators (Node 91/91, Python 117/117 green).

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 508 core tests,
25 data tests and 74 doctests green.
2026-05-22 21:21:56 +02:00
kingchenc 6643f7a81d F13b: add True Range, Chaikin Volatility, Z-Score and Linear Regression Angle
Second half of the eight indicators that fill out the new family taxonomy.

- Rust core: true_range.rs (TrueRange — the raw single-bar volatility ATR
  averages), chaikin_volatility.rs (ChaikinVolatility — rate of change of a
  smoothed high-low spread), z_score.rs (ZScore — price normalised against
  its rolling mean and standard deviation) and linreg_angle.rs (LinRegAngle
  — the rolling regression slope as a degree angle). Each with a full
  Indicator impl, runnable doctest and reference / property / warmup /
  reset / batch==streaming tests.
- Python / Node / WASM: classes wired through all three bindings (ZScore
  and LinRegAngle ride the scalar macros where possible) plus .pyi stubs
  and __init__.py / __all__ entries.
- Wiki: four new Indicator-*.md pages.

The eight-family taxonomy restructure (Overview / Home / README / folder
layout) lands next in F13c.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 508 core tests,
25 data tests and 74 doctests green.
2026-05-22 21:06:36 +02:00
kingchenc e452d35a27 F13a: add Accelerator Oscillator, Balance of Power, Choppiness Index and Vertical Horizontal Filter
First half of the eight indicators that fill out the new family taxonomy.

- Rust core: accelerator_oscillator.rs (AcceleratorOscillator — AO minus a
  short SMA of itself), balance_of_power.rs (BalanceOfPower — per-bar
  (close-open)/(high-low)), choppiness_index.rs (ChoppinessIndex — summed
  true range over the high-low span, log-scaled) and
  vertical_horizontal_filter.rs (VerticalHorizontalFilter — net move over
  total move). Each with a full Indicator impl, runnable doctest and
  reference / property / warmup / reset / batch==streaming tests.
- Python / Node / WASM: classes wired through all three bindings
  (BalanceOfPower carries an explicit open column; VHF rides the scalar
  macros) plus .pyi stubs and __init__.py / __all__ entries.
- Wiki: four new Indicator-*.md pages.

The eight-family taxonomy restructure (Overview / Home / README / folder
layout) lands in F13c once F13b's four indicators are in.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 481 core tests,
25 data tests and 70 doctests green.
2026-05-22 20:57:52 +02:00
kingchenc 27f37f5347 docs(python): refresh the binding README's indicator list to 63
The Python binding README still advertised the original 25 indicators and
the four-family list. Bring it in line with the root README: 63 indicators
across the four classical families plus the statistics group.
2026-05-22 20:31:52 +02:00
kingchenc 2f3b5cc3be F-Abschluss: wire the Python package, refresh docs and extend the test suites
Finalises the F1-F12 indicator expansion (25 -> 63 indicators).

- Python `wickra/__init__.py`: import and re-export all 63 indicators,
  grouped by family, with a matching `__all__`. The package previously
  exposed only the original 25 even though the compiled module and the
  `.pyi` stubs already carried the rest.
- Docs: `Home.md` and `README.md` indicator counts and family tables
  updated to 63; `Indicators-Overview.md` already restructured per family
  in F10-F12; `Warmup-Periods.md` gains all 38 new indicators across the
  single- and multi-output tables (and the stale two-arg `Psar::new`
  example is corrected to three args); `CHANGELOG.md` `[Unreleased]` lists
  every new indicator by family.
- Tests: `bindings/node/__tests__/indicators.test.js` covers all 63
  indicators (streaming==batch plus four new reference-value checks),
  80/80 green; new `bindings/python/tests/test_new_indicators.py` covers
  the 38 additions (streaming==batch, shapes, reference values,
  lifecycle), Python suite 105/105 green.
- `bindings/node/index.js` regenerated by `napi build`.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 454 core tests,
25 data tests, 66 doctests, 80 Node tests and 105 Python tests green;
`cargo check -p wickra-wasm --tests` green.
2026-05-22 20:04:13 +02:00
kingchenc 2d0ee926c5 F12: add price transforms and rolling linear regression
- Rust core: typical_price.rs ((H+L+C)/3), median_price.rs ((H+L)/2),
  weighted_close.rs ((H+L+2C)/4) — stateless per-bar OHLC transforms — and
  linreg.rs (LinearRegression — endpoint of a rolling ordinary-least-squares
  fit) and linreg_slope.rs (LinRegSlope — slope of that fit). Each with a
  full Indicator impl, runnable doctest and reference / property / warmup /
  reset / batch==streaming tests.
- Python: PyTypicalPrice / PyMedianPrice / PyWeightedClose /
  PyLinearRegression / PyLinRegSlope PyO3 classes + module registration +
  .pyi stubs.
- Node: explicit TypicalPriceNode / MedianPriceNode / WeightedCloseNode /
  LinearRegressionNode / LinRegSlopeNode; index.d.ts and index.js updated.
- WASM: explicit WasmTypicalPrice / WasmMedianPrice / WasmWeightedClose;
  WasmLinearRegression / WasmLinRegSlope via the scalar macro.
- Wiki: a new indicators/statistics/ folder with five Indicator-*.md pages,
  a new "Statistics" family in Indicators-Overview.md and Home.md.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 454 core tests,
25 data tests and 66 doctests green.
2026-05-22 19:52:04 +02:00
kingchenc 21bbd521b3 F11: add SuperTrend, Chandelier Exit, Chande Kroll Stop and ATR Trailing Stop
- Rust core: super_trend.rs (SuperTrend — ATR-banded trailing stop with
  flip logic; SuperTrendOutput { value, direction }), chandelier_exit.rs
  (Chandelier Exit — ATR stop hung off the window's highest high / lowest
  low; ChandelierExitOutput { long_stop, short_stop }),
  chande_kroll_stop.rs (Chande Kroll Stop — a two-stage ATR stop;
  ChandeKrollStopOutput { stop_long, stop_short }), atr_trailing_stop.rs
  (ATR Trailing Stop — a single ratcheting close-based stop). Each with a
  full Indicator impl, runnable doctest and reference / property / warmup
  / reset / batch==streaming tests.
- Python: PySuperTrend / PyChandelierExit / PyChandeKrollStop /
  PyAtrTrailingStop PyO3 classes (struct outputs as tuples and (n, 2)
  arrays) + module registration + .pyi stubs.
- Node: explicit SuperTrendNode / ChandelierExitNode / ChandeKrollStopNode
  / AtrTrailingStopNode with SuperTrendValue / ChandelierExitValue /
  ChandeKrollStopValue objects; index.d.ts and index.js updated.
- WASM: WasmSuperTrend / WasmChandelierExit / WasmChandeKrollStop /
  WasmAtrTrailingStop.
- Wiki: Indicator-SuperTrend/ChandelierExit/ChandeKrollStop/
  AtrTrailingStop.md plus rows in the "Trailing stop" table of
  Indicators-Overview.md and entries in Home.md.
- Add clippy.toml with doc-valid-idents for the proper noun "LeBeau".

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 427 core tests,
25 data tests and 61 doctests green.
2026-05-22 19:42:14 +02:00
kingchenc 0b11a523a0 F10: add Chaikin Money Flow, Chaikin Oscillator, Force Index and Ease of Movement
- Rust core: cmf.rs (Chaikin Money Flow — summed money-flow volume over
  summed volume, bounded to [-1, +1]), chaikin_oscillator.rs (Chaikin
  Oscillator — the MACD of the ADL, EMA(ADL, fast) - EMA(ADL, slow)),
  force_index.rs (Elder's Force Index — EMA of price change scaled by
  volume), ease_of_movement.rs (Arms' Ease of Movement — SMA of distance
  travelled per unit of volume). Each with a full Indicator impl,
  runnable doctest and reference / property / warmup / reset /
  batch==streaming tests.
- Python: PyChaikinMoneyFlow / PyChaikinOscillator / PyForceIndex /
  PyEaseOfMovement PyO3 classes + module registration + .pyi stubs.
- Node: explicit ChaikinMoneyFlowNode / ChaikinOscillatorNode /
  ForceIndexNode / EaseOfMovementNode; index.d.ts and index.js updated.
- WASM: WasmChaikinMoneyFlow / WasmChaikinOscillator / WasmForceIndex /
  WasmEaseOfMovement.
- Wiki: Indicator-ChaikinMoneyFlow/ChaikinOscillator/ForceIndex/
  EaseOfMovement.md plus a new "Oscillators" sub-table in
  Indicators-Overview.md and entries in Home.md.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 402 core tests,
25 data tests and 57 doctests green.
2026-05-22 19:25:32 +02:00
kingchenc 81962485af F9: add Accumulation/Distribution Line and Volume-Price Trend
Completes the F9 family (Cumulative volume) end to end:

- Rust core: adl.rs (Accumulation/Distribution Line — cumulative
  range-weighted volume) and vpt.rs (Volume-Price Trend — cumulative
  volume scaled by percentage price change). Each with a full Indicator
  impl, runnable doctest and reference / cumulative-property / warmup /
  reset / batch==streaming tests.
- Python: PyAdl / PyVolumePriceTrend PyO3 classes + module registration
  + .pyi stubs (no parameters, like OBV/VWAP).
- Node: explicit AdlNode and VolumePriceTrendNode; index.d.ts and
  index.js updated.
- WASM: WasmAdl and WasmVolumePriceTrend.
- Wiki: Indicator-Adl.md and Indicator-VolumePriceTrend.md plus rows in
  Indicators-Overview.md and entries in Home.md.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 373 core tests,
25 data tests and 53 doctests green.
2026-05-22 18:38:21 +02:00
kingchenc 99dd144576 F8: add Bollinger Bandwidth and %b
Completes the F8 family (Bands & channels) end to end:

- Rust core: bollinger_bandwidth.rs ((upper - lower) / middle — the
  squeeze gauge) and percent_b.rs ((price - lower) / (upper - lower) —
  price position within the bands, unclamped). Both wrap BollingerBands
  and carry a full Indicator impl, runnable doctest and reference /
  constant-series / definition-consistency / warmup / reset /
  batch==streaming tests.
- Python: PyBollingerBandwidth / PyPercentB PyO3 classes + module
  registration + .pyi stubs (defaults (20, 2.0)).
- Node: explicit BollingerBandwidthNode and PercentBNode; index.d.ts
  and index.js updated.
- WASM: WasmBollingerBandwidth / WasmPercentB via the scalar macro.
- Wiki: Indicator-BollingerBandwidth.md and Indicator-PercentB.md plus
  rows in Indicators-Overview.md and entries in Home.md.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 362 core tests,
25 data tests and 51 doctests green.
2026-05-22 18:30:49 +02:00
kingchenc 6c58d3827c F7: add NATR, StdDev, Ulcer Index and Historical Volatility
Completes the F7 family (Volatility) end to end:

- Rust core: natr.rs (ATR as a percentage of close), std_dev.rs
  (rolling population standard deviation), ulcer_index.rs (RMS of
  trailing-high drawdowns — downside-only risk), historical_volatility.rs
  (annualised sample stddev of log returns). Each with a full Indicator
  impl, runnable doctest and reference / constant-series / warmup /
  reset / batch==streaming tests.
- Python: PyNatr / PyStdDev / PyUlcerIndex / PyHistoricalVolatility
  PyO3 classes + module registration + .pyi stubs.
- Node: StdDevNode / UlcerIndexNode via the scalar macro, explicit
  NatrNode and HistoricalVolatilityNode; index.d.ts and index.js updated.
- WASM: WasmStdDev / WasmUlcerIndex / WasmHistoricalVolatility via the
  scalar macro, explicit WasmNatr.
- Wiki: Indicator-Natr/StdDev/UlcerIndex/HistoricalVolatility.md plus
  rows in Indicators-Overview.md and entries in Home.md.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 350 core tests,
25 data tests and 49 doctests green.
2026-05-22 18:26:29 +02:00
kingchenc 16c0639f0c F6: add Aroon Oscillator, Vortex and Mass Index
Completes the F6 family (Trend strength) end to end:

- Rust core: aroon_oscillator.rs (AroonUp - AroonDown, one-line trend
  gauge), vortex.rs (Vortex Indicator VI+/VI- with the VortexOutput
  struct), mass_index.rs (Dorsey's range-expansion sum of the
  EMA-of-range ratio). Each with a full Indicator impl, runnable doctest
  and reference / saturation / warmup / reset / batch==streaming tests.
- Python: PyAroonOscillator / PyVortex / PyMassIndex PyO3 classes +
  module registration + .pyi stubs (defaults Aroon=14, Vortex=14,
  MassIndex=(9,25)).
- Node: explicit AroonOscillatorNode, VortexNode (with VortexValue
  object) and MassIndexNode; index.d.ts and index.js updated.
- WASM: WasmAroonOscillator, WasmVortex, WasmMassIndex.
- Wiki: Indicator-AroonOscillator/Vortex/MassIndex.md plus rows in
  Indicators-Overview.md and entries in Home.md.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 320 core tests,
25 data tests and 45 doctests green.
2026-05-22 18:17:38 +02:00
kingchenc 54148cad5b F5: add PPO, DPO and Coppock Curve price oscillators
Completes the F5 family (Price oscillators) end to end:

- Rust core: ppo.rs (Percentage Price Oscillator — MACD as a percentage
  of the slow EMA), dpo.rs (Detrended Price Oscillator — shifted price
  minus its SMA), coppock.rs (Coppock Curve — WMA of two summed ROCs).
  Each with a full Indicator impl, runnable doctest and reference /
  constant-series / warmup / reset / batch==streaming / non-finite tests.
- Python: PyPpo / PyDpo / PyCoppock PyO3 classes + module registration
  + .pyi stubs (defaults PPO=(12,26), DPO=20, Coppock=(14,11,10)).
- Node: DpoNode via the scalar macro, explicit PpoNode and CoppockNode;
  index.d.ts and index.js updated.
- WASM: WasmDpo / WasmPpo / WasmCoppock via the scalar macro.
- Wiki: Indicator-Ppo/Dpo/Coppock.md plus rows in Indicators-Overview.md
  and entries in Home.md.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 300 core tests,
25 data tests and 42 doctests green.
2026-05-22 18:09:10 +02:00
kingchenc e24e7726ce F4: add StochRSI and Ultimate Oscillator
Completes the F4 family (Stochastic oscillators) end to end:

- Rust core: stoch_rsi.rs (Stochastic Oscillator applied to the RSI
  series, bounded [0,100]) and ultimate_oscillator.rs (Larry Williams'
  weighted three-timeframe buying-pressure oscillator). Each with a full
  Indicator impl, runnable doctest and reference / saturation / bounds /
  warmup / reset / batch==streaming tests.
- Python: PyStochRsi / PyUltimateOscillator PyO3 classes + module
  registration + .pyi stubs (defaults StochRSI=(14,14), UO=(7,14,28)).
- Node: explicit StochRsiNode and UltimateOscillatorNode; index.d.ts
  and index.js updated.
- WASM: WasmStochRsi via the scalar macro, explicit
  WasmUltimateOscillator.
- Wiki: Indicator-StochRsi.md and Indicator-UltimateOscillator.md plus
  rows in Indicators-Overview.md and entries in Home.md.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 278 core tests,
25 data tests and 39 doctests green.
2026-05-22 18:02:44 +02:00
kingchenc 7728151c87 F3: add MOM, CMO, TSI and PMO momentum indicators
Completes the F3 family (Momentum) end to end:

- Rust core: mom.rs (raw price-difference momentum), cmo.rs (Chande
  Momentum Oscillator — unsmoothed gain/loss sum, bounded [-100,100]),
  tsi.rs (True Strength Index — double-EMA-smoothed momentum ratio),
  pmo.rs (DecisionPoint Price Momentum Oscillator — doubly-smoothed ROC
  with the 2/period custom smoothing). Each with a full Indicator impl,
  runnable doctest and reference-value / saturation / warmup / reset /
  batch==streaming / non-finite tests.
- Python: PyMom / PyCmo / PyTsi / PyPmo PyO3 classes + module
  registration + .pyi stubs (defaults MOM=10, CMO=14, TSI=(25,13),
  PMO=(35,20)).
- Node: MomNode / CmoNode via the scalar macro, explicit TsiNode and
  PmoNode; index.d.ts and index.js updated.
- WASM: WasmMom / WasmCmo / WasmTsi / WasmPmo via the scalar macro.
- Wiki: Indicator-Mom/Cmo/Tsi/Pmo.md plus rows in Indicators-Overview.md
  and entries in Home.md.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 262 core tests,
25 data tests and 37 doctests green.
2026-05-22 17:53:46 +02:00
kingchenc 780a176072 F2: add ZLEMA, T3 and VWMA advanced moving averages
Completes the F2 family (Advanced MAs) end to end:

- Rust core: zlema.rs (Zero-Lag EMA over the de-lagged series
  2·price − price[lag]), t3.rs (Tillson's six-EMA cascade with the
  volume-factor polynomial), vwma.rs (volume-weighted rolling mean with
  a zero-volume fallback to the unweighted mean). Each with a full
  Indicator impl, runnable doctest and reference-value / warmup /
  reset / batch==streaming / non-finite tests.
- Python: PyZlema / PyT3 / PyVwma PyO3 classes + module registration
  + .pyi stubs (T3 defaults v=0.7).
- Node: ZlemaNode via the scalar macro, explicit T3Node and VwmaNode
  classes; index.d.ts and index.js updated.
- WASM: WasmZlema / WasmT3 via the scalar macro, explicit WasmVwma.
- Wiki: Indicator-Zlema.md, Indicator-T3.md, Indicator-Vwma.md plus
  rows in Indicators-Overview.md and entries in Home.md.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 232 core tests,
25 data tests and 33 doctests green.
2026-05-22 17:45:02 +02:00
kingchenc ed7324115c F1: wire SMMA and TRIMA through every binding and the wiki
Completes the F1 family (Simple & Weighted MAs). The Rust core for both
SMMA (Wilder's RMA) and TRIMA (triangular MA) already landed; this adds
the remaining Definition-of-Done steps:

- Python: PySmma / PyTrima PyO3 classes + module registration + .pyi stubs.
- Node: SmmaNode / TrimaNode via the scalar-indicator macro; index.d.ts
  and index.js updated for the two new classes.
- WASM: WasmSmma / WasmTrima via the scalar-indicator macro.
- Wiki: Indicator-Smma.md and Indicator-Trima.md (full pages) plus rows
  in Indicators-Overview.md and entries in Home.md.

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 208 core tests,
25 data tests and 31 doctests green.
2026-05-22 17:34:38 +02:00
kingchenc abd2d80f8d F1: add SMMA and TRIMA moving averages (core)
First step of the indicator-family expansion (see the F section of
todo-detailed.md). Family F1 — Simple & Weighted MAs — gains two
members alongside the existing Sma/Ema/Wma:

- Smma — Wilder's smoothed moving average (RMA): SMA-seeded, then the
  (prev*(n-1)+x)/n recurrence. The average underlying RSI and ATR.
- Trima — triangular moving average: two stacked SMAs (n1/n2 split by
  parity) that triangular-weight the window. Genuine stacking — the
  outer SMA consumes the inner SMA's output.

Both implement the full Indicator trait with reference-value, warmup,
reset, batch==streaming and non-finite-input tests, a runnable doctest,
and are re-exported from the crate root. 208 core tests + 30 doctests
pass; clippy and fmt clean.
2026-05-22 17:10:52 +02:00
kingchenc 3e8c48eefc fix(wasm): call expect() directly instead of ok().expect() in tests
The WASM binding's test module (added in B6) used `.ok().expect(...)`
on the Result-returning constructors. clippy's ok_expect lint rejects
this under the workspace's `-D warnings`, and the CI rust job lints
wickra-wasm with --all-targets — so the branch would fail CI.

Replace all seven `.ok().expect(...)` with `.expect(...)` directly;
JsError implements Debug, so this compiles and gives a better panic
message. clippy and fmt are now clean for wickra-wasm.
2026-05-22 16:47:16 +02:00
kingchenc a4d8c40dc2 E6: extend the Python release matrix to musl and Windows arm64
python-wheels built only glibc Linux (x86_64/aarch64), macOS, and
Windows x64 — Alpine/musl users and Windows arm64 had no wheel.

Add musllinux_1_2 wheels for x86_64 and aarch64 Linux and an
aarch64 wheel on the windows-11-arm runner. The upload artifact name
now includes the manylinux value so the glibc and musl builds of the
same architecture do not collide. The Node release matrix already
covers linux-arm64 and win32-arm64 (added in B11); Node musl is left
out, as B11 documented, because it needs a cross/container setup.
2026-05-22 16:45:20 +02:00
kingchenc 78c31d1bed E16: add a cargo-fuzz harness
The repository had no fuzzing setup despite several natural targets —
the CSV parser, the Binance envelope deserializer, and the stateful
indicator/aggregator update paths.

Add a fuzz/ cargo-fuzz crate (detached from the workspace via its own
[workspace] table and the parent's exclude) with four targets:

- csv_reader      — CandleReader over arbitrary bytes
- binance_envelope — RawWsEnvelope deserialization from arbitrary strings
- indicator_update — RSI/EMA streaming + batch over arbitrary f64 series
- tick_aggregator — TickAggregator over arbitrary tick triples

Each target asserts the no-panic contract: malformed input must surface
as an Err. fuzz/README.md documents running them (nightly + cargo-fuzz).
2026-05-22 16:44:19 +02:00
kingchenc 4b3227a15f E15: add a runnable doctest to every indicator type
Only two doctests existed in wickra-core; none of the 25 indicator
types carried a runnable rustdoc example.

Add an "# Example" doctest to every public indicator type (all 26,
including RollingVwap): construct the indicator and stream 80 inputs
through update, asserting a value is produced. The candle-input
indicators build valid OHLCV candles inline. cargo test --doc
-p wickra-core now runs 28 doctests, all passing; fmt and clippy clean.
2026-05-22 16:41:43 +02:00
kingchenc 5568596e18 E8: add code coverage to CI
CI had no coverage measurement, and although .gitignore listed coverage
artefacts nothing produced them.

Add a `coverage` job that runs cargo-llvm-cov over the three pure-Rust
crates (with wickra-data's live-binance feature so the Binance parser
tests count), emits lcov, and uploads to Codecov. The Codecov upload
uses fail_ci_if_error: false so a Codecov outage cannot break CI. Both
new actions (taiki-e/install-action, codecov/codecov-action) are
SHA-pinned. Add a coverage badge to the README.
2026-05-22 16:38:01 +02:00
kingchenc 5917d4928f E13: add the WASM quickstart and the data-layer wiki page
The wiki had quickstarts for Python, Rust, and Node but none for the
WebAssembly binding, and the wickra-data crate (CSV reader, tick
aggregator, resampler, Binance feed) was not documented anywhere.

- Quickstart-WASM.md: install via npm, building with wasm-pack, and
  streaming/batch/multi-output usage in a browser or bundler.
- Data-Layer.md: the wickra-data crate — CandleReader, TickAggregator
  (including the opt-in gap fill), Resampler/resample_all, and the
  feature-gated Binance live feed.
- Home.md links both from the wiki contents list.
2026-05-22 16:36:29 +02:00
kingchenc 9d822d26aa E18: make the RollingVwap documentation directly linkable
RollingVwap is a separate public type (pub struct RollingVwap in
vwap.rs) and Indicator-Vwap.md already documents it in a full
"## RollingVwap (finite window)" section, but it was not directly
reachable: the Overview row added in E12 pointed at a #rollingvwap
anchor that does not exist.

Fix the Overview link to the real #rollingvwap-finite-window anchor and
add a jump-to note at the top of Indicator-Vwap.md so both public types
are reachable in one click.
2026-05-22 16:35:01 +02:00
kingchenc 0d20973a4d E12: link every Overview row to its deep-dive page
Indicators-Overview.md only had a "Deep dive" column in the Trend
tables; the Momentum, Volatility and Volume tables left readers without
a path to the per-indicator pages.

Add a "Deep dive" column to all eight remaining tables, linking each of
the 18 rows to its indicators/<family>/Indicator-*.md page. RollingVwap
points at the RollingVwap section of Indicator-Vwap.md (added in E18).
All link targets verified to exist.
2026-05-22 16:33:44 +02:00
kingchenc f628bd5fb7 E11: classify TRIX as a momentum indicator consistently
Indicators-Overview.md listed Trix in the Trend section's EMA-family
table, while the README and the docs folder layout
(indicators/momentum/Indicator-Trix.md) place it under Momentum.

Move the Trix row into the Momentum "Unbounded oscillators" table — it
emits a rate of change, not a price-scale trend line — and leave a note
in the Trend section pointing there. Momentum is now the single
canonical family across the README, the folder layout, and the
overview.
2026-05-22 16:31:43 +02:00
kingchenc 87b3f383d6 E5: update the warmup docs to the post-A5 behavior
A5 changed Keltner and HMA to feed their sibling sub-indicators
unconditionally, so warmup_period() is now the exact first-emission
index for every indicator. The wiki still described the old
?-starvation behavior as correct.

- Indicator-Keltner.md: the Warmup section, the worked example output
  (first emission now at i=2, not i=4), the summary table row, and the
  "reported warmup understates" pitfall now state that warmup_period()
  is exact. Example output regenerated by running the code.
- Indicator-Hma.md: the Warmup section, all three language examples
  (first Some at index 10, not 13), the table row, and the chaining
  pitfall corrected. Outputs regenerated.
- Indicators-Overview.md: dropped the claim that Hma and Kama lag their
  reported warmup — both were verified exact.
2026-05-22 16:30:56 +02:00
kingchenc 71e46a1ea6 E10: add cargo-deny supply-chain checks
The repository had no supply-chain auditing — no deny.toml and no CI
job to catch vulnerable, unmaintained, wrongly-licensed, or
unexpectedly-sourced dependencies.

Add deny.toml covering advisories, bans, licenses and sources:

- licenses: an allow-list of the permissive licenses the dependency
  tree actually uses, plus the workspace's own PolyForm-Noncommercial
  license and a scoped LLVM-exception for target-lexicon.
- bans: warn on duplicate versions, deny external wildcard deps
  (internal path deps are allowed).
- sources: only crates.io.
- advisories: RUSTSEC-2025-0020 (pyo3 0.22) is ignored with a documented
  reason — it is reachable only through bindings/python and the pyo3
  upgrade is tracked separately; the published crates do not use pyo3.

Add a `supply-chain` CI job running cargo-deny-action (SHA-pinned).
`cargo deny check` passes locally: advisories/bans/licenses/sources ok.
2026-05-22 16:25:58 +02:00
kingchenc 51f64b53c0 E7: correct inaccuracies in the README
- "## Indicators in 0.1.0" -> "## Indicators" (the heading drifted from
  the actual version; making it version-neutral stops the drift).
- Project layout: there is no top-level benches/ directory — Rust
  benches and examples live inside their crate. The tree now shows
  crates/wickra/benches, the per-crate examples, and the new
  bindings/node and bindings/wasm examples/ directories.
- Node "Example" pointed at a test file; it now points at the real
  bindings/node/examples/streaming.js (added in E17).
- "## Test counts" hardcoded numbers (171/11/56/7) that drift on every
  added test. Replaced with a version-neutral "## Testing" section that
  describes what each suite covers, including the WASM tests.
2026-05-22 16:23:43 +02:00
kingchenc b919c33dee E17: add a runnable Node example
bindings/node had no examples/ directory — the README pointed at a test
file as its "Example". (bindings/wasm/examples/index.html already
exists and is a complete browser demo, so only the Node side was
missing.)

Add bindings/node/examples/streaming.js: a deterministic synthetic
price series fed tick by tick through SMA, EMA, RSI and MACD, printing
a status line and flagging overbought/oversold candidates — the same
O(1)-per-update streaming model a live bot would use. Verified against
the built native module.
2026-05-22 16:22:52 +02:00
kingchenc a79606b4ce E9: complete the published crate manifests
The three published crates had no documentation link and no docs.rs
configuration, so wickra-data's feature-gated live-binance module would
not render on docs.rs.

Add documentation = "https://docs.rs/<crate>" and a
[package.metadata.docs.rs] section with all-features = true to
wickra-core, wickra, and wickra-data. No `exclude` is added: each crate
directory contains only src/ (plus benches/examples that are useful
source), so there is nothing irrelevant to drop from the .crate.
2026-05-22 16:20:43 +02:00
kingchenc 9fd926ecd8 E19: unify the author metadata
The workspace Cargo.toml declared authors = ["Wickra Contributors"]
while the npm package.json and the WASM package.json enrich step in
release.yml both use "kingchenc <kingchencp@gmail.com>".

Make the canonical author "kingchenc <kingchencp@gmail.com>" — the
actual maintainer, already used by both npm packages — and align the
workspace manifest to it. All three registries now agree.
2026-05-22 16:19:22 +02:00
kingchenc b3ddbea584 E14: remove hardcoded local paths from the docs
Indicators-Overview.md referenced the absolute author-machine paths
D:\Coding\Wickra\crates\... and D:\Coding\Wickra\bindings\... in its
"Source-of-truth files" section, and seven trend-indicator pages had
Node examples that did require('D:/Coding/Wickra/bindings/node').

Replace the overview paths with repo-relative GitHub links and change
the Node examples to require('wickra'), the published npm package name
a reader would actually use. No D:/Coding path remains anywhere in docs.
2026-05-22 16:18:48 +02:00
kingchenc 278b6afaa4 E4: commit the documentation sources
The 33 Markdown files under docs/wiki/ were never tracked. Commit them
into the repository so the documentation is versioned alongside the
code: 8 top-level pages plus 25 per-indicator deep dives under
indicators/{momentum,trend,volatility,volume}/.

The pages are kept in-repo (not pushed to a flat GitHub Wiki), so the
relative indicators/<family>/... links in Home.md resolve correctly
when rendered on GitHub.
2026-05-22 16:18:04 +02:00
kingchenc 94cab88278 E3: add community health files
The repository had no contributor-facing documentation or automation
config. Add the standard set:

- CHANGELOG.md (Keep a Changelog format, 0.1.0-0.1.4 plus Unreleased)
- CONTRIBUTING.md (build/test steps, change standards, PolyForm-NC note)
- SECURITY.md (private reporting, supported versions)
- CODE_OF_CONDUCT.md (Contributor Covenant 2.1)
- .github/ISSUE_TEMPLATE (bug report, feature request, config)
- .github/PULL_REQUEST_TEMPLATE.md
- .github/dependabot.yml (cargo, npm, pip, github-actions — the last
  keeps the D1 SHA pins current)
- .github/CODEOWNERS
2026-05-22 16:17:15 +02:00
kingchenc 53b8b6e282 E2: add an MSRV verification job to CI
Every CI job used dtolnay/rust-toolchain on stable, so the declared
minimum supported Rust version was never exercised — an accidental use
of a newer API would only break for downstream users on an older
compiler.

Add an `msrv` job with a two-row matrix: the workspace crates
(wickra-core, wickra, wickra-data) build and test on Rust 1.75, and the
node binding on Rust 1.77, matching the rust-version each manifest
declares. Both rows use the SHA-pinned toolchain action.
2026-05-22 12:37:15 +02:00