8a103ef92006e0fbdcb8fc2d2a6e4c74f26ebb08
10 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8a103ef920 |
feat(data-layer): TickAggregator (tick-to-candle) in all 10 languages (#309)
* feat(data-layer): TickAggregator in Node, WASM, Python + C ABI hub First data-layer feature (F2): roll trade ticks up into fixed-timeframe OHLCV candles, exposed natively and over the C ABI. - wickra-data wired as a binding dependency (workspace dep; its wickra-core dep is default-features=false so it never forces rayon into the rayon-free WASM build — native bindings re-enable parallel through their own dependency). - Node `TickAggregator(bucket, gapFill?)` -> `push(price, size, ts): Candle[]`; WASM the same (array of objects); Python `push(...) -> list[tuple]`. - C ABI: `WickraCandle` struct + `wickra_tick_aggregator_new/push/free` (push writes candles into a caller buffer and returns the count), generated via the capi generator's new DATA_LAYER section; cbindgen now parses wickra-data so `TickAggregator` is a forward-declared opaque; header vendored to bindings/go. Verified bit-identical across Node/WASM/Python/C/C++ (o=100 h=101 l=100 c=101 v=3 ts=0 for the shared 3-tick probe). WIP: Go/C#/Java/R generated bindings and the cross-language golden are still pending. * feat(data-layer): TickAggregator in Go, C#, Java, R (lossless push/drain) Complete F2 across all 10 languages: the C-ABI tick aggregator now uses a two-step push/drain so gap-fill candles are never lost, and the four generated bindings expose it idiomatically. - C ABI redesigned: opaque TickAggregator handle (inner aggregator + pending buffer); push consumes a tick and returns the closed-candle count, drain copies them into a count-sized caller buffer. - Go: NewTickAggregator + Push(price,size,ts) []Candle; C#: TickAggregator + Candle[] Push(...); Java: TickAggregator + Candle[] push(...); R: TickAggregator constructor + push() S3 generic returning an (n x 6) numeric matrix. - Candle output record generated per language from WickraCandle. Verified bit-identical to the native bindings (o=100 h=101 l=100 c=101 v=3 ts=0) in Go, C#, Java, and R at runtime; R passes R CMD check (pre-existing doc warnings only). WIP: cross-language data-layer golden + CHANGELOG still pending. * test(data-layer): cross-language golden for the tick aggregator + CHANGELOG gen_golden emits a deterministic tick stream (testdata/golden/data_ticks.csv) and the reference candle streams with and without gap filling (data_candles.csv, data_candles_gap.csv). Every binding replays the shared ticks through its TickAggregator and checks the candles bit-for-bit (fp tolerance) against the Rust reference: - Node / WASM / Python / Go / C# / Java / R: a dedicated parity test each. - C / C++: data_layer_test.c (compiled as both, run as ctest). The gap-fill fixture closes several candles from a single push, exercising the lossless push/drain path. Records the feature under CHANGELOG [Unreleased]. * fix(examples): rename the CSV-loader candle to WickraBar The example CSV helper (wickra_csv.h) defined its own struct WickraCandle, which now collides with the public C ABI WickraCandle (the tick aggregator output) in any example that includes both headers (backtest, multi_timeframe, the strategy examples). The public type owns the name; rename the example loader's bar to WickraBar. The generated golden_test.c is untouched (its only match was the unrelated WickraCandleVolumeOutput). |
||
|
|
4f708d410d |
test: golden-pin the four de-duplicated indicators across all bindings (#305)
* test: golden-pin the four de-duplicated indicators across all C-ABI bindings Extend gen_golden to emit reference fixtures for AdOscillator (ADOSC), IntradayIntensity, AwesomeOscillatorHistogram and AverageDrawdown, and replay them through the Go / C# / Java / R golden harnesses so their corrected definitions stay bit-identical to the Rust core in every binding. Go suite verified locally (gcc 13 + cgo): all 9 golden tests pass; C#/Java/R use the same fixtures and harness pattern (CI-verified). First step of extending the golden coverage beyond the seven archetype representatives. * test: golden-pin the scalar-output tranche (308 indicators) against Rust Extend gen_golden with a generated emit_scalar that writes reference fixtures for every single-f64-output indicator (scalar / candle / pairwise input) using valid constructor params, and add a manifest-driven generic Python golden replay that reconstructs each by its native name and checks it bit-for-bit against the Rust output. 308 indicators now value-tied to the Rust core in Python (pytest: 308/308). Takes golden coverage from the 7 archetype representatives to 308+ of the catalogue. 22 scalar indicators with non-default constructor constraints are skipped by gen_golden for now (logged), as are non-f64-output ones; multi-output, exotic inputs and the per-indicator arg arities of the C-ABI/Node replays follow. Generated + verified locally with the full toolchain. * test: golden-pin the multi-output tranche (70 indicators) in Python Add a generated emit_multi to gen_golden (per-indicator Output-field access, one CSV column per field) and a manifest-driven generic Python replay that checks every field of each multi-output indicator against the Rust reference. 70 multi-output indicators now value-tied to Rust in Python; combined with the scalar tranche, 378 indicators are golden-pinned. 8 multi with non-default param constraints and 5 with non-f64 Output fields (Option/Vec/i64) are deferred. pytest green. * test(golden): add 30 constraint-tuned indicators to scalar/multi golden suite Emit golden fixtures for 22 scalar-output and 8 multi-output indicators whose constructors need non-default parameters (Alma, Jma, Psar, T3, Mama, DoubleBollinger, ZigZag, ...). All 408 fixtures replay bit-for-bit through the Python binding. * test(golden): cover 36 missed scalar/multi indicators Add 26 single-output (LinearRegression family, HT cycle, Candle volatility estimators, DrawdownDuration) and 10 multi-output (BollingerBands, MACD/MACDEXT/MACDFIX, Camarilla, VWAP bands, ...) indicators to the golden suite. 444 fixtures replay bit-for-bit through the Python binding. * test(golden): cover 50 exotic-input indicators Add deterministic synthetic feeders for the DerivativesTick (17), CrossSection (15), Trade (8), TradeQuote (3) and OrderBook (7) families, derived from the shared OHLCV input series in both gen_golden and a new Python replay harness (test_golden_exotic). All 494 fixtures replay bit-for-bit through the Python binding. * test(golden): complete 514-indicator golden coverage Add the final tranches: 3 mixed multi-output indicators (Ichimoku, WilliamsFractals, LeadLagCrossCorrelation), 6 histogram profiles (time/volume seasonality + TPO/volume price profiles), 10 alt-chart bar builders and the footprint. Every one of the 514 distinct indicators now has a Rust-generated g_<Canonical>.csv fixture and a generic Python replay (scalar/multi/exotic/profile/bars), all passing bit-for-bit. * test(golden): add generic Node replay for all 514 indicators A manifest-driven node:test harness reconstructs every indicator by its native class, feeds the same synthetic stream derived from the shared golden input, and checks output bit-for-bit against the Rust reference fixtures (scalar/multi/exotic/profile/bars). node_manifest.json is generated from index.d.ts plus the Python-side manifests. 514/514 pass. * test(golden): add generated Go replay for all 514 indicators golden_all_test.go (generated by gen_golden_test.py) reconstructs every Go indicator, feeds the shared synthetic stream and checks output bit-for-bit against the Rust reference fixtures. A reflection-based comparator flattens multi-output structs, profiles and bar slices so one path covers all archetypes. This is the first C-ABI binding verified across the full catalogue. 514/514 pass. * test(golden): add generated C# replay for all 514 indicators GoldenAllTests.g.cs (generated by gen_golden_test.py) reconstructs every C# indicator, feeds the shared synthetic stream and checks output bit-for-bit against the Rust reference fixtures via a reflection-based flatten covering scalar/multi/profile/bar archetypes. 514/514 pass. Also add the '#nullable enable' directive the compiler requires to the generated Indicators.g.cs, clearing the four CS8669 warnings on the nullable double[] profile return types. * fix(java): marshal C ABI bool params correctly; add 514 golden replay The Java FFM binding marshalled the cross-section state flags (newHigh, newLow, aboveMa, onBuySignal) as JAVA_DOUBLE arrays, but the C ABI takes them as const bool* (one byte each), so the native side read the low byte of each 8-byte double and saw every flag as false. Add WickraNative. boolSegment and use it across the 15 cross-section indicators. Also pass the MacdExt MaType arguments as byte to match the uint8_t downcall descriptor (was int, throwing WrongMethodTypeException). Add GoldenAllTest.java (generated by gen_golden_test.py): a reflection runner replaying all 514 indicators against the Rust reference fixtures. The bugs above were found by this test; 514/514 now pass. * fix(r): marshal C ABI bool flags correctly; add 514 golden replay The R wrapper passed the cross-section state flags as (bool *)REAL(x), reinterpreting the 8-byte doubles as 1-byte bools so the native side read every flag as false. Add wk_bool_vec to convert each flag vector into a real C bool buffer and use it for all 15 cross-section update wrappers. Add test-golden-all.R + generated golden_specs.R: a reflective runner replaying all 514 indicators against the Rust reference fixtures. The bug above was found by this test; verified 514/514 pass locally. * test(golden): add WASM replay for all 514 indicators A manifest-driven node:test harness loads the nodejs-target wasm-pack build, reconstructs every indicator by its JS class, feeds the shared synthetic stream and checks output bit-for-bit against the Rust reference fixtures. wasm_manifest.json is generated from the wasm .d.ts plus the shared manifests; a recursive flattener covers scalar, multi (Reflect objects), profile and bar shapes. 514/514 pass locally (wasm-pack build --target nodejs, then node --test). * test(golden): add C and C++ replay for all 514 indicators golden_test.c (generated by gen_golden_test.py) drives every indicator through the C ABI (wickra.h) and checks output bit-for-bit against the Rust reference fixtures. golden_test.cpp #includes the same source so the identical runner is compiled and run under both gcc (C) and g++ (C++) via the CMake targets golden_test / golden_test_cpp — proving the extern "C" header is consumable from each language. Both 514/514 (verified via ctest). * test(golden): gofmt the generated Go golden replay * test(golden): make the Node fixture reader CRLF-safe and pin fixtures to LF |
||
|
|
2ef60874b9 |
fix(examples): satisfy clippy -D warnings in gen_golden (#291)
Replace `i as i64` with `i64::try_from(i)` (cast_possible_wrap) and rename the OHLCV destructure to descriptive names (many_single_char_names) so `cargo clippy --all-targets --all-features -- -D warnings` passes on the 1.95 toolchain. Dev-tool only; no library change. |
||
|
|
8bfe24ac1d |
test(golden): language-neutral fixtures + per-binding parity runners (#255)
**Task 5 — golden-fixture parity for the C-ABI bindings.** Lifts the C#/Go/Java/R tests from *one indicator per archetype* toward reference-value parity, catching FFI wiring bugs (swapped params, wrong multi-output field) the math-only core tests cannot see. ## What's here - **`examples/rust/src/bin/gen_golden.rs`** + **`testdata/golden/*.csv`** — a Rust generator computing a deterministic OHLCV series plus the core's reference outputs for a curated archetype-spanning set: scalar (`Sma`/`Ema`/`Rsi`), candle (`Atr`), scalar multi-output (`MACD`), candle multi-output (`ADX`), pairwise (`Beta`). `nan` marks warmup. Regenerate with `cargo run -p wickra-examples --bin gen_golden`. - **Parity runners** replaying the identical fixtures through each FFI (rel-tol 1e-6), each a standard test in the binding's existing suite (no `ci.yml` change — rides `dotnet test` / `go test` / `mvn install` / `R CMD`+testthat). A walk-up search locates `testdata/golden` regardless of run dir. - **C#** (`bindings/csharp/.../GoldenTests.cs`) — ✅ validated locally, 7/7 pass. - **Go** (`bindings/go/golden_test.go`) — ✅ validated locally, pass. - **Java** (`bindings/java/.../GoldenTests.java`) — modeled on the archetype API; validated by CI (no local mvn). - **R** (`bindings/r/tests/testthat/test-golden.R`) — modeled on the archetype API; validated by CI (no local Rscript). ## Notes - The curated set spans every marshalling archetype; extending the indicator list is mechanical (add to the generator + regenerate). Bars/profile archetypes can be added next. - No new CI jobs. |
||
|
|
3ebcb3f758 |
Per-binding throughput benchmarks + test-coverage gaps (#246)
Adds a `throughput` benchmark to every target and closes two small test-coverage documentation/QA gaps. One PR, no merge of binding code beyond the additive benchmarks and one C test. ## 1. Per-binding throughput benchmarks (all 9 targets) Each benchmark feeds a deterministic synthetic OHLCV series through three indicators chosen by **FFI call-signature archetype** (not algorithm — the same Rust core runs underneath all bindings): - `SMA(20)` — 1-in → 1-out (baseline boundary cost) - `ATR(14)` — multi-in → 1-out (input marshalling) - `MACD(12,26,9)` — 1-in → multi-out (output marshalling) Streaming is timed for all three; batch for the single-output SMA and ATR (median of 3 runs, after a warmup pass). New: Python (PyO3), WASM, C (CMake), C# (Stopwatch), Go, Java (FFM), R, and the Rust core baseline (`examples/rust/.../throughput.rs`, **no FFI** — the ceiling the bindings are measured against and the value their batch paths converge towards). Node already had `throughput.js`. **Not a speed claim:** there is no comparable streaming TA library for C, C#, Go, Java, R or WASM to compare against, so these are raw per-binding throughput numbers documenting each language's FFI overhead — see BENCHMARKS.md §3. The "Wickra is fast" claim still lives in §1/§2 (Rust core + the Python/Rust cross-library runs). ## 2. README `## Testing`: C# and C bullets The section listed every layer except C# and C, even though both have suites. Adds the two missing bullets. ## 3. C archetype ctest `examples/c/archetypes.c` drives one indicator per FFI archetype through the real C boundary (scalar + batch==streaming, multi-output, bars, profile, array input) plus reset, invalid-parameter and NULL-safety — the C counterpart of the Go/R/Java archetype suites. Runs on three OSes via the existing CMake/ctest. ## Notes - Benchmarks are not CI-gated (manual-run scripts, like the existing `throughput.js`); no `ci.yml`/`release.yml` changes. - Docs: BENCHMARKS.md §3, a `## Benchmark` section in every binding README, a CHANGELOG entry. - Verified locally by running: Rust, Python, C, C#, Go, Java (real numbers); the C archetype ctest with `-Wall -Wextra -Wpedantic -Werror`. WASM and R are API-correct and syntax-checked but need their own toolchains to run. |
||
|
|
c212f91256 |
docs(examples): add 3 end-to-end strategy examples (Rust + Python) (#65)
Wires real indicators into complete signal -> fill -> PnL -> equity loops over the checked-in BTCUSDT datasets, with per-trade Sharpe and max-drawdown reported on stdout. Closes the gap where existing examples showed only the mechanics of calling `update`/`batch` but not how Wickra plugs into a trading-system shape. Three strategies, each in Rust + Python (six files total): - strategy_rsi_mean_reversion — RSI(14) thresholds (30/70) on 1h BTCUSDT. Binary position, 0.1% per-trade fee. - strategy_macd_adx — MACD crossover entries gated by ADX(14) > 20 on 1h BTCUSDT. Trend-follower demo of multi-indicator gating. - strategy_bollinger_squeeze — Bollinger-bandwidth 180-day-low squeeze + upper-band breakout entry, ATR(14) * 2 stop. On 1d BTCUSDT for interpretable lookback. Each file is self-contained — print_summary is inlined per script so the example stays a single-file read. Every script prints a NOT-financial-advice notice next to its results. examples/README.md updated to list the new bins/scripts. |
||
|
|
43b0b26736 |
examples: add parallel-assets demos for Rust and Node
Python's parallel_assets.py demoed GIL-release multi-core throughput; Rust and Node both lacked a sibling that shows their own native parallelism. Close the gap with two real, runnable examples. * examples/rust/src/bin/parallel_assets.rs — synthesises an (assets, bars) panel with a deterministic per-asset LCG, runs a serial baseline, then `Sma::batch_parallel` / `Rsi::batch_parallel` via rayon, asserts the two outputs are element-wise identical and prints the speedup. Toggle indicator with `--indicator sma|rsi`. * examples/node/parallel_assets.js — same shape, but the parallel run is a `worker_threads` pool that re-loads the native binding in each worker. Each worker computes the last non-null indicator value for its slice; the main thread aggregates and verifies serial == parallel per asset. Both examples report timings and the serial-vs-parallel sanity check passes. Defaults (200 × 5000) keep the example fast on dev hardware; larger `--assets`/`--bars` is where the speedup numbers move (Node's worker spawn cost dominates the smallest sizes, which is honest and educational). examples/README.md gains the two new rows. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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.
|