Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f0677d62d | |||
| ce792cf8b8 | |||
| 120b6ac265 | |||
| 4f708d410d | |||
| de1112ea91 | |||
| 82d7479011 | |||
| a3950bf31b | |||
| a0a7bc4e62 | |||
| 0c925aa9d5 | |||
| d9e6807ca0 | |||
| eb50ae4e90 | |||
| 4a12f60a88 | |||
| 8003572d92 | |||
| db5bd5e028 | |||
| 2ef60874b9 | |||
| d59cd44043 |
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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[t−lookback]`; 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
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -18,12 +18,13 @@
|
||||
[](https://www.bestpractices.dev/projects/13094)
|
||||
[](https://github.com/wickra-lib/wickra/attestations)
|
||||
[](https://docs.wickra.org)
|
||||
[](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 **11–56×**
|
||||
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 |
|
||||
|------------------|-------------|-------------|-----------------------------|-----------:|--------|
|
||||
| **★ Wickra**| **clean** | **yes, O(1)** | **Rust · Python · Node · WASM · C · C# · Go · Java · R** | **514** | **yes** |
|
||||
| **★ 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,18 @@
|
||||
# Wickra — .NET
|
||||
# Wickra — C#
|
||||
|
||||
[](https://github.com/wickra-lib/wickra/actions/workflows/ci.yml)
|
||||
[](https://codecov.io/gh/wickra-lib/wickra)
|
||||
[](https://www.nuget.org/packages/Wickra)
|
||||
[](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
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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")
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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_<Canonical>.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")
|
||||
@@ -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
Reference in New Issue
Block a user