Compare commits

..

16 Commits

Author SHA1 Message Date
kingchenc 5f0677d62d release: bump 0.9.1 -> 0.9.2 (#306) 2026-06-15 06:02:05 +02:00
kingchenc ce792cf8b8 fix: clear the last two build warnings (C# CA2255, WASM license files) (#304)
- C#: the [ModuleInitializer] that registers the DllImport resolver is the
  one legitimate library use of the attribute (a static ctor would run too
  late), so suppress CA2255 with a documented justification — the dotnet
  build is now warning-clean.
- WASM: add LICENSE-MIT + LICENSE-APACHE to bindings/wasm so wasm-pack stops
  warning about missing license files and the published npm package ships
  its license texts.

Full warnings audit: Rust, C, C++, Go, Java, Node, Python, C#, WASM all
build clean; R installs clean.
2026-06-15 05:26:06 +02:00
kingchenc 120b6ac265 docs+ci: surface 10-language golden verification (#303)
* docs+ci: surface 10-language golden verification; add WASM golden CI

- README: add C++ to the Quickstart list and state prominently that all 514
  indicators are replayed through all 10 languages and checked bit-for-bit
  against the Rust reference.
- CHANGELOG: document the cross-language golden suite, the Java and R C-ABI
  bool-marshalling fixes, the C# nullable directive and the live_binance rename.
- ci.yml: run the WASM golden suite (nodejs-target build + node --test); the
  C/C++ golden tests already run via the C-ABI job's ctest and the other
  bindings pick up their golden runners in their existing test suites.

* readme: add verified badge + prominent per-language throughput table

- Add the 'verified across 10 languages' badge to the badge row, linking to
  the FAQ that explains the cross-language golden parity.
- Surface a per-binding throughput table (the cost of each language's FFI
  boundary) so readers can pick a binding that keeps up with streaming hot
  loops — the cross-library benchmarks stay in BENCHMARKS.md.

* changelog: note the verified badge and per-binding throughput table

* docs: fix cross-language consistency (audit)

- docs/README.md: add the missing C++ quickstart link.
- README testing section: the golden parity now covers all 10 languages and
  all 514 indicators (was 'four C-ABI bindings, 7 archetype indicators').
- CHANGELOG: the live_binance rename also covers the C examples.
2026-06-15 05:07:52 +02:00
kingchenc 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
2026-06-15 04:48:51 +02:00
kingchenc de1112ea91 chore(examples): rename live_trading examples to live_binance (#301)
The examples stream a live Binance feed into the indicators and print signals;
they place no orders, so 'live_trading' overstated them and was inconsistent
with the C/Go/R examples already named live_binance. Rename the Python/Node/WASM
files to live_binance.* and update every reference, run command, header, and the
project-tree listings. Accurate use-case wording ('suitable for live trading
bots') and the risk disclaimers are left unchanged.
2026-06-15 03:41:19 +02:00
kingchenc 82d7479011 fix: de-duplicate four indicators by correcting their definitions (#300)
* fix(core): de-duplicate 3 indicators by correcting their definitions

Behavioral audit found these computed identically to another indicator:

- AverageDrawdown was the mean per-bar under-water fraction = PainIndex.
  Now the conventional average drawdown: mean of the maximum depths of the
  distinct drawdown episodes in the window.
- IntradayIntensity was a cumulative line = the A/D Line (Adl); its normalized
  form is the Chaikin Money Flow (Cmf). Now the raw per-bar Bostian intensity
  volume*(2c-h-l)/(h-l), distinct from both.
- AwesomeOscillatorHistogram was AO - SMA(AO, n) = AcceleratorOscillator. Now
  the AO momentum AO[t] - AO[t-lookback] (the histogram delta); the 3rd
  parameter is reinterpreted from sma_period to lookback (default 1).

Constructor signatures are unchanged, so the bindings keep their API. Core
unit tests rewritten with the new reference values; workspace tests + clippy
green. Binding value-tests and deep-dive docs are updated separately.

* fix(core): redefine AdOscillator as the A/D Oscillator (was a Wad duplicate)

AdOscillator computed the cumulative volume-free Williams A/D line, identical
to the Wad indicator. Redefine it as the Williams A/D *Oscillator*: the same
line minus its 13-bar SMA, so it oscillates around zero (mean-reverting) while
Wad stays the drifting cumulative line for divergence analysis. The canonical
name AdOscillator is now accurate; the trait name() becomes "ADOSC".

Constructor stays no-arg (internal 13-bar signal). Unit tests rewritten and
cross-checked against Wad - SMA(Wad, 13). The native bindings' "WilliamsAD"
alias is renamed to "ADOSC" separately.

* fix(bindings): rename WilliamsAD alias to ADOSC and update value tests

Follows the core de-duplication: the native bindings exposed the Williams A/D
line as 'WilliamsAD', which is now the A/D Oscillator. Rename the Python /
Node.js / WASM alias to 'ADOSC' (regenerated node index.js / index.d.ts) and
update the binding value-tests for the four redefined indicators
(AverageDrawdown episode mean, AwesomeOscillatorHistogram momentum warmup,
the Wad-line reference test now uses ta.Wad()). Python suite and node suite
both pass (pytest all green, node 584/584).

* docs: record indicator de-duplication in README and CHANGELOG

README volume family: 'Williams A/D' -> 'Williams A/D Oscillator', 'Intraday
Intensity Index' -> 'Intraday Intensity'. CHANGELOG [Unreleased] documents the
four redefinitions and the native WilliamsAD -> ADOSC rename as breaking.

* test(core): cover Default impl and drop dead match arm

Codecov flagged AdOscillator::default() (never exercised) and the unreachable
_ => panic!() arm in the AwesomeOscillatorHistogram test. Exercise Default in
the accessors test and rewrite the histogram check as an if-let, removing the
dead arm.
2026-06-15 03:41:15 +02:00
kingchenc a3950bf31b release: bump 0.9.0 -> 0.9.1 (#299) 2026-06-14 02:42:45 +02:00
kingchenc a0a7bc4e62 docs(readme): add a Requirements section with per-language supported versions (#298)
There was no single place listing the minimum supported version per language.
Add a Requirements table after Languages (Rust 1.86, Python 3.9, Node 20, WASM,
C99, C++14, .NET 8, Go 1.23, Java 22, R >= 2.10) linking to the full
Requirements page in the docs.
2026-06-14 01:09:02 +02:00
kingchenc 0c925aa9d5 feat(c-abi): expose warmup_period / is_ready across the C ABI bindings (#297)
* feat(c-abi): expose warmup_period / is_ready across the C ABI bindings

The C ABI hub exposed new/update/batch/reset/free per indicator but not the
Indicator::warmup_period / is_ready queries that the native (Python/Node/WASM)
bindings already had, so C/C#/Go/Java/R callers could not ask an indicator
whether it was warmed up without feeding it and watching for NaN.

Regenerated from the ScriptHelpers capi + language generators:
- bindings/c: wickra_<ind>_warmup_period (size_t) and wickra_<ind>_is_ready
  (bool) for every indicator (504; the 10 alt-chart bar builders are excluded
  by design). wickra.h regenerated via cbindgen (additive only).
- bindings/csharp: int WarmupPeriod() / bool IsReady() on each wrapper.
- bindings/go: WarmupPeriod() int / IsReady() bool.
- bindings/java: int warmupPeriod() / boolean isReady().
- bindings/r: C glue + registration; hand-written warmup_period() / is_ready()
  S3 generics in methods.R, plus NAMESPACE exports.

Tests: C-ABI Rust unit tests, the C examples/archetypes.c suite, and the C#,
Go, Java and R archetype suites all gain a warmup/is_ready transition check.

* build(go): sync vendored wickra.h with the C ABI header

The Go binding vendors bindings/c/include/wickra.h; refresh it with the new
warmup_period / is_ready declarations so the CI sync check passes.
2026-06-14 01:04:06 +02:00
kingchenc d9e6807ca0 fix: correct RelativeStrengthAB binding wrapper casing (#296)
The WASM and Node binding wrappers were named with the lowercase-b acronym
(WasmRelativeStrengthAb, RelativeStrengthAbNode) while the core type and every
other surface use RelativeStrengthAB. The published JS/WASM class name was
already correct via js_name/js_class, so this only aligns the internal Rust
identifiers and the auto-generated TypeScript type alias
(RelativeStrengthAbNode -> RelativeStrengthABNode). Runtime API unchanged.
2026-06-14 00:50:03 +02:00
kingchenc eb50ae4e90 deps(python): upgrade pyo3 + rust-numpy to 0.29, clear 2 advisories (#295)
rust-numpy 0.29 lifted its pyo3 ^0.28 pin, so the resolver can now select
pyo3 0.29. Bump both (kept as a pair) and drop the temporary not-affected
exceptions for RUSTSEC-2026-0176 and RUSTSEC-2026-0177 from deny.toml and
osv-scanner.toml — pyo3 0.29 fixes both advisories.

No public API change; the Python test suite passes unchanged (957 tests).
2026-06-14 00:36:52 +02:00
kingchenc 4a12f60a88 ci: test the Node binding on the 22/24 LTS (18/20 are EOL) (#294)
* ci: test the Node binding on the 22/24 LTS (18/20 are EOL)

Node 18 is EOL and 20 reaches EOL; move the binding CI matrix from [18, 20]
to [22, 24] (both LTS), bump the fixed Node setups in ci.yml/release.yml to 22,
and raise package.json engines to >= 20.

The N-API 8 binary is ABI-stable across Node versions, so no per-version build
is needed. The `npm test` script (`node --test __tests__/`) breaks on Node 22
— it resolves `__tests__` as a module — so switch to `node --test` (auto-
discovery). Verified locally on Node 22: build + 584 tests green.

* build(node): sync package-lock engines.node to >= 20

* ci(node): drop the __tests__/ path arg so node --test auto-discovers

On Node 22+, `node --test __tests__/` resolves the directory as a single
module and fails with one unrunnable subtest. `node --test` (no path)
auto-discovers every *.test.js under the package, matching the package.json
test script. Verified locally: 584 tests pass.
2026-06-14 00:23:36 +02:00
kingchenc 8003572d92 docs(core): add runnable rustdoc examples to 23 indicators (#293)
The fib_* / auto_fib / golden_pocket Fibonacci indicators, the harami_cross /
tristar / tower_top_bottom candlestick patterns and the td_* DeMark family
lacked the `/// # Example` runnable doctest that ARCHITECTURE.md requires of
every indicator. Add a minimal construct-and-feed example to each. The
swing-tracker helper `pattern_swing` is not an `Indicator` and is left out.
All 23 pass `cargo test --doc` (487 doctests green).
2026-06-14 00:09:07 +02:00
kingchenc db5bd5e028 ci: build the Java binding on JDK 25 LTS (22 is EOL) (#292)
Java 22 is a non-LTS that has reached end of life; bump the build JDK in
ci.yml and release.yml to the 25 LTS. The pom pins `maven.compiler.release`
to 22, so the emitted bytecode and the Java 22+ runtime floor (FFM API, final
since 22) are unchanged. Not 21 — FFM was only preview there.
2026-06-13 23:55:49 +02:00
kingchenc 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.
2026-06-13 23:35:29 +02:00
kingchenc d59cd44043 docs: standardise language naming + binding security sections (#290)
* docs: standardise language naming and add binding security sections

Canonical binding list everywhere: Rust, Python, Node.js, WASM, C, C++, C#,
Go, Java, R. Use C# (not .NET) as the language label, WASM (not WebAssembly)
in prose, and frame the C ABI as a hub rather than a list item.

- Bump stale indicator counts (200+ -> 514) and family count (sixteen ->
  twenty-four) in the Node/Python/WASM and docs READMEs.
- Add a short Security section to all eight binding READMEs.
- Relabel benchmark rows (C -> C / C++, C# / .NET -> C#).
- Fix the 'language stecker' wording in the C#/Go/R API intros.
- Documentation only; no code or public API changes.

* release.yml: extend install snippets and expose version output

Add the missing registry installs to the release body (dotnet, go, Gradle/
Maven Central, r-universe) alongside cargo/pip/npm, and expose a v-stripped
'version' output from the tag step for the Gradle coordinate. Also fix the
C-ABI language order in the assets note (C# before Go).

* release.yml: correct the release body (10 languages, all registries)

Reframe the tagline to '10 languages' (native Rust/Python/Node.js/WASM + a C
ABI hub for C, C++, C#, Go, Java, R) instead of '4 language registries', note
that C#/Java/Go/R publish to NuGet/Maven/Go/r-universe via their own jobs, and
tidy the Node.js label and the C-ABI hub list.
2026-06-13 23:34:24 +02:00
1165 changed files with 158444 additions and 592 deletions
+7
View File
@@ -32,3 +32,10 @@ bindings/r/src/Makevars.in text eol=lf
bindings/r/src/Makevars.win text eol=lf
bindings/r/src/wickra.c text eol=lf
# Golden fixtures are replayed byte-for-byte by every binding's parity test. Pin
# them to LF so a Windows `core.autocrlf=true` checkout doesn't rewrite them as
# CRLF — which silently broke the Node reader (`Number('inf\r')` is NaN, and a
# blank "no-bar" row gained a stray `\r`), failing only on Windows runners. The
# tolerant readers (Python `splitlines`/`float`) hid the same hazard.
testdata/golden/** text eol=lf
+2 -2
View File
@@ -30,9 +30,9 @@ assignees: ""
## Environment
- Wickra version:
- Language / binding: <!-- Rust crate / Python / Node / WASM / C ABI / C# (.NET) / Go / Java / R -->
- Language / binding: <!-- Rust crate / Python / Node.js / WASM / C / C++ / C# / Go / Java / R -->
- OS and architecture:
- Rust / Python / Node / .NET version (If relevant):
- Rust / Python / Node.js / .NET version (If relevant):
## Additional context
@@ -15,9 +15,9 @@ assignees: []
- [ ] Rust crate (`wickra`)
- [ ] Python (`pip install wickra`)
- [ ] Node.js (`npm install wickra`)
- [ ] WebAssembly
- [ ] WASM
- [ ] C ABI (`bindings/c`)
- [ ] C# / .NET (`Wickra` on NuGet)
- [ ] C# (`Wickra` on NuGet)
- [ ] Go (`bindings/go`)
- [ ] Java (`org.wickra:wickra` on Maven Central)
- [ ] R (`bindings/r`)
@@ -31,7 +31,7 @@ assignees: []
| Binding version | `e.g. python 0.4.2 / node 0.4.2` |
| OS / arch | `e.g. Windows 11 x86_64, Linux glibc` |
| Rust toolchain | `rustc --version` (If building from source) |
| Python / Node / .NET version | `python --version` / `node --version` / `dotnet --version` |
| Python / Node.js / .NET version | `python --version` / `node --version` / `dotnet --version` |
## Minimal reproducer
+1 -1
View File
@@ -26,7 +26,7 @@ assignees: ""
- [ ] Should be exposed in the Node binding
- [ ] Should be exposed in the WASM binding
- [ ] Should be exposed in the C ABI
- [ ] Should be exposed in the C# / .NET binding
- [ ] Should be exposed in the C# binding
- [ ] Should be exposed in the Go binding
- [ ] Should be exposed in the Java binding
- [ ] Should be exposed in the R binding
@@ -13,7 +13,7 @@ assignees: []
## Affected code path
- Indicator / API: `e.g. EMA.update`
- Binding: `Rust / Python / Node / Wasm / C ABI / C# (.NET) / Go / Java / R`
- Binding: `Rust / Python / Node.js / WASM / C / C++ / C# / Go / Java / R`
- Hot loop or one-shot call?
## Versions compared
+1 -1
View File
@@ -33,4 +33,4 @@ import wickra as ta
## Environment (Only if relevant)
- Wickra version: `e.g. 0.4.2`
- Binding: `Rust / Python / Node / Wasm / C ABI / C# (.NET) / Go / Java / R`
- Binding: `Rust / Python / Node.js / WASM / C / C++ / C# / Go / Java / R`
+1 -1
View File
@@ -22,7 +22,7 @@
- [ ] `cargo clippy --workspace --all-targets -- -D warnings` is clean.
- [ ] `cargo test --workspace` passes.
- [ ] New behaviour has tests; bug fixes have a regression test.
- [ ] Public API changes are mirrored in the Python / Node / WASM bindings, and the C ABI + C# + Go + Java + R bindings are regenerated
- [ ] Public API changes are mirrored in the Python / Node.js / WASM bindings, and the C ABI + C# + Go + Java + R bindings are regenerated
and their type stubs (If applicable).
- [ ] The relevant page on the [documentation site](https://docs.wickra.org)
and the `README.md` are updated (If applicable). Docs edits go to a
+2 -2
View File
@@ -22,9 +22,9 @@ Please fill in the sections below. Delete any that don't apply.
- [ ] Rust crate (`crates/wickra`)
- [ ] Python binding (`bindings/python`)
- [ ] Node.js binding (`bindings/node`)
- [ ] WebAssembly binding (`bindings/wasm`)
- [ ] WASM binding (`bindings/wasm`)
- [ ] C ABI (`bindings/c`)
- [ ] C# / .NET binding (`bindings/csharp`)
- [ ] C# binding (`bindings/csharp`)
- [ ] Go binding (`bindings/go`)
- [ ] Java binding (`bindings/java`)
- [ ] R binding (`bindings/r`)
+19 -12
View File
@@ -116,7 +116,7 @@ jobs:
continue-on-error: true
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "20"
node-version: "22"
- name: Wait before Node retry
if: steps.setup_node.outcome == 'failure'
@@ -129,7 +129,7 @@ jobs:
if: steps.setup_node.outcome == 'failure'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "20"
node-version: "22"
- name: Set up Python
id: setup_python
@@ -231,7 +231,7 @@ jobs:
continue-on-error: true
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "20"
node-version: "22"
- name: Wait before Node retry
if: steps.setup_node.outcome == 'failure'
@@ -244,7 +244,7 @@ jobs:
if: steps.setup_node.outcome == 'failure'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "20"
node-version: "22"
- name: Cache cargo
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
@@ -566,6 +566,12 @@ jobs:
test -f bindings/wasm/pkg/wickra_wasm_bg.wasm
test -f bindings/wasm/pkg/wickra_wasm.d.ts
- name: Build WASM package (nodejs target) for the golden suite
run: wasm-pack build bindings/wasm --target nodejs --release --out-dir pkg
- name: Golden parity — all 514 indicators vs the Rust reference
run: node --test bindings/wasm/tests/golden.test.js
node:
name: Node ${{ matrix.node-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
@@ -574,7 +580,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: ["18", "20"]
node-version: ["22", "24"]
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
@@ -637,7 +643,7 @@ jobs:
with:
timeout_minutes: 6
max_attempts: 2
command: cd bindings/node && node --test __tests__/
command: cd bindings/node && node --test
shell: bash
c-abi:
@@ -975,18 +981,19 @@ jobs:
# The binding links the C ABI hub at runtime through the Java FFM API; build
# it first so WickraNative's development fallback finds
# target/release/wickra.{so,dylib,dll}. JDK 22+ is required for the final FFM
# API, so it is installed explicitly (runners ship 17/21).
# target/release/wickra.{so,dylib,dll}. The FFM API is final since JDK 22; we
# build on the 25 LTS (22 is EOL; the pom pins bytecode to release 22 so the
# runtime floor stays Java 22). Installed explicitly (runners ship 17/21).
- name: Build the C ABI library
run: cargo build -p wickra-c --release
- name: Set up JDK 22
- name: Set up JDK 25
id: setup-java
continue-on-error: true
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: temurin
java-version: "22"
java-version: "25"
cache: maven
- name: Retry JDK setup (CDN flake)
@@ -996,12 +1003,12 @@ jobs:
echo "::warning::setup-java failed (likely CDN flake), waiting 30s before retry..."
sleep 30
- name: Set up JDK 22 (retry)
- name: Set up JDK 25 (retry)
if: steps.setup-java.outcome == 'failure'
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: temurin
java-version: "22"
java-version: "25"
cache: maven
- name: Java info
+31 -13
View File
@@ -258,7 +258,7 @@ jobs:
continue-on-error: true
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "20"
node-version: "22"
- name: Wait before Node retry
if: steps.setup_node.outcome == 'failure'
@@ -271,7 +271,7 @@ jobs:
if: steps.setup_node.outcome == 'failure'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "20"
node-version: "22"
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27
with:
@@ -319,7 +319,7 @@ jobs:
continue-on-error: true
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "20"
node-version: "22"
registry-url: "https://registry.npmjs.org"
- name: Wait before Node retry
@@ -333,7 +333,7 @@ jobs:
if: steps.setup_node.outcome == 'failure'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "20"
node-version: "22"
registry-url: "https://registry.npmjs.org"
- name: Install Node deps
@@ -488,7 +488,7 @@ jobs:
continue-on-error: true
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "20"
node-version: "22"
registry-url: "https://registry.npmjs.org"
- name: Wait before Node retry
@@ -502,7 +502,7 @@ jobs:
if: steps.setup_node.outcome == 'failure'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "20"
node-version: "22"
registry-url: "https://registry.npmjs.org"
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27
@@ -757,11 +757,11 @@ jobs:
# setup-java writes a settings.xml with the 'central' server credentials
# (mapped from the env vars below) and imports the GPG signing key.
- name: Set up JDK 22 + Maven Central credentials + GPG
- name: Set up JDK 25 + Maven Central credentials + GPG
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: temurin
java-version: "22"
java-version: "25"
server-id: central
server-username: CENTRAL_USERNAME
server-password: CENTRAL_PASSWORD
@@ -876,6 +876,7 @@ jobs:
# bundle to this same release without re-resolving it.
outputs:
tag: ${{ steps.tag.outputs.tag }}
version: ${{ steps.tag.outputs.version }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
@@ -904,6 +905,7 @@ jobs:
exit 1
fi
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "version=${tag#v}" >> "$GITHUB_OUTPUT"
echo "::notice::attaching assets to release $tag"
- name: Download all build artifacts
@@ -941,7 +943,7 @@ jobs:
# the provenance bundle is attached (P24, immutability-ready).
draft: true
body: |
Wickra ${{ github.ref_name }} — streaming-first technical indicators across 4 language registries plus a C ABI.
Wickra ${{ github.ref_name }} — streaming-first technical indicators for 10 languages: native Rust, Python, Node.js, WASM plus a C ABI hub for C, C++, C#, Go, Java, R.
### Install
@@ -950,21 +952,37 @@ jobs:
pip install wickra
npm install wickra
npm install wickra-wasm
dotnet add package Wickra
go get github.com/wickra-lib/wickra-go
```
Java (Gradle, Maven Central):
```gradle
implementation("org.wickra:wickra:${{ steps.tag.outputs.version }}")
```
R (r-universe):
```r
install.packages("wickra", repos = "https://wickra-lib.r-universe.dev")
```
### Attached assets
Pre-built artefacts for every supported platform — the same files that
were uploaded to crates.io, PyPI, and npm by this workflow run.
Pre-built artefacts for every supported platform — the same files this
workflow run published to crates.io, PyPI, and npm. C# ships to NuGet,
Java to Maven Central, Go to the wickra-go module, and R to r-universe
via their own release jobs.
- `*.whl` / `wickra-*.tar.gz` — Python wheels + sdist (5 platforms, ABI3 ≥ 3.9)
- `wickra.*.node` — native Node bindings (linux-x64-gnu, darwin-x64,
- `wickra.*.node` — native Node.js bindings (linux-x64-gnu, darwin-x64,
darwin-arm64, win32-x64-msvc)
- `wickra-*.tgz` — npm-pack tarballs (main package + per-platform subpackages + WASM)
- `*.crate` — cargo source crates (wickra-core, wickra-data, wickra)
- `wickra-c-<target>.tar.gz` — C ABI: `include/wickra.h` + `wickra.hpp`
and the cdylib/staticlib per target (linux/macos/windows × x64/arm64),
the hub for C / C++ / Go / C# / Java / R
the hub for C, C++, C#, Go, Java and R
### Auto-generated changelog
+5 -5
View File
@@ -31,7 +31,7 @@ or replace lives behind a separate crate boundary.
┌────────────┼────────────┬────────────────┐
│ │ │ │
┌──▼───────┐ ┌──▼───────┐ ┌──▼───────────┐ ┌──▼──────────────────┐
│ Python │ │ Node │ │ WASM │ │ C ABI (cbindgen) │
│ Python │ │ Node.js │ │ WASM │ │ C ABI (cbindgen) │
│ (PyO3) │ │ (napi-rs)│ │(wasm-bindgen)│ │ cdylib + header │
└──────────┘ └──────────┘ └──────────────┘ └─────────┬───────────┘
│ linked by
@@ -41,11 +41,11 @@ or replace lives behind a separate crate boundary.
└─────────────────────┘
```
Python, Node and WASM are *native* Rust bindings (PyO3 / napi-rs /
Python, Node.js and WASM are *native* Rust bindings (PyO3 / napi-rs /
wasm-bindgen). The C ABI is the *hub* every other C-capable language links
against: it builds to a `cdylib`/`staticlib` plus a generated `wickra.h`, and
downstream languages link that one artifact rather than each re-wrapping the
core. C and C++ link it directly; the **C# / .NET** binding (`bindings/csharp`,
core. C and C++ link it directly; the **C#** binding (`bindings/csharp`,
on NuGet), the **Go** binding (`bindings/go`, cgo), the **R** binding
(`bindings/r`, `.Call`) and the **Java** binding (`bindings/java`, the Java FFM
API / Panama, on Maven Central) are all generated from `wickra.h`.
@@ -57,7 +57,7 @@ API / Panama, on Maven Central) are all generated from `wickra.h`.
| `wickra-data` | `crates/wickra-data` | CSV reader, tick aggregator, resampler, live exchange feeds (feature-gated) | `tokio`, `tokio-tungstenite` (live), `serde_json` |
| `wickra-python` | `bindings/python` | `_wickra` PyO3 module + Python package | `pyo3`, `numpy`, depends on `wickra-core` |
| `wickra-node` | `bindings/node` | NAPI-RS native binding | `napi`, depends on `wickra-core` |
| `wickra-wasm` | `bindings/wasm` | WebAssembly binding | `wasm-bindgen`, depends on `wickra-core` |
| `wickra-wasm` | `bindings/wasm` | WASM binding | `wasm-bindgen`, depends on `wickra-core` |
| `wickra-c` | `bindings/c` | C ABI hub — `cdylib`/`staticlib` + generated `wickra.h` (cbindgen) | depends on `wickra-core` |
| `wickra-examples` | `examples/rust` | runnable binary examples | depends on `wickra`, `wickra-data` |
@@ -227,7 +227,7 @@ A handful of indicators need care beyond naive accumulation:
A typical full-stack call sequence for a Python live-trading example:
```
[ Python: live_trading.py ]
[ Python: live_binance.py ]
[ binance.AsyncClient WebSocket ] ──── wickra_data live feed ───┐
+6 -6
View File
@@ -98,8 +98,8 @@ python -m benchmarks.compare_libraries
## 3. Per-binding throughput — the cost of the boundary
The sections above compare Wickra against other libraries, which only exists for
Python and Rust (there is no comparable streaming TA library for C, C#, Go, Java,
R or WebAssembly to benchmark against). Every binding calls the **same** Rust
Python and Rust (there is no comparable streaming TA library for C, C++, C#, Go, Java,
R or WASM to benchmark against). Every binding calls the **same** Rust
core, so these per-binding benchmarks are **not** a speed claim and **not** a
cross-library ratio — they document the raw cost of crossing each language's FFI
boundary, in million updates per second (Mupd/s).
@@ -127,12 +127,12 @@ AMD Ryzen 9 9950X):
| Target | streaming (Mupd/s) | batch (Mupd/s) |
|----------------------|-------------------:|---------------:|
| Rust core (no FFI) | 391 | 500 |
| C | 383 | 330 |
| C# / .NET | 337 | 244 |
| C / C++ | 383 | 330 |
| C# | 337 | 244 |
| Python | 33 | 488 |
| Java | 28 | 175 |
| Go | 24 | 400 |
| WebAssembly | 19 | 167 |
| WASM | 19 | 167 |
| Node.js | 17 | 10 |
| R | 0.1 | 193 |
@@ -160,7 +160,7 @@ node bindings/node/benchmarks/throughput.js # native napi-
cargo build -p wickra-c --release # the C ABI hub
cmake -S bindings/c/benchmarks -B build/cbench && cmake --build build/cbench \
&& ./build/cbench/throughput # raw C ABI
dotnet run -c Release --project bindings/csharp/benchmarks # C# / .NET (P/Invoke)
dotnet run -c Release --project bindings/csharp/benchmarks # C# (P/Invoke)
( cd bindings/go/benchmarks && go run . ) # Go (cgo)
mvn -q -f bindings/java install -DskipTests \
&& mvn -q -f bindings/java/benchmarks exec:exec -Dexec.mainClass=org.wickra.benchmarks.Throughput
+107 -1
View File
@@ -7,6 +7,110 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.9.2] - 2026-06-15
### Added
- **Cross-language golden parity for all 514 indicators across all 10 languages.**
A new `gen_golden` reference emits a deterministic OHLCV input series plus the
Rust output of every one of the 514 indicators to `testdata/golden/`. Each
binding now replays that shared input and is checked **bit-for-bit against the
Rust reference**, covering every archetype (scalar, multi-output, pairwise,
derivatives-tick, cross-section, order-book, trade, profile, alt-chart bars,
footprint):
- Python, Node.js, Java and R via reflection-driven runners.
- Go, C# and C/C++ via generated dispatch (`golden_all_test.go`,
`GoldenAllTests.g.cs`, `examples/c/golden_test.c` compiled as both C and C++).
- WASM via a `node --test` runner over the nodejs-target build.
- CI now runs the WASM golden suite; the C/C++ golden tests run as `ctest`
targets in the existing C-ABI job, and the Python/Node/Go/C#/Java/R suites pick
up their golden runners automatically.
- **README:** a "verified across 10 languages" badge (linking to the FAQ that
explains the cross-language golden parity) and a per-binding throughput table so
readers can pick a binding by its streaming FFI cost.
### Fixed
- **Java binding marshalled C ABI `bool` parameters incorrectly.** The
cross-section state flags (`newHigh`, `newLow`, `aboveMa`, `onBuySignal`) were
allocated as `JAVA_DOUBLE` arrays and passed to `const bool*` parameters, so the
native side read the low byte of each 8-byte double and saw every flag as
`false` (affecting e.g. `NewHighsNewLows`, `HighLowIndex`, `BullishPercentIndex`,
`PercentAboveMa`). They are now packed into a real `bool` buffer. `MacdExt`'s
`MaType` arguments are now passed as `byte` to match the `uint8_t` downcall.
- **R binding marshalled C ABI `bool` flags incorrectly.** `(bool *)REAL(x)`
reinterpreted the 8-byte doubles as 1-byte bools across the 15 cross-section
update wrappers, reading every flag as `false`; the flags are now converted into
a real C `bool` buffer.
- C# binding: added the `#nullable enable` directive the generated
`Indicators.g.cs` requires, clearing four `CS8669` warnings.
### Changed
- Renamed the `live_trading` examples to `live_binance` across the Python, Node.js,
WASM and C examples — they poll Binance market data, they do not place trades.
- **Breaking — de-duplicated four indicators that computed identically to another
one.** Each is now its own distinct, correctly-defined indicator (the catalogue
stays at the same count):
- `AverageDrawdown` now reports the mean of the maximum depths of the distinct
drawdown episodes in the window (previously the per-bar mean under-water
fraction, which equalled `PainIndex`).
- `IntradayIntensity` now reports the raw per-bar Bostian intensity
`volume * (2*close high low) / (high low)` (previously a cumulative line
that equalled the A/D Line `Adl`; its normalized form is `Cmf`).
- `AwesomeOscillatorHistogram` now reports the AO momentum
`AO[t] AO[tlookback]`; its third parameter is the momentum `lookback`
(default 1) instead of an SMA period (the old `AO SMA(AO, n)` equalled
`AcceleratorOscillator`).
- `AdOscillator` is now the Williams **A/D Oscillator** (`WAD SMA(WAD, 13)`),
distinct from the cumulative Williams A/D line `Wad`. Its native (Python /
Node.js / WASM) alias is renamed **`WilliamsAD``ADOSC`**.
## [0.9.1] - 2026-06-14
### Added
- C ABI hub: every indicator now exposes `wickra_<ind>_warmup_period` and
`wickra_<ind>_is_ready`, closing the gap with the native bindings (which
already had them). The C-ABI languages surface them idiomatically: C# `int
WarmupPeriod()` / `bool IsReady()`, Go `WarmupPeriod()` / `IsReady()`, Java
`int warmupPeriod()` / `boolean isReady()`, and R `warmup_period()` /
`is_ready()` generics. The alt-chart bar builders are excluded by design (a
candle can complete 0..n bars, so they have no warmup).
- Runnable rustdoc examples for 23 indicators that previously lacked one.
- A Requirements reference documenting the minimum supported version per
language — a new page in the documentation site plus README, marketing-site
and organization-profile sections.
### Changed
- Raised the minimum Node.js version to 20 — Node 18 reached end-of-life. The
prebuilt N-API addon is now tested on the active LTS lines (22 and 24).
- The Java binding now builds on the JDK 25 LTS in CI (JDK 22 reached
end-of-life); the published bytecode still targets Java 22, so the runtime
requirement is unchanged.
- Standardised programming-language naming and ordering across all docs, READMEs,
the documentation site, marketing site, organization profile and GitHub
repository descriptions. Canonical list:
`Rust, Python, Node.js, WASM, C, C++, C#, Go, Java, R`. Uses C# (not .NET) as
the language label, lists C and C++ separately, prefers `Node.js` and `WASM` in
prose, and frames the C ABI as a hub (`C ABI hub → …`) rather than a
language-list entry. Documentation only — no code or public API changes.
- Python binding: upgraded `pyo3` and `rust-numpy` from 0.28 to 0.29. No public
API changes; the full test suite passes unchanged.
### Fixed
- Corrected the internal casing of the `RelativeStrengthAB` binding wrappers,
which used `...Ab` (`WasmRelativeStrengthAb` in the WASM crate,
`RelativeStrengthAbNode` in the Node crate) while every other surface uses the
acronym `AB`. The published JS/WASM class name was already `RelativeStrengthAB`
(set via `js_name`/`js_class`), so the runtime API is unchanged; the only
visible change is the auto-generated TypeScript type alias, renamed
`RelativeStrengthAbNode``RelativeStrengthABNode` in `index.d.ts`.
### Security
- Resolved the pyo3 advisories RUSTSEC-2026-0176 (out-of-bounds read in
`PyList`/`PyTuple` `nth`/`nth_back`) and RUSTSEC-2026-0177 (missing `Sync`
bound on `PyCFunction::new_closure`) by upgrading to pyo3 0.29, which fixes
both. The upgrade was previously blocked upstream by rust-numpy 0.28 pinning
pyo3 `^0.28`; rust-numpy 0.29 lifts that pin. The not-affected exceptions are
removed from `deny.toml` and `osv-scanner.toml`.
## [0.9.0] - 2026-06-13
Maintenance release: Java build-dependency updates and CI/Dependabot
@@ -1615,7 +1719,9 @@ public API changes.
optional Binance live feed.
- Bindings for Python, Node.js, and WebAssembly.
[Unreleased]: https://github.com/wickra-lib/wickra/compare/v0.9.0...HEAD
[Unreleased]: https://github.com/wickra-lib/wickra/compare/v0.9.2...HEAD
[0.9.2]: https://github.com/wickra-lib/wickra/compare/v0.9.1...v0.9.2
[0.9.1]: https://github.com/wickra-lib/wickra/compare/v0.9.0...v0.9.1
[0.9.0]: https://github.com/wickra-lib/wickra/compare/v0.8.9...v0.9.0
[0.8.9]: https://github.com/wickra-lib/wickra/compare/v0.8.8...v0.8.9
[0.8.8]: https://github.com/wickra-lib/wickra/compare/v0.8.7...v0.8.8
+2 -2
View File
@@ -22,7 +22,7 @@ licensed as above, without any additional terms or conditions.
| `bindings/node` | napi-rs bindings (`wickra` on npm). |
| `bindings/wasm` | wasm-bindgen bindings (`wickra-wasm` on npm). |
| `bindings/c` | C ABI — `cdylib` + `staticlib` + generated `include/wickra.h`. The hub for C / C++ and any C-capable language. |
| `bindings/csharp` | .NET binding over the C ABI (`Wickra` on NuGet) — `[LibraryImport]` P/Invoke generated from `wickra.h`. |
| `bindings/csharp` | C# binding over the C ABI (`Wickra` on NuGet) — `[LibraryImport]` P/Invoke generated from `wickra.h`. |
| `bindings/go` | Go binding over the C ABI via cgo (module tag `bindings/go/vX.Y.Z`) — wrappers generated from `wickra.h`. |
| `bindings/r` | R binding over the C ABI via `.Call` (R package) — C glue + R wrappers generated from `wickra.h`. |
| `bindings/java` | Java binding over the C ABI via the FFM API (Panama, Maven Central) — wrappers generated from `wickra.h`. |
@@ -107,7 +107,7 @@ installed. Dependabot also keeps the `.github/requirements` pins current.
- **Streaming parity.** An indicator's `batch` output must equal the sequence
of `update` calls.
- **Bindings.** A change to a public indicator API must be mirrored across the
Python, Node, and WASM bindings, including their type stubs / `.d.ts`. The C ABI
Python, Node.js, and WASM bindings, including their type stubs / `.d.ts`. The C ABI
(`bindings/c`) is generated from the core, so regenerate it from the core and
commit `src/lib.rs` + `include/wickra.h`. The C# binding (`bindings/csharp`) is
generated from `wickra.h`, so regenerate and commit its `Generated/*.g.cs` too.
Generated
+21 -22
View File
@@ -945,9 +945,9 @@ dependencies = [
[[package]]
name = "numpy"
version = "0.28.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778da78c64ddc928ebf5ad9df5edf0789410ff3bdbf3619aed51cd789a6af1e2"
checksum = "6a5b15d63a5ff39e378daed0e1340d3a5964703ea9712eb09a0dc66fade996f4"
dependencies = [
"libc",
"ndarray",
@@ -1152,9 +1152,9 @@ dependencies = [
[[package]]
name = "pyo3"
version = "0.28.3"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12"
checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
dependencies = [
"libc",
"once_cell",
@@ -1166,18 +1166,18 @@ dependencies = [
[[package]]
name = "pyo3-build-config"
version = "0.28.3"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e"
checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
dependencies = [
"target-lexicon",
]
[[package]]
name = "pyo3-ffi"
version = "0.28.3"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e"
checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
dependencies = [
"libc",
"pyo3-build-config",
@@ -1185,9 +1185,9 @@ dependencies = [
[[package]]
name = "pyo3-macros"
version = "0.28.3"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813"
checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
@@ -1197,13 +1197,12 @@ dependencies = [
[[package]]
name = "pyo3-macros-backend"
version = "0.28.3"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb"
checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
dependencies = [
"heck",
"proc-macro2",
"pyo3-build-config",
"quote",
"syn",
]
@@ -1944,7 +1943,7 @@ dependencies = [
[[package]]
name = "wickra"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"approx",
"criterion",
@@ -1955,7 +1954,7 @@ dependencies = [
[[package]]
name = "wickra-bench"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"criterion",
"kand",
@@ -1967,14 +1966,14 @@ dependencies = [
[[package]]
name = "wickra-c"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"wickra-core",
]
[[package]]
name = "wickra-core"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"approx",
"proptest",
@@ -1984,7 +1983,7 @@ dependencies = [
[[package]]
name = "wickra-data"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"approx",
"csv",
@@ -2001,7 +2000,7 @@ dependencies = [
[[package]]
name = "wickra-examples"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"serde_json",
"tokio",
@@ -2011,7 +2010,7 @@ dependencies = [
[[package]]
name = "wickra-node"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"napi",
"napi-build",
@@ -2021,7 +2020,7 @@ dependencies = [
[[package]]
name = "wickra-python"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"numpy",
"pyo3",
@@ -2030,7 +2029,7 @@ dependencies = [
[[package]]
name = "wickra-wasm"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"console_error_panic_hook",
"js-sys",
+4 -4
View File
@@ -14,7 +14,7 @@ members = [
exclude = ["fuzz"]
[workspace.package]
version = "0.9.0"
version = "0.9.2"
authors = ["kingchenc <support@wickra.org>"]
edition = "2021"
rust-version = "1.86"
@@ -26,7 +26,7 @@ keywords = ["finance", "trading", "indicators", "technical-analysis", "ta"]
categories = ["finance", "mathematics", "science"]
[workspace.dependencies]
wickra-core = { path = "crates/wickra-core", version = "0.9.0" }
wickra-core = { path = "crates/wickra-core", version = "0.9.2" }
thiserror = "2"
rayon = "1.10"
@@ -37,8 +37,8 @@ approx = "0.5"
criterion = { version = "0.8", features = ["html_reports"] }
# Python binding
pyo3 = { version = "0.28", features = ["extension-module", "abi3-py39"] }
numpy = "0.28"
pyo3 = { version = "0.29", features = ["extension-module", "abi3-py39"] }
numpy = "0.29"
[workspace.lints.rust]
unsafe_code = "forbid"
+80 -20
View File
@@ -18,12 +18,13 @@
[![OpenSSF Best Practices](https://raw.githubusercontent.com/wickra-lib/.github/main/profile/badges/best-practices.svg)](https://www.bestpractices.dev/projects/13094)
[![Build provenance](https://raw.githubusercontent.com/wickra-lib/.github/main/profile/badges/provenance.svg)](https://github.com/wickra-lib/wickra/attestations)
[![Docs](https://raw.githubusercontent.com/wickra-lib/.github/main/profile/badges/docs.svg)](https://docs.wickra.org)
[![Verified across 10 languages](https://raw.githubusercontent.com/wickra-lib/.github/main/profile/badges/verified.svg)](https://docs.wickra.org/FAQ#do-all-the-language-bindings-compute-the-same-values)
**Streaming-first technical indicators. Install with `pip install wickra` — no system dependencies.**
Wickra is a multi-language technical-analysis library with a Rust core and
native bindings for Python, Node.js and WebAssembly, plus a C ABI that C, C++,
C# / .NET, Go, Java, R and any other C-capable language links against. Every indicator is a
native bindings for Python, Node.js and WASM, plus a C ABI that C, C++,
C#, Go, Java, R and any other C-capable language links against. Every indicator is a
state machine that updates in O(1) per new data point, so live trading bots and
historical backtests share the exact same implementation.
@@ -53,6 +54,7 @@ Full documentation lives at **[docs.wickra.org](https://docs.wickra.org)**:
[Node](https://docs.wickra.org/Quickstart-Node),
[WASM](https://docs.wickra.org/Quickstart-WASM),
[C](https://docs.wickra.org/Quickstart-C),
[C++](https://docs.wickra.org/Quickstart-C),
[C#](https://docs.wickra.org/Quickstart-CSharp),
[Go](https://docs.wickra.org/Quickstart-Go),
[Java](https://docs.wickra.org/Quickstart-Java),
@@ -81,14 +83,19 @@ times to get there.
breadth, Renko/Kagi/Point&Figure bars, Ehlers DSP cycles, risk/performance
metrics — every single one updating in **O(1) per tick**. TA-Lib ships ~150 and
none of them stream.
- **One Rust core, five first-class targets.** Native **Python · Node.js ·
WebAssembly · Rust** plus a **C ABI** for C / C++, C# / .NET, Go, Java, R and any other C-capable language —
- **One Rust core, five first-class targets.** Native **Rust · Python · Node.js ·
WASM** plus a **C ABI** for C, C++, C#, Go, Java, R and any other C-capable language —
identical math, identical results, zero per-language reimplementation and zero
GIL bottleneck.
- **Correct by construction, not by hope.** Every `update` validates its input,
runs a real warmup, and returns an `Option` so a single bad tick can't silently
poison state. `batch == streaming` is **bit-exact, fuzzed and 100 %-line-covered
for all 514 indicators**.
- **Identical across every language — proven, not promised.** All 514 indicators
are replayed through **all 10 languages** (Rust · Python · Node.js · WASM · C ·
C++ · C# · Go · Java · R) and checked **bit-for-bit against the Rust reference**
via shared golden fixtures in CI. The math is verifiably the same everywhere —
this very check caught and fixed two real cross-language marshalling bugs.
- **Orders of magnitude faster where it counts.** In streaming Wickra is **1156×**
faster than the only other incremental peer and **thousands of times** faster
than recompute-on-every-tick libraries. On batch it wins several rows outright
@@ -106,7 +113,7 @@ Every other library forces one of those compromises. Wickra doesn't:
| Library | Install | Streaming | Languages | Indicators | Active |
|------------------|-------------|-------------|-----------------------------|-----------:|--------|
| **★&nbsp;Wickra**| **clean** | **yes, O(1)** | **Rust · Python · Node · WASM · C · C# · Go · Java · R** | **514** | **yes** |
| **★&nbsp;Wickra**| **clean** | **yes, O(1)** | **Rust · Python · Node.js · WASM · C · C++ · C# · Go · Java · R** | **514** | **yes** |
| kand | clean | yes | Python · WASM · Rust | ~60 | yes |
| ta-rs | clean | yes | Rust only | ~30 | stale |
| yata | clean | partial | Rust only | ~35 | yes |
@@ -137,12 +144,38 @@ elsewhere for `None`-warmup, NaN-safety and bit-exact `batch == streaming`.
Full tables (Rust + Python, streaming + batch) and how to reproduce them live in
**[BENCHMARKS.md](BENCHMARKS.md)**.
### Pick your language with eyes open — per-binding throughput
Every binding calls the **same** Rust core, so this is **not** a speed claim — it
is the raw cost of crossing each language's FFI boundary (`SMA(20)`, 200 000 bars,
Ryzen 9 9950X, million updates/sec). **Batch is near-core everywhere; streaming is
where the boundary shows** — so if you stream tick-by-tick, the table tells you
which binding keeps up and which to avoid for hot loops.
| Language | streaming (Mupd/s) | batch (Mupd/s) |
|-----------------|-------------------:|---------------:|
| Rust (no FFI) | 391 | 500 |
| C / C++ | 383 | 330 |
| C# | 337 | 244 |
| Python | 33 | 488 |
| Java | 28 | 175 |
| Go | 24 | 400 |
| WASM | 19 | 167 |
| Node.js | 17 | 10 |
| R | 0.1 | 193 |
All ten share one verified implementation (see the verification badge above), so
the *numbers* differ but the *values* are bit-for-bit identical. Methodology and
the per-indicator breakdown are in [BENCHMARKS.md](BENCHMARKS.md#3-per-binding-throughput--the-cost-of-the-boundary).
## Indicators
514 streaming-first indicators across twenty-four families. Every one passes the
`batch == streaming` equivalence test, reference-value tests, and reset
semantics tests. Each has a per-indicator deep dive (formula, parameters,
warmup) at [docs.wickra.org](https://docs.wickra.org/Indicators-Overview).
semantics tests — and is replayed through **all 10 languages** and checked
bit-for-bit against the Rust reference (golden fixtures, in CI). Each has a
per-indicator deep dive (formula, parameters, warmup) at
[docs.wickra.org](https://docs.wickra.org/Indicators-Overview).
| Family | Indicators |
|--------|-----------|
@@ -153,7 +186,7 @@ warmup) at [docs.wickra.org](https://docs.wickra.org/Indicators-Overview).
| Volatility & Bands | ATR, Bollinger Bands, Keltner Channels, Donchian Channels, NATR, StdDev, Ulcer Index, Historical Volatility, Bollinger Bandwidth, %B, True Range, Chaikin Volatility, RVI (Relative Volatility Index), Parkinson Volatility, Garman-Klass Volatility, Rogers-Satchell Volatility, Yang-Zhang Volatility, Volatility Cone |
| Bands & Channels | MA Envelope, Acceleration Bands, STARC Bands, ATR Bands, Hurst Channel, LinReg Channel, Standard Error Bands, Double Bollinger Bands, TTM Squeeze, Fractal Chaos Bands, VWAP StdDev Bands, Quartile Bands, Bomar Bands, Median Channel, Projection Bands, Projection Oscillator |
| Trailing Stops | Parabolic SAR, Parabolic SAR Extended (SAREXT), SuperTrend, Chandelier Exit, Chande Kroll Stop, ATR Trailing Stop, HiLo Activator, Volty Stop, Yo-Yo Exit, Donchian Channel Stop, Percentage Trailing Stop, Step Trailing Stop, Renko Trailing Stop, Kase DevStop, Elder SafeZone, ATR Ratchet, NRTR, Time-Based Stop, Modified MA Stop |
| Volume | OBV, VWAP (cumulative + rolling), ADL, Volume-Price Trend, Chaikin Money Flow, Chaikin Oscillator, Force Index, Ease of Movement, Klinger Volume Oscillator, Volume Oscillator, NVI, PVI, Williams A/D, Anchored VWAP, Demand Index, TSV, VZO, Market Facilitation Index, Volume RSI, Williams Accumulation/Distribution, Twiggs Money Flow, Trade Volume Index, Intraday Intensity Index, Better Volume, Volume-Weighted MACD |
| Volume | OBV, VWAP (cumulative + rolling), ADL, Volume-Price Trend, Chaikin Money Flow, Chaikin Oscillator, Force Index, Ease of Movement, Klinger Volume Oscillator, Volume Oscillator, NVI, PVI, Williams A/D Oscillator, Anchored VWAP, Demand Index, TSV, VZO, Market Facilitation Index, Volume RSI, Williams Accumulation/Distribution, Twiggs Money Flow, Trade Volume Index, Intraday Intensity, Better Volume, Volume-Weighted MACD |
| Price Statistics | Typical Price, Median Price, Weighted Close, Linear Regression, Linear Regression Slope, Z-Score, Linear Regression Angle, Variance, Coefficient of Variation, Skewness, Kurtosis, Standard Error, Detrended StdDev, R², Median Absolute Deviation, Autocorrelation, Hurst Exponent, Pearson Correlation, Beta, Pairwise Beta, Pair Spread Z-Score, Lead-Lag Cross-Correlation, Cointegration, Relative Strength A-vs-B, Spearman Correlation, Mid Price, Mid Point, Average Price, Linear Regression Intercept, Time Series Forecast, Rolling Correlation, Rolling Covariance, OU Half-Life, Spread Hurst, Distance SSD, Beta-Neutral Spread, Variance Ratio, Granger Causality, Kalman Hedge Ratio, Spread Bollinger Bands, Spread AR(1) Coefficient, Jarque-Bera, Rolling Min-Max Scaler, Shannon Entropy, Sample Entropy, Kendall Tau |
| Ehlers / Cycle (DSP) | MAMA, FAMA, Fisher Transform, Inverse Fisher Transform, SuperSmoother, Hilbert Dominant Cycle, Hilbert Phasor, Hilbert DC Phase, Hilbert Trend Mode, Sine Wave, Decycler, Decycler Oscillator, Roofing Filter, Center of Gravity, Cybernetic Cycle, Adaptive Cycle, Empirical Mode Decomposition, Ehlers Stochastic, Instantaneous Trendline, Highpass Filter, Reflex, Trendflex, Correlation Trend Indicator, Adaptive RSI, Universal Oscillator, Adaptive CCI, Bandpass Filter, Even Better Sinewave, Autocorrelation Periodogram |
| Pivots & S/R | Classic Pivots, Fibonacci Pivots, Camarilla, Woodie Pivots, DeMark Pivots, Williams Fractals, ZigZag, Central Pivot Range, Murrey Math Lines, Andrews Pitchfork, Volume-Weighted Support/Resistance, Pivot Reversal |
@@ -190,7 +223,7 @@ it — regenerate from the core).
| Browser / WASM | `npm install wickra-wasm` | `examples/wasm/index.html` |
| Rust | `cargo add wickra` | `examples/rust/src/bin/backtest.rs` |
| C / C++ (C ABI) | header + library, see [`bindings/c`](bindings/c) | `examples/c/streaming.c` |
| C# / .NET (C ABI) | `dotnet add package Wickra`, see [`bindings/csharp`](bindings/csharp) | `examples/csharp/streaming` |
| C# (C ABI) | `dotnet add package Wickra`, see [`bindings/csharp`](bindings/csharp) | `examples/csharp/streaming` |
| Go (cgo, C ABI) | `go get github.com/wickra-lib/wickra/bindings/go`, see [`bindings/go`](bindings/go) | `examples/go/streaming` |
| Java (FFM, C ABI) | Maven Central `org.wickra:wickra`, see [`bindings/java`](bindings/java) | `examples/java` (`Streaming`) |
| R (`.Call`, C ABI) | `R CMD INSTALL bindings/r`, see [`bindings/r`](bindings/r) | `examples/r/streaming.R` |
@@ -202,6 +235,29 @@ The wickra-core crate is `unsafe`-forbidden, so the native bindings are
memory-safe end to end. The C ABI runs the same safe core; only its thin FFI
boundary uses `unsafe`, and the caller owns handle lifetimes (`_new` / `_free`).
## Requirements
The minimum supported version per language. Prebuilt packages (Rust, Python,
Node.js, WASM, C#) need only the runtime; the C-ABI bindings that compile on
install — Go (cgo) and R (`.Call`) — also need a C compiler, and Java runs with
`--enable-native-access=ALL-UNNAMED`.
| Language | Package | Minimum supported |
|-------------|--------------------------------------|----------------------------|
| Rust | crates.io · `wickra` | 1.86 (MSRV) |
| Python | PyPI · `wickra` (abi3 wheel) | 3.9 (tested through 3.13) |
| Node.js | npm · `wickra` (N-API 8) | 20 (tested on 22 · 24 LTS) |
| WASM | npm · `wickra-wasm` | any modern JS engine |
| C | `wickra.h` + library (releases) | C99 compiler |
| C++ | `wickra.hpp` over the C ABI | C++14 compiler |
| C# | NuGet · `Wickra` | .NET 8 (`net8.0`) |
| Go | module · `wickra-lib/wickra-go` | Go 1.23 (cgo) |
| Java | Maven Central · `org.wickra:wickra` | Java 22 (FFM / Panama) |
| R | source package | R ≥ 2.10 (Rtools on Win.) |
Full per-language detail (runtime vs. build-from-source) is on the
[Requirements page](https://docs.wickra.org/Requirements) in the docs.
## Rust API
```rust
@@ -247,8 +303,8 @@ while let Some(event) = stream.next_event().await? {
}
```
A Python live-trading example using the public `websockets` package lives at
`examples/python/live_trading.py`.
A Python live Binance feed example using the public `websockets` package lives at
`examples/python/live_binance.py`.
## Project layout
@@ -264,15 +320,15 @@ wickra/
│ ├── node/ napi-rs (publishes on npm)
│ ├── wasm/ wasm-bindgen (browsers, bundlers, Node)
│ ├── c/ C ABI (cdylib + staticlib) + generated include/wickra.h
│ ├── csharp/ .NET binding over the C ABI (publishes on NuGet)
│ ├── csharp/ C# binding over the C ABI (publishes on NuGet)
│ ├── go/ Go binding over the C ABI via cgo (module tag)
│ ├── r/ R binding over the C ABI via .Call (R package)
│ └── java/ Java binding over the C ABI via the FFM API (Maven Central)
├── examples/ examples/README.md indexes every language
│ ├── data/ real BTCUSDT OHLCV datasets, one per timeframe
│ ├── rust/ Rust workspace member (`wickra-examples`)
│ ├── python/ backtest, live trading, parallel assets, multi-tf
│ ├── node/ streaming, backtest, live trading (load `wickra`)
│ ├── python/ backtest, live Binance feed, parallel assets, multi-tf
│ ├── node/ streaming, backtest, live Binance feed (load `wickra`)
│ ├── wasm/ browser demo for `wickra-wasm`
│ ├── c/ C smoke + streaming, C++ RAII wrapper
│ ├── csharp/ streaming, backtest, strategies (load `Wickra`)
@@ -312,7 +368,7 @@ cargo build -p wickra-c --release
cmake -S examples/c -B examples/c/build -DWICKRA_LIB_DIR="$PWD/target/release"
cmake --build examples/c/build && ctest --test-dir examples/c/build --output-on-failure
# C# / .NET binding (requires the .NET 8 SDK; links the C ABI above)
# C# binding (requires the .NET 8 SDK; links the C ABI above)
dotnet test bindings/csharp/Wickra.Tests/Wickra.Tests.csproj
# Go binding (requires a C compiler for cgo; links the C ABI above)
@@ -358,11 +414,15 @@ Every layer is covered; run the suites with the commands in
- `bindings/java`: JUnit cases covering one indicator per FFI archetype
(scalar/batch, multi-output, bars, profile, array input) plus batch equivalence.
The four C-ABI bindings (C#, Go, Java, R) additionally replay a shared,
language-neutral golden fixture (`testdata/golden/*.csv`, generated by
`cargo run -p wickra-examples --bin gen_golden`) and assert exact parity with the
Rust reference outputs across every archetype (SMA, EMA, RSI, ATR, MACD, ADX,
Beta), catching FFI wiring bugs the math-only core tests cannot see.
On top of those per-binding tests, **all 10 languages** (Rust, Python, Node.js,
WASM, C, C++, C#, Go, Java, R) replay a shared, language-neutral golden fixture
(`testdata/golden/*.csv`, generated by
`cargo run -p wickra-examples --bin gen_golden`) and assert **bit-for-bit parity
with the Rust reference for every one of the 514 indicators** across every
archetype (scalar, multi-output, pairwise, derivatives-tick, cross-section,
order-book, trade, profile, alt-chart bars, footprint). This catches FFI wiring
bugs the math-only core tests cannot see — it has already found and fixed real
cross-language marshalling bugs in the Java and R bindings.
## Contributing
+2 -2
View File
@@ -21,8 +21,8 @@ minor releases; breaking changes are called out in the changelog.
versioning stability for a 1.0 release.
- **Performance.** Keep per-tick updates O(1) and maintain the benchmark suite;
investigate further allocation and cache improvements.
- **Bindings parity.** Keep the Python, Node.js and WebAssembly bindings — plus
the C ABI and the C# / .NET, Go, Java and R bindings generated from it — in lockstep with the
- **Bindings parity.** Keep the Python, Node.js and WASM bindings — plus
the C ABI and the C#, Go, Java and R bindings generated from it — in lockstep with the
Rust core, including type stubs and platform coverage.
- **Documentation.** Maintain a deep-dive page per indicator on
<https://docs.wickra.org>, plus quickstarts and cookbook material.
+3 -3
View File
@@ -2,13 +2,13 @@
## Supported versions
Wickra is pre-1.0. Security fixes are applied to the latest released `0.9.0`
Wickra is pre-1.0. Security fixes are applied to the latest released `0.9.2`
version only; please upgrade to the newest release before reporting an issue.
| Version | Supported |
| --- | --- |
| 0.9.0 (latest) | :white_check_mark: |
| < 0.9.0 | :x: |
| 0.9.2 (latest) | :white_check_mark: |
| < 0.9.2 | :x: |
## Reporting a vulnerability
+1 -1
View File
@@ -7,7 +7,7 @@ Thanks for using Wickra! Here is where to get help, depending on what you need.
Most questions are answered in the documentation:
- **Docs site:** <https://docs.wickra.org> — quickstarts for Rust, Python,
Node.js, WebAssembly, C, C#, Go, Java and R, a per-indicator reference, warmup periods, the
Node.js, WASM, C, C++, C#, Go, Java and R, a per-indicator reference, warmup periods, the
data layer, and an FAQ.
- **README:** <https://github.com/wickra-lib/wickra#readme> — installation and a
quick overview.
+2 -2
View File
@@ -3,8 +3,8 @@
This document describes Wickra's attack surface and the threats considered,
together with their mitigations. It complements the security assurance case in
[`SECURITY.md`](SECURITY.md). Wickra is a computational technical-analysis
library (a Rust core with Python, Node.js and WebAssembly bindings plus a C ABI
and the .NET, Go, Java and R bindings built on it),
library (a Rust core with Python, Node.js and WASM bindings plus a C ABI
and the C#, Go, Java and R bindings built on it),
not a network service or trading system; the attack surface is correspondingly
small.
+11 -4
View File
@@ -9,12 +9,12 @@
library plus a generated `wickra.h` — no system dependencies.**
Wickra is a multi-language technical-analysis library with a Rust core and
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C#, Go, Java, R and any
bindings for Python, Node.js and WASM, plus a C ABI for C, C++, C#, Go, Java, R and any
other C-capable language. Every indicator is an O(1)
streaming state machine, so live trading bots and historical backtests share
the exact same implementation. This package is the **C ABI hub**: it compiles the
core to a C-compatible shared/static library plus a generated header, so any
C-capable language (C, C++, Go, C#, Java, R) links against one artifact instead
C-capable language (C, C++, C#, Go, Java, R) links against one artifact instead
of re-wrapping every indicator natively.
## Install
@@ -77,10 +77,17 @@ the main repository and documentation site:
- **Docs** (C quickstart, cookbook, TA-Lib migration): <https://docs.wickra.org/Quickstart-C>
- **Runnable examples:** [`examples/c/`](https://github.com/wickra-lib/wickra/tree/main/examples/c)
Wickra ships native bindings for Python, Node.js, WebAssembly and Rust, plus this
C ABI hub that any C-capable language (C, C++, Go, C#, Java, R) links against —
Wickra ships native bindings for Python, Node.js, WASM and Rust, plus this
C ABI hub that any C-capable language (C, C++, C#, Go, Java, R) links against —
all exposing the same indicators from the shared Rust core.
## Security
Found a security issue? **Please don't open a public issue.** Report it privately
via the affected repository's *Security* tab (*"Report a vulnerability"*) or email
**support@wickra.org** with a subject line starting `[wickra security]`. Full
policy: <https://github.com/wickra-lib/wickra/blob/main/SECURITY.md>.
## Disclaimer
Wickra is an indicator toolkit, not a trading system. The values it computes
File diff suppressed because it is too large Load Diff
+13710
View File
File diff suppressed because it is too large Load Diff
+13 -6
View File
@@ -1,18 +1,18 @@
# Wickra — .NET
# Wickra — C#
[![CI](https://github.com/wickra-lib/wickra/actions/workflows/ci.yml/badge.svg)](https://github.com/wickra-lib/wickra/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/wickra-lib/wickra/branch/main/graph/badge.svg)](https://codecov.io/gh/wickra-lib/wickra)
[![NuGet](https://img.shields.io/nuget/v/Wickra.svg?logo=nuget&color=blue)](https://www.nuget.org/packages/Wickra)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT_OR_Apache--2.0-blue)](https://github.com/wickra-lib/wickra#license)
**Streaming-first technical indicators for .NET. `dotnet add package Wickra`
**Streaming-first technical indicators for C#. `dotnet add package Wickra`
prebuilt native library, no system dependencies.**
Wickra is a multi-language technical-analysis library with a Rust core and
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C#, Go, Java, R and any
bindings for Python, Node.js and WASM, plus a C ABI for C, C++, C#, Go, Java, R and any
other C-capable language. Every indicator is an O(1)
streaming state machine, so live trading bots and historical backtests share
the exact same implementation. This package is the .NET binding; it consumes the
the exact same implementation. This package is the C# binding; it consumes the
C ABI hub through `[LibraryImport]` P/Invoke and exposes all 514 streaming-first
indicators as idiomatic `IDisposable` classes.
@@ -73,10 +73,17 @@ the main repository and documentation site:
- **Docs** (quickstarts, cookbook, TA-Lib migration): <https://docs.wickra.org>
- **Runnable examples:** [`examples/csharp/`](https://github.com/wickra-lib/wickra/tree/main/examples/csharp)
Wickra ships native bindings for Python, Node.js, WebAssembly and Rust, plus a
C ABI hub that any C-capable language (C, C++, Go, C#, Java, R) links against —
Wickra ships native bindings for Python, Node.js, WASM and Rust, plus a
C ABI hub that any C-capable language (C, C++, C#, Go, Java, R) links against —
all exposing the same indicators from the shared, `unsafe`-forbidden Rust core.
## Security
Found a security issue? **Please don't open a public issue.** Report it privately
via the affected repository's *Security* tab (*"Report a vulnerability"*) or email
**support@wickra.org** with a subject line starting `[wickra security]`. Full
policy: <https://github.com/wickra-lib/wickra/blob/main/SECURITY.md>.
## Disclaimer
Wickra is an indicator toolkit, not a trading system. The values it computes
@@ -34,6 +34,21 @@ public class ArchetypeTests
Assert.InRange(last, 1.0, 10.0);
}
[Fact]
public void Query_WarmupPeriodAndIsReady()
{
using var sma = new Sma(3);
Assert.Equal(3, sma.WarmupPeriod());
Assert.False(sma.IsReady());
sma.Update(1.0);
sma.Update(2.0);
Assert.False(sma.IsReady());
sma.Update(3.0);
Assert.True(sma.IsReady());
sma.Reset();
Assert.False(sma.IsReady());
}
[Fact]
public void Candle_Atr_IsFinitePositive()
{
File diff suppressed because it is too large Load Diff
@@ -159,4 +159,58 @@ public class GoldenTests
AssertClose(got.Value.Adx, Cell(e[2]), i, "adx.adx");
}
}
// --- the four de-duplicated indicators ------------------------------------
[Fact]
public void Candle_AdOscillator_MatchesGolden()
{
var input = Input();
var expected = ReadCsv("ad_oscillator");
using var ad = new AdOscillator();
for (var i = 0; i < input.Length; i++)
{
var (o, h, l, c, v) = (input[i][0], input[i][1], input[i][2], input[i][3], input[i][4]);
AssertClose(ad.Update(o, h, l, c, v, i), Cell(expected[i][0]), i, "ad_oscillator");
}
}
[Fact]
public void Candle_IntradayIntensity_MatchesGolden()
{
var input = Input();
var expected = ReadCsv("intraday_intensity");
using var ii = new IntradayIntensity();
for (var i = 0; i < input.Length; i++)
{
var (o, h, l, c, v) = (input[i][0], input[i][1], input[i][2], input[i][3], input[i][4]);
AssertClose(ii.Update(o, h, l, c, v, i), Cell(expected[i][0]), i, "intraday_intensity");
}
}
[Fact]
public void Candle_AwesomeOscillatorHistogram_MatchesGolden()
{
var input = Input();
var expected = ReadCsv("awesome_oscillator_histogram");
using var aoh = new AwesomeOscillatorHistogram(5, 34, 1);
for (var i = 0; i < input.Length; i++)
{
var (o, h, l, c, v) = (input[i][0], input[i][1], input[i][2], input[i][3], input[i][4]);
AssertClose(aoh.Update(o, h, l, c, v, i), Cell(expected[i][0]), i, "awesome_oscillator_histogram");
}
}
[Fact]
public void Scalar_AverageDrawdown_MatchesGolden()
{
var input = Input();
var expected = ReadCsv("average_drawdown");
using var avg = new AverageDrawdown(20);
for (var i = 0; i < input.Length; i++)
{
// generator fed the close column as the equity-curve sample.
AssertClose(avg.Update(input[i][3]), Cell(expected[i][0]), i, "average_drawdown");
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -11,7 +11,7 @@
<!-- NuGet package metadata -->
<PackageId>Wickra</PackageId>
<Version>0.9.0</Version>
<Version>0.9.2</Version>
<Authors>kingchenc</Authors>
<Description>High-performance streaming technical-analysis indicators (514 indicators) for .NET, backed by the native Rust core via the Wickra C ABI.</Description>
<PackageLicenseExpression>MIT OR Apache-2.0</PackageLicenseExpression>
+7
View File
@@ -23,6 +23,13 @@ internal static class WickraNative
// Any exported symbol works as a fingerprint; sma_new exists in every build.
private const string SentinelSymbol = "wickra_sma_new";
// CA2255 warns against [ModuleInitializer] in libraries, but registering the
// native-library resolver before any P/Invoke runs is exactly the advanced
// scenario the attribute exists for: a static constructor would run too late
// (only on first access to this type), letting the default resolver fail first.
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Usage", "CA2255:The 'ModuleInitializer' attribute should not be used in libraries",
Justification = "The DllImport resolver must be registered before the first P/Invoke; a static constructor would run too late.")]
[ModuleInitializer]
internal static void Register()
{
+300
View File
@@ -0,0 +1,300 @@
"""Generate Wickra.Tests/GoldenAllTests.g.cs: a value-parity test that replays
the shared golden input through every one of the 514 C# indicators and checks
output bit-for-bit against the Rust reference fixtures g_<Canonical>.csv.
Run from repo root: python bindings/csharp/gen_golden_test.py
"""
import glob
import json
import os
import re
ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))
G = os.path.join(ROOT, "testdata", "golden")
GEN = open(os.path.join(ROOT, "bindings", "csharp", "Wickra", "Generated", "Indicators.g.cs"), encoding="utf-8").read()
# C# constructor parameter types per class.
ctor_types = {}
cur = None
for line in GEN.splitlines():
m = re.match(r"public sealed class (\w+)", line)
if m:
cur = m.group(1)
continue
if cur:
cm = re.match(r"\s*public %s\(([^)]*)\)" % re.escape(cur), line)
if cm:
ps = cm.group(1).strip()
types = [p.strip().rsplit(" ", 1)[0].strip() for p in ps.split(",")] if ps else []
ctor_types[cur] = types
cur = None
# Unified archetype + params, keyed by canonical (== C# class name).
spec = {}
for e in json.load(open(os.path.join(G, "scalar_manifest.json"))):
arch = {"f64": "scalar_f64", "Candle": "scalar_candle", "(f64, f64)": "pairwise"}[e["input"]]
spec[e["canonical"]] = {"arch": arch, "params": e["params"]}
for e in json.load(open(os.path.join(G, "multi_manifest.json"))):
arch = {"f64": "multi_f64", "Candle": "multi_candle", "(f64, f64)": "multi_pairwise"}[e["input"]]
spec[e["canonical"]] = {"arch": arch, "params": e["params"], "n": e["n"]}
ex = json.load(open(os.path.join(G, "exotic_manifest.json")))
for e in ex["deriv"]:
spec[e["canonical"]] = {"arch": "deriv_multi" if "n" in e else "deriv", "params": e["params"], "n": e.get("n")}
for e in ex["cross"]:
spec[e["canonical"]] = {"arch": "cross", "params": e["params"]}
for e in ex["trade"]:
spec[e["canonical"]] = {"arch": "trade", "params": e["params"]}
for e in ex["trademid"]:
spec[e["canonical"]] = {"arch": "trademid", "params": e["params"]}
for e in ex["ob"]:
spec[e["canonical"]] = {"arch": "ob", "params": e["params"]}
for e in json.load(open(os.path.join(G, "profile_manifest.json"))):
spec[e["canonical"]] = {"arch": "profile_" + e["kind"], "params": e["params"], "width": e["width"]}
for e in json.load(open(os.path.join(G, "bars_manifest.json"))):
arch = "footprint" if e["canonical"] == "Footprint" else "bars_" + e["feed"]
spec[e["canonical"]] = {"arch": arch, "params": e["params"]}
canons = sorted(os.path.basename(f)[2:-4] for f in glob.glob(os.path.join(G, "g_*.csv")))
def lit(value, cstype):
if cstype == "int":
return str(int(round(value)))
if cstype == "uint":
return f"{int(round(value))}u"
if cstype == "byte":
return f"(byte){int(round(value))}"
f = float(value)
s = repr(f)
return s if ("." in s or "e" in s or "E" in s) else s + ".0"
def ctor_call(canon):
types = ctor_types.get(canon, [])
vals = spec[canon]["params"]
args = ", ".join(lit(v, t) for v, t in zip(vals, types))
return f"new Wickra.{canon}({args})"
def block(canon):
s = spec[canon]
a = s["arch"]
L = [f" [Fact]", f" public void Golden_{canon}()", " {"]
L.append(f" using var ind = {ctor_call(canon)};")
L.append(" var got = new List<double[]>();")
L.append(" for (var i = 0; i < Rows.Length; i++)")
L.append(" {")
L.append(" var r = Rows[i];")
if a == "scalar_f64":
L.append(" got.Add(new[] { ind.Update(r[3]) });")
elif a == "pairwise":
L.append(" got.Add(new[] { ind.Update(r[3], r[0]) });")
elif a == "scalar_candle":
L.append(" got.Add(new[] { ind.Update(r[0], r[1], r[2], r[3], r[4], i) });")
elif a == "trade":
L.append(" got.Add(new[] { ind.Update(r[3], r[4], r[3] >= r[0], i) });")
elif a == "trademid":
L.append(" got.Add(new[] { ind.Update(r[3], r[4], r[3] >= r[0], i, (r[1] + r[2]) / 2) });")
elif a == "ob":
L.append(" var (bp, bs, ap, asz) = ObLists(r);")
L.append(" got.Add(new[] { ind.Update(bp, bs, ap, asz) });")
elif a == "deriv":
L.append(" var d = DerivFields(r);")
L.append(" got.Add(new[] { ind.Update(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], i) });")
elif a == "deriv_multi":
L.append(" var d = DerivFields(r);")
L.append(" got.Add(FlattenNullable(ind.Update(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], i), %d));" % s["n"])
elif a == "cross":
L.append(" var (ch, vo, nh, nl, am, ob) = CrossLists(r);")
L.append(" got.Add(new[] { ind.Update(ch, vo, nh, nl, am, ob, i) });")
elif a == "multi_f64":
L.append(" got.Add(FlattenNullable(ind.Update(r[3]), %d));" % s["n"])
elif a == "multi_pairwise":
L.append(" got.Add(FlattenNullable(ind.Update(r[3], r[0]), %d));" % s["n"])
elif a == "multi_candle":
L.append(" got.Add(FlattenNullable(ind.Update(r[0], r[1], r[2], r[3], r[4], i), %d));" % s["n"])
elif a == "profile_bins":
L.append(" var bins = ind.Update(r[0], r[1], r[2], r[3], r[4], i);")
L.append(" got.Add(bins ?? NanRow(%d));" % s["width"])
elif a == "profile_pricebins":
L.append(" got.Add(FlattenNullable(ind.Update(r[0], r[1], r[2], r[3], r[4], i), %d));" % s["width"])
elif a == "bars_close":
L.append(" got.Add(FlattenBars(ind.Update(r[3], r[3], r[3], r[3], 1.0, 0)));")
elif a == "bars_candle4":
L.append(" got.Add(FlattenBars(ind.Update(r[0], r[1], r[2], r[3], 1.0, 0)));")
elif a == "bars_candle5":
L.append(" got.Add(FlattenBars(ind.Update(r[0], r[1], r[2], r[3], r[4], 0)));")
elif a == "footprint":
L.append(" got.Add(FlattenBars(ind.Update(r[3], r[4], r[3] >= r[0], i)));")
else:
raise SystemExit("arch " + a)
L.append(" }")
L.append(f' Compare("{canon}", got);')
L.append(" }")
return "\n".join(L)
HEADER = '''// <auto-generated>
// Generated by gen_golden_test.py. DO NOT EDIT.
//
// Value-parity for every one of the 514 C# indicators: the shared golden input
// is replayed through each one and checked bit-for-bit against the Rust
// reference fixtures testdata/golden/g_<Canonical>.csv. Multi-output, profile
// and bar shapes are flattened by reflection so one comparator covers all
// archetypes. Regenerate with: python bindings/csharp/gen_golden_test.py
// </auto-generated>
#nullable enable
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using Xunit;
namespace Wickra.Tests;
public class GoldenAllTests
{
private const double Tol = 1e-6;
private static readonly double[][] Rows = LoadInput();
private static string GoldenDir([System.Runtime.CompilerServices.CallerFilePath] string file = "") =>
Path.GetFullPath(Path.Combine(Path.GetDirectoryName(file)!, "..", "..", "..", "testdata", "golden"));
private static double Cell(string s) =>
s == "nan" ? double.NaN
: s == "inf" ? double.PositiveInfinity
: s == "-inf" ? double.NegativeInfinity
: double.Parse(s, CultureInfo.InvariantCulture);
private static double[][] LoadInput()
{
var lines = File.ReadAllLines(Path.Combine(GoldenDir(), "input.csv"));
return lines.Skip(1).Where(l => l.Length > 0)
.Select(l => l.Split(',').Select(x => double.Parse(x, CultureInfo.InvariantCulture)).ToArray())
.ToArray();
}
// Keep blank lines (a candle on which no bar closed) so rows stay aligned.
private static double[]?[] ReadFixture(string name)
{
var lines = File.ReadAllLines(Path.Combine(GoldenDir(), "g_" + name + ".csv"));
return lines.Skip(1).Select(l => l.Length == 0 ? Array.Empty<double>() : l.Split(',').Select(Cell).ToArray()).ToArray();
}
private static double[] NanRow(int n)
{
var r = new double[n];
for (var i = 0; i < n; i++) r[i] = double.NaN;
return r;
}
private static double[] FlattenStruct(object o)
{
var props = o.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.OrderBy(p => p.MetadataToken);
var list = new List<double>();
foreach (var p in props)
{
var v = p.GetValue(o);
switch (v)
{
case double d: list.Add(d); break;
case float f: list.Add(f); break;
case long l: list.Add(l); break;
case int n: list.Add(n); break;
case double[] arr: list.AddRange(arr); break;
}
}
return list.ToArray();
}
private static double[] FlattenNullable<T>(T? value, int width) where T : struct =>
value.HasValue ? FlattenStruct(value.Value) : NanRow(width);
private static double[] FlattenBars<T>(T[] bars)
{
var list = new List<double>();
foreach (var bar in bars) list.AddRange(FlattenStruct(bar!));
return list.ToArray();
}
private static double[] DerivFields(double[] r)
{
double o = r[0], h = r[1], l = r[2], c = r[3], v = r[4];
return new[]
{
(c - o) / c * 0.01, c, c - 0.5, c + 1.0, v * 10.0, v * 0.6, v * 0.4,
v * 0.55, v * 0.45, h - c, c - l,
};
}
private static (double[], double[], bool[], bool[], bool[], bool[]) CrossLists(double[] r)
{
double o = r[0], c = r[3], v = r[4];
var change = new double[5];
var volume = new double[5];
var nh = new bool[5];
var nl = new bool[5];
var am = new bool[5];
var ob = new bool[5];
for (var j = 0; j < 5; j++)
{
change[j] = (c - o) + j;
volume[j] = v + j * 10.0;
nh[j] = j % 2 == 0;
nl[j] = j % 3 == 0;
am[j] = j % 2 == 0;
ob[j] = j % 3 == 0;
}
return (change, volume, nh, nl, am, ob);
}
private static (double[], double[], double[], double[]) ObLists(double[] r)
{
double c = r[3], v = r[4];
var bp = new double[5];
var bs = new double[5];
var ap = new double[5];
var asz = new double[5];
for (var k = 0; k < 5; k++)
{
var kf = k + 1;
bp[k] = c - 0.1 * kf;
bs[k] = v / kf;
ap[k] = c + 0.1 * kf;
asz[k] = v * 0.9 / kf;
}
return (bp, bs, ap, asz);
}
private static void Compare(string name, List<double[]> got)
{
var exp = ReadFixture(name);
Assert.True(exp.Length == got.Count, $"{name}: {exp.Length} fixture rows vs {got.Count} computed");
for (var i = 0; i < exp.Length; i++)
{
var want = exp[i]!;
var g = got[i];
Assert.True(want.Length == g.Length, $"{name} row {i}: arity {g.Length} vs {want.Length}");
for (var k = 0; k < want.Length; k++)
{
var w = want[k];
if (double.IsNaN(w)) { Assert.True(double.IsNaN(g[k]), $"{name} row {i} col {k}: want NaN got {g[k]}"); continue; }
if (double.IsInfinity(w)) { Assert.True(double.IsInfinity(g[k]) && Math.Sign(g[k]) == Math.Sign(w), $"{name} row {i} col {k}: want {w} got {g[k]}"); continue; }
var tol = Tol * Math.Max(1.0, Math.Abs(w));
Assert.True(Math.Abs(g[k] - w) <= tol, $"{name} row {i} col {k}: got {g[k]} want {w}");
}
}
}
'''
out = [HEADER]
for canon in canons:
out.append(block(canon))
out.append("}")
open(os.path.join(ROOT, "bindings", "csharp", "Wickra.Tests", "GoldenAllTests.g.cs"), "w", encoding="utf-8").write("\n".join(out) + "\n")
print("generated GoldenAllTests.g.cs with", len(canons), "indicators")
+9 -2
View File
@@ -8,7 +8,7 @@
**Streaming-first technical indicators for Go, over the Wickra C ABI hub via cgo.**
Wickra is a multi-language technical-analysis library with a Rust core and
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C#, Go, Java, R and
bindings for Python, Node.js and WASM, plus a C ABI for C, C++, C#, Go, Java, R and
any other C-capable language. Every indicator is an O(1) streaming state machine,
so live trading bots and historical backtests share the exact same
implementation. This package is the Go binding; it consumes the C ABI hub through
@@ -108,10 +108,17 @@ main repository and documentation site:
- **Docs** (quickstarts, cookbook, TA-Lib migration): <https://docs.wickra.org>
- **Runnable examples:** [`examples/go/`](https://github.com/wickra-lib/wickra/tree/main/examples/go)
Wickra ships native bindings for Python, Node.js, WebAssembly and Rust, plus a
Wickra ships native bindings for Python, Node.js, WASM and Rust, plus a
C ABI hub that any C-capable language (C, C++, C#, Go, Java, R) links against —
all exposing the same indicators from the shared, `unsafe`-forbidden Rust core.
## Security
Found a security issue? **Please don't open a public issue.** Report it privately
via the affected repository's *Security* tab (*"Report a vulnerability"*) or email
**support@wickra.org** with a subject line starting `[wickra security]`. Full
policy: <https://github.com/wickra-lib/wickra/blob/main/SECURITY.md>.
## Disclaimer
Wickra is an indicator toolkit, not a trading system. The values it computes are
+337
View File
@@ -0,0 +1,337 @@
"""Generate golden_all_test.go: a value-parity test that replays the shared
golden input through every one of the 514 Go indicators and checks output
bit-for-bit against the Rust-generated g_<Canonical>.csv fixtures.
Run from repo root: python bindings/go/gen_golden_test.py
"""
import glob
import json
import os
import re
ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))
G = os.path.join(ROOT, "testdata", "golden")
GEN = open(os.path.join(ROOT, "bindings", "go", "indicators_gen.go"), encoding="utf-8").read()
# Go constructor parameter types, keyed by canonical (== Go type name).
ctor_types = {}
for m in re.finditer(r"func New(\w+)\(([^)]*)\)\s*\(\*\w+, error\)", GEN):
name, ps = m.group(1), m.group(2).strip()
types = []
if ps:
for p in ps.split(","):
p = p.strip()
_, _, ty = p.partition(" ")
types.append(ty.strip())
ctor_types[name] = types
# Unified archetype + params, keyed by canonical.
spec = {} # canon -> dict(arch, params, width?, n?)
scal = json.load(open(os.path.join(G, "scalar_manifest.json")))
for e in scal:
inp = e["input"]
arch = {"f64": "scalar_f64", "Candle": "scalar_candle", "(f64, f64)": "pairwise"}[inp]
spec[e["canonical"]] = {"arch": arch, "params": e["params"]}
for e in json.load(open(os.path.join(G, "multi_manifest.json"))):
inp = e["input"]
arch = {"f64": "multi_f64", "Candle": "multi_candle", "(f64, f64)": "multi_pairwise"}[inp]
spec[e["canonical"]] = {"arch": arch, "params": e["params"], "n": e["n"]}
ex = json.load(open(os.path.join(G, "exotic_manifest.json")))
for e in ex["deriv"]:
spec[e["canonical"]] = {"arch": "deriv_multi" if "n" in e else "deriv", "params": e["params"], "n": e.get("n")}
for e in ex["cross"]:
spec[e["canonical"]] = {"arch": "cross", "params": e["params"]}
for e in ex["trade"]:
spec[e["canonical"]] = {"arch": "trade", "params": e["params"]}
for e in ex["trademid"]:
spec[e["canonical"]] = {"arch": "trademid", "params": e["params"]}
for e in ex["ob"]:
spec[e["canonical"]] = {"arch": "ob", "params": e["params"]}
for e in json.load(open(os.path.join(G, "profile_manifest.json"))):
spec[e["canonical"]] = {"arch": "profile_" + e["kind"], "params": e["params"], "width": e["width"]}
for e in json.load(open(os.path.join(G, "bars_manifest.json"))):
arch = "footprint" if e["canonical"] == "Footprint" else "bars_" + e["feed"]
spec[e["canonical"]] = {"arch": arch, "params": e["params"]}
canons = sorted(os.path.basename(f)[2:-4] for f in glob.glob(os.path.join(G, "g_*.csv")))
def go_param(value, gotype):
intlike = gotype in ("int", "int32", "int64", "uint", "uintptr", "usize")
if intlike:
return str(int(round(value)))
# float64
return repr(float(value)) if "." in repr(float(value)) or "e" in repr(float(value)) else f"{float(value)}"
def ctor_call(canon):
types = ctor_types.get(canon, [])
vals = spec[canon]["params"]
args = ", ".join(go_param(v, t) for v, t in zip(vals, types))
return f"New{canon}({args})"
# Update-call expression + output handling per archetype.
def block(canon):
s = spec[canon]
a = s["arch"]
ctor = ctor_call(canon)
lines = [f'\tt.Run("{canon}", func(t *testing.T) {{']
lines.append(f"\t\tind, err := {ctor}")
lines.append('\t\tif err != nil {')
lines.append(f'\t\t\tt.Fatalf("new {canon}: %v", err)')
lines.append("\t\t}")
lines.append("\t\tgot := make([][]float64, len(rows))")
lines.append("\t\tfor i, r := range rows {")
if a == "scalar_f64":
upd = "ind.Update(r[3])"
lines.append(f"\t\t\tgot[i] = []float64{{{upd}}}")
elif a == "pairwise":
lines.append("\t\t\tgot[i] = []float64{ind.Update(r[3], r[0])}")
elif a == "scalar_candle":
lines.append("\t\t\tgot[i] = []float64{ind.Update(r[0], r[1], r[2], r[3], r[4], int64(i))}")
elif a == "trade":
lines.append("\t\t\tgot[i] = []float64{ind.Update(r[3], r[4], r[3] >= r[0], int64(i))}")
elif a == "trademid":
lines.append("\t\t\tgot[i] = []float64{ind.Update(r[3], r[4], r[3] >= r[0], int64(i), (r[1]+r[2])/2)}")
elif a == "ob":
lines.append("\t\t\tbp, bs, ap, as_ := obLists(r)")
lines.append("\t\t\tgot[i] = []float64{ind.Update(bp, bs, ap, as_)}")
elif a == "deriv":
lines.append("\t\t\td := derivFields(r)")
lines.append("\t\t\tgot[i] = []float64{ind.Update(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], int64(i))}")
elif a == "deriv_multi":
lines.append("\t\t\td := derivFields(r)")
lines.append("\t\t\tout, ok := ind.Update(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], int64(i))")
lines.append(f"\t\t\tgot[i] = reflectRow(out, ok, {s['n']})")
elif a == "cross":
lines.append("\t\t\tch, vo, nh, nl, am, ob_ := crossLists(r)")
lines.append("\t\t\tgot[i] = []float64{ind.Update(ch, vo, nh, nl, am, ob_, int64(i))}")
elif a in ("multi_f64",):
lines.append("\t\t\tout, ok := ind.Update(r[3])")
lines.append(f"\t\t\tgot[i] = reflectRow(out, ok, {s['n']})")
elif a == "multi_pairwise":
lines.append("\t\t\tout, ok := ind.Update(r[3], r[0])")
lines.append(f"\t\t\tgot[i] = reflectRow(out, ok, {s['n']})")
elif a == "multi_candle":
lines.append("\t\t\tout, ok := ind.Update(r[0], r[1], r[2], r[3], r[4], int64(i))")
lines.append(f"\t\t\tgot[i] = reflectRow(out, ok, {s['n']})")
elif a == "profile_bins":
lines.append("\t\t\tbins, ok := ind.Update(r[0], r[1], r[2], r[3], r[4], int64(i))")
lines.append(f"\t\t\tif ok {{ got[i] = bins }} else {{ got[i] = nanRow({s['width']}) }}")
elif a == "profile_pricebins":
lines.append("\t\t\tout, ok := ind.Update(r[0], r[1], r[2], r[3], r[4], int64(i))")
lines.append(f"\t\t\tgot[i] = reflectRow(out, ok, {s['width']})")
elif a == "bars_close":
lines.append("\t\t\tgot[i] = flattenBars(ind.Update(r[3], r[3], r[3], r[3], 1.0, 0))")
elif a == "bars_candle4":
lines.append("\t\t\tgot[i] = flattenBars(ind.Update(r[0], r[1], r[2], r[3], 1.0, 0))")
elif a == "bars_candle5":
lines.append("\t\t\tgot[i] = flattenBars(ind.Update(r[0], r[1], r[2], r[3], r[4], 0))")
elif a == "footprint":
lines.append("\t\t\tgot[i] = flattenBars(ind.Update(r[3], r[4], r[3] >= r[0], int64(i)))")
else:
raise SystemExit("unknown arch " + a)
lines.append("\t\t}")
lines.append(f'\t\tcompareGolden(t, "{canon}", got)')
lines.append("\t})")
return "\n".join(lines)
HEADER = '''// Code generated by gen_golden_test.py. DO NOT EDIT.
//
// Value-parity for every one of the 514 Go indicators: the shared golden input
// is replayed through each one and checked bit-for-bit against the Rust
// reference fixtures testdata/golden/g_<Canonical>.csv. Multi-output, profile
// and bar shapes are flattened by reflection so a single comparator covers all
// archetypes. Regenerate with: python bindings/go/gen_golden_test.py
package wickra
import (
\t"bufio"
\t"math"
\t"os"
\t"reflect"
\t"strings"
\t"testing"
)
// readGoldenRaw keeps blank lines (a candle on which no bar closed) so bar rows
// stay aligned to the input; non-bar fixtures contain no blank lines.
func readGoldenRaw(t *testing.T, name string) [][]string {
\tt.Helper()
\tf, err := os.Open("../../testdata/golden/" + name + ".csv")
\tif err != nil {
\t\tt.Fatalf("open %s: %v", name, err)
\t}
\tdefer f.Close()
\tvar rows [][]string
\tsc := bufio.NewScanner(f)
\tsc.Buffer(make([]byte, 0, 1024*1024), 1024*1024)
\tfirst := true
\tfor sc.Scan() {
\t\tline := sc.Text()
\t\tif first {
\t\t\tfirst = false
\t\t\tcontinue
\t\t}
\t\tif line == "" {
\t\t\trows = append(rows, []string{})
\t\t\tcontinue
\t\t}
\t\trows = append(rows, strings.Split(line, ","))
\t}
\treturn rows
}
func nanRow(n int) []float64 {
\tr := make([]float64, n)
\tfor i := range r {
\t\tr[i] = math.NaN()
\t}
\treturn r
}
func reflectRow(out any, ok bool, width int) []float64 {
\tif !ok {
\t\treturn nanRow(width)
\t}
\tv := reflect.ValueOf(out)
\trow := make([]float64, 0, width)
\tfor k := 0; k < v.NumField(); k++ {
\t\trow = appendField(row, v.Field(k))
\t}
\treturn row
}
func appendField(row []float64, f reflect.Value) []float64 {
\tswitch f.Kind() {
\tcase reflect.Float64, reflect.Float32:
\t\treturn append(row, f.Float())
\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
\t\treturn append(row, float64(f.Int()))
\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
\t\treturn append(row, float64(f.Uint()))
\tcase reflect.Slice:
\t\tfor j := 0; j < f.Len(); j++ {
\t\t\trow = appendField(row, f.Index(j))
\t\t}
\t\treturn row
\tdefault:
\t\treturn row
\t}
}
func flattenBars(bars any) []float64 {
\tv := reflect.ValueOf(bars)
\trow := []float64{}
\tfor i := 0; i < v.Len(); i++ {
\t\tbar := v.Index(i)
\t\tfor k := 0; k < bar.NumField(); k++ {
\t\t\trow = appendField(row, bar.Field(k))
\t\t}
\t}
\treturn row
}
// Synthetic feeds derived from one OHLCV row, identical to gen_golden's Rust
// construction (DerivativesTick / CrossSection / OrderBook).
func derivFields(r []float64) [11]float64 {
\to, h, l, c, v := r[0], r[1], r[2], r[3], r[4]
\treturn [11]float64{
\t\t(c - o) / c * 0.01, // funding_rate
\t\tc, // mark_price
\t\tc - 0.5, // index_price
\t\tc + 1.0, // futures_price
\t\tv * 10.0, // open_interest
\t\tv * 0.6, // long_size
\t\tv * 0.4, // short_size
\t\tv * 0.55, // taker_buy_volume
\t\tv * 0.45, // taker_sell_volume
\t\th - c, // long_liquidation
\t\tc - l, // short_liquidation
\t}
}
func crossLists(r []float64) ([]float64, []float64, []bool, []bool, []bool, []bool) {
\to, c, v := r[0], r[3], r[4]
\tchange := make([]float64, 5)
\tvolume := make([]float64, 5)
\tnewHigh := make([]bool, 5)
\tnewLow := make([]bool, 5)
\taboveMa := make([]bool, 5)
\tonBuy := make([]bool, 5)
\tfor j := 0; j < 5; j++ {
\t\tjf := float64(j)
\t\tchange[j] = (c - o) + jf
\t\tvolume[j] = v + jf*10.0
\t\tnewHigh[j] = j%2 == 0
\t\tnewLow[j] = j%3 == 0
\t\taboveMa[j] = j%2 == 0
\t\tonBuy[j] = j%3 == 0
\t}
\treturn change, volume, newHigh, newLow, aboveMa, onBuy
}
func obLists(r []float64) ([]float64, []float64, []float64, []float64) {
\tc, v := r[3], r[4]
\tbidPx := make([]float64, 5)
\tbidSz := make([]float64, 5)
\taskPx := make([]float64, 5)
\taskSz := make([]float64, 5)
\tfor k := 0; k < 5; k++ {
\t\tkf := float64(k + 1)
\t\tbidPx[k] = c - 0.1*kf
\t\tbidSz[k] = v / kf
\t\taskPx[k] = c + 0.1*kf
\t\taskSz[k] = v * 0.9 / kf
\t}
\treturn bidPx, bidSz, askPx, askSz
}
func compareGolden(t *testing.T, name string, got [][]float64) {
\tt.Helper()
\texp := readGoldenRaw(t, "g_"+name)
\tif len(exp) != len(got) {
\t\tt.Fatalf("%s: %d fixture rows vs %d computed", name, len(exp), len(got))
\t}
\tfor i := range exp {
\t\tif len(exp[i]) != len(got[i]) {
\t\t\tt.Fatalf("%s row %d: arity %d vs %d", name, i, len(got[i]), len(exp[i]))
\t\t}
\t\tfor k := range exp[i] {
\t\t\twant := goldenCell(exp[i][k])
\t\t\tg := got[i][k]
\t\t\tif math.IsNaN(want) {
\t\t\t\tif !math.IsNaN(g) {
\t\t\t\t\tt.Fatalf("%s row %d col %d: want NaN got %v", name, i, k, g)
\t\t\t\t}
\t\t\t\tcontinue
\t\t\t}
\t\t\tif math.IsInf(want, 0) {
\t\t\t\tif !math.IsInf(g, 0) || (g > 0) != (want > 0) {
\t\t\t\t\tt.Fatalf("%s row %d col %d: want %v got %v", name, i, k, want, g)
\t\t\t\t}
\t\t\t\tcontinue
\t\t\t}
\t\t\ttol := goldenTol * math.Max(1.0, math.Abs(want))
\t\t\tif math.Abs(g-want) > tol {
\t\t\t\tt.Fatalf("%s row %d col %d: got %v want %v", name, i, k, g, want)
\t\t\t}
\t\t}
\t}
}
func TestGoldenAll(t *testing.T) {
\trows := goldenInput(t)
'''
# bars need blank-line-preserving fixture reads; reuse readGolden but it skips
# blanks. We need a raw reader for bars and input.
out = [HEADER]
for canon in canons:
out.append(block(canon))
out.append("}")
open(os.path.join(ROOT, "bindings", "go", "golden_all_test.go"), "w", encoding="utf-8").write("\n".join(out) + "\n")
print("generated golden_all_test.go with", len(canons), "indicators")
File diff suppressed because it is too large Load Diff
+59
View File
@@ -189,3 +189,62 @@ func TestGoldenAdx(t *testing.T) {
assertGoldenClose(t, out.Adx, goldenCell(exp[i][2]), i, "adx.adx")
}
}
// The four de-duplicated indicators: pin their corrected definitions against
// the Rust reference so the Go FFI stays bit-identical.
func TestGoldenAdOscillator(t *testing.T) {
input := goldenInput(t)
exp := readGolden(t, "ad_oscillator")
ad, err := NewAdOscillator()
if err != nil {
t.Fatal(err)
}
defer ad.Close()
for i := range input {
got := ad.Update(input[i][0], input[i][1], input[i][2], input[i][3], input[i][4], int64(i))
assertGoldenClose(t, got, goldenCell(exp[i][0]), i, "ad_oscillator")
}
}
func TestGoldenIntradayIntensity(t *testing.T) {
input := goldenInput(t)
exp := readGolden(t, "intraday_intensity")
ii, err := NewIntradayIntensity()
if err != nil {
t.Fatal(err)
}
defer ii.Close()
for i := range input {
got := ii.Update(input[i][0], input[i][1], input[i][2], input[i][3], input[i][4], int64(i))
assertGoldenClose(t, got, goldenCell(exp[i][0]), i, "intraday_intensity")
}
}
func TestGoldenAwesomeOscillatorHistogram(t *testing.T) {
input := goldenInput(t)
exp := readGolden(t, "awesome_oscillator_histogram")
aoh, err := NewAwesomeOscillatorHistogram(5, 34, 1)
if err != nil {
t.Fatal(err)
}
defer aoh.Close()
for i := range input {
got := aoh.Update(input[i][0], input[i][1], input[i][2], input[i][3], input[i][4], int64(i))
assertGoldenClose(t, got, goldenCell(exp[i][0]), i, "awesome_oscillator_histogram")
}
}
func TestGoldenAverageDrawdown(t *testing.T) {
input := goldenInput(t)
exp := readGolden(t, "average_drawdown")
avg, err := NewAverageDrawdown(20)
if err != nil {
t.Fatal(err)
}
defer avg.Close()
for i := range input {
// generator fed the close column as the equity-curve sample.
assertGoldenClose(t, avg.Update(input[i][3]), goldenCell(exp[i][0]), i, "average_drawdown")
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+28
View File
@@ -24,6 +24,34 @@ func TestScalarKnownValue(t *testing.T) {
}
}
func TestWarmupPeriodAndIsReady(t *testing.T) {
s, err := NewSma(3)
if err != nil {
t.Fatalf("NewSma: %v", err)
}
defer s.Close()
if got := s.WarmupPeriod(); got != 3 {
t.Fatalf("sma(3) WarmupPeriod = %d, want 3", got)
}
if s.IsReady() {
t.Fatal("sma is ready before any update")
}
s.Update(1)
s.Update(2)
if s.IsReady() {
t.Fatal("sma is ready mid-warmup")
}
s.Update(3)
if !s.IsReady() {
t.Fatal("sma is not ready after the warmup period")
}
s.Reset()
if s.IsReady() {
t.Fatal("sma is ready after reset")
}
}
func TestScalarBatchMatchesStreaming(t *testing.T) {
input := []float64{1, 2, 3, 4, 5, 6, 7, 8}
+12 -5
View File
@@ -9,7 +9,7 @@
& Memory API — prebuilt native library, no JNI, no system dependencies.**
Wickra is a multi-language technical-analysis library with a Rust core and
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C#, Go, Java, R
bindings for Python, Node.js and WASM, plus a C ABI for C, C++, C#, Go, Java, R
and any other C-capable language. Every indicator is an O(1) streaming state
machine, so live trading bots and historical backtests share the exact same
implementation. This package is the Java binding; it consumes the C ABI hub
@@ -30,14 +30,14 @@ Maven:
<dependency>
<groupId>org.wickra</groupId>
<artifactId>wickra</artifactId>
<version>0.9.0</version>
<version>0.9.2</version>
</dependency>
```
Gradle:
```kotlin
implementation("org.wickra:wickra:0.9.0")
implementation("org.wickra:wickra:0.9.2")
```
The native library ships prebuilt per platform (Linux, macOS, Windows — x64 and
@@ -98,10 +98,17 @@ the main repository and documentation site:
- **Docs** (quickstarts, cookbook, TA-Lib migration): <https://docs.wickra.org>
- **Runnable examples:** [`examples/java/`](https://github.com/wickra-lib/wickra/tree/main/examples/java)
Wickra ships native bindings for Python, Node.js, WebAssembly and Rust, plus a
C ABI hub that any C-capable language (C, C++, Go, C#, Java, R) links against —
Wickra ships native bindings for Python, Node.js, WASM and Rust, plus a
C ABI hub that any C-capable language (C, C++, C#, Go, Java, R) links against —
all exposing the same indicators from the shared, `unsafe`-forbidden Rust core.
## Security
Found a security issue? **Please don't open a public issue.** Report it privately
via the affected repository's *Security* tab (*"Report a vulnerability"*) or email
**support@wickra.org** with a subject line starting `[wickra security]`. Full
policy: <https://github.com/wickra-lib/wickra/blob/main/SECURITY.md>.
## Disclaimer
Wickra is an indicator toolkit, not a trading system. The values it computes
+279
View File
@@ -0,0 +1,279 @@
"""Generate src/test/java/org/wickra/GoldenAllTest.java: a reflection-driven
value-parity test that replays the shared golden input through every one of the
514 Java indicators and checks output bit-for-bit against the Rust reference
fixtures g_<Canonical>.csv. The per-indicator spec is embedded so the test has
no JSON dependency; a single reflective runner covers all archetypes.
Run from repo root: python bindings/java/gen_golden_test.py
"""
import json
import os
ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MAN = json.load(open(os.path.join(ROOT, "testdata", "golden", "golden_manifest.json")))
def params_lit(ps):
if not ps:
return "new double[]{}"
return "new double[]{" + ", ".join(repr(float(p)) for p in ps) + "}"
specs = []
for e in MAN:
n = e.get("n", e.get("width", 0))
specs.append(f' new Spec("{e["canonical"]}", "{e["arch"]}", {params_lit(e["params"])}, {n}),')
specs_block = "\n".join(specs)
TEMPLATE = '''// Code generated by gen_golden_test.py. DO NOT EDIT.
package org.wickra;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.RecordComponent;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
/**
* Reflection-driven value parity for the whole 514-indicator catalogue: each
* indicator is reconstructed by its class name, fed the synthetic stream derived
* from the shared golden input (identical to gen_golden's Rust construction) and
* checked bit-for-bit against testdata/golden/g_&lt;Canonical&gt;.csv. One runner
* flattens scalar, multi-output records, profiles and bar arrays by reflection.
*/
class GoldenAllTest {
private static final double TOL = 1e-6;
private record Spec(String canonical, String arch, double[] params, int width) {}
private static final Spec[] SPECS = {
%SPECS%
};
private static Path goldenDir() {
java.io.File d = new java.io.File("").getAbsoluteFile();
while (d != null) {
java.io.File g = new java.io.File(d, "testdata/golden");
if (g.isDirectory()) {
return g.toPath();
}
d = d.getParentFile();
}
throw new IllegalStateException("testdata/golden not found");
}
private static double cell(String s) {
return switch (s) {
case "nan" -> Double.NaN;
case "inf" -> Double.POSITIVE_INFINITY;
case "-inf" -> Double.NEGATIVE_INFINITY;
default -> Double.parseDouble(s);
};
}
private static double[][] input() throws IOException {
List<String> lines = Files.readAllLines(goldenDir().resolve("input.csv"));
List<double[]> rows = new ArrayList<>();
for (int i = 1; i < lines.size(); i++) {
if (lines.get(i).isEmpty()) continue;
String[] p = lines.get(i).split(",");
double[] r = new double[p.length];
for (int j = 0; j < p.length; j++) r[j] = Double.parseDouble(p[j]);
rows.add(r);
}
return rows.toArray(new double[0][]);
}
// Keep blank lines (a candle on which no bar closed) so rows stay aligned.
private static double[][] fixture(String name) throws IOException {
List<String> lines = Files.readAllLines(goldenDir().resolve("g_" + name + ".csv"));
List<double[]> rows = new ArrayList<>();
for (int i = 1; i < lines.size(); i++) {
String ln = lines.get(i);
if (ln.isEmpty()) {
rows.add(new double[0]);
continue;
}
String[] p = ln.split(",");
double[] r = new double[p.length];
for (int j = 0; j < p.length; j++) r[j] = cell(p[j]);
rows.add(r);
}
return rows.toArray(new double[0][]);
}
private static double[] nanRow(int n) {
double[] r = new double[n];
java.util.Arrays.fill(r, Double.NaN);
return r;
}
private static double[] derivFields(double[] r) {
double o = r[0], h = r[1], l = r[2], c = r[3], v = r[4];
return new double[]{
(c - o) / c * 0.01, c, c - 0.5, c + 1.0, v * 10.0, v * 0.6, v * 0.4,
v * 0.55, v * 0.45, h - c, c - l,
};
}
private static double[] crossList(double[] r, int which) {
double o = r[0], c = r[3], v = r[4];
double[] out = new double[5];
for (int j = 0; j < 5; j++) {
out[j] = switch (which) {
case 0 -> (c - o) + j;
case 1 -> v + j * 10.0;
case 2 -> j % 2 == 0 ? 1.0 : 0.0; // newHigh
case 3 -> j % 3 == 0 ? 1.0 : 0.0; // newLow
case 4 -> j % 2 == 0 ? 1.0 : 0.0; // aboveMa
default -> j % 3 == 0 ? 1.0 : 0.0; // onBuySignal
};
}
return out;
}
private static double[] obList(double[] r, int which) {
double c = r[3], v = r[4];
double[] out = new double[5];
for (int k = 0; k < 5; k++) {
double kf = k + 1;
out[k] = switch (which) {
case 0 -> c - 0.1 * kf;
case 1 -> v / kf;
case 2 -> c + 0.1 * kf;
default -> v * 0.9 / kf;
};
}
return out;
}
private static double[] flattenRecord(Object o) throws Exception {
RecordComponent[] rc = o.getClass().getRecordComponents();
List<Double> list = new ArrayList<>();
for (RecordComponent c : rc) {
Object v = c.getAccessor().invoke(o);
if (v instanceof double[] arr) {
for (double d : arr) list.add(d);
} else if (v instanceof Number num) {
list.add(num.doubleValue());
}
}
double[] out = new double[list.size()];
for (int i = 0; i < out.length; i++) out[i] = list.get(i);
return out;
}
private static double[] flattenArray(Object arr) throws Exception {
int len = Array.getLength(arr);
List<Double> list = new ArrayList<>();
for (int i = 0; i < len; i++) {
for (double d : flattenRecord(Array.get(arr, i))) list.add(d);
}
double[] out = new double[list.size()];
for (int i = 0; i < out.length; i++) out[i] = list.get(i);
return out;
}
private static Object construct(Spec s) throws Exception {
Class<?> cls = Class.forName("org.wickra." + s.canonical());
Constructor<?> ctor = cls.getConstructors()[0];
Class<?>[] pt = ctor.getParameterTypes();
Object[] args = new Object[pt.length];
for (int i = 0; i < pt.length; i++) {
double v = s.params()[i];
if (pt[i] == int.class) args[i] = (int) Math.round(v);
else if (pt[i] == long.class) args[i] = (long) Math.round(v);
else args[i] = v;
}
return ctor.newInstance(args);
}
private static Method updateMethod(Object ind) {
for (Method m : ind.getClass().getMethods()) {
if (m.getName().equals("update")) return m;
}
throw new IllegalStateException("no update on " + ind.getClass());
}
private static double[] row(Spec s, Object ind, Method upd, double[] r, int i) throws Exception {
double o = r[0], h = r[1], l = r[2], c = r[3], v = r[4];
Object res = switch (s.arch()) {
case "scalar_f64", "multi_f64" -> upd.invoke(ind, c);
case "pairwise", "multi_pairwise" -> upd.invoke(ind, c, o);
case "scalar_candle", "multi_candle", "profile_bins", "profile_pricebins" ->
upd.invoke(ind, o, h, l, c, v, (long) i);
case "trade" -> upd.invoke(ind, c, v, c >= o, (long) i);
case "trademid" -> upd.invoke(ind, c, v, c >= o, (long) i, (h + l) / 2);
case "ob" -> upd.invoke(ind, obList(r, 0), obList(r, 1), obList(r, 2), obList(r, 3));
case "cross" -> upd.invoke(ind, crossList(r, 0), crossList(r, 1), crossList(r, 2),
crossList(r, 3), crossList(r, 4), crossList(r, 5), (long) i);
case "deriv", "deriv_multi" -> {
double[] d = derivFields(r);
yield upd.invoke(ind, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], (long) i);
}
case "bars_close" -> upd.invoke(ind, c, c, c, c, 1.0, 0L);
case "bars_candle4" -> upd.invoke(ind, o, h, l, c, 1.0, 0L);
case "bars_candle5" -> upd.invoke(ind, o, h, l, c, v, 0L);
case "footprint" -> upd.invoke(ind, c, v, c >= o, (long) i);
default -> throw new IllegalStateException("arch " + s.arch());
};
return switch (s.arch()) {
case "scalar_f64", "scalar_candle", "pairwise", "trade", "trademid", "ob", "cross", "deriv" ->
new double[]{((Number) res).doubleValue()};
case "multi_f64", "multi_candle", "multi_pairwise", "deriv_multi", "profile_pricebins" ->
res == null ? nanRow(s.width()) : flattenRecord(res);
case "profile_bins" -> res == null ? nanRow(s.width()) : (double[]) res;
default -> flattenArray(res); // bars_*, footprint
};
}
@TestFactory
List<DynamicTest> golden() throws Exception {
double[][] rows = input();
List<DynamicTest> tests = new ArrayList<>();
for (Spec s : SPECS) {
tests.add(dynamicTest(s.canonical(), () -> {
Object ind = construct(s);
Method upd = updateMethod(ind);
double[][] exp = fixture(s.canonical());
assertTrue(exp.length == rows.length, s.canonical() + ": row count " + exp.length + " vs " + rows.length);
for (int i = 0; i < rows.length; i++) {
double[] got = row(s, ind, upd, rows[i], i);
double[] want = exp[i];
assertTrue(got.length == want.length,
s.canonical() + " row " + i + ": arity " + got.length + " vs " + want.length);
for (int k = 0; k < want.length; k++) {
double w = want[k], g = got[k];
if (Double.isNaN(w)) {
assertTrue(Double.isNaN(g), s.canonical() + " row " + i + " col " + k + ": want NaN got " + g);
} else if (Double.isInfinite(w)) {
assertTrue(Double.isInfinite(g) && Math.signum(g) == Math.signum(w),
s.canonical() + " row " + i + " col " + k + ": want " + w + " got " + g);
} else {
double tol = TOL * Math.max(1.0, Math.abs(w));
assertTrue(Math.abs(g - w) <= tol,
s.canonical() + " row " + i + " col " + k + ": got " + g + " want " + w);
}
}
}
}));
}
return tests;
}
}
'''
out = TEMPLATE.replace("%SPECS%", specs_block)
dest = os.path.join(ROOT, "bindings", "java", "src", "test", "java", "org", "wickra", "GoldenAllTest.java")
open(dest, "w", encoding="utf-8").write(out)
print("generated GoldenAllTest.java with", len(MAN), "indicators")
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>org.wickra</groupId>
<artifactId>wickra</artifactId>
<version>0.9.0</version>
<version>0.9.2</version>
<packaging>jar</packaging>
<name>Wickra</name>
@@ -71,6 +71,26 @@ public final class AbandonedBaby implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ABANDONED_BABY_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ABANDONED_BABY_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class Abcd implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ABCD_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ABCD_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -47,16 +47,36 @@ public final class AbsoluteBreadthIndex implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_ABSOLUTE_BREADTH_INDEX_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ABSOLUTE_BREADTH_INDEX_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ABSOLUTE_BREADTH_INDEX_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -47,6 +47,26 @@ public final class AccelerationBands implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ACCELERATION_BANDS_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ACCELERATION_BANDS_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -80,6 +80,26 @@ public final class AcceleratorOscillator implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ACCELERATOR_OSCILLATOR_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ACCELERATOR_OSCILLATOR_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class AdOscillator implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AD_OSCILLATOR_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AD_OSCILLATOR_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -47,16 +47,36 @@ public final class AdVolumeLine implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_AD_VOLUME_LINE_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AD_VOLUME_LINE_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AD_VOLUME_LINE_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -74,6 +74,26 @@ public final class AdaptiveCci implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ADAPTIVE_CCI_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ADAPTIVE_CCI_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -51,6 +51,26 @@ public final class AdaptiveCycle implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ADAPTIVE_CYCLE_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ADAPTIVE_CYCLE_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -54,6 +54,26 @@ public final class AdaptiveLaguerreFilter implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ADAPTIVE_LAGUERRE_FILTER_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ADAPTIVE_LAGUERRE_FILTER_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -54,6 +54,26 @@ public final class AdaptiveRsi implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ADAPTIVE_RSI_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ADAPTIVE_RSI_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class Adl implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ADL_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ADL_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class AdvanceBlock implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ADVANCE_BLOCK_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ADVANCE_BLOCK_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -47,16 +47,36 @@ public final class AdvanceDecline implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_ADVANCE_DECLINE_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ADVANCE_DECLINE_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ADVANCE_DECLINE_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -47,16 +47,36 @@ public final class AdvanceDeclineRatio implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_ADVANCE_DECLINE_RATIO_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ADVANCE_DECLINE_RATIO_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ADVANCE_DECLINE_RATIO_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -47,6 +47,26 @@ public final class Adx implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ADX_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ADX_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -74,6 +74,26 @@ public final class Adxr implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ADXR_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ADXR_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -53,6 +53,26 @@ public final class Alligator implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ALLIGATOR_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ALLIGATOR_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -54,6 +54,26 @@ public final class Alma implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ALMA_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ALMA_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -58,6 +58,26 @@ public final class Alpha implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ALPHA_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ALPHA_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -39,6 +39,26 @@ public final class AmihudIlliquidity implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AMIHUD_ILLIQUIDITY_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AMIHUD_ILLIQUIDITY_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -51,6 +51,26 @@ public final class AnchoredRsi implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ANCHORED_RSI_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ANCHORED_RSI_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class AnchoredVwap implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ANCHORED_VWAP_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ANCHORED_VWAP_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -47,6 +47,26 @@ public final class AndrewsPitchfork implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ANDREWS_PITCHFORK_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ANDREWS_PITCHFORK_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -57,6 +57,26 @@ public final class Apo implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_APO_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_APO_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -46,6 +46,26 @@ public final class Aroon implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AROON_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AROON_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -74,6 +74,26 @@ public final class AroonOscillator implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AROON_OSCILLATOR_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AROON_OSCILLATOR_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -74,6 +74,26 @@ public final class Atr implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ATR_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ATR_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -47,6 +47,26 @@ public final class AtrBands implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ATR_BANDS_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ATR_BANDS_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -46,6 +46,26 @@ public final class AtrRatchet implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ATR_RATCHET_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ATR_RATCHET_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -74,6 +74,26 @@ public final class AtrTrailingStop implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_ATR_TRAILING_STOP_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_ATR_TRAILING_STOP_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -48,6 +48,26 @@ public final class AutoFib implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AUTO_FIB_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AUTO_FIB_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -57,6 +57,26 @@ public final class Autocorrelation implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AUTOCORRELATION_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AUTOCORRELATION_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -57,6 +57,26 @@ public final class AutocorrelationPeriodogram implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AUTOCORRELATION_PERIODOGRAM_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AUTOCORRELATION_PERIODOGRAM_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -74,6 +74,26 @@ public final class AverageDailyRange implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AVERAGE_DAILY_RANGE_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AVERAGE_DAILY_RANGE_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -54,6 +54,26 @@ public final class AverageDrawdown implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AVERAGE_DRAWDOWN_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AVERAGE_DRAWDOWN_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class AvgPrice implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AVG_PRICE_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AVG_PRICE_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -77,6 +77,26 @@ public final class AwesomeOscillator implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AWESOME_OSCILLATOR_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AWESOME_OSCILLATOR_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -80,6 +80,26 @@ public final class AwesomeOscillatorHistogram implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_AWESOME_OSCILLATOR_HISTOGRAM_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_AWESOME_OSCILLATOR_HISTOGRAM_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class BalanceOfPower implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BALANCE_OF_POWER_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BALANCE_OF_POWER_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -54,6 +54,26 @@ public final class BandpassFilter implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BANDPASS_FILTER_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BANDPASS_FILTER_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class Bat implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BAT_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BAT_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class BeltHold implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BELT_HOLD_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BELT_HOLD_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -58,6 +58,26 @@ public final class Beta implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BETA_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BETA_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -58,6 +58,26 @@ public final class BetaNeutralSpread implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BETA_NEUTRAL_SPREAD_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BETA_NEUTRAL_SPREAD_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -74,6 +74,26 @@ public final class BetterVolume implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BETTER_VOLUME_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BETTER_VOLUME_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -54,6 +54,26 @@ public final class BipowerVariation implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BIPOWER_VARIATION_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BIPOWER_VARIATION_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class BodySizePct implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BODY_SIZE_PCT_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BODY_SIZE_PCT_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -48,6 +48,26 @@ public final class BollingerBands implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BOLLINGER_BANDS_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BOLLINGER_BANDS_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -54,6 +54,26 @@ public final class BollingerBandwidth implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BOLLINGER_BANDWIDTH_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BOLLINGER_BANDWIDTH_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -47,6 +47,26 @@ public final class BomarBands implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BOMAR_BANDS_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BOMAR_BANDS_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -50,16 +50,36 @@ public final class BreadthThrust implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_BREADTH_THRUST_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BREADTH_THRUST_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BREADTH_THRUST_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class Breakaway implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BREAKAWAY_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BREAKAWAY_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -47,16 +47,36 @@ public final class BullishPercentIndex implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_BULLISH_PERCENT_INDEX_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BULLISH_PERCENT_INDEX_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BULLISH_PERCENT_INDEX_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -54,6 +54,26 @@ public final class BurkeRatio implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BURKE_RATIO_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BURKE_RATIO_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -71,6 +71,26 @@ public final class Butterfly implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_BUTTERFLY_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_BUTTERFLY_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {
@@ -36,6 +36,26 @@ public final class CalendarSpread implements AutoCloseable {
}
}
/** Number of updates required before update() yields a value. */
public int warmupPeriod() {
try {
long n = (long) NativeMethods.WICKRA_CALENDAR_SPREAD_WARMUP_PERIOD.invokeExact(handle);
return (int) n;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Whether the indicator has consumed enough input to emit a value. */
public boolean isReady() {
try {
byte r = (byte) NativeMethods.WICKRA_CALENDAR_SPREAD_IS_READY.invokeExact(handle);
return r != 0;
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
}
/** Reset to the just-constructed state. */
public void reset() {
try {

Some files were not shown because too many files have changed in this diff Show More