From 91e05e3c26df0938d6494741d0c6f46668bec344 Mon Sep 17 00:00:00 2001 From: kingchenc Date: Tue, 9 Jun 2026 02:07:03 +0200 Subject: [PATCH] C ABI hub crate (bindings/c) foundation (#222) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What Introduces `wickra-c` — a `cdylib` + `staticlib` that exposes the Rust core over a **C ABI**. This is the hub every C-capable language (C, C++, Go, C#, Java, R) links against, instead of re-wiring each indicator natively. The native Python/Node/WASM bindings are untouched; this is purely additive, for ecosystems without first-class Rust tooling. ## Scope (foundation slice) This PR deliberately validates the **whole pipeline end to end with one indicator (SMA)** before scaling to all 514, so the CI / cross-OS / header-drift mechanics are proven green first. - Opaque `*mut T` handles; `wickra__{new,update,batch,reset,free}`. - NaN sentinel for warmup / NULL handles; caller-owned batch buffers; every function NULL-safe. - cbindgen generates and commits `bindings/c/include/wickra.h` with opaque handle typedefs. - A C smoke example (`examples/c/`) links the header + compiled library and runs (CMake + ctest). - A `c-abi` CI job builds the library and runs the smoke test on **Linux, macOS and Windows**, plus a header drift check on Linux. ## Notes - The per-indicator FFI blocks are plain `#[no_mangle]` functions, **not** a macro: cbindgen cannot see macro-generated functions on stable Rust (macro expansion needs nightly), so the blocks are written literally and will be generated mechanically by the ScriptHelpers `capi` wrapper in a follow-up (same model as the committed-but-generated Node `index.js`). - `bindings/c` cannot inherit the workspace `forbid(unsafe_code)` lint (the C boundary needs raw pointers), so it mirrors every workspace lint and only relaxes `unsafe_code`. The Rust core stays `unsafe`-forbidden. ## Follow-ups (separate PRs) - ScriptHelpers `capi` generator + wire the scalar family (~235). - Hand-written blocks for multi-output / custom-input / bars (~279). - Docs consistency wave (README / docs / webpage: Python·Node·WASM·Rust → +C). - Release wiring (native-lib matrix + header/lib GH-release assets) — gated. --- .gitattributes | 5 + .github/workflows/ci.yml | 55 + .github/workflows/release.yml | 62 +- CHANGELOG.md | 6 + CONTRIBUTING.md | 5 +- Cargo.lock | 7 + Cargo.toml | 1 + README.md | 35 +- bindings/c/Cargo.toml | 46 + bindings/c/README.md | 59 + bindings/c/cbindgen.toml | 20 + bindings/c/include/wickra.h | 10658 ++++++++ bindings/c/include/wickra.hpp | 66 + bindings/c/src/lib.rs | 44291 ++++++++++++++++++++++++++++++++ examples/README.md | 13 + examples/c/CMakeLists.txt | 53 + examples/c/README.md | 68 + examples/c/smoke.c | 64 + examples/c/smoke.cpp | 36 + examples/c/streaming.c | 36 + 20 files changed, 55571 insertions(+), 15 deletions(-) create mode 100644 bindings/c/Cargo.toml create mode 100644 bindings/c/README.md create mode 100644 bindings/c/cbindgen.toml create mode 100644 bindings/c/include/wickra.h create mode 100644 bindings/c/include/wickra.hpp create mode 100644 bindings/c/src/lib.rs create mode 100644 examples/c/CMakeLists.txt create mode 100644 examples/c/README.md create mode 100644 examples/c/smoke.c create mode 100644 examples/c/smoke.cpp create mode 100644 examples/c/streaming.c diff --git a/.gitattributes b/.gitattributes index 60b14774..1ae4ab5d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,8 @@ # Shell scripts must keep LF line endings so they run on Linux/macOS CI and # local shells regardless of the committer's platform autocrlf setting. *.sh text eol=lf + +# The cbindgen-generated C header is committed; pin it to LF so its CI drift +# check (regenerate + `git diff`) never trips on a CRLF normalization. +bindings/c/include/wickra.h text eol=lf + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dccd7c5c..4f68bfdb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -622,6 +622,61 @@ jobs: working-directory: bindings/node run: node --test __tests__/ + c-abi: + name: C ABI on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 + + - name: Cache cargo + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 + continue-on-error: true # cache is an optimisation; never block on a stuck/slow restore + timeout-minutes: 6 + + - name: Install cbindgen + uses: taiki-e/install-action@0fd46367812ee04360509b4169d9f659d6892bb2 # v2.79.15 + timeout-minutes: 10 # fail fast on a stuck download instead of hanging the job + with: + tool: cbindgen + + - name: Build the C ABI library (cdylib + staticlib) + run: cargo build -p wickra-c --release + + - name: Rust unit tests + run: cargo test -p wickra-c + + # The generated header is platform-independent, so checking drift on one OS + # is enough — and avoids a spurious CRLF/LF diff on the Windows runner. + - name: Check the committed header is in sync with cbindgen + if: runner.os == 'Linux' + shell: bash + run: | + cbindgen --config bindings/c/cbindgen.toml --crate wickra-c --output bindings/c/include/wickra.h + if ! git diff --quiet -- bindings/c/include/wickra.h; then + echo "::error::bindings/c/include/wickra.h is out of sync — run cbindgen and commit the result" + git --no-pager diff -- bindings/c/include/wickra.h + exit 1 + fi + + # The real cross-language test: a foreign C consumer links the generated + # header + the compiled library and runs. If this passes on all three OSes, + # every C-capable language can link the same way. + - name: Build and run the C smoke example (CMake + ctest) + shell: bash + run: | + cmake -S examples/c -B examples/c/build + cmake --build examples/c/build --config Release + ctest --test-dir examples/c/build -C Release --output-on-failure + # The cross-library benchmark has moved to a dedicated scheduled workflow # (.github/workflows/bench.yml) — see audit finding R10. It runs nightly # at 03:00 UTC and on-demand via `workflow_dispatch`, and is no longer on diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 232890b7..60c0c2a7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -569,9 +569,64 @@ jobs: # the old "publish, then upload provenance" order would have the provenance # upload rejected once immutability is enabled. # -------------------------------------------------------------------------- + # -------------------------------------------------------------------------- + # C ABI native libraries (bindings/c) — built per target on a native runner + # (no cross toolchain needed) and attached to the GitHub Release as the + # distribution channel. There is no package registry for the C ABI. + # -------------------------------------------------------------------------- + c-abi-build: + name: C ABI library (${{ matrix.target }}) + strategy: + fail-fast: false + matrix: + include: + - { host: ubuntu-latest, target: x86_64-unknown-linux-gnu } + - { host: ubuntu-24.04-arm, target: aarch64-unknown-linux-gnu } + - { host: macos-latest, target: x86_64-apple-darwin } + - { host: macos-latest, target: aarch64-apple-darwin } + - { host: windows-latest, target: x86_64-pc-windows-msvc } + - { host: windows-11-arm, target: aarch64-pc-windows-msvc } + runs-on: ${{ matrix.host }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 + with: + targets: ${{ matrix.target }} + + - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 + continue-on-error: true # cache is an optimisation; never block on a stuck/slow restore + timeout-minutes: 6 + + - name: Build the C ABI library (cdylib + staticlib) + run: cargo build -p wickra-c --release --target ${{ matrix.target }} + + - name: Package header + libraries + shell: bash + run: | + set -e + dir="wickra-c-${{ matrix.target }}" + mkdir -p "$dir/include" "$dir/lib" + cp bindings/c/include/wickra.h bindings/c/include/wickra.hpp "$dir/include/" + for f in libwickra.so libwickra.a libwickra.dylib wickra.dll wickra.dll.lib wickra.lib; do + src="target/${{ matrix.target }}/release/$f" + [ -f "$src" ] && cp "$src" "$dir/lib/" + done + tar -czf "$dir.tar.gz" "$dir" + echo "packaged $dir:"; ls -lR "$dir" + + - name: Upload artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: c-abi-${{ matrix.target }} + path: wickra-c-${{ matrix.target }}.tar.gz + if-no-files-found: error + github-release: name: Attach assets to the draft GitHub Release - needs: [cargo-publish, python-publish, node-publish, wasm-publish] + needs: [cargo-publish, python-publish, node-publish, wasm-publish, c-abi-build] runs-on: ubuntu-latest permissions: contents: write @@ -644,7 +699,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. + Wickra ${{ github.ref_name }} — streaming-first technical indicators across 4 language registries plus a C ABI. ### Install @@ -665,6 +720,9 @@ jobs: 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-.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 ### Auto-generated changelog diff --git a/CHANGELOG.md b/CHANGELOG.md index adaf1ba1..f0701125 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- **C ABI (`bindings/c`)** — a `cdylib` + `staticlib` plus a generated + `include/wickra.h` exposing all 514 indicators and 10 bar builders over an + opaque-handle C ABI: the hub any C-capable language (C, C++, Go, C#, Java, R) + links against, complementing the native Python/Node/WASM bindings. Ships C + smoke + streaming examples and an optional `wickra.hpp` C++ RAII wrapper. ## [0.7.4] - 2026-06-08 - **Three-Line Break** — Three-line-break bars (reversal needs N-line break) (`THREE_LINE_BREAK_BARS`). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7c11145d..6c72a0c6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,6 +21,7 @@ licensed as above, without any additional terms or conditions. | `bindings/python` | PyO3 bindings (`wickra` on PyPI). | | `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. | | `examples/` | Runnable examples. | | `docs/` | Pointer to the documentation site (docs.wickra.org); the docs live in the `wickra-lib/wickra-docs` repo. | @@ -102,7 +103,9 @@ 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`. + Python, Node, and WASM bindings, including their type stubs / `.d.ts`. The C ABI + (`bindings/c`) is generated from the core, so regenerate it (the ScriptHelpers + `capi` wrapper) and commit `src/lib.rs` + `include/wickra.h`. - **Docs.** Update the relevant page on the [documentation site](https://docs.wickra.org) and the `README.md` when behaviour or the public API changes. The docs live in diff --git a/Cargo.lock b/Cargo.lock index 6e2cb94f..08858e6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1965,6 +1965,13 @@ dependencies = [ "yata", ] +[[package]] +name = "wickra-c" +version = "0.7.4" +dependencies = [ + "wickra-core", +] + [[package]] name = "wickra-core" version = "0.7.4" diff --git a/Cargo.toml b/Cargo.toml index 565b494d..887c61d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "bindings/python", "bindings/wasm", "bindings/node", + "bindings/c", "examples/rust", "crates/wickra-bench", ] diff --git a/README.md b/README.md index ca8cade4..c0fe8563 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,9 @@ **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 -bindings for Python, Node.js, and WebAssembly. Every indicator is a state -machine that updates in O(1) per new data point, so live trading bots and +native bindings for Python, Node.js and WebAssembly, plus a C ABI that any +C-capable language (C, C++, and beyond) 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. ```python @@ -71,9 +72,10 @@ 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, four first-class targets.** Native **Python · Node.js · - WebAssembly · Rust** — identical math, identical results, zero per-language - reimplementation and zero GIL bottleneck. +- **One Rust core, five first-class targets.** Native **Python · Node.js · + WebAssembly · Rust** plus a **C ABI** for C / C++ and any 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 @@ -95,7 +97,7 @@ Every other library forces one of those compromises. Wickra doesn't: | Library | Install | Streaming | Languages | Indicators | Active | |------------------|-------------|-------------|-----------------------------|-----------:|--------| -| **★ Wickra**| **clean** | **yes, O(1)** | **Python · Node · WASM · Rust** | **514** | **yes** | +| **★ Wickra**| **clean** | **yes, O(1)** | **Python · Node · WASM · Rust · C** | **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 | @@ -166,8 +168,8 @@ as one column each. `Doji` is direction-less by default (`+1.0` / `0.0`); construct it in signed mode (`Doji::new().signed()`, `Doji(signed=True)`, `new Doji(true)`) for a dragonfly / gravestone `±1` reading. -Adding a new indicator means implementing one trait in Rust; all four bindings -inherit it automatically. +Adding a new indicator means implementing one trait in Rust; all five bindings +inherit it automatically (the C ABI is generated from the core). ## Languages @@ -177,12 +179,14 @@ inherit it automatically. | Node.js (napi-rs) | `npm install wickra` | `examples/node/backtest.js` | | 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` | Each binding ships several runnable examples (streaming, backtest, live feed); [`examples/README.md`](examples/README.md) is the full cross-language index. -The wickra-core crate is `unsafe`-forbidden, so every binding inherits a -memory-safe implementation. +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`). ## Rust API @@ -244,13 +248,15 @@ wickra/ ├── bindings/ │ ├── python/ PyO3 + maturin (publishes on PyPI) │ ├── node/ napi-rs (publishes on npm) -│ └── wasm/ wasm-bindgen (browsers, bundlers, Node) +│ ├── wasm/ wasm-bindgen (browsers, bundlers, Node) +│ └── c/ C ABI (cdylib + staticlib) + generated include/wickra.h ├── 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`) -│ └── wasm/ browser demo for `wickra-wasm` +│ ├── wasm/ browser demo for `wickra-wasm` +│ └── c/ C smoke + streaming, C++ RAII wrapper └── .github/workflows/ CI and release pipelines ``` @@ -278,6 +284,11 @@ wasm-pack build bindings/wasm --target web --release --features panic-hook # Node binding (requires @napi-rs/cli) cd bindings/node && npm install && npm run build && npm test + +# C ABI (cdylib + staticlib + generated header) +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 ``` ## Testing diff --git a/bindings/c/Cargo.toml b/bindings/c/Cargo.toml new file mode 100644 index 00000000..b6e8958d --- /dev/null +++ b/bindings/c/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "wickra-c" +description = "C ABI (cdylib + staticlib) for the Wickra streaming-first technical indicators library — the hub every C-capable language (C, C++, Go, C#, Java, R) links against." +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +readme = "README.md" +keywords.workspace = true +categories.workspace = true +publish = false + +[lib] +name = "wickra" +crate-type = ["cdylib", "staticlib"] + +# The C ABI inherently needs `unsafe` (raw pointers across the FFI boundary, +# `#[export_name]` symbol control). The workspace forbids `unsafe_code`, so this +# crate cannot inherit `workspace = true`; it mirrors every workspace lint and +# only relaxes `unsafe_code` to `allow` (parity with how the proc-macro bindings +# emit their unsafe). The Rust core stays `unsafe`-forbidden — this is the one +# crate where the boundary lives. +[lints.rust] +unsafe_code = "allow" +missing_debug_implementations = "warn" +unreachable_pub = "warn" +unused_must_use = "deny" + +[lints.clippy] +all = { level = "warn", priority = -1 } +pedantic = { level = "warn", priority = -1 } +module_name_repetitions = "allow" +must_use_candidate = "allow" +missing_errors_doc = "allow" +missing_panics_doc = "allow" +cast_precision_loss = "allow" +cast_possible_truncation = "allow" +cast_sign_loss = "allow" +similar_names = "allow" +float_cmp = "allow" + +[dependencies] +wickra-core = { workspace = true } diff --git a/bindings/c/README.md b/bindings/c/README.md new file mode 100644 index 00000000..36e62d18 --- /dev/null +++ b/bindings/c/README.md @@ -0,0 +1,59 @@ +# wickra-c + +C ABI for [Wickra](https://github.com/wickra-lib/wickra) — streaming-first +technical indicators with a Rust core. This crate is the **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 +of re-wiring every indicator natively. + +The native Python, Node, and WebAssembly bindings are unaffected — this is +additive, for the ecosystems without first-class Rust tooling. + +## Artifacts + +```sh +cargo build -p wickra-c --release +``` + +- `target/release/libwickra.{so,dylib}` / `wickra.dll` (+ `wickra.dll.lib` import lib on Windows) +- `target/release/libwickra.a` / `wickra.lib` (static) +- [`include/wickra.h`](include/wickra.h) — generated by cbindgen, committed. + +## API shape + +Each indicator is exposed as five `extern "C"` functions over an opaque handle: + +```c +struct Sma *wickra_sma_new(uintptr_t period); /* NULL on bad params */ +double wickra_sma_update(struct Sma *h, double value); /* NaN during warmup */ +void wickra_sma_batch(struct Sma *h, const double *in, double *out, uintptr_t n); +void wickra_sma_reset(struct Sma *h); +void wickra_sma_free(struct Sma *h); /* exactly once per _new */ +``` + +Conventions: + +- **Opaque handles.** `wickra__new` returns a `T *` you must release with + exactly one `wickra__free`. There is no RAII across the boundary. +- **NaN sentinel.** Scalar outputs return `NaN` while warming up or on a `NULL` + handle, mirroring the other bindings — no error codes for the common path. +- **Caller-owned batch buffers.** `wickra__batch` writes one output per + input into a buffer you provide; nothing is allocated across the boundary. +- **NULL-safe.** Every function tolerates a `NULL` handle without crashing. + +## Header regeneration + +The header is generated and committed; CI checks it is in sync: + +```sh +cbindgen --config bindings/c/cbindgen.toml --crate wickra-c --output bindings/c/include/wickra.h +``` + +## Examples + +Runnable C examples (build via CMake or a direct compiler invocation) live in +[`examples/c`](../../examples/c). + +## License + +`MIT OR Apache-2.0`, the same as the rest of Wickra. diff --git a/bindings/c/cbindgen.toml b/bindings/c/cbindgen.toml new file mode 100644 index 00000000..20ea02d8 --- /dev/null +++ b/bindings/c/cbindgen.toml @@ -0,0 +1,20 @@ +language = "C" +header = "/* Wickra C ABI — generated by cbindgen. Do not edit by hand. */" +include_guard = "WICKRA_H" +pragma_once = true +# Wrap the declarations in `extern "C"` under __cplusplus so the header is usable +# from C++ (the optional wickra.hpp RAII layer and any C++ consumer). +cpp_compat = true +tab_width = 4 +# Off: cbindgen copies the Rust struct/fn doc comments verbatim, and some core +# indicator docs contain markdown (e.g. `**1/8**/**7/8**`) whose `*/` would close +# the C block comment early and break the header. Usage docs live in the crate +# README and examples; the header is a pure declaration contract. +documentation = false + +[parse] +# Parse wickra-core too so the opaque indicator handle types (Sma, Ema, …) are +# discovered and emitted as forward-declared opaque structs. Their fields are +# never exposed — only `T *` handles cross the boundary. +parse_deps = true +include = ["wickra-core"] diff --git a/bindings/c/include/wickra.h b/bindings/c/include/wickra.h new file mode 100644 index 00000000..0c4ca4e9 --- /dev/null +++ b/bindings/c/include/wickra.h @@ -0,0 +1,10658 @@ +/* Wickra C ABI — generated by cbindgen. Do not edit by hand. */ + +#ifndef WICKRA_H +#define WICKRA_H + +#pragma once + +#include +#include +#include +#include + +typedef struct AbandonedBaby AbandonedBaby; + +typedef struct Abcd Abcd; + +typedef struct AbsoluteBreadthIndex AbsoluteBreadthIndex; + +typedef struct AccelerationBands AccelerationBands; + +typedef struct AcceleratorOscillator AcceleratorOscillator; + +typedef struct AdOscillator AdOscillator; + +typedef struct AdVolumeLine AdVolumeLine; + +typedef struct AdaptiveCci AdaptiveCci; + +typedef struct AdaptiveCycle AdaptiveCycle; + +typedef struct AdaptiveLaguerreFilter AdaptiveLaguerreFilter; + +typedef struct AdaptiveRsi AdaptiveRsi; + +typedef struct Adl Adl; + +typedef struct AdvanceBlock AdvanceBlock; + +typedef struct AdvanceDecline AdvanceDecline; + +typedef struct AdvanceDeclineRatio AdvanceDeclineRatio; + +typedef struct Adx Adx; + +typedef struct Adxr Adxr; + +typedef struct Alligator Alligator; + +typedef struct Alma Alma; + +typedef struct Alpha Alpha; + +typedef struct AmihudIlliquidity AmihudIlliquidity; + +typedef struct AnchoredRsi AnchoredRsi; + +typedef struct AnchoredVwap AnchoredVwap; + +typedef struct AndrewsPitchfork AndrewsPitchfork; + +typedef struct Apo Apo; + +typedef struct Aroon Aroon; + +typedef struct AroonOscillator AroonOscillator; + +typedef struct Atr Atr; + +typedef struct AtrBands AtrBands; + +typedef struct AtrRatchet AtrRatchet; + +typedef struct AtrTrailingStop AtrTrailingStop; + +typedef struct AutoFib AutoFib; + +typedef struct Autocorrelation Autocorrelation; + +typedef struct AutocorrelationPeriodogram AutocorrelationPeriodogram; + +typedef struct AverageDailyRange AverageDailyRange; + +typedef struct AverageDrawdown AverageDrawdown; + +typedef struct AvgPrice AvgPrice; + +typedef struct AwesomeOscillator AwesomeOscillator; + +typedef struct AwesomeOscillatorHistogram AwesomeOscillatorHistogram; + +typedef struct BalanceOfPower BalanceOfPower; + +typedef struct BandpassFilter BandpassFilter; + +typedef struct Bat Bat; + +typedef struct BeltHold BeltHold; + +typedef struct Beta Beta; + +typedef struct BetaNeutralSpread BetaNeutralSpread; + +typedef struct BetterVolume BetterVolume; + +typedef struct BipowerVariation BipowerVariation; + +typedef struct BodySizePct BodySizePct; + +typedef struct BollingerBands BollingerBands; + +typedef struct BollingerBandwidth BollingerBandwidth; + +typedef struct BomarBands BomarBands; + +typedef struct BreadthThrust BreadthThrust; + +typedef struct Breakaway Breakaway; + +typedef struct BullishPercentIndex BullishPercentIndex; + +typedef struct BurkeRatio BurkeRatio; + +typedef struct Butterfly Butterfly; + +typedef struct CalendarSpread CalendarSpread; + +typedef struct CalmarRatio CalmarRatio; + +typedef struct Camarilla Camarilla; + +typedef struct CandleVolume CandleVolume; + +typedef struct Cci Cci; + +typedef struct CenterOfGravity CenterOfGravity; + +typedef struct CentralPivotRange CentralPivotRange; + +typedef struct Cfo Cfo; + +typedef struct ChaikinMoneyFlow ChaikinMoneyFlow; + +typedef struct ChaikinOscillator ChaikinOscillator; + +typedef struct ChaikinVolatility ChaikinVolatility; + +typedef struct ChandeKrollStop ChandeKrollStop; + +typedef struct ChandelierExit ChandelierExit; + +typedef struct ChoppinessIndex ChoppinessIndex; + +typedef struct ClassicPivots ClassicPivots; + +typedef struct CloseVsOpen CloseVsOpen; + +typedef struct ClosingMarubozu ClosingMarubozu; + +typedef struct Cmo Cmo; + +typedef struct CoefficientOfVariation CoefficientOfVariation; + +typedef struct Cointegration Cointegration; + +typedef struct CommonSenseRatio CommonSenseRatio; + +typedef struct CompositeProfile CompositeProfile; + +typedef struct ConcealingBabySwallow ConcealingBabySwallow; + +typedef struct ConditionalValueAtRisk ConditionalValueAtRisk; + +typedef struct ConnorsRsi ConnorsRsi; + +typedef struct Coppock Coppock; + +typedef struct CorrelationTrendIndicator CorrelationTrendIndicator; + +typedef struct Counterattack Counterattack; + +typedef struct Crab Crab; + +typedef struct CumulativeVolumeDelta CumulativeVolumeDelta; + +typedef struct CumulativeVolumeIndex CumulativeVolumeIndex; + +typedef struct CupAndHandle CupAndHandle; + +typedef struct CyberneticCycle CyberneticCycle; + +typedef struct Cypher Cypher; + +typedef struct DayOfWeekProfile DayOfWeekProfile; + +typedef struct Decycler Decycler; + +typedef struct DecyclerOscillator DecyclerOscillator; + +typedef struct Dema Dema; + +typedef struct DemandIndex DemandIndex; + +typedef struct DemarkPivots DemarkPivots; + +typedef struct DepthSlope DepthSlope; + +typedef struct DerivativeOscillator DerivativeOscillator; + +typedef struct DetrendedStdDev DetrendedStdDev; + +typedef struct DisparityIndex DisparityIndex; + +typedef struct DistanceSsd DistanceSsd; + +typedef struct Doji Doji; + +typedef struct DojiStar DojiStar; + +typedef struct DollarBars DollarBars; + +typedef struct Donchian Donchian; + +typedef struct DonchianStop DonchianStop; + +typedef struct DoubleBollinger DoubleBollinger; + +typedef struct DoubleTopBottom DoubleTopBottom; + +typedef struct DownsideGapThreeMethods DownsideGapThreeMethods; + +typedef struct Dpo Dpo; + +typedef struct DragonflyDoji DragonflyDoji; + +typedef struct DrawdownDuration DrawdownDuration; + +typedef struct DumplingTop DumplingTop; + +typedef struct Dx Dx; + +typedef struct DynamicMomentumIndex DynamicMomentumIndex; + +typedef struct EaseOfMovement EaseOfMovement; + +typedef struct EffectiveSpread EffectiveSpread; + +typedef struct EhlersStochastic EhlersStochastic; + +typedef struct Ehma Ehma; + +typedef struct ElderImpulse ElderImpulse; + +typedef struct ElderRay ElderRay; + +typedef struct ElderSafeZone ElderSafeZone; + +typedef struct Ema Ema; + +typedef struct EmpiricalModeDecomposition EmpiricalModeDecomposition; + +typedef struct Engulfing Engulfing; + +typedef struct Equivolume Equivolume; + +typedef struct EstimatedLeverageRatio EstimatedLeverageRatio; + +typedef struct EvenBetterSinewave EvenBetterSinewave; + +typedef struct EveningDojiStar EveningDojiStar; + +typedef struct Evwma Evwma; + +typedef struct EwmaVolatility EwmaVolatility; + +typedef struct Expectancy Expectancy; + +typedef struct FallingThreeMethods FallingThreeMethods; + +typedef struct Fama Fama; + +typedef struct FibArcs FibArcs; + +typedef struct FibChannel FibChannel; + +typedef struct FibConfluence FibConfluence; + +typedef struct FibExtension FibExtension; + +typedef struct FibFan FibFan; + +typedef struct FibProjection FibProjection; + +typedef struct FibRetracement FibRetracement; + +typedef struct FibTimeZones FibTimeZones; + +typedef struct FibonacciPivots FibonacciPivots; + +typedef struct FisherRsi FisherRsi; + +typedef struct FisherTransform FisherTransform; + +typedef struct FlagPennant FlagPennant; + +typedef struct Footprint Footprint; + +typedef struct ForceIndex ForceIndex; + +typedef struct FractalChaosBands FractalChaosBands; + +typedef struct Frama Frama; + +typedef struct FryPanBottom FryPanBottom; + +typedef struct FundingBasis FundingBasis; + +typedef struct FundingImpliedApr FundingImpliedApr; + +typedef struct FundingRate FundingRate; + +typedef struct FundingRateMean FundingRateMean; + +typedef struct FundingRateZScore FundingRateZScore; + +typedef struct GainLossRatio GainLossRatio; + +typedef struct GainToPainRatio GainToPainRatio; + +typedef struct GapSideBySideWhite GapSideBySideWhite; + +typedef struct Garch11 Garch11; + +typedef struct GarmanKlassVolatility GarmanKlassVolatility; + +typedef struct Gartley Gartley; + +typedef struct GatorOscillator GatorOscillator; + +typedef struct GeneralizedDema GeneralizedDema; + +typedef struct GeometricMa GeometricMa; + +typedef struct GoldenPocket GoldenPocket; + +typedef struct GrangerCausality GrangerCausality; + +typedef struct GravestoneDoji GravestoneDoji; + +typedef struct Hammer Hammer; + +typedef struct HangingMan HangingMan; + +typedef struct Harami Harami; + +typedef struct HaramiCross HaramiCross; + +typedef struct HasbrouckInformationShare HasbrouckInformationShare; + +typedef struct HeadAndShoulders HeadAndShoulders; + +typedef struct HeikinAshi HeikinAshi; + +typedef struct HeikinAshiOscillator HeikinAshiOscillator; + +typedef struct HiLoActivator HiLoActivator; + +typedef struct HighLowIndex HighLowIndex; + +typedef struct HighLowRange HighLowRange; + +typedef struct HighLowVolumeNodes HighLowVolumeNodes; + +typedef struct HighWave HighWave; + +typedef struct HighpassFilter HighpassFilter; + +typedef struct Hikkake Hikkake; + +typedef struct HikkakeModified HikkakeModified; + +typedef struct HilbertDominantCycle HilbertDominantCycle; + +typedef struct HistoricalVolatility HistoricalVolatility; + +typedef struct Hma Hma; + +typedef struct HoltWinters HoltWinters; + +typedef struct HomingPigeon HomingPigeon; + +typedef struct HtDcPhase HtDcPhase; + +typedef struct HtPhasor HtPhasor; + +typedef struct HtTrendMode HtTrendMode; + +typedef struct HurstChannel HurstChannel; + +typedef struct HurstExponent HurstExponent; + +typedef struct Ichimoku Ichimoku; + +typedef struct IdenticalThreeCrows IdenticalThreeCrows; + +typedef struct ImbalanceBars ImbalanceBars; + +typedef struct InNeck InNeck; + +typedef struct Inertia Inertia; + +typedef struct InformationRatio InformationRatio; + +typedef struct InitialBalance InitialBalance; + +typedef struct InstantaneousTrendline InstantaneousTrendline; + +typedef struct IntradayIntensity IntradayIntensity; + +typedef struct IntradayMomentumIndex IntradayMomentumIndex; + +typedef struct IntradayVolatilityProfile IntradayVolatilityProfile; + +typedef struct InverseFisherTransform InverseFisherTransform; + +typedef struct InvertedHammer InvertedHammer; + +typedef struct JarqueBera JarqueBera; + +typedef struct Jma Jma; + +typedef struct JumpIndicator JumpIndicator; + +typedef struct KRatio KRatio; + +typedef struct KagiBars KagiBars; + +typedef struct KalmanHedgeRatio KalmanHedgeRatio; + +typedef struct Kama Kama; + +typedef struct KaseDevStop KaseDevStop; + +typedef struct KasePermissionStochastic KasePermissionStochastic; + +typedef struct KellyCriterion KellyCriterion; + +typedef struct Keltner Keltner; + +typedef struct KendallTau KendallTau; + +typedef struct Kicking Kicking; + +typedef struct KickingByLength KickingByLength; + +typedef struct Kst Kst; + +typedef struct Kurtosis Kurtosis; + +typedef struct Kvo Kvo; + +typedef struct KylesLambda KylesLambda; + +typedef struct LadderBottom LadderBottom; + +typedef struct LaguerreRsi LaguerreRsi; + +typedef struct LeadLagCrossCorrelation LeadLagCrossCorrelation; + +typedef struct LinRegAngle LinRegAngle; + +typedef struct LinRegChannel LinRegChannel; + +typedef struct LinRegIntercept LinRegIntercept; + +typedef struct LinRegSlope LinRegSlope; + +typedef struct LinearRegression LinearRegression; + +typedef struct LiquidationFeatures LiquidationFeatures; + +typedef struct LogReturn LogReturn; + +typedef struct LongLeggedDoji LongLeggedDoji; + +typedef struct LongLine LongLine; + +typedef struct LongShortRatio LongShortRatio; + +typedef struct M2Measure M2Measure; + +typedef struct MaEnvelope MaEnvelope; + +typedef struct MacdExt MacdExt; + +typedef struct MacdFix MacdFix; + +typedef struct MacdHistogram MacdHistogram; + +typedef struct MacdIndicator MacdIndicator; + +typedef struct Mama Mama; + +typedef struct MarketFacilitationIndex MarketFacilitationIndex; + +typedef struct MartinRatio MartinRatio; + +typedef struct Marubozu Marubozu; + +typedef struct MassIndex MassIndex; + +typedef struct MatHold MatHold; + +typedef struct MatchingLow MatchingLow; + +typedef struct MaxDrawdown MaxDrawdown; + +typedef struct McClellanOscillator McClellanOscillator; + +typedef struct McClellanSummationIndex McClellanSummationIndex; + +typedef struct McGinleyDynamic McGinleyDynamic; + +typedef struct MedianAbsoluteDeviation MedianAbsoluteDeviation; + +typedef struct MedianChannel MedianChannel; + +typedef struct MedianMa MedianMa; + +typedef struct MedianPrice MedianPrice; + +typedef struct Mfi Mfi; + +typedef struct Microprice Microprice; + +typedef struct MidPoint MidPoint; + +typedef struct MidPrice MidPrice; + +typedef struct MinusDi MinusDi; + +typedef struct MinusDm MinusDm; + +typedef struct ModifiedMaStop ModifiedMaStop; + +typedef struct Mom Mom; + +typedef struct MorningDojiStar MorningDojiStar; + +typedef struct MorningEveningStar MorningEveningStar; + +typedef struct MurreyMathLines MurreyMathLines; + +typedef struct NakedPoc NakedPoc; + +typedef struct Natr Natr; + +typedef struct NewHighsNewLows NewHighsNewLows; + +typedef struct NewPriceLines NewPriceLines; + +typedef struct Nrtr Nrtr; + +typedef struct Nvi Nvi; + +typedef struct OIPriceDivergence OIPriceDivergence; + +typedef struct OIWeighted OIWeighted; + +typedef struct Obv Obv; + +typedef struct OiToVolumeRatio OiToVolumeRatio; + +typedef struct OmegaRatio OmegaRatio; + +typedef struct OnNeck OnNeck; + +typedef struct OpenInterestDelta OpenInterestDelta; + +typedef struct OpenInterestMomentum OpenInterestMomentum; + +typedef struct OpeningMarubozu OpeningMarubozu; + +typedef struct OpeningRange OpeningRange; + +typedef struct OrderBookImbalanceFull OrderBookImbalanceFull; + +typedef struct OrderBookImbalanceTop1 OrderBookImbalanceTop1; + +typedef struct OrderBookImbalanceTopN OrderBookImbalanceTopN; + +typedef struct OrderFlowImbalance OrderFlowImbalance; + +typedef struct OuHalfLife OuHalfLife; + +typedef struct OvernightGap OvernightGap; + +typedef struct OvernightIntradayReturn OvernightIntradayReturn; + +typedef struct PainIndex PainIndex; + +typedef struct PairSpreadZScore PairSpreadZScore; + +typedef struct PairwiseBeta PairwiseBeta; + +typedef struct ParkinsonVolatility ParkinsonVolatility; + +typedef struct PearsonCorrelation PearsonCorrelation; + +typedef struct PercentAboveMa PercentAboveMa; + +typedef struct PercentB PercentB; + +typedef struct PercentageTrailingStop PercentageTrailingStop; + +typedef struct PerpetualPremiumIndex PerpetualPremiumIndex; + +typedef struct Pgo Pgo; + +typedef struct PiercingDarkCloud PiercingDarkCloud; + +typedef struct Pin Pin; + +typedef struct PivotReversal PivotReversal; + +typedef struct PlusDi PlusDi; + +typedef struct PlusDm PlusDm; + +typedef struct Pmo Pmo; + +typedef struct PointAndFigureBars PointAndFigureBars; + +typedef struct PolarizedFractalEfficiency PolarizedFractalEfficiency; + +typedef struct Ppo Ppo; + +typedef struct PpoHistogram PpoHistogram; + +typedef struct ProfileShape ProfileShape; + +typedef struct ProfitFactor ProfitFactor; + +typedef struct ProjectionBands ProjectionBands; + +typedef struct ProjectionOscillator ProjectionOscillator; + +typedef struct Psar Psar; + +typedef struct Pvi Pvi; + +typedef struct Qqe Qqe; + +typedef struct Qstick Qstick; + +typedef struct QuartileBands QuartileBands; + +typedef struct QuotedSpread QuotedSpread; + +typedef struct RSquared RSquared; + +typedef struct RangeBars RangeBars; + +typedef struct RealizedSpread RealizedSpread; + +typedef struct RealizedVolatility RealizedVolatility; + +typedef struct RecoveryFactor RecoveryFactor; + +typedef struct RectangleRange RectangleRange; + +typedef struct Reflex Reflex; + +typedef struct RegimeLabel RegimeLabel; + +typedef struct RelativeStrengthAB RelativeStrengthAB; + +typedef struct RenkoBars RenkoBars; + +typedef struct RenkoTrailingStop RenkoTrailingStop; + +typedef struct RickshawMan RickshawMan; + +typedef struct RisingThreeMethods RisingThreeMethods; + +typedef struct Rmi Rmi; + +typedef struct Roc Roc; + +typedef struct Rocp Rocp; + +typedef struct Rocr Rocr; + +typedef struct Rocr100 Rocr100; + +typedef struct RogersSatchellVolatility RogersSatchellVolatility; + +typedef struct RollMeasure RollMeasure; + +typedef struct RollingCorrelation RollingCorrelation; + +typedef struct RollingCovariance RollingCovariance; + +typedef struct RollingIqr RollingIqr; + +typedef struct RollingMinMaxScaler RollingMinMaxScaler; + +typedef struct RollingPercentileRank RollingPercentileRank; + +typedef struct RollingQuantile RollingQuantile; + +typedef struct RollingVwap RollingVwap; + +typedef struct RoofingFilter RoofingFilter; + +typedef struct Rsi Rsi; + +typedef struct Rsx Rsx; + +typedef struct RunBars RunBars; + +typedef struct Rvi Rvi; + +typedef struct RviVolatility RviVolatility; + +typedef struct Rwi Rwi; + +typedef struct SampleEntropy SampleEntropy; + +typedef struct SarExt SarExt; + +typedef struct SeasonalZScore SeasonalZScore; + +typedef struct SeparatingLines SeparatingLines; + +typedef struct SessionHighLow SessionHighLow; + +typedef struct SessionRange SessionRange; + +typedef struct SessionVwap SessionVwap; + +typedef struct ShannonEntropy ShannonEntropy; + +typedef struct Shark Shark; + +typedef struct SharpeRatio SharpeRatio; + +typedef struct ShootingStar ShootingStar; + +typedef struct ShortLine ShortLine; + +typedef struct SignedVolume SignedVolume; + +typedef struct SineWave SineWave; + +typedef struct SineWeightedMa SineWeightedMa; + +typedef struct SinglePrints SinglePrints; + +typedef struct Skewness Skewness; + +typedef struct Sma Sma; + +typedef struct Smi Smi; + +typedef struct Smma Smma; + +typedef struct SmoothedHeikinAshi SmoothedHeikinAshi; + +typedef struct SortinoRatio SortinoRatio; + +typedef struct SpearmanCorrelation SpearmanCorrelation; + +typedef struct SpinningTop SpinningTop; + +typedef struct SpreadAr1Coefficient SpreadAr1Coefficient; + +typedef struct SpreadBollingerBands SpreadBollingerBands; + +typedef struct SpreadHurst SpreadHurst; + +typedef struct StalledPattern StalledPattern; + +typedef struct StandardError StandardError; + +typedef struct StandardErrorBands StandardErrorBands; + +typedef struct StarcBands StarcBands; + +typedef struct Stc Stc; + +typedef struct StdDev StdDev; + +typedef struct StepTrailingStop StepTrailingStop; + +typedef struct SterlingRatio SterlingRatio; + +typedef struct StickSandwich StickSandwich; + +typedef struct StochRsi StochRsi; + +typedef struct Stochastic Stochastic; + +typedef struct StochasticCci StochasticCci; + +typedef struct SuperSmoother SuperSmoother; + +typedef struct SuperTrend SuperTrend; + +typedef struct T3 T3; + +typedef struct TailRatio TailRatio; + +typedef struct TakerBuySellRatio TakerBuySellRatio; + +typedef struct Takuri Takuri; + +typedef struct TasukiGap TasukiGap; + +typedef struct TdCamouflage TdCamouflage; + +typedef struct TdClop TdClop; + +typedef struct TdClopwin TdClopwin; + +typedef struct TdCombo TdCombo; + +typedef struct TdCountdown TdCountdown; + +typedef struct TdDWave TdDWave; + +typedef struct TdDeMarker TdDeMarker; + +typedef struct TdDifferential TdDifferential; + +typedef struct TdLines TdLines; + +typedef struct TdMovingAverage TdMovingAverage; + +typedef struct TdOpen TdOpen; + +typedef struct TdPressure TdPressure; + +typedef struct TdPropulsion TdPropulsion; + +typedef struct TdRangeProjection TdRangeProjection; + +typedef struct TdRei TdRei; + +typedef struct TdRiskLevel TdRiskLevel; + +typedef struct TdSequential TdSequential; + +typedef struct TdSetup TdSetup; + +typedef struct TdTrap TdTrap; + +typedef struct Tema Tema; + +typedef struct TermStructureBasis TermStructureBasis; + +typedef struct ThreeDrives ThreeDrives; + +typedef struct ThreeInside ThreeInside; + +typedef struct ThreeLineBreak ThreeLineBreak; + +typedef struct ThreeLineBreakBars ThreeLineBreakBars; + +typedef struct ThreeLineStrike ThreeLineStrike; + +typedef struct ThreeOutside ThreeOutside; + +typedef struct ThreeSoldiersOrCrows ThreeSoldiersOrCrows; + +typedef struct ThreeStarsInSouth ThreeStarsInSouth; + +typedef struct Thrusting Thrusting; + +typedef struct TickBars TickBars; + +typedef struct TickIndex TickIndex; + +typedef struct Tii Tii; + +typedef struct TimeBasedStop TimeBasedStop; + +typedef struct TimeOfDayReturnProfile TimeOfDayReturnProfile; + +typedef struct TowerTopBottom TowerTopBottom; + +typedef struct TpoProfile TpoProfile; + +typedef struct TradeImbalance TradeImbalance; + +typedef struct TradeSignAutocorrelation TradeSignAutocorrelation; + +typedef struct TradeVolumeIndex TradeVolumeIndex; + +typedef struct TrendLabel TrendLabel; + +typedef struct TrendStrengthIndex TrendStrengthIndex; + +typedef struct Trendflex Trendflex; + +typedef struct TreynorRatio TreynorRatio; + +typedef struct Triangle Triangle; + +typedef struct Trima Trima; + +typedef struct Trin Trin; + +typedef struct TripleTopBottom TripleTopBottom; + +typedef struct Tristar Tristar; + +typedef struct Trix Trix; + +typedef struct TrueRange TrueRange; + +typedef struct Tsf Tsf; + +typedef struct TsfOscillator TsfOscillator; + +typedef struct Tsi Tsi; + +typedef struct Tsv Tsv; + +typedef struct TtmSqueeze TtmSqueeze; + +typedef struct TtmTrend TtmTrend; + +typedef struct TurnOfMonth TurnOfMonth; + +typedef struct Tweezer Tweezer; + +typedef struct TwiggsMoneyFlow TwiggsMoneyFlow; + +typedef struct TwoCrows TwoCrows; + +typedef struct TypicalPrice TypicalPrice; + +typedef struct UlcerIndex UlcerIndex; + +typedef struct UltimateOscillator UltimateOscillator; + +typedef struct UniqueThreeRiver UniqueThreeRiver; + +typedef struct UniversalOscillator UniversalOscillator; + +typedef struct UpDownVolumeRatio UpDownVolumeRatio; + +typedef struct UpsideGapThreeMethods UpsideGapThreeMethods; + +typedef struct UpsideGapTwoCrows UpsideGapTwoCrows; + +typedef struct UpsidePotentialRatio UpsidePotentialRatio; + +typedef struct ValueArea ValueArea; + +typedef struct ValueAtRisk ValueAtRisk; + +typedef struct Variance Variance; + +typedef struct VarianceRatio VarianceRatio; + +typedef struct VerticalHorizontalFilter VerticalHorizontalFilter; + +typedef struct Vidya Vidya; + +typedef struct VolatilityCone VolatilityCone; + +typedef struct VolatilityOfVolatility VolatilityOfVolatility; + +typedef struct VolatilityRatio VolatilityRatio; + +typedef struct VoltyStop VoltyStop; + +typedef struct VolumeBars VolumeBars; + +typedef struct VolumeByTimeProfile VolumeByTimeProfile; + +typedef struct VolumeOscillator VolumeOscillator; + +typedef struct VolumePriceTrend VolumePriceTrend; + +typedef struct VolumeProfile VolumeProfile; + +typedef struct VolumeRsi VolumeRsi; + +typedef struct VolumeWeightedMacd VolumeWeightedMacd; + +typedef struct VolumeWeightedSr VolumeWeightedSr; + +typedef struct Vortex Vortex; + +typedef struct Vpin Vpin; + +typedef struct Vwap Vwap; + +typedef struct VwapStdDevBands VwapStdDevBands; + +typedef struct Vwma Vwma; + +typedef struct Vzo Vzo; + +typedef struct Wad Wad; + +typedef struct WavePm WavePm; + +typedef struct WaveTrend WaveTrend; + +typedef struct Wedge Wedge; + +typedef struct WeightedClose WeightedClose; + +typedef struct WickRatio WickRatio; + +typedef struct WilliamsFractals WilliamsFractals; + +typedef struct WilliamsR WilliamsR; + +typedef struct WinRate WinRate; + +typedef struct Wma Wma; + +typedef struct WoodiePivots WoodiePivots; + +typedef struct YangZhangVolatility YangZhangVolatility; + +typedef struct YoyoExit YoyoExit; + +typedef struct ZScore ZScore; + +typedef struct ZeroLagMacd ZeroLagMacd; + +typedef struct ZigZag ZigZag; + +typedef struct Zlema Zlema; + +typedef struct WickraAccelerationBandsOutput { + double upper; + double middle; + double lower; +} WickraAccelerationBandsOutput; + +typedef struct WickraAdxOutput { + double plus_di; + double minus_di; + double adx; +} WickraAdxOutput; + +typedef struct WickraAlligatorOutput { + double jaw; + double teeth; + double lips; +} WickraAlligatorOutput; + +typedef struct WickraAndrewsPitchforkOutput { + double median; + double upper; + double lower; +} WickraAndrewsPitchforkOutput; + +typedef struct WickraAroonOutput { + double up; + double down; +} WickraAroonOutput; + +typedef struct WickraAtrBandsOutput { + double upper; + double middle; + double lower; +} WickraAtrBandsOutput; + +typedef struct WickraAtrRatchetOutput { + double value; + double direction; +} WickraAtrRatchetOutput; + +typedef struct WickraAutoFibOutput { + double level_0; + double level_236; + double level_382; + double level_500; + double level_618; + double level_786; + double level_1000; +} WickraAutoFibOutput; + +typedef struct WickraBollingerOutput { + double upper; + double middle; + double lower; + double stddev; +} WickraBollingerOutput; + +typedef struct WickraBomarBandsOutput { + double upper; + double middle; + double lower; +} WickraBomarBandsOutput; + +typedef struct WickraCamarillaPivotsOutput { + double pp; + double r1; + double r2; + double r3; + double r4; + double s1; + double s2; + double s3; + double s4; +} WickraCamarillaPivotsOutput; + +typedef struct WickraCandleVolumeOutput { + double body; + double width; +} WickraCandleVolumeOutput; + +typedef struct WickraCentralPivotRangeOutput { + double pivot; + double tc; + double bc; +} WickraCentralPivotRangeOutput; + +typedef struct WickraChandeKrollStopOutput { + double stop_long; + double stop_short; +} WickraChandeKrollStopOutput; + +typedef struct WickraChandelierExitOutput { + double long_stop; + double short_stop; +} WickraChandelierExitOutput; + +typedef struct WickraClassicPivotsOutput { + double pp; + double r1; + double r2; + double r3; + double s1; + double s2; + double s3; +} WickraClassicPivotsOutput; + +typedef struct WickraCointegrationOutput { + double hedge_ratio; + double spread; + double adf_stat; +} WickraCointegrationOutput; + +typedef struct WickraCompositeProfileOutput { + double poc; + double vah; + double val; +} WickraCompositeProfileOutput; + +typedef struct WickraDemarkPivotsOutput { + double pp; + double r1; + double s1; +} WickraDemarkPivotsOutput; + +typedef struct WickraDonchianOutput { + double upper; + double middle; + double lower; +} WickraDonchianOutput; + +typedef struct WickraDonchianStopOutput { + double stop_long; + double stop_short; +} WickraDonchianStopOutput; + +typedef struct WickraDoubleBollingerOutput { + double upper_outer; + double upper_inner; + double middle; + double lower_inner; + double lower_outer; +} WickraDoubleBollingerOutput; + +typedef struct WickraElderRayOutput { + double bull_power; + double bear_power; +} WickraElderRayOutput; + +typedef struct WickraElderSafeZoneOutput { + double value; + double direction; +} WickraElderSafeZoneOutput; + +typedef struct WickraEquivolumeOutput { + double height; + double width; +} WickraEquivolumeOutput; + +typedef struct WickraFibArcsOutput { + double arc_382; + double arc_500; + double arc_618; +} WickraFibArcsOutput; + +typedef struct WickraFibChannelOutput { + double base; + double level_618; + double level_1000; + double level_1618; +} WickraFibChannelOutput; + +typedef struct WickraFibConfluenceOutput { + double price; + double strength; +} WickraFibConfluenceOutput; + +typedef struct WickraFibExtensionOutput { + double level_1272; + double level_1414; + double level_1618; + double level_2000; + double level_2618; +} WickraFibExtensionOutput; + +typedef struct WickraFibFanOutput { + double fan_382; + double fan_500; + double fan_618; +} WickraFibFanOutput; + +typedef struct WickraFibProjectionOutput { + double level_618; + double level_1000; + double level_1618; + double level_2618; +} WickraFibProjectionOutput; + +typedef struct WickraFibRetracementOutput { + double level_0; + double level_236; + double level_382; + double level_500; + double level_618; + double level_786; + double level_1000; +} WickraFibRetracementOutput; + +typedef struct WickraFibTimeZonesOutput { + double on_zone; + double bars_to_next; +} WickraFibTimeZonesOutput; + +typedef struct WickraFibonacciPivotsOutput { + double pp; + double r1; + double r2; + double r3; + double s1; + double s2; + double s3; +} WickraFibonacciPivotsOutput; + +typedef struct WickraFractalChaosBandsOutput { + double upper; + double lower; +} WickraFractalChaosBandsOutput; + +typedef struct WickraGatorOscillatorOutput { + double upper; + double lower; +} WickraGatorOscillatorOutput; + +typedef struct WickraGoldenPocketOutput { + double low; + double mid; + double high; +} WickraGoldenPocketOutput; + +typedef struct WickraHeikinAshiOutput { + double open; + double high; + double low; + double close; +} WickraHeikinAshiOutput; + +typedef struct WickraHighLowVolumeNodesOutput { + double hvn; + double lvn; +} WickraHighLowVolumeNodesOutput; + +typedef struct WickraHtPhasorOutput { + double inphase; + double quadrature; +} WickraHtPhasorOutput; + +typedef struct WickraHurstChannelOutput { + double upper; + double middle; + double lower; +} WickraHurstChannelOutput; + +typedef struct WickraIchimokuOutput { + double tenkan; + double kijun; + double senkou_a; + double senkou_b; + double chikou; +} WickraIchimokuOutput; + +typedef struct WickraInitialBalanceOutput { + double high; + double low; +} WickraInitialBalanceOutput; + +typedef struct WickraKalmanHedgeRatioOutput { + double hedge_ratio; + double intercept; + double spread; +} WickraKalmanHedgeRatioOutput; + +typedef struct WickraKaseDevStopOutput { + double value; + double direction; +} WickraKaseDevStopOutput; + +typedef struct WickraKasePermissionStochasticOutput { + double fast; + double slow; +} WickraKasePermissionStochasticOutput; + +typedef struct WickraKeltnerOutput { + double upper; + double middle; + double lower; +} WickraKeltnerOutput; + +typedef struct WickraKstOutput { + double kst; + double signal; +} WickraKstOutput; + +typedef struct WickraLeadLagCrossCorrelationOutput { + int64_t lag; + double correlation; +} WickraLeadLagCrossCorrelationOutput; + +typedef struct WickraLinRegChannelOutput { + double upper; + double middle; + double lower; +} WickraLinRegChannelOutput; + +typedef struct WickraLiquidationFeaturesOutput { + double long_; + double short_; + double net; + double total; + double imbalance; +} WickraLiquidationFeaturesOutput; + +typedef struct WickraMaEnvelopeOutput { + double upper; + double middle; + double lower; +} WickraMaEnvelopeOutput; + +typedef struct WickraMacdOutput { + double macd; + double signal; + double histogram; +} WickraMacdOutput; + +typedef struct WickraMamaOutput { + double mama; + double fama; +} WickraMamaOutput; + +typedef struct WickraMedianChannelOutput { + double upper; + double middle; + double lower; +} WickraMedianChannelOutput; + +typedef struct WickraModifiedMaStopOutput { + double value; + double direction; +} WickraModifiedMaStopOutput; + +typedef struct WickraMurreyMathLinesOutput { + double mm8_8; + double mm7_8; + double mm6_8; + double mm5_8; + double mm4_8; + double mm3_8; + double mm2_8; + double mm1_8; + double mm0_8; +} WickraMurreyMathLinesOutput; + +typedef struct WickraNrtrOutput { + double value; + double direction; +} WickraNrtrOutput; + +typedef struct WickraOpeningRangeOutput { + double high; + double low; + double breakout_distance; +} WickraOpeningRangeOutput; + +typedef struct WickraOvernightIntradayReturnOutput { + double overnight; + double intraday; +} WickraOvernightIntradayReturnOutput; + +typedef struct WickraProjectionBandsOutput { + double upper; + double middle; + double lower; +} WickraProjectionBandsOutput; + +typedef struct WickraQqeOutput { + double rsi_ma; + double trailing_line; +} WickraQqeOutput; + +typedef struct WickraQuartileBandsOutput { + double upper; + double middle; + double lower; +} WickraQuartileBandsOutput; + +typedef struct WickraRelativeStrengthOutput { + double ratio; + double ratio_ma; + double ratio_rsi; +} WickraRelativeStrengthOutput; + +typedef struct WickraRwiOutput { + double high; + double low; +} WickraRwiOutput; + +typedef struct WickraSessionHighLowOutput { + double high; + double low; +} WickraSessionHighLowOutput; + +typedef struct WickraSessionRangeOutput { + double asia; + double eu; + double us; +} WickraSessionRangeOutput; + +typedef struct WickraSmoothedHeikinAshiOutput { + double open; + double high; + double low; + double close; +} WickraSmoothedHeikinAshiOutput; + +typedef struct WickraSpreadBollingerBandsOutput { + double middle; + double upper; + double lower; + double percent_b; +} WickraSpreadBollingerBandsOutput; + +typedef struct WickraStandardErrorBandsOutput { + double upper; + double middle; + double lower; +} WickraStandardErrorBandsOutput; + +typedef struct WickraStarcBandsOutput { + double upper; + double middle; + double lower; +} WickraStarcBandsOutput; + +typedef struct WickraStochasticOutput { + double k; + double d; +} WickraStochasticOutput; + +typedef struct WickraSuperTrendOutput { + double value; + double direction; +} WickraSuperTrendOutput; + +typedef struct WickraTdLinesOutput { + double resistance; + double support; +} WickraTdLinesOutput; + +typedef struct WickraTdMovingAverageOutput { + double st1; + double st2; +} WickraTdMovingAverageOutput; + +typedef struct WickraTdRangeProjectionOutput { + double high; + double low; +} WickraTdRangeProjectionOutput; + +typedef struct WickraTdRiskLevelOutput { + double buy_risk; + double sell_risk; +} WickraTdRiskLevelOutput; + +typedef struct WickraTdSequentialOutput { + double setup; + double countdown; + double direction; +} WickraTdSequentialOutput; + +typedef struct WickraTtmSqueezeOutput { + double squeeze; + double momentum; +} WickraTtmSqueezeOutput; + +typedef struct WickraValueAreaOutput { + double poc; + double vah; + double val; +} WickraValueAreaOutput; + +typedef struct WickraVolatilityConeOutput { + double current; + double min; + double median; + double max; + double percentile; +} WickraVolatilityConeOutput; + +typedef struct WickraVolumeWeightedMacdOutput { + double macd; + double signal; + double histogram; +} WickraVolumeWeightedMacdOutput; + +typedef struct WickraVolumeWeightedSrOutput { + double support; + double resistance; +} WickraVolumeWeightedSrOutput; + +typedef struct WickraVortexOutput { + double plus; + double minus; +} WickraVortexOutput; + +typedef struct WickraVwapStdDevBandsOutput { + double upper; + double middle; + double lower; + double stddev; +} WickraVwapStdDevBandsOutput; + +typedef struct WickraWaveTrendOutput { + double wt1; + double wt2; +} WickraWaveTrendOutput; + +typedef struct WickraWilliamsFractalsOutput { + double up; + double down; +} WickraWilliamsFractalsOutput; + +typedef struct WickraWoodiePivotsOutput { + double pp; + double r1; + double r2; + double s1; + double s2; +} WickraWoodiePivotsOutput; + +typedef struct WickraZeroLagMacdOutput { + double macd; + double signal; + double histogram; +} WickraZeroLagMacdOutput; + +typedef struct WickraZigZagOutput { + double swing; + double direction; +} WickraZigZagOutput; + +typedef struct WickraTpoProfileOutputScalars { + double price_low; + double price_high; +} WickraTpoProfileOutputScalars; + +typedef struct WickraVolumeProfileOutputScalars { + double price_low; + double price_high; +} WickraVolumeProfileOutputScalars; + +typedef struct WickraDollarBar { + double open; + double high; + double low; + double close; + double volume; + double dollar; +} WickraDollarBar; + +typedef struct WickraImbalanceBar { + double open; + double high; + double low; + double close; + double imbalance; + int8_t direction; +} WickraImbalanceBar; + +typedef struct WickraKagiBar { + double start; + double end; + int8_t direction; +} WickraKagiBar; + +typedef struct WickraPnfColumn { + int8_t direction; + double high; + double low; +} WickraPnfColumn; + +typedef struct WickraRangeBar { + double open; + double close; + int8_t direction; +} WickraRangeBar; + +typedef struct WickraRenkoBrick { + double open; + double close; + int8_t direction; +} WickraRenkoBrick; + +typedef struct WickraRunBar { + double open; + double high; + double low; + double close; + uintptr_t length; + int8_t direction; +} WickraRunBar; + +typedef struct WickraLineBreakBar { + double open; + double close; + int8_t direction; +} WickraLineBreakBar; + +typedef struct WickraTickBar { + double open; + double high; + double low; + double close; + double volume; +} WickraTickBar; + +typedef struct WickraVolumeBar { + double open; + double high; + double low; + double close; + double volume; +} WickraVolumeBar; + +typedef struct WickraFootprintLevel { + double price; + double bid_vol; + double ask_vol; +} WickraFootprintLevel; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +struct AdaptiveCycle *wickra_adaptive_cycle_new(void); + +double wickra_adaptive_cycle_update(struct AdaptiveCycle *handle, double value); + +void wickra_adaptive_cycle_batch(struct AdaptiveCycle *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_adaptive_cycle_reset(struct AdaptiveCycle *handle); + +void wickra_adaptive_cycle_free(struct AdaptiveCycle *handle); + +struct AdaptiveLaguerreFilter *wickra_adaptive_laguerre_filter_new(uintptr_t period); + +double wickra_adaptive_laguerre_filter_update(struct AdaptiveLaguerreFilter *handle, double value); + +void wickra_adaptive_laguerre_filter_batch(struct AdaptiveLaguerreFilter *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_adaptive_laguerre_filter_reset(struct AdaptiveLaguerreFilter *handle); + +void wickra_adaptive_laguerre_filter_free(struct AdaptiveLaguerreFilter *handle); + +struct AdaptiveRsi *wickra_adaptive_rsi_new(uintptr_t period); + +double wickra_adaptive_rsi_update(struct AdaptiveRsi *handle, double value); + +void wickra_adaptive_rsi_batch(struct AdaptiveRsi *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_adaptive_rsi_reset(struct AdaptiveRsi *handle); + +void wickra_adaptive_rsi_free(struct AdaptiveRsi *handle); + +struct Alma *wickra_alma_new(uintptr_t period, double offset, double sigma); + +double wickra_alma_update(struct Alma *handle, double value); + +void wickra_alma_batch(struct Alma *handle, const double *input, double *out, uintptr_t n); + +void wickra_alma_reset(struct Alma *handle); + +void wickra_alma_free(struct Alma *handle); + +struct AnchoredRsi *wickra_anchored_rsi_new(void); + +double wickra_anchored_rsi_update(struct AnchoredRsi *handle, double value); + +void wickra_anchored_rsi_batch(struct AnchoredRsi *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_anchored_rsi_reset(struct AnchoredRsi *handle); + +void wickra_anchored_rsi_free(struct AnchoredRsi *handle); + +struct Apo *wickra_apo_new(uintptr_t fast, uintptr_t slow); + +double wickra_apo_update(struct Apo *handle, double value); + +void wickra_apo_batch(struct Apo *handle, const double *input, double *out, uintptr_t n); + +void wickra_apo_reset(struct Apo *handle); + +void wickra_apo_free(struct Apo *handle); + +struct Autocorrelation *wickra_autocorrelation_new(uintptr_t period, uintptr_t lag); + +double wickra_autocorrelation_update(struct Autocorrelation *handle, double value); + +void wickra_autocorrelation_batch(struct Autocorrelation *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_autocorrelation_reset(struct Autocorrelation *handle); + +void wickra_autocorrelation_free(struct Autocorrelation *handle); + +struct AutocorrelationPeriodogram *wickra_autocorrelation_periodogram_new(uintptr_t min_period, + uintptr_t max_period); + +double wickra_autocorrelation_periodogram_update(struct AutocorrelationPeriodogram *handle, + double value); + +void wickra_autocorrelation_periodogram_batch(struct AutocorrelationPeriodogram *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_autocorrelation_periodogram_reset(struct AutocorrelationPeriodogram *handle); + +void wickra_autocorrelation_periodogram_free(struct AutocorrelationPeriodogram *handle); + +struct AverageDrawdown *wickra_average_drawdown_new(uintptr_t period); + +double wickra_average_drawdown_update(struct AverageDrawdown *handle, double value); + +void wickra_average_drawdown_batch(struct AverageDrawdown *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_average_drawdown_reset(struct AverageDrawdown *handle); + +void wickra_average_drawdown_free(struct AverageDrawdown *handle); + +struct BandpassFilter *wickra_bandpass_filter_new(uintptr_t period, double bandwidth); + +double wickra_bandpass_filter_update(struct BandpassFilter *handle, double value); + +void wickra_bandpass_filter_batch(struct BandpassFilter *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_bandpass_filter_reset(struct BandpassFilter *handle); + +void wickra_bandpass_filter_free(struct BandpassFilter *handle); + +struct BipowerVariation *wickra_bipower_variation_new(uintptr_t period); + +double wickra_bipower_variation_update(struct BipowerVariation *handle, double value); + +void wickra_bipower_variation_batch(struct BipowerVariation *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_bipower_variation_reset(struct BipowerVariation *handle); + +void wickra_bipower_variation_free(struct BipowerVariation *handle); + +struct BollingerBandwidth *wickra_bollinger_bandwidth_new(uintptr_t period, double multiplier); + +double wickra_bollinger_bandwidth_update(struct BollingerBandwidth *handle, double value); + +void wickra_bollinger_bandwidth_batch(struct BollingerBandwidth *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_bollinger_bandwidth_reset(struct BollingerBandwidth *handle); + +void wickra_bollinger_bandwidth_free(struct BollingerBandwidth *handle); + +struct BurkeRatio *wickra_burke_ratio_new(uintptr_t period); + +double wickra_burke_ratio_update(struct BurkeRatio *handle, double value); + +void wickra_burke_ratio_batch(struct BurkeRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_burke_ratio_reset(struct BurkeRatio *handle); + +void wickra_burke_ratio_free(struct BurkeRatio *handle); + +struct CalmarRatio *wickra_calmar_ratio_new(uintptr_t period); + +double wickra_calmar_ratio_update(struct CalmarRatio *handle, double value); + +void wickra_calmar_ratio_batch(struct CalmarRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_calmar_ratio_reset(struct CalmarRatio *handle); + +void wickra_calmar_ratio_free(struct CalmarRatio *handle); + +struct CenterOfGravity *wickra_center_of_gravity_new(uintptr_t period); + +double wickra_center_of_gravity_update(struct CenterOfGravity *handle, double value); + +void wickra_center_of_gravity_batch(struct CenterOfGravity *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_center_of_gravity_reset(struct CenterOfGravity *handle); + +void wickra_center_of_gravity_free(struct CenterOfGravity *handle); + +struct Cfo *wickra_cfo_new(uintptr_t period); + +double wickra_cfo_update(struct Cfo *handle, double value); + +void wickra_cfo_batch(struct Cfo *handle, const double *input, double *out, uintptr_t n); + +void wickra_cfo_reset(struct Cfo *handle); + +void wickra_cfo_free(struct Cfo *handle); + +struct Cmo *wickra_cmo_new(uintptr_t period); + +double wickra_cmo_update(struct Cmo *handle, double value); + +void wickra_cmo_batch(struct Cmo *handle, const double *input, double *out, uintptr_t n); + +void wickra_cmo_reset(struct Cmo *handle); + +void wickra_cmo_free(struct Cmo *handle); + +struct CoefficientOfVariation *wickra_coefficient_of_variation_new(uintptr_t period); + +double wickra_coefficient_of_variation_update(struct CoefficientOfVariation *handle, double value); + +void wickra_coefficient_of_variation_batch(struct CoefficientOfVariation *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_coefficient_of_variation_reset(struct CoefficientOfVariation *handle); + +void wickra_coefficient_of_variation_free(struct CoefficientOfVariation *handle); + +struct CommonSenseRatio *wickra_common_sense_ratio_new(uintptr_t period); + +double wickra_common_sense_ratio_update(struct CommonSenseRatio *handle, double value); + +void wickra_common_sense_ratio_batch(struct CommonSenseRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_common_sense_ratio_reset(struct CommonSenseRatio *handle); + +void wickra_common_sense_ratio_free(struct CommonSenseRatio *handle); + +struct ConditionalValueAtRisk *wickra_conditional_value_at_risk_new(uintptr_t period, + double confidence); + +double wickra_conditional_value_at_risk_update(struct ConditionalValueAtRisk *handle, double value); + +void wickra_conditional_value_at_risk_batch(struct ConditionalValueAtRisk *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_conditional_value_at_risk_reset(struct ConditionalValueAtRisk *handle); + +void wickra_conditional_value_at_risk_free(struct ConditionalValueAtRisk *handle); + +struct ConnorsRsi *wickra_connors_rsi_new(uintptr_t period_rsi, + uintptr_t period_streak, + uintptr_t period_rank); + +double wickra_connors_rsi_update(struct ConnorsRsi *handle, double value); + +void wickra_connors_rsi_batch(struct ConnorsRsi *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_connors_rsi_reset(struct ConnorsRsi *handle); + +void wickra_connors_rsi_free(struct ConnorsRsi *handle); + +struct Coppock *wickra_coppock_new(uintptr_t roc_long_period, + uintptr_t roc_short_period, + uintptr_t wma_period); + +double wickra_coppock_update(struct Coppock *handle, double value); + +void wickra_coppock_batch(struct Coppock *handle, const double *input, double *out, uintptr_t n); + +void wickra_coppock_reset(struct Coppock *handle); + +void wickra_coppock_free(struct Coppock *handle); + +struct CorrelationTrendIndicator *wickra_correlation_trend_indicator_new(uintptr_t period); + +double wickra_correlation_trend_indicator_update(struct CorrelationTrendIndicator *handle, + double value); + +void wickra_correlation_trend_indicator_batch(struct CorrelationTrendIndicator *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_correlation_trend_indicator_reset(struct CorrelationTrendIndicator *handle); + +void wickra_correlation_trend_indicator_free(struct CorrelationTrendIndicator *handle); + +struct CyberneticCycle *wickra_cybernetic_cycle_new(uintptr_t period); + +double wickra_cybernetic_cycle_update(struct CyberneticCycle *handle, double value); + +void wickra_cybernetic_cycle_batch(struct CyberneticCycle *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_cybernetic_cycle_reset(struct CyberneticCycle *handle); + +void wickra_cybernetic_cycle_free(struct CyberneticCycle *handle); + +struct Decycler *wickra_decycler_new(uintptr_t period); + +double wickra_decycler_update(struct Decycler *handle, double value); + +void wickra_decycler_batch(struct Decycler *handle, const double *input, double *out, uintptr_t n); + +void wickra_decycler_reset(struct Decycler *handle); + +void wickra_decycler_free(struct Decycler *handle); + +struct DecyclerOscillator *wickra_decycler_oscillator_new(uintptr_t fast, uintptr_t slow); + +double wickra_decycler_oscillator_update(struct DecyclerOscillator *handle, double value); + +void wickra_decycler_oscillator_batch(struct DecyclerOscillator *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_decycler_oscillator_reset(struct DecyclerOscillator *handle); + +void wickra_decycler_oscillator_free(struct DecyclerOscillator *handle); + +struct Dema *wickra_dema_new(uintptr_t period); + +double wickra_dema_update(struct Dema *handle, double value); + +void wickra_dema_batch(struct Dema *handle, const double *input, double *out, uintptr_t n); + +void wickra_dema_reset(struct Dema *handle); + +void wickra_dema_free(struct Dema *handle); + +struct DerivativeOscillator *wickra_derivative_oscillator_new(uintptr_t rsi_period, + uintptr_t smooth1, + uintptr_t smooth2, + uintptr_t signal_period); + +double wickra_derivative_oscillator_update(struct DerivativeOscillator *handle, double value); + +void wickra_derivative_oscillator_batch(struct DerivativeOscillator *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_derivative_oscillator_reset(struct DerivativeOscillator *handle); + +void wickra_derivative_oscillator_free(struct DerivativeOscillator *handle); + +struct DetrendedStdDev *wickra_detrended_std_dev_new(uintptr_t period); + +double wickra_detrended_std_dev_update(struct DetrendedStdDev *handle, double value); + +void wickra_detrended_std_dev_batch(struct DetrendedStdDev *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_detrended_std_dev_reset(struct DetrendedStdDev *handle); + +void wickra_detrended_std_dev_free(struct DetrendedStdDev *handle); + +struct DisparityIndex *wickra_disparity_index_new(uintptr_t period); + +double wickra_disparity_index_update(struct DisparityIndex *handle, double value); + +void wickra_disparity_index_batch(struct DisparityIndex *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_disparity_index_reset(struct DisparityIndex *handle); + +void wickra_disparity_index_free(struct DisparityIndex *handle); + +struct Dpo *wickra_dpo_new(uintptr_t period); + +double wickra_dpo_update(struct Dpo *handle, double value); + +void wickra_dpo_batch(struct Dpo *handle, const double *input, double *out, uintptr_t n); + +void wickra_dpo_reset(struct Dpo *handle); + +void wickra_dpo_free(struct Dpo *handle); + +struct DrawdownDuration *wickra_drawdown_duration_new(void); + +double wickra_drawdown_duration_update(struct DrawdownDuration *handle, double value); + +void wickra_drawdown_duration_batch(struct DrawdownDuration *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_drawdown_duration_reset(struct DrawdownDuration *handle); + +void wickra_drawdown_duration_free(struct DrawdownDuration *handle); + +struct DynamicMomentumIndex *wickra_dynamic_momentum_index_new(uintptr_t period); + +double wickra_dynamic_momentum_index_update(struct DynamicMomentumIndex *handle, double value); + +void wickra_dynamic_momentum_index_batch(struct DynamicMomentumIndex *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_dynamic_momentum_index_reset(struct DynamicMomentumIndex *handle); + +void wickra_dynamic_momentum_index_free(struct DynamicMomentumIndex *handle); + +struct EhlersStochastic *wickra_ehlers_stochastic_new(uintptr_t period); + +double wickra_ehlers_stochastic_update(struct EhlersStochastic *handle, double value); + +void wickra_ehlers_stochastic_batch(struct EhlersStochastic *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_ehlers_stochastic_reset(struct EhlersStochastic *handle); + +void wickra_ehlers_stochastic_free(struct EhlersStochastic *handle); + +struct Ehma *wickra_ehma_new(uintptr_t period); + +double wickra_ehma_update(struct Ehma *handle, double value); + +void wickra_ehma_batch(struct Ehma *handle, const double *input, double *out, uintptr_t n); + +void wickra_ehma_reset(struct Ehma *handle); + +void wickra_ehma_free(struct Ehma *handle); + +struct ElderImpulse *wickra_elder_impulse_new(uintptr_t ema_period, + uintptr_t macd_fast, + uintptr_t macd_slow, + uintptr_t macd_signal); + +double wickra_elder_impulse_update(struct ElderImpulse *handle, double value); + +void wickra_elder_impulse_batch(struct ElderImpulse *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_elder_impulse_reset(struct ElderImpulse *handle); + +void wickra_elder_impulse_free(struct ElderImpulse *handle); + +struct Ema *wickra_ema_new(uintptr_t period); + +double wickra_ema_update(struct Ema *handle, double value); + +void wickra_ema_batch(struct Ema *handle, const double *input, double *out, uintptr_t n); + +void wickra_ema_reset(struct Ema *handle); + +void wickra_ema_free(struct Ema *handle); + +struct EmpiricalModeDecomposition *wickra_empirical_mode_decomposition_new(uintptr_t period, + double fraction); + +double wickra_empirical_mode_decomposition_update(struct EmpiricalModeDecomposition *handle, + double value); + +void wickra_empirical_mode_decomposition_batch(struct EmpiricalModeDecomposition *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_empirical_mode_decomposition_reset(struct EmpiricalModeDecomposition *handle); + +void wickra_empirical_mode_decomposition_free(struct EmpiricalModeDecomposition *handle); + +struct EvenBetterSinewave *wickra_even_better_sinewave_new(uintptr_t hp_period, + uintptr_t ssf_length); + +double wickra_even_better_sinewave_update(struct EvenBetterSinewave *handle, double value); + +void wickra_even_better_sinewave_batch(struct EvenBetterSinewave *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_even_better_sinewave_reset(struct EvenBetterSinewave *handle); + +void wickra_even_better_sinewave_free(struct EvenBetterSinewave *handle); + +struct EwmaVolatility *wickra_ewma_volatility_new(double lambda); + +double wickra_ewma_volatility_update(struct EwmaVolatility *handle, double value); + +void wickra_ewma_volatility_batch(struct EwmaVolatility *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_ewma_volatility_reset(struct EwmaVolatility *handle); + +void wickra_ewma_volatility_free(struct EwmaVolatility *handle); + +struct Expectancy *wickra_expectancy_new(uintptr_t period); + +double wickra_expectancy_update(struct Expectancy *handle, double value); + +void wickra_expectancy_batch(struct Expectancy *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_expectancy_reset(struct Expectancy *handle); + +void wickra_expectancy_free(struct Expectancy *handle); + +struct Fama *wickra_fama_new(double fast_limit, double slow_limit); + +double wickra_fama_update(struct Fama *handle, double value); + +void wickra_fama_batch(struct Fama *handle, const double *input, double *out, uintptr_t n); + +void wickra_fama_reset(struct Fama *handle); + +void wickra_fama_free(struct Fama *handle); + +struct FisherRsi *wickra_fisher_rsi_new(uintptr_t period); + +double wickra_fisher_rsi_update(struct FisherRsi *handle, double value); + +void wickra_fisher_rsi_batch(struct FisherRsi *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_fisher_rsi_reset(struct FisherRsi *handle); + +void wickra_fisher_rsi_free(struct FisherRsi *handle); + +struct FisherTransform *wickra_fisher_transform_new(uintptr_t period); + +double wickra_fisher_transform_update(struct FisherTransform *handle, double value); + +void wickra_fisher_transform_batch(struct FisherTransform *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_fisher_transform_reset(struct FisherTransform *handle); + +void wickra_fisher_transform_free(struct FisherTransform *handle); + +struct Frama *wickra_frama_new(uintptr_t period); + +double wickra_frama_update(struct Frama *handle, double value); + +void wickra_frama_batch(struct Frama *handle, const double *input, double *out, uintptr_t n); + +void wickra_frama_reset(struct Frama *handle); + +void wickra_frama_free(struct Frama *handle); + +struct GainLossRatio *wickra_gain_loss_ratio_new(uintptr_t period); + +double wickra_gain_loss_ratio_update(struct GainLossRatio *handle, double value); + +void wickra_gain_loss_ratio_batch(struct GainLossRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_gain_loss_ratio_reset(struct GainLossRatio *handle); + +void wickra_gain_loss_ratio_free(struct GainLossRatio *handle); + +struct GainToPainRatio *wickra_gain_to_pain_ratio_new(uintptr_t period); + +double wickra_gain_to_pain_ratio_update(struct GainToPainRatio *handle, double value); + +void wickra_gain_to_pain_ratio_batch(struct GainToPainRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_gain_to_pain_ratio_reset(struct GainToPainRatio *handle); + +void wickra_gain_to_pain_ratio_free(struct GainToPainRatio *handle); + +struct Garch11 *wickra_garch11_new(double omega, double alpha, double beta); + +double wickra_garch11_update(struct Garch11 *handle, double value); + +void wickra_garch11_batch(struct Garch11 *handle, const double *input, double *out, uintptr_t n); + +void wickra_garch11_reset(struct Garch11 *handle); + +void wickra_garch11_free(struct Garch11 *handle); + +struct GeneralizedDema *wickra_generalized_dema_new(uintptr_t period, double v); + +double wickra_generalized_dema_update(struct GeneralizedDema *handle, double value); + +void wickra_generalized_dema_batch(struct GeneralizedDema *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_generalized_dema_reset(struct GeneralizedDema *handle); + +void wickra_generalized_dema_free(struct GeneralizedDema *handle); + +struct GeometricMa *wickra_geometric_ma_new(uintptr_t period); + +double wickra_geometric_ma_update(struct GeometricMa *handle, double value); + +void wickra_geometric_ma_batch(struct GeometricMa *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_geometric_ma_reset(struct GeometricMa *handle); + +void wickra_geometric_ma_free(struct GeometricMa *handle); + +struct HighpassFilter *wickra_highpass_filter_new(uintptr_t period); + +double wickra_highpass_filter_update(struct HighpassFilter *handle, double value); + +void wickra_highpass_filter_batch(struct HighpassFilter *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_highpass_filter_reset(struct HighpassFilter *handle); + +void wickra_highpass_filter_free(struct HighpassFilter *handle); + +struct HilbertDominantCycle *wickra_hilbert_dominant_cycle_new(void); + +double wickra_hilbert_dominant_cycle_update(struct HilbertDominantCycle *handle, double value); + +void wickra_hilbert_dominant_cycle_batch(struct HilbertDominantCycle *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_hilbert_dominant_cycle_reset(struct HilbertDominantCycle *handle); + +void wickra_hilbert_dominant_cycle_free(struct HilbertDominantCycle *handle); + +struct HistoricalVolatility *wickra_historical_volatility_new(uintptr_t period, + uintptr_t trading_periods); + +double wickra_historical_volatility_update(struct HistoricalVolatility *handle, double value); + +void wickra_historical_volatility_batch(struct HistoricalVolatility *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_historical_volatility_reset(struct HistoricalVolatility *handle); + +void wickra_historical_volatility_free(struct HistoricalVolatility *handle); + +struct Hma *wickra_hma_new(uintptr_t period); + +double wickra_hma_update(struct Hma *handle, double value); + +void wickra_hma_batch(struct Hma *handle, const double *input, double *out, uintptr_t n); + +void wickra_hma_reset(struct Hma *handle); + +void wickra_hma_free(struct Hma *handle); + +struct HoltWinters *wickra_holt_winters_new(double alpha, double beta); + +double wickra_holt_winters_update(struct HoltWinters *handle, double value); + +void wickra_holt_winters_batch(struct HoltWinters *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_holt_winters_reset(struct HoltWinters *handle); + +void wickra_holt_winters_free(struct HoltWinters *handle); + +struct HtDcPhase *wickra_ht_dc_phase_new(void); + +double wickra_ht_dc_phase_update(struct HtDcPhase *handle, double value); + +void wickra_ht_dc_phase_batch(struct HtDcPhase *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_ht_dc_phase_reset(struct HtDcPhase *handle); + +void wickra_ht_dc_phase_free(struct HtDcPhase *handle); + +struct HtTrendMode *wickra_ht_trend_mode_new(void); + +double wickra_ht_trend_mode_update(struct HtTrendMode *handle, double value); + +void wickra_ht_trend_mode_batch(struct HtTrendMode *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_ht_trend_mode_reset(struct HtTrendMode *handle); + +void wickra_ht_trend_mode_free(struct HtTrendMode *handle); + +struct HurstExponent *wickra_hurst_exponent_new(uintptr_t period, uintptr_t chunks); + +double wickra_hurst_exponent_update(struct HurstExponent *handle, double value); + +void wickra_hurst_exponent_batch(struct HurstExponent *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_hurst_exponent_reset(struct HurstExponent *handle); + +void wickra_hurst_exponent_free(struct HurstExponent *handle); + +struct InstantaneousTrendline *wickra_instantaneous_trendline_new(uintptr_t period); + +double wickra_instantaneous_trendline_update(struct InstantaneousTrendline *handle, double value); + +void wickra_instantaneous_trendline_batch(struct InstantaneousTrendline *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_instantaneous_trendline_reset(struct InstantaneousTrendline *handle); + +void wickra_instantaneous_trendline_free(struct InstantaneousTrendline *handle); + +struct InverseFisherTransform *wickra_inverse_fisher_transform_new(double scale); + +double wickra_inverse_fisher_transform_update(struct InverseFisherTransform *handle, double value); + +void wickra_inverse_fisher_transform_batch(struct InverseFisherTransform *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_inverse_fisher_transform_reset(struct InverseFisherTransform *handle); + +void wickra_inverse_fisher_transform_free(struct InverseFisherTransform *handle); + +struct JarqueBera *wickra_jarque_bera_new(uintptr_t period); + +double wickra_jarque_bera_update(struct JarqueBera *handle, double value); + +void wickra_jarque_bera_batch(struct JarqueBera *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_jarque_bera_reset(struct JarqueBera *handle); + +void wickra_jarque_bera_free(struct JarqueBera *handle); + +struct Jma *wickra_jma_new(uintptr_t period, double phase, uint32_t power); + +double wickra_jma_update(struct Jma *handle, double value); + +void wickra_jma_batch(struct Jma *handle, const double *input, double *out, uintptr_t n); + +void wickra_jma_reset(struct Jma *handle); + +void wickra_jma_free(struct Jma *handle); + +struct JumpIndicator *wickra_jump_indicator_new(uintptr_t period, double threshold); + +double wickra_jump_indicator_update(struct JumpIndicator *handle, double value); + +void wickra_jump_indicator_batch(struct JumpIndicator *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_jump_indicator_reset(struct JumpIndicator *handle); + +void wickra_jump_indicator_free(struct JumpIndicator *handle); + +struct KRatio *wickra_k_ratio_new(uintptr_t period); + +double wickra_k_ratio_update(struct KRatio *handle, double value); + +void wickra_k_ratio_batch(struct KRatio *handle, const double *input, double *out, uintptr_t n); + +void wickra_k_ratio_reset(struct KRatio *handle); + +void wickra_k_ratio_free(struct KRatio *handle); + +struct Kama *wickra_kama_new(uintptr_t er_period, uintptr_t fast, uintptr_t slow); + +double wickra_kama_update(struct Kama *handle, double value); + +void wickra_kama_batch(struct Kama *handle, const double *input, double *out, uintptr_t n); + +void wickra_kama_reset(struct Kama *handle); + +void wickra_kama_free(struct Kama *handle); + +struct KellyCriterion *wickra_kelly_criterion_new(uintptr_t period); + +double wickra_kelly_criterion_update(struct KellyCriterion *handle, double value); + +void wickra_kelly_criterion_batch(struct KellyCriterion *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_kelly_criterion_reset(struct KellyCriterion *handle); + +void wickra_kelly_criterion_free(struct KellyCriterion *handle); + +struct Kurtosis *wickra_kurtosis_new(uintptr_t period); + +double wickra_kurtosis_update(struct Kurtosis *handle, double value); + +void wickra_kurtosis_batch(struct Kurtosis *handle, const double *input, double *out, uintptr_t n); + +void wickra_kurtosis_reset(struct Kurtosis *handle); + +void wickra_kurtosis_free(struct Kurtosis *handle); + +struct LaguerreRsi *wickra_laguerre_rsi_new(double gamma); + +double wickra_laguerre_rsi_update(struct LaguerreRsi *handle, double value); + +void wickra_laguerre_rsi_batch(struct LaguerreRsi *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_laguerre_rsi_reset(struct LaguerreRsi *handle); + +void wickra_laguerre_rsi_free(struct LaguerreRsi *handle); + +struct LinearRegression *wickra_linear_regression_new(uintptr_t period); + +double wickra_linear_regression_update(struct LinearRegression *handle, double value); + +void wickra_linear_regression_batch(struct LinearRegression *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_linear_regression_reset(struct LinearRegression *handle); + +void wickra_linear_regression_free(struct LinearRegression *handle); + +struct LinRegAngle *wickra_lin_reg_angle_new(uintptr_t period); + +double wickra_lin_reg_angle_update(struct LinRegAngle *handle, double value); + +void wickra_lin_reg_angle_batch(struct LinRegAngle *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_lin_reg_angle_reset(struct LinRegAngle *handle); + +void wickra_lin_reg_angle_free(struct LinRegAngle *handle); + +struct LinRegIntercept *wickra_lin_reg_intercept_new(uintptr_t period); + +double wickra_lin_reg_intercept_update(struct LinRegIntercept *handle, double value); + +void wickra_lin_reg_intercept_batch(struct LinRegIntercept *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_lin_reg_intercept_reset(struct LinRegIntercept *handle); + +void wickra_lin_reg_intercept_free(struct LinRegIntercept *handle); + +struct LinRegSlope *wickra_lin_reg_slope_new(uintptr_t period); + +double wickra_lin_reg_slope_update(struct LinRegSlope *handle, double value); + +void wickra_lin_reg_slope_batch(struct LinRegSlope *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_lin_reg_slope_reset(struct LinRegSlope *handle); + +void wickra_lin_reg_slope_free(struct LinRegSlope *handle); + +struct LogReturn *wickra_log_return_new(uintptr_t period); + +double wickra_log_return_update(struct LogReturn *handle, double value); + +void wickra_log_return_batch(struct LogReturn *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_log_return_reset(struct LogReturn *handle); + +void wickra_log_return_free(struct LogReturn *handle); + +struct M2Measure *wickra_m2_measure_new(uintptr_t period, + double risk_free, + double benchmark_stddev); + +double wickra_m2_measure_update(struct M2Measure *handle, double value); + +void wickra_m2_measure_batch(struct M2Measure *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_m2_measure_reset(struct M2Measure *handle); + +void wickra_m2_measure_free(struct M2Measure *handle); + +struct MacdHistogram *wickra_macd_histogram_new(uintptr_t fast, uintptr_t slow, uintptr_t signal); + +double wickra_macd_histogram_update(struct MacdHistogram *handle, double value); + +void wickra_macd_histogram_batch(struct MacdHistogram *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_macd_histogram_reset(struct MacdHistogram *handle); + +void wickra_macd_histogram_free(struct MacdHistogram *handle); + +struct MartinRatio *wickra_martin_ratio_new(uintptr_t period); + +double wickra_martin_ratio_update(struct MartinRatio *handle, double value); + +void wickra_martin_ratio_batch(struct MartinRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_martin_ratio_reset(struct MartinRatio *handle); + +void wickra_martin_ratio_free(struct MartinRatio *handle); + +struct MaxDrawdown *wickra_max_drawdown_new(uintptr_t period); + +double wickra_max_drawdown_update(struct MaxDrawdown *handle, double value); + +void wickra_max_drawdown_batch(struct MaxDrawdown *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_max_drawdown_reset(struct MaxDrawdown *handle); + +void wickra_max_drawdown_free(struct MaxDrawdown *handle); + +struct McGinleyDynamic *wickra_mc_ginley_dynamic_new(uintptr_t period); + +double wickra_mc_ginley_dynamic_update(struct McGinleyDynamic *handle, double value); + +void wickra_mc_ginley_dynamic_batch(struct McGinleyDynamic *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_mc_ginley_dynamic_reset(struct McGinleyDynamic *handle); + +void wickra_mc_ginley_dynamic_free(struct McGinleyDynamic *handle); + +struct MedianAbsoluteDeviation *wickra_median_absolute_deviation_new(uintptr_t period); + +double wickra_median_absolute_deviation_update(struct MedianAbsoluteDeviation *handle, + double value); + +void wickra_median_absolute_deviation_batch(struct MedianAbsoluteDeviation *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_median_absolute_deviation_reset(struct MedianAbsoluteDeviation *handle); + +void wickra_median_absolute_deviation_free(struct MedianAbsoluteDeviation *handle); + +struct MedianMa *wickra_median_ma_new(uintptr_t period); + +double wickra_median_ma_update(struct MedianMa *handle, double value); + +void wickra_median_ma_batch(struct MedianMa *handle, const double *input, double *out, uintptr_t n); + +void wickra_median_ma_reset(struct MedianMa *handle); + +void wickra_median_ma_free(struct MedianMa *handle); + +struct MidPoint *wickra_mid_point_new(uintptr_t period); + +double wickra_mid_point_update(struct MidPoint *handle, double value); + +void wickra_mid_point_batch(struct MidPoint *handle, const double *input, double *out, uintptr_t n); + +void wickra_mid_point_reset(struct MidPoint *handle); + +void wickra_mid_point_free(struct MidPoint *handle); + +struct Mom *wickra_mom_new(uintptr_t period); + +double wickra_mom_update(struct Mom *handle, double value); + +void wickra_mom_batch(struct Mom *handle, const double *input, double *out, uintptr_t n); + +void wickra_mom_reset(struct Mom *handle); + +void wickra_mom_free(struct Mom *handle); + +struct OmegaRatio *wickra_omega_ratio_new(uintptr_t period, double threshold); + +double wickra_omega_ratio_update(struct OmegaRatio *handle, double value); + +void wickra_omega_ratio_batch(struct OmegaRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_omega_ratio_reset(struct OmegaRatio *handle); + +void wickra_omega_ratio_free(struct OmegaRatio *handle); + +struct PainIndex *wickra_pain_index_new(uintptr_t period); + +double wickra_pain_index_update(struct PainIndex *handle, double value); + +void wickra_pain_index_batch(struct PainIndex *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_pain_index_reset(struct PainIndex *handle); + +void wickra_pain_index_free(struct PainIndex *handle); + +struct PercentB *wickra_percent_b_new(uintptr_t period, double multiplier); + +double wickra_percent_b_update(struct PercentB *handle, double value); + +void wickra_percent_b_batch(struct PercentB *handle, const double *input, double *out, uintptr_t n); + +void wickra_percent_b_reset(struct PercentB *handle); + +void wickra_percent_b_free(struct PercentB *handle); + +struct PercentageTrailingStop *wickra_percentage_trailing_stop_new(double percent); + +double wickra_percentage_trailing_stop_update(struct PercentageTrailingStop *handle, double value); + +void wickra_percentage_trailing_stop_batch(struct PercentageTrailingStop *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_percentage_trailing_stop_reset(struct PercentageTrailingStop *handle); + +void wickra_percentage_trailing_stop_free(struct PercentageTrailingStop *handle); + +struct Pmo *wickra_pmo_new(uintptr_t smoothing1, uintptr_t smoothing2); + +double wickra_pmo_update(struct Pmo *handle, double value); + +void wickra_pmo_batch(struct Pmo *handle, const double *input, double *out, uintptr_t n); + +void wickra_pmo_reset(struct Pmo *handle); + +void wickra_pmo_free(struct Pmo *handle); + +struct PolarizedFractalEfficiency *wickra_polarized_fractal_efficiency_new(uintptr_t period, + uintptr_t smoothing); + +double wickra_polarized_fractal_efficiency_update(struct PolarizedFractalEfficiency *handle, + double value); + +void wickra_polarized_fractal_efficiency_batch(struct PolarizedFractalEfficiency *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_polarized_fractal_efficiency_reset(struct PolarizedFractalEfficiency *handle); + +void wickra_polarized_fractal_efficiency_free(struct PolarizedFractalEfficiency *handle); + +struct Ppo *wickra_ppo_new(uintptr_t fast, uintptr_t slow); + +double wickra_ppo_update(struct Ppo *handle, double value); + +void wickra_ppo_batch(struct Ppo *handle, const double *input, double *out, uintptr_t n); + +void wickra_ppo_reset(struct Ppo *handle); + +void wickra_ppo_free(struct Ppo *handle); + +struct PpoHistogram *wickra_ppo_histogram_new(uintptr_t fast, uintptr_t slow, uintptr_t signal); + +double wickra_ppo_histogram_update(struct PpoHistogram *handle, double value); + +void wickra_ppo_histogram_batch(struct PpoHistogram *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_ppo_histogram_reset(struct PpoHistogram *handle); + +void wickra_ppo_histogram_free(struct PpoHistogram *handle); + +struct ProfitFactor *wickra_profit_factor_new(uintptr_t period); + +double wickra_profit_factor_update(struct ProfitFactor *handle, double value); + +void wickra_profit_factor_batch(struct ProfitFactor *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_profit_factor_reset(struct ProfitFactor *handle); + +void wickra_profit_factor_free(struct ProfitFactor *handle); + +struct RSquared *wickra_r_squared_new(uintptr_t period); + +double wickra_r_squared_update(struct RSquared *handle, double value); + +void wickra_r_squared_batch(struct RSquared *handle, const double *input, double *out, uintptr_t n); + +void wickra_r_squared_reset(struct RSquared *handle); + +void wickra_r_squared_free(struct RSquared *handle); + +struct RealizedVolatility *wickra_realized_volatility_new(uintptr_t period); + +double wickra_realized_volatility_update(struct RealizedVolatility *handle, double value); + +void wickra_realized_volatility_batch(struct RealizedVolatility *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_realized_volatility_reset(struct RealizedVolatility *handle); + +void wickra_realized_volatility_free(struct RealizedVolatility *handle); + +struct RecoveryFactor *wickra_recovery_factor_new(void); + +double wickra_recovery_factor_update(struct RecoveryFactor *handle, double value); + +void wickra_recovery_factor_batch(struct RecoveryFactor *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_recovery_factor_reset(struct RecoveryFactor *handle); + +void wickra_recovery_factor_free(struct RecoveryFactor *handle); + +struct Reflex *wickra_reflex_new(uintptr_t period); + +double wickra_reflex_update(struct Reflex *handle, double value); + +void wickra_reflex_batch(struct Reflex *handle, const double *input, double *out, uintptr_t n); + +void wickra_reflex_reset(struct Reflex *handle); + +void wickra_reflex_free(struct Reflex *handle); + +struct RegimeLabel *wickra_regime_label_new(uintptr_t vol_period, uintptr_t lookback); + +double wickra_regime_label_update(struct RegimeLabel *handle, double value); + +void wickra_regime_label_batch(struct RegimeLabel *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_regime_label_reset(struct RegimeLabel *handle); + +void wickra_regime_label_free(struct RegimeLabel *handle); + +struct RenkoTrailingStop *wickra_renko_trailing_stop_new(double block_size); + +double wickra_renko_trailing_stop_update(struct RenkoTrailingStop *handle, double value); + +void wickra_renko_trailing_stop_batch(struct RenkoTrailingStop *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_renko_trailing_stop_reset(struct RenkoTrailingStop *handle); + +void wickra_renko_trailing_stop_free(struct RenkoTrailingStop *handle); + +struct Rmi *wickra_rmi_new(uintptr_t period, uintptr_t momentum); + +double wickra_rmi_update(struct Rmi *handle, double value); + +void wickra_rmi_batch(struct Rmi *handle, const double *input, double *out, uintptr_t n); + +void wickra_rmi_reset(struct Rmi *handle); + +void wickra_rmi_free(struct Rmi *handle); + +struct Roc *wickra_roc_new(uintptr_t period); + +double wickra_roc_update(struct Roc *handle, double value); + +void wickra_roc_batch(struct Roc *handle, const double *input, double *out, uintptr_t n); + +void wickra_roc_reset(struct Roc *handle); + +void wickra_roc_free(struct Roc *handle); + +struct Rocp *wickra_rocp_new(uintptr_t period); + +double wickra_rocp_update(struct Rocp *handle, double value); + +void wickra_rocp_batch(struct Rocp *handle, const double *input, double *out, uintptr_t n); + +void wickra_rocp_reset(struct Rocp *handle); + +void wickra_rocp_free(struct Rocp *handle); + +struct Rocr *wickra_rocr_new(uintptr_t period); + +double wickra_rocr_update(struct Rocr *handle, double value); + +void wickra_rocr_batch(struct Rocr *handle, const double *input, double *out, uintptr_t n); + +void wickra_rocr_reset(struct Rocr *handle); + +void wickra_rocr_free(struct Rocr *handle); + +struct Rocr100 *wickra_rocr100_new(uintptr_t period); + +double wickra_rocr100_update(struct Rocr100 *handle, double value); + +void wickra_rocr100_batch(struct Rocr100 *handle, const double *input, double *out, uintptr_t n); + +void wickra_rocr100_reset(struct Rocr100 *handle); + +void wickra_rocr100_free(struct Rocr100 *handle); + +struct RollingIqr *wickra_rolling_iqr_new(uintptr_t period); + +double wickra_rolling_iqr_update(struct RollingIqr *handle, double value); + +void wickra_rolling_iqr_batch(struct RollingIqr *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_rolling_iqr_reset(struct RollingIqr *handle); + +void wickra_rolling_iqr_free(struct RollingIqr *handle); + +struct RollingMinMaxScaler *wickra_rolling_min_max_scaler_new(uintptr_t period); + +double wickra_rolling_min_max_scaler_update(struct RollingMinMaxScaler *handle, double value); + +void wickra_rolling_min_max_scaler_batch(struct RollingMinMaxScaler *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_rolling_min_max_scaler_reset(struct RollingMinMaxScaler *handle); + +void wickra_rolling_min_max_scaler_free(struct RollingMinMaxScaler *handle); + +struct RollingPercentileRank *wickra_rolling_percentile_rank_new(uintptr_t period); + +double wickra_rolling_percentile_rank_update(struct RollingPercentileRank *handle, double value); + +void wickra_rolling_percentile_rank_batch(struct RollingPercentileRank *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_rolling_percentile_rank_reset(struct RollingPercentileRank *handle); + +void wickra_rolling_percentile_rank_free(struct RollingPercentileRank *handle); + +struct RollingQuantile *wickra_rolling_quantile_new(uintptr_t period, double quantile); + +double wickra_rolling_quantile_update(struct RollingQuantile *handle, double value); + +void wickra_rolling_quantile_batch(struct RollingQuantile *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_rolling_quantile_reset(struct RollingQuantile *handle); + +void wickra_rolling_quantile_free(struct RollingQuantile *handle); + +struct RoofingFilter *wickra_roofing_filter_new(uintptr_t lp_period, uintptr_t hp_period); + +double wickra_roofing_filter_update(struct RoofingFilter *handle, double value); + +void wickra_roofing_filter_batch(struct RoofingFilter *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_roofing_filter_reset(struct RoofingFilter *handle); + +void wickra_roofing_filter_free(struct RoofingFilter *handle); + +struct Rsi *wickra_rsi_new(uintptr_t period); + +double wickra_rsi_update(struct Rsi *handle, double value); + +void wickra_rsi_batch(struct Rsi *handle, const double *input, double *out, uintptr_t n); + +void wickra_rsi_reset(struct Rsi *handle); + +void wickra_rsi_free(struct Rsi *handle); + +struct Rsx *wickra_rsx_new(uintptr_t length); + +double wickra_rsx_update(struct Rsx *handle, double value); + +void wickra_rsx_batch(struct Rsx *handle, const double *input, double *out, uintptr_t n); + +void wickra_rsx_reset(struct Rsx *handle); + +void wickra_rsx_free(struct Rsx *handle); + +struct RviVolatility *wickra_rvi_volatility_new(uintptr_t period); + +double wickra_rvi_volatility_update(struct RviVolatility *handle, double value); + +void wickra_rvi_volatility_batch(struct RviVolatility *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_rvi_volatility_reset(struct RviVolatility *handle); + +void wickra_rvi_volatility_free(struct RviVolatility *handle); + +struct SampleEntropy *wickra_sample_entropy_new(uintptr_t period, uintptr_t m, double r_factor); + +double wickra_sample_entropy_update(struct SampleEntropy *handle, double value); + +void wickra_sample_entropy_batch(struct SampleEntropy *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_sample_entropy_reset(struct SampleEntropy *handle); + +void wickra_sample_entropy_free(struct SampleEntropy *handle); + +struct ShannonEntropy *wickra_shannon_entropy_new(uintptr_t period, uintptr_t bins); + +double wickra_shannon_entropy_update(struct ShannonEntropy *handle, double value); + +void wickra_shannon_entropy_batch(struct ShannonEntropy *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_shannon_entropy_reset(struct ShannonEntropy *handle); + +void wickra_shannon_entropy_free(struct ShannonEntropy *handle); + +struct SharpeRatio *wickra_sharpe_ratio_new(uintptr_t period, double risk_free); + +double wickra_sharpe_ratio_update(struct SharpeRatio *handle, double value); + +void wickra_sharpe_ratio_batch(struct SharpeRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_sharpe_ratio_reset(struct SharpeRatio *handle); + +void wickra_sharpe_ratio_free(struct SharpeRatio *handle); + +struct SineWave *wickra_sine_wave_new(void); + +double wickra_sine_wave_update(struct SineWave *handle, double value); + +void wickra_sine_wave_batch(struct SineWave *handle, const double *input, double *out, uintptr_t n); + +void wickra_sine_wave_reset(struct SineWave *handle); + +void wickra_sine_wave_free(struct SineWave *handle); + +struct SineWeightedMa *wickra_sine_weighted_ma_new(uintptr_t period); + +double wickra_sine_weighted_ma_update(struct SineWeightedMa *handle, double value); + +void wickra_sine_weighted_ma_batch(struct SineWeightedMa *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_sine_weighted_ma_reset(struct SineWeightedMa *handle); + +void wickra_sine_weighted_ma_free(struct SineWeightedMa *handle); + +struct Skewness *wickra_skewness_new(uintptr_t period); + +double wickra_skewness_update(struct Skewness *handle, double value); + +void wickra_skewness_batch(struct Skewness *handle, const double *input, double *out, uintptr_t n); + +void wickra_skewness_reset(struct Skewness *handle); + +void wickra_skewness_free(struct Skewness *handle); + +struct Sma *wickra_sma_new(uintptr_t period); + +double wickra_sma_update(struct Sma *handle, double value); + +void wickra_sma_batch(struct Sma *handle, const double *input, double *out, uintptr_t n); + +void wickra_sma_reset(struct Sma *handle); + +void wickra_sma_free(struct Sma *handle); + +struct Smma *wickra_smma_new(uintptr_t period); + +double wickra_smma_update(struct Smma *handle, double value); + +void wickra_smma_batch(struct Smma *handle, const double *input, double *out, uintptr_t n); + +void wickra_smma_reset(struct Smma *handle); + +void wickra_smma_free(struct Smma *handle); + +struct SortinoRatio *wickra_sortino_ratio_new(uintptr_t period, double mar); + +double wickra_sortino_ratio_update(struct SortinoRatio *handle, double value); + +void wickra_sortino_ratio_batch(struct SortinoRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_sortino_ratio_reset(struct SortinoRatio *handle); + +void wickra_sortino_ratio_free(struct SortinoRatio *handle); + +struct StandardError *wickra_standard_error_new(uintptr_t period); + +double wickra_standard_error_update(struct StandardError *handle, double value); + +void wickra_standard_error_batch(struct StandardError *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_standard_error_reset(struct StandardError *handle); + +void wickra_standard_error_free(struct StandardError *handle); + +struct Stc *wickra_stc_new(uintptr_t fast, uintptr_t slow, uintptr_t schaff_period, double factor); + +double wickra_stc_update(struct Stc *handle, double value); + +void wickra_stc_batch(struct Stc *handle, const double *input, double *out, uintptr_t n); + +void wickra_stc_reset(struct Stc *handle); + +void wickra_stc_free(struct Stc *handle); + +struct StdDev *wickra_std_dev_new(uintptr_t period); + +double wickra_std_dev_update(struct StdDev *handle, double value); + +void wickra_std_dev_batch(struct StdDev *handle, const double *input, double *out, uintptr_t n); + +void wickra_std_dev_reset(struct StdDev *handle); + +void wickra_std_dev_free(struct StdDev *handle); + +struct StepTrailingStop *wickra_step_trailing_stop_new(double step_size); + +double wickra_step_trailing_stop_update(struct StepTrailingStop *handle, double value); + +void wickra_step_trailing_stop_batch(struct StepTrailingStop *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_step_trailing_stop_reset(struct StepTrailingStop *handle); + +void wickra_step_trailing_stop_free(struct StepTrailingStop *handle); + +struct SterlingRatio *wickra_sterling_ratio_new(uintptr_t period); + +double wickra_sterling_ratio_update(struct SterlingRatio *handle, double value); + +void wickra_sterling_ratio_batch(struct SterlingRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_sterling_ratio_reset(struct SterlingRatio *handle); + +void wickra_sterling_ratio_free(struct SterlingRatio *handle); + +struct StochRsi *wickra_stoch_rsi_new(uintptr_t rsi_period, uintptr_t stoch_period); + +double wickra_stoch_rsi_update(struct StochRsi *handle, double value); + +void wickra_stoch_rsi_batch(struct StochRsi *handle, const double *input, double *out, uintptr_t n); + +void wickra_stoch_rsi_reset(struct StochRsi *handle); + +void wickra_stoch_rsi_free(struct StochRsi *handle); + +struct SuperSmoother *wickra_super_smoother_new(uintptr_t period); + +double wickra_super_smoother_update(struct SuperSmoother *handle, double value); + +void wickra_super_smoother_batch(struct SuperSmoother *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_super_smoother_reset(struct SuperSmoother *handle); + +void wickra_super_smoother_free(struct SuperSmoother *handle); + +struct T3 *wickra_t3_new(uintptr_t period, double v); + +double wickra_t3_update(struct T3 *handle, double value); + +void wickra_t3_batch(struct T3 *handle, const double *input, double *out, uintptr_t n); + +void wickra_t3_reset(struct T3 *handle); + +void wickra_t3_free(struct T3 *handle); + +struct TailRatio *wickra_tail_ratio_new(uintptr_t period); + +double wickra_tail_ratio_update(struct TailRatio *handle, double value); + +void wickra_tail_ratio_batch(struct TailRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_tail_ratio_reset(struct TailRatio *handle); + +void wickra_tail_ratio_free(struct TailRatio *handle); + +struct Tema *wickra_tema_new(uintptr_t period); + +double wickra_tema_update(struct Tema *handle, double value); + +void wickra_tema_batch(struct Tema *handle, const double *input, double *out, uintptr_t n); + +void wickra_tema_reset(struct Tema *handle); + +void wickra_tema_free(struct Tema *handle); + +struct Tii *wickra_tii_new(uintptr_t sma_period, uintptr_t dev_period); + +double wickra_tii_update(struct Tii *handle, double value); + +void wickra_tii_batch(struct Tii *handle, const double *input, double *out, uintptr_t n); + +void wickra_tii_reset(struct Tii *handle); + +void wickra_tii_free(struct Tii *handle); + +struct TrendLabel *wickra_trend_label_new(uintptr_t period); + +double wickra_trend_label_update(struct TrendLabel *handle, double value); + +void wickra_trend_label_batch(struct TrendLabel *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_trend_label_reset(struct TrendLabel *handle); + +void wickra_trend_label_free(struct TrendLabel *handle); + +struct TrendStrengthIndex *wickra_trend_strength_index_new(uintptr_t period); + +double wickra_trend_strength_index_update(struct TrendStrengthIndex *handle, double value); + +void wickra_trend_strength_index_batch(struct TrendStrengthIndex *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_trend_strength_index_reset(struct TrendStrengthIndex *handle); + +void wickra_trend_strength_index_free(struct TrendStrengthIndex *handle); + +struct Trendflex *wickra_trendflex_new(uintptr_t period); + +double wickra_trendflex_update(struct Trendflex *handle, double value); + +void wickra_trendflex_batch(struct Trendflex *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_trendflex_reset(struct Trendflex *handle); + +void wickra_trendflex_free(struct Trendflex *handle); + +struct Trima *wickra_trima_new(uintptr_t period); + +double wickra_trima_update(struct Trima *handle, double value); + +void wickra_trima_batch(struct Trima *handle, const double *input, double *out, uintptr_t n); + +void wickra_trima_reset(struct Trima *handle); + +void wickra_trima_free(struct Trima *handle); + +struct Trix *wickra_trix_new(uintptr_t period); + +double wickra_trix_update(struct Trix *handle, double value); + +void wickra_trix_batch(struct Trix *handle, const double *input, double *out, uintptr_t n); + +void wickra_trix_reset(struct Trix *handle); + +void wickra_trix_free(struct Trix *handle); + +struct Tsf *wickra_tsf_new(uintptr_t period); + +double wickra_tsf_update(struct Tsf *handle, double value); + +void wickra_tsf_batch(struct Tsf *handle, const double *input, double *out, uintptr_t n); + +void wickra_tsf_reset(struct Tsf *handle); + +void wickra_tsf_free(struct Tsf *handle); + +struct TsfOscillator *wickra_tsf_oscillator_new(uintptr_t period); + +double wickra_tsf_oscillator_update(struct TsfOscillator *handle, double value); + +void wickra_tsf_oscillator_batch(struct TsfOscillator *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_tsf_oscillator_reset(struct TsfOscillator *handle); + +void wickra_tsf_oscillator_free(struct TsfOscillator *handle); + +struct Tsi *wickra_tsi_new(uintptr_t long_, uintptr_t short_); + +double wickra_tsi_update(struct Tsi *handle, double value); + +void wickra_tsi_batch(struct Tsi *handle, const double *input, double *out, uintptr_t n); + +void wickra_tsi_reset(struct Tsi *handle); + +void wickra_tsi_free(struct Tsi *handle); + +struct UlcerIndex *wickra_ulcer_index_new(uintptr_t period); + +double wickra_ulcer_index_update(struct UlcerIndex *handle, double value); + +void wickra_ulcer_index_batch(struct UlcerIndex *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_ulcer_index_reset(struct UlcerIndex *handle); + +void wickra_ulcer_index_free(struct UlcerIndex *handle); + +struct UniversalOscillator *wickra_universal_oscillator_new(uintptr_t period); + +double wickra_universal_oscillator_update(struct UniversalOscillator *handle, double value); + +void wickra_universal_oscillator_batch(struct UniversalOscillator *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_universal_oscillator_reset(struct UniversalOscillator *handle); + +void wickra_universal_oscillator_free(struct UniversalOscillator *handle); + +struct UpsidePotentialRatio *wickra_upside_potential_ratio_new(uintptr_t period, double mar); + +double wickra_upside_potential_ratio_update(struct UpsidePotentialRatio *handle, double value); + +void wickra_upside_potential_ratio_batch(struct UpsidePotentialRatio *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_upside_potential_ratio_reset(struct UpsidePotentialRatio *handle); + +void wickra_upside_potential_ratio_free(struct UpsidePotentialRatio *handle); + +struct ValueAtRisk *wickra_value_at_risk_new(uintptr_t period, double confidence); + +double wickra_value_at_risk_update(struct ValueAtRisk *handle, double value); + +void wickra_value_at_risk_batch(struct ValueAtRisk *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_value_at_risk_reset(struct ValueAtRisk *handle); + +void wickra_value_at_risk_free(struct ValueAtRisk *handle); + +struct Variance *wickra_variance_new(uintptr_t period); + +double wickra_variance_update(struct Variance *handle, double value); + +void wickra_variance_batch(struct Variance *handle, const double *input, double *out, uintptr_t n); + +void wickra_variance_reset(struct Variance *handle); + +void wickra_variance_free(struct Variance *handle); + +struct VerticalHorizontalFilter *wickra_vertical_horizontal_filter_new(uintptr_t period); + +double wickra_vertical_horizontal_filter_update(struct VerticalHorizontalFilter *handle, + double value); + +void wickra_vertical_horizontal_filter_batch(struct VerticalHorizontalFilter *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_vertical_horizontal_filter_reset(struct VerticalHorizontalFilter *handle); + +void wickra_vertical_horizontal_filter_free(struct VerticalHorizontalFilter *handle); + +struct Vidya *wickra_vidya_new(uintptr_t period, uintptr_t cmo_period); + +double wickra_vidya_update(struct Vidya *handle, double value); + +void wickra_vidya_batch(struct Vidya *handle, const double *input, double *out, uintptr_t n); + +void wickra_vidya_reset(struct Vidya *handle); + +void wickra_vidya_free(struct Vidya *handle); + +struct VolatilityOfVolatility *wickra_volatility_of_volatility_new(uintptr_t vol_window, + uintptr_t vov_window); + +double wickra_volatility_of_volatility_update(struct VolatilityOfVolatility *handle, double value); + +void wickra_volatility_of_volatility_batch(struct VolatilityOfVolatility *handle, + const double *input, + double *out, + uintptr_t n); + +void wickra_volatility_of_volatility_reset(struct VolatilityOfVolatility *handle); + +void wickra_volatility_of_volatility_free(struct VolatilityOfVolatility *handle); + +struct WavePm *wickra_wave_pm_new(uintptr_t length, uintptr_t smoothing); + +double wickra_wave_pm_update(struct WavePm *handle, double value); + +void wickra_wave_pm_batch(struct WavePm *handle, const double *input, double *out, uintptr_t n); + +void wickra_wave_pm_reset(struct WavePm *handle); + +void wickra_wave_pm_free(struct WavePm *handle); + +struct WinRate *wickra_win_rate_new(uintptr_t period); + +double wickra_win_rate_update(struct WinRate *handle, double value); + +void wickra_win_rate_batch(struct WinRate *handle, const double *input, double *out, uintptr_t n); + +void wickra_win_rate_reset(struct WinRate *handle); + +void wickra_win_rate_free(struct WinRate *handle); + +struct Wma *wickra_wma_new(uintptr_t period); + +double wickra_wma_update(struct Wma *handle, double value); + +void wickra_wma_batch(struct Wma *handle, const double *input, double *out, uintptr_t n); + +void wickra_wma_reset(struct Wma *handle); + +void wickra_wma_free(struct Wma *handle); + +struct ZScore *wickra_z_score_new(uintptr_t period); + +double wickra_z_score_update(struct ZScore *handle, double value); + +void wickra_z_score_batch(struct ZScore *handle, const double *input, double *out, uintptr_t n); + +void wickra_z_score_reset(struct ZScore *handle); + +void wickra_z_score_free(struct ZScore *handle); + +struct Zlema *wickra_zlema_new(uintptr_t period); + +double wickra_zlema_update(struct Zlema *handle, double value); + +void wickra_zlema_batch(struct Zlema *handle, const double *input, double *out, uintptr_t n); + +void wickra_zlema_reset(struct Zlema *handle); + +void wickra_zlema_free(struct Zlema *handle); + +struct Alpha *wickra_alpha_new(uintptr_t period, double risk_free); + +double wickra_alpha_update(struct Alpha *handle, double x, double y); + +void wickra_alpha_batch(struct Alpha *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_alpha_reset(struct Alpha *handle); + +void wickra_alpha_free(struct Alpha *handle); + +struct Beta *wickra_beta_new(uintptr_t period); + +double wickra_beta_update(struct Beta *handle, double x, double y); + +void wickra_beta_batch(struct Beta *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_beta_reset(struct Beta *handle); + +void wickra_beta_free(struct Beta *handle); + +struct BetaNeutralSpread *wickra_beta_neutral_spread_new(uintptr_t period); + +double wickra_beta_neutral_spread_update(struct BetaNeutralSpread *handle, double x, double y); + +void wickra_beta_neutral_spread_batch(struct BetaNeutralSpread *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_beta_neutral_spread_reset(struct BetaNeutralSpread *handle); + +void wickra_beta_neutral_spread_free(struct BetaNeutralSpread *handle); + +struct DistanceSsd *wickra_distance_ssd_new(uintptr_t period); + +double wickra_distance_ssd_update(struct DistanceSsd *handle, double x, double y); + +void wickra_distance_ssd_batch(struct DistanceSsd *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_distance_ssd_reset(struct DistanceSsd *handle); + +void wickra_distance_ssd_free(struct DistanceSsd *handle); + +struct GrangerCausality *wickra_granger_causality_new(uintptr_t period, uintptr_t lag); + +double wickra_granger_causality_update(struct GrangerCausality *handle, double x, double y); + +void wickra_granger_causality_batch(struct GrangerCausality *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_granger_causality_reset(struct GrangerCausality *handle); + +void wickra_granger_causality_free(struct GrangerCausality *handle); + +struct HasbrouckInformationShare *wickra_hasbrouck_information_share_new(uintptr_t period); + +double wickra_hasbrouck_information_share_update(struct HasbrouckInformationShare *handle, + double x, + double y); + +void wickra_hasbrouck_information_share_batch(struct HasbrouckInformationShare *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_hasbrouck_information_share_reset(struct HasbrouckInformationShare *handle); + +void wickra_hasbrouck_information_share_free(struct HasbrouckInformationShare *handle); + +struct InformationRatio *wickra_information_ratio_new(uintptr_t period); + +double wickra_information_ratio_update(struct InformationRatio *handle, double x, double y); + +void wickra_information_ratio_batch(struct InformationRatio *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_information_ratio_reset(struct InformationRatio *handle); + +void wickra_information_ratio_free(struct InformationRatio *handle); + +struct KendallTau *wickra_kendall_tau_new(uintptr_t period); + +double wickra_kendall_tau_update(struct KendallTau *handle, double x, double y); + +void wickra_kendall_tau_batch(struct KendallTau *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_kendall_tau_reset(struct KendallTau *handle); + +void wickra_kendall_tau_free(struct KendallTau *handle); + +struct OuHalfLife *wickra_ou_half_life_new(uintptr_t period); + +double wickra_ou_half_life_update(struct OuHalfLife *handle, double x, double y); + +void wickra_ou_half_life_batch(struct OuHalfLife *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_ou_half_life_reset(struct OuHalfLife *handle); + +void wickra_ou_half_life_free(struct OuHalfLife *handle); + +struct PairSpreadZScore *wickra_pair_spread_z_score_new(uintptr_t beta_period, uintptr_t z_period); + +double wickra_pair_spread_z_score_update(struct PairSpreadZScore *handle, double x, double y); + +void wickra_pair_spread_z_score_batch(struct PairSpreadZScore *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_pair_spread_z_score_reset(struct PairSpreadZScore *handle); + +void wickra_pair_spread_z_score_free(struct PairSpreadZScore *handle); + +struct PairwiseBeta *wickra_pairwise_beta_new(uintptr_t period); + +double wickra_pairwise_beta_update(struct PairwiseBeta *handle, double x, double y); + +void wickra_pairwise_beta_batch(struct PairwiseBeta *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_pairwise_beta_reset(struct PairwiseBeta *handle); + +void wickra_pairwise_beta_free(struct PairwiseBeta *handle); + +struct PearsonCorrelation *wickra_pearson_correlation_new(uintptr_t period); + +double wickra_pearson_correlation_update(struct PearsonCorrelation *handle, double x, double y); + +void wickra_pearson_correlation_batch(struct PearsonCorrelation *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_pearson_correlation_reset(struct PearsonCorrelation *handle); + +void wickra_pearson_correlation_free(struct PearsonCorrelation *handle); + +struct RollingCorrelation *wickra_rolling_correlation_new(uintptr_t period); + +double wickra_rolling_correlation_update(struct RollingCorrelation *handle, double x, double y); + +void wickra_rolling_correlation_batch(struct RollingCorrelation *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_rolling_correlation_reset(struct RollingCorrelation *handle); + +void wickra_rolling_correlation_free(struct RollingCorrelation *handle); + +struct RollingCovariance *wickra_rolling_covariance_new(uintptr_t period); + +double wickra_rolling_covariance_update(struct RollingCovariance *handle, double x, double y); + +void wickra_rolling_covariance_batch(struct RollingCovariance *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_rolling_covariance_reset(struct RollingCovariance *handle); + +void wickra_rolling_covariance_free(struct RollingCovariance *handle); + +struct SpearmanCorrelation *wickra_spearman_correlation_new(uintptr_t period); + +double wickra_spearman_correlation_update(struct SpearmanCorrelation *handle, double x, double y); + +void wickra_spearman_correlation_batch(struct SpearmanCorrelation *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_spearman_correlation_reset(struct SpearmanCorrelation *handle); + +void wickra_spearman_correlation_free(struct SpearmanCorrelation *handle); + +struct SpreadAr1Coefficient *wickra_spread_ar1_coefficient_new(uintptr_t period); + +double wickra_spread_ar1_coefficient_update(struct SpreadAr1Coefficient *handle, + double x, + double y); + +void wickra_spread_ar1_coefficient_batch(struct SpreadAr1Coefficient *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_spread_ar1_coefficient_reset(struct SpreadAr1Coefficient *handle); + +void wickra_spread_ar1_coefficient_free(struct SpreadAr1Coefficient *handle); + +struct SpreadHurst *wickra_spread_hurst_new(uintptr_t period); + +double wickra_spread_hurst_update(struct SpreadHurst *handle, double x, double y); + +void wickra_spread_hurst_batch(struct SpreadHurst *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_spread_hurst_reset(struct SpreadHurst *handle); + +void wickra_spread_hurst_free(struct SpreadHurst *handle); + +struct TreynorRatio *wickra_treynor_ratio_new(uintptr_t period, double risk_free); + +double wickra_treynor_ratio_update(struct TreynorRatio *handle, double x, double y); + +void wickra_treynor_ratio_batch(struct TreynorRatio *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_treynor_ratio_reset(struct TreynorRatio *handle); + +void wickra_treynor_ratio_free(struct TreynorRatio *handle); + +struct VarianceRatio *wickra_variance_ratio_new(uintptr_t period, uintptr_t q); + +double wickra_variance_ratio_update(struct VarianceRatio *handle, double x, double y); + +void wickra_variance_ratio_batch(struct VarianceRatio *handle, + const double *x, + const double *y, + double *out, + uintptr_t n); + +void wickra_variance_ratio_reset(struct VarianceRatio *handle); + +void wickra_variance_ratio_free(struct VarianceRatio *handle); + +struct AbandonedBaby *wickra_abandoned_baby_new(void); + +double wickra_abandoned_baby_update(struct AbandonedBaby *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_abandoned_baby_batch(struct AbandonedBaby *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_abandoned_baby_reset(struct AbandonedBaby *handle); + +void wickra_abandoned_baby_free(struct AbandonedBaby *handle); + +struct Abcd *wickra_abcd_new(void); + +double wickra_abcd_update(struct Abcd *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_abcd_batch(struct Abcd *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_abcd_reset(struct Abcd *handle); + +void wickra_abcd_free(struct Abcd *handle); + +struct AcceleratorOscillator *wickra_accelerator_oscillator_new(uintptr_t ao_fast, + uintptr_t ao_slow, + uintptr_t signal_period); + +double wickra_accelerator_oscillator_update(struct AcceleratorOscillator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_accelerator_oscillator_batch(struct AcceleratorOscillator *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_accelerator_oscillator_reset(struct AcceleratorOscillator *handle); + +void wickra_accelerator_oscillator_free(struct AcceleratorOscillator *handle); + +struct AdOscillator *wickra_ad_oscillator_new(void); + +double wickra_ad_oscillator_update(struct AdOscillator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_ad_oscillator_batch(struct AdOscillator *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_ad_oscillator_reset(struct AdOscillator *handle); + +void wickra_ad_oscillator_free(struct AdOscillator *handle); + +struct AdaptiveCci *wickra_adaptive_cci_new(uintptr_t period); + +double wickra_adaptive_cci_update(struct AdaptiveCci *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_adaptive_cci_batch(struct AdaptiveCci *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_adaptive_cci_reset(struct AdaptiveCci *handle); + +void wickra_adaptive_cci_free(struct AdaptiveCci *handle); + +struct Adl *wickra_adl_new(void); + +double wickra_adl_update(struct Adl *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_adl_batch(struct Adl *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_adl_reset(struct Adl *handle); + +void wickra_adl_free(struct Adl *handle); + +struct AdvanceBlock *wickra_advance_block_new(void); + +double wickra_advance_block_update(struct AdvanceBlock *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_advance_block_batch(struct AdvanceBlock *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_advance_block_reset(struct AdvanceBlock *handle); + +void wickra_advance_block_free(struct AdvanceBlock *handle); + +struct Adxr *wickra_adxr_new(uintptr_t period); + +double wickra_adxr_update(struct Adxr *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_adxr_batch(struct Adxr *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_adxr_reset(struct Adxr *handle); + +void wickra_adxr_free(struct Adxr *handle); + +struct AnchoredVwap *wickra_anchored_vwap_new(void); + +double wickra_anchored_vwap_update(struct AnchoredVwap *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_anchored_vwap_batch(struct AnchoredVwap *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_anchored_vwap_reset(struct AnchoredVwap *handle); + +void wickra_anchored_vwap_free(struct AnchoredVwap *handle); + +struct AroonOscillator *wickra_aroon_oscillator_new(uintptr_t period); + +double wickra_aroon_oscillator_update(struct AroonOscillator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_aroon_oscillator_batch(struct AroonOscillator *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_aroon_oscillator_reset(struct AroonOscillator *handle); + +void wickra_aroon_oscillator_free(struct AroonOscillator *handle); + +struct Atr *wickra_atr_new(uintptr_t period); + +double wickra_atr_update(struct Atr *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_atr_batch(struct Atr *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_atr_reset(struct Atr *handle); + +void wickra_atr_free(struct Atr *handle); + +struct AtrTrailingStop *wickra_atr_trailing_stop_new(uintptr_t atr_period, double multiplier); + +double wickra_atr_trailing_stop_update(struct AtrTrailingStop *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_atr_trailing_stop_batch(struct AtrTrailingStop *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_atr_trailing_stop_reset(struct AtrTrailingStop *handle); + +void wickra_atr_trailing_stop_free(struct AtrTrailingStop *handle); + +struct AverageDailyRange *wickra_average_daily_range_new(uintptr_t period, + int32_t utc_offset_minutes); + +double wickra_average_daily_range_update(struct AverageDailyRange *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_average_daily_range_batch(struct AverageDailyRange *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_average_daily_range_reset(struct AverageDailyRange *handle); + +void wickra_average_daily_range_free(struct AverageDailyRange *handle); + +struct AvgPrice *wickra_avg_price_new(void); + +double wickra_avg_price_update(struct AvgPrice *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_avg_price_batch(struct AvgPrice *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_avg_price_reset(struct AvgPrice *handle); + +void wickra_avg_price_free(struct AvgPrice *handle); + +struct AwesomeOscillator *wickra_awesome_oscillator_new(uintptr_t fast, uintptr_t slow); + +double wickra_awesome_oscillator_update(struct AwesomeOscillator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_awesome_oscillator_batch(struct AwesomeOscillator *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_awesome_oscillator_reset(struct AwesomeOscillator *handle); + +void wickra_awesome_oscillator_free(struct AwesomeOscillator *handle); + +struct AwesomeOscillatorHistogram *wickra_awesome_oscillator_histogram_new(uintptr_t fast, + uintptr_t slow, + uintptr_t sma_period); + +double wickra_awesome_oscillator_histogram_update(struct AwesomeOscillatorHistogram *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_awesome_oscillator_histogram_batch(struct AwesomeOscillatorHistogram *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_awesome_oscillator_histogram_reset(struct AwesomeOscillatorHistogram *handle); + +void wickra_awesome_oscillator_histogram_free(struct AwesomeOscillatorHistogram *handle); + +struct BalanceOfPower *wickra_balance_of_power_new(void); + +double wickra_balance_of_power_update(struct BalanceOfPower *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_balance_of_power_batch(struct BalanceOfPower *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_balance_of_power_reset(struct BalanceOfPower *handle); + +void wickra_balance_of_power_free(struct BalanceOfPower *handle); + +struct Bat *wickra_bat_new(void); + +double wickra_bat_update(struct Bat *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_bat_batch(struct Bat *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_bat_reset(struct Bat *handle); + +void wickra_bat_free(struct Bat *handle); + +struct BeltHold *wickra_belt_hold_new(void); + +double wickra_belt_hold_update(struct BeltHold *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_belt_hold_batch(struct BeltHold *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_belt_hold_reset(struct BeltHold *handle); + +void wickra_belt_hold_free(struct BeltHold *handle); + +struct BetterVolume *wickra_better_volume_new(uintptr_t period); + +double wickra_better_volume_update(struct BetterVolume *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_better_volume_batch(struct BetterVolume *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_better_volume_reset(struct BetterVolume *handle); + +void wickra_better_volume_free(struct BetterVolume *handle); + +struct BodySizePct *wickra_body_size_pct_new(void); + +double wickra_body_size_pct_update(struct BodySizePct *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_body_size_pct_batch(struct BodySizePct *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_body_size_pct_reset(struct BodySizePct *handle); + +void wickra_body_size_pct_free(struct BodySizePct *handle); + +struct Breakaway *wickra_breakaway_new(void); + +double wickra_breakaway_update(struct Breakaway *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_breakaway_batch(struct Breakaway *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_breakaway_reset(struct Breakaway *handle); + +void wickra_breakaway_free(struct Breakaway *handle); + +struct Butterfly *wickra_butterfly_new(void); + +double wickra_butterfly_update(struct Butterfly *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_butterfly_batch(struct Butterfly *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_butterfly_reset(struct Butterfly *handle); + +void wickra_butterfly_free(struct Butterfly *handle); + +struct Cci *wickra_cci_new(uintptr_t period); + +double wickra_cci_update(struct Cci *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_cci_batch(struct Cci *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_cci_reset(struct Cci *handle); + +void wickra_cci_free(struct Cci *handle); + +struct ChaikinOscillator *wickra_chaikin_oscillator_new(uintptr_t fast, uintptr_t slow); + +double wickra_chaikin_oscillator_update(struct ChaikinOscillator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_chaikin_oscillator_batch(struct ChaikinOscillator *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_chaikin_oscillator_reset(struct ChaikinOscillator *handle); + +void wickra_chaikin_oscillator_free(struct ChaikinOscillator *handle); + +struct ChaikinVolatility *wickra_chaikin_volatility_new(uintptr_t ema_period, uintptr_t roc_period); + +double wickra_chaikin_volatility_update(struct ChaikinVolatility *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_chaikin_volatility_batch(struct ChaikinVolatility *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_chaikin_volatility_reset(struct ChaikinVolatility *handle); + +void wickra_chaikin_volatility_free(struct ChaikinVolatility *handle); + +struct ChoppinessIndex *wickra_choppiness_index_new(uintptr_t period); + +double wickra_choppiness_index_update(struct ChoppinessIndex *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_choppiness_index_batch(struct ChoppinessIndex *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_choppiness_index_reset(struct ChoppinessIndex *handle); + +void wickra_choppiness_index_free(struct ChoppinessIndex *handle); + +struct CloseVsOpen *wickra_close_vs_open_new(void); + +double wickra_close_vs_open_update(struct CloseVsOpen *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_close_vs_open_batch(struct CloseVsOpen *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_close_vs_open_reset(struct CloseVsOpen *handle); + +void wickra_close_vs_open_free(struct CloseVsOpen *handle); + +struct ClosingMarubozu *wickra_closing_marubozu_new(void); + +double wickra_closing_marubozu_update(struct ClosingMarubozu *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_closing_marubozu_batch(struct ClosingMarubozu *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_closing_marubozu_reset(struct ClosingMarubozu *handle); + +void wickra_closing_marubozu_free(struct ClosingMarubozu *handle); + +struct ChaikinMoneyFlow *wickra_chaikin_money_flow_new(uintptr_t period); + +double wickra_chaikin_money_flow_update(struct ChaikinMoneyFlow *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_chaikin_money_flow_batch(struct ChaikinMoneyFlow *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_chaikin_money_flow_reset(struct ChaikinMoneyFlow *handle); + +void wickra_chaikin_money_flow_free(struct ChaikinMoneyFlow *handle); + +struct ConcealingBabySwallow *wickra_concealing_baby_swallow_new(void); + +double wickra_concealing_baby_swallow_update(struct ConcealingBabySwallow *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_concealing_baby_swallow_batch(struct ConcealingBabySwallow *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_concealing_baby_swallow_reset(struct ConcealingBabySwallow *handle); + +void wickra_concealing_baby_swallow_free(struct ConcealingBabySwallow *handle); + +struct Counterattack *wickra_counterattack_new(void); + +double wickra_counterattack_update(struct Counterattack *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_counterattack_batch(struct Counterattack *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_counterattack_reset(struct Counterattack *handle); + +void wickra_counterattack_free(struct Counterattack *handle); + +struct Crab *wickra_crab_new(void); + +double wickra_crab_update(struct Crab *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_crab_batch(struct Crab *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_crab_reset(struct Crab *handle); + +void wickra_crab_free(struct Crab *handle); + +struct CupAndHandle *wickra_cup_and_handle_new(void); + +double wickra_cup_and_handle_update(struct CupAndHandle *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_cup_and_handle_batch(struct CupAndHandle *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_cup_and_handle_reset(struct CupAndHandle *handle); + +void wickra_cup_and_handle_free(struct CupAndHandle *handle); + +struct Cypher *wickra_cypher_new(void); + +double wickra_cypher_update(struct Cypher *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_cypher_batch(struct Cypher *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_cypher_reset(struct Cypher *handle); + +void wickra_cypher_free(struct Cypher *handle); + +struct DemandIndex *wickra_demand_index_new(uintptr_t period); + +double wickra_demand_index_update(struct DemandIndex *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_demand_index_batch(struct DemandIndex *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_demand_index_reset(struct DemandIndex *handle); + +void wickra_demand_index_free(struct DemandIndex *handle); + +struct Doji *wickra_doji_new(void); + +double wickra_doji_update(struct Doji *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_doji_batch(struct Doji *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_doji_reset(struct Doji *handle); + +void wickra_doji_free(struct Doji *handle); + +struct DojiStar *wickra_doji_star_new(void); + +double wickra_doji_star_update(struct DojiStar *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_doji_star_batch(struct DojiStar *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_doji_star_reset(struct DojiStar *handle); + +void wickra_doji_star_free(struct DojiStar *handle); + +struct DoubleTopBottom *wickra_double_top_bottom_new(void); + +double wickra_double_top_bottom_update(struct DoubleTopBottom *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_double_top_bottom_batch(struct DoubleTopBottom *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_double_top_bottom_reset(struct DoubleTopBottom *handle); + +void wickra_double_top_bottom_free(struct DoubleTopBottom *handle); + +struct DownsideGapThreeMethods *wickra_downside_gap_three_methods_new(void); + +double wickra_downside_gap_three_methods_update(struct DownsideGapThreeMethods *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_downside_gap_three_methods_batch(struct DownsideGapThreeMethods *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_downside_gap_three_methods_reset(struct DownsideGapThreeMethods *handle); + +void wickra_downside_gap_three_methods_free(struct DownsideGapThreeMethods *handle); + +struct DragonflyDoji *wickra_dragonfly_doji_new(void); + +double wickra_dragonfly_doji_update(struct DragonflyDoji *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_dragonfly_doji_batch(struct DragonflyDoji *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_dragonfly_doji_reset(struct DragonflyDoji *handle); + +void wickra_dragonfly_doji_free(struct DragonflyDoji *handle); + +struct DumplingTop *wickra_dumpling_top_new(uintptr_t period); + +double wickra_dumpling_top_update(struct DumplingTop *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_dumpling_top_batch(struct DumplingTop *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_dumpling_top_reset(struct DumplingTop *handle); + +void wickra_dumpling_top_free(struct DumplingTop *handle); + +struct Dx *wickra_dx_new(uintptr_t period); + +double wickra_dx_update(struct Dx *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_dx_batch(struct Dx *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_dx_reset(struct Dx *handle); + +void wickra_dx_free(struct Dx *handle); + +struct EaseOfMovement *wickra_ease_of_movement_new(uintptr_t period); + +double wickra_ease_of_movement_update(struct EaseOfMovement *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_ease_of_movement_batch(struct EaseOfMovement *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_ease_of_movement_reset(struct EaseOfMovement *handle); + +void wickra_ease_of_movement_free(struct EaseOfMovement *handle); + +struct Engulfing *wickra_engulfing_new(void); + +double wickra_engulfing_update(struct Engulfing *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_engulfing_batch(struct Engulfing *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_engulfing_reset(struct Engulfing *handle); + +void wickra_engulfing_free(struct Engulfing *handle); + +struct EveningDojiStar *wickra_evening_doji_star_new(void); + +double wickra_evening_doji_star_update(struct EveningDojiStar *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_evening_doji_star_batch(struct EveningDojiStar *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_evening_doji_star_reset(struct EveningDojiStar *handle); + +void wickra_evening_doji_star_free(struct EveningDojiStar *handle); + +struct Evwma *wickra_evwma_new(uintptr_t period); + +double wickra_evwma_update(struct Evwma *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_evwma_batch(struct Evwma *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_evwma_reset(struct Evwma *handle); + +void wickra_evwma_free(struct Evwma *handle); + +struct FallingThreeMethods *wickra_falling_three_methods_new(void); + +double wickra_falling_three_methods_update(struct FallingThreeMethods *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_falling_three_methods_batch(struct FallingThreeMethods *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_falling_three_methods_reset(struct FallingThreeMethods *handle); + +void wickra_falling_three_methods_free(struct FallingThreeMethods *handle); + +struct FlagPennant *wickra_flag_pennant_new(void); + +double wickra_flag_pennant_update(struct FlagPennant *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_flag_pennant_batch(struct FlagPennant *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_flag_pennant_reset(struct FlagPennant *handle); + +void wickra_flag_pennant_free(struct FlagPennant *handle); + +struct ForceIndex *wickra_force_index_new(uintptr_t period); + +double wickra_force_index_update(struct ForceIndex *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_force_index_batch(struct ForceIndex *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_force_index_reset(struct ForceIndex *handle); + +void wickra_force_index_free(struct ForceIndex *handle); + +struct FryPanBottom *wickra_fry_pan_bottom_new(uintptr_t period); + +double wickra_fry_pan_bottom_update(struct FryPanBottom *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_fry_pan_bottom_batch(struct FryPanBottom *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_fry_pan_bottom_reset(struct FryPanBottom *handle); + +void wickra_fry_pan_bottom_free(struct FryPanBottom *handle); + +struct GapSideBySideWhite *wickra_gap_side_by_side_white_new(void); + +double wickra_gap_side_by_side_white_update(struct GapSideBySideWhite *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_gap_side_by_side_white_batch(struct GapSideBySideWhite *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_gap_side_by_side_white_reset(struct GapSideBySideWhite *handle); + +void wickra_gap_side_by_side_white_free(struct GapSideBySideWhite *handle); + +struct GarmanKlassVolatility *wickra_garman_klass_volatility_new(uintptr_t period, + uintptr_t trading_periods); + +double wickra_garman_klass_volatility_update(struct GarmanKlassVolatility *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_garman_klass_volatility_batch(struct GarmanKlassVolatility *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_garman_klass_volatility_reset(struct GarmanKlassVolatility *handle); + +void wickra_garman_klass_volatility_free(struct GarmanKlassVolatility *handle); + +struct Gartley *wickra_gartley_new(void); + +double wickra_gartley_update(struct Gartley *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_gartley_batch(struct Gartley *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_gartley_reset(struct Gartley *handle); + +void wickra_gartley_free(struct Gartley *handle); + +struct GravestoneDoji *wickra_gravestone_doji_new(void); + +double wickra_gravestone_doji_update(struct GravestoneDoji *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_gravestone_doji_batch(struct GravestoneDoji *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_gravestone_doji_reset(struct GravestoneDoji *handle); + +void wickra_gravestone_doji_free(struct GravestoneDoji *handle); + +struct Hammer *wickra_hammer_new(void); + +double wickra_hammer_update(struct Hammer *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_hammer_batch(struct Hammer *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_hammer_reset(struct Hammer *handle); + +void wickra_hammer_free(struct Hammer *handle); + +struct HangingMan *wickra_hanging_man_new(void); + +double wickra_hanging_man_update(struct HangingMan *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_hanging_man_batch(struct HangingMan *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_hanging_man_reset(struct HangingMan *handle); + +void wickra_hanging_man_free(struct HangingMan *handle); + +struct Harami *wickra_harami_new(void); + +double wickra_harami_update(struct Harami *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_harami_batch(struct Harami *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_harami_reset(struct Harami *handle); + +void wickra_harami_free(struct Harami *handle); + +struct HaramiCross *wickra_harami_cross_new(void); + +double wickra_harami_cross_update(struct HaramiCross *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_harami_cross_batch(struct HaramiCross *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_harami_cross_reset(struct HaramiCross *handle); + +void wickra_harami_cross_free(struct HaramiCross *handle); + +struct HeadAndShoulders *wickra_head_and_shoulders_new(void); + +double wickra_head_and_shoulders_update(struct HeadAndShoulders *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_head_and_shoulders_batch(struct HeadAndShoulders *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_head_and_shoulders_reset(struct HeadAndShoulders *handle); + +void wickra_head_and_shoulders_free(struct HeadAndShoulders *handle); + +struct HeikinAshiOscillator *wickra_heikin_ashi_oscillator_new(uintptr_t period); + +double wickra_heikin_ashi_oscillator_update(struct HeikinAshiOscillator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_heikin_ashi_oscillator_batch(struct HeikinAshiOscillator *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_heikin_ashi_oscillator_reset(struct HeikinAshiOscillator *handle); + +void wickra_heikin_ashi_oscillator_free(struct HeikinAshiOscillator *handle); + +struct HighLowRange *wickra_high_low_range_new(void); + +double wickra_high_low_range_update(struct HighLowRange *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_high_low_range_batch(struct HighLowRange *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_high_low_range_reset(struct HighLowRange *handle); + +void wickra_high_low_range_free(struct HighLowRange *handle); + +struct HighWave *wickra_high_wave_new(void); + +double wickra_high_wave_update(struct HighWave *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_high_wave_batch(struct HighWave *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_high_wave_reset(struct HighWave *handle); + +void wickra_high_wave_free(struct HighWave *handle); + +struct Hikkake *wickra_hikkake_new(void); + +double wickra_hikkake_update(struct Hikkake *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_hikkake_batch(struct Hikkake *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_hikkake_reset(struct Hikkake *handle); + +void wickra_hikkake_free(struct Hikkake *handle); + +struct HikkakeModified *wickra_hikkake_modified_new(void); + +double wickra_hikkake_modified_update(struct HikkakeModified *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_hikkake_modified_batch(struct HikkakeModified *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_hikkake_modified_reset(struct HikkakeModified *handle); + +void wickra_hikkake_modified_free(struct HikkakeModified *handle); + +struct HiLoActivator *wickra_hi_lo_activator_new(uintptr_t period); + +double wickra_hi_lo_activator_update(struct HiLoActivator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_hi_lo_activator_batch(struct HiLoActivator *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_hi_lo_activator_reset(struct HiLoActivator *handle); + +void wickra_hi_lo_activator_free(struct HiLoActivator *handle); + +struct HomingPigeon *wickra_homing_pigeon_new(void); + +double wickra_homing_pigeon_update(struct HomingPigeon *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_homing_pigeon_batch(struct HomingPigeon *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_homing_pigeon_reset(struct HomingPigeon *handle); + +void wickra_homing_pigeon_free(struct HomingPigeon *handle); + +struct IdenticalThreeCrows *wickra_identical_three_crows_new(void); + +double wickra_identical_three_crows_update(struct IdenticalThreeCrows *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_identical_three_crows_batch(struct IdenticalThreeCrows *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_identical_three_crows_reset(struct IdenticalThreeCrows *handle); + +void wickra_identical_three_crows_free(struct IdenticalThreeCrows *handle); + +struct InNeck *wickra_in_neck_new(void); + +double wickra_in_neck_update(struct InNeck *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_in_neck_batch(struct InNeck *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_in_neck_reset(struct InNeck *handle); + +void wickra_in_neck_free(struct InNeck *handle); + +struct Inertia *wickra_inertia_new(uintptr_t rvi_period, uintptr_t linreg_period); + +double wickra_inertia_update(struct Inertia *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_inertia_batch(struct Inertia *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_inertia_reset(struct Inertia *handle); + +void wickra_inertia_free(struct Inertia *handle); + +struct IntradayIntensity *wickra_intraday_intensity_new(void); + +double wickra_intraday_intensity_update(struct IntradayIntensity *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_intraday_intensity_batch(struct IntradayIntensity *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_intraday_intensity_reset(struct IntradayIntensity *handle); + +void wickra_intraday_intensity_free(struct IntradayIntensity *handle); + +struct IntradayMomentumIndex *wickra_intraday_momentum_index_new(uintptr_t period); + +double wickra_intraday_momentum_index_update(struct IntradayMomentumIndex *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_intraday_momentum_index_batch(struct IntradayMomentumIndex *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_intraday_momentum_index_reset(struct IntradayMomentumIndex *handle); + +void wickra_intraday_momentum_index_free(struct IntradayMomentumIndex *handle); + +struct InvertedHammer *wickra_inverted_hammer_new(void); + +double wickra_inverted_hammer_update(struct InvertedHammer *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_inverted_hammer_batch(struct InvertedHammer *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_inverted_hammer_reset(struct InvertedHammer *handle); + +void wickra_inverted_hammer_free(struct InvertedHammer *handle); + +struct Kicking *wickra_kicking_new(void); + +double wickra_kicking_update(struct Kicking *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_kicking_batch(struct Kicking *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_kicking_reset(struct Kicking *handle); + +void wickra_kicking_free(struct Kicking *handle); + +struct KickingByLength *wickra_kicking_by_length_new(void); + +double wickra_kicking_by_length_update(struct KickingByLength *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_kicking_by_length_batch(struct KickingByLength *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_kicking_by_length_reset(struct KickingByLength *handle); + +void wickra_kicking_by_length_free(struct KickingByLength *handle); + +struct Kvo *wickra_kvo_new(uintptr_t fast, uintptr_t slow); + +double wickra_kvo_update(struct Kvo *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_kvo_batch(struct Kvo *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_kvo_reset(struct Kvo *handle); + +void wickra_kvo_free(struct Kvo *handle); + +struct LadderBottom *wickra_ladder_bottom_new(void); + +double wickra_ladder_bottom_update(struct LadderBottom *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_ladder_bottom_batch(struct LadderBottom *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_ladder_bottom_reset(struct LadderBottom *handle); + +void wickra_ladder_bottom_free(struct LadderBottom *handle); + +struct LongLeggedDoji *wickra_long_legged_doji_new(void); + +double wickra_long_legged_doji_update(struct LongLeggedDoji *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_long_legged_doji_batch(struct LongLeggedDoji *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_long_legged_doji_reset(struct LongLeggedDoji *handle); + +void wickra_long_legged_doji_free(struct LongLeggedDoji *handle); + +struct LongLine *wickra_long_line_new(void); + +double wickra_long_line_update(struct LongLine *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_long_line_batch(struct LongLine *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_long_line_reset(struct LongLine *handle); + +void wickra_long_line_free(struct LongLine *handle); + +struct MarketFacilitationIndex *wickra_market_facilitation_index_new(void); + +double wickra_market_facilitation_index_update(struct MarketFacilitationIndex *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_market_facilitation_index_batch(struct MarketFacilitationIndex *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_market_facilitation_index_reset(struct MarketFacilitationIndex *handle); + +void wickra_market_facilitation_index_free(struct MarketFacilitationIndex *handle); + +struct Marubozu *wickra_marubozu_new(void); + +double wickra_marubozu_update(struct Marubozu *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_marubozu_batch(struct Marubozu *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_marubozu_reset(struct Marubozu *handle); + +void wickra_marubozu_free(struct Marubozu *handle); + +struct MassIndex *wickra_mass_index_new(uintptr_t ema_period, uintptr_t sum_period); + +double wickra_mass_index_update(struct MassIndex *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_mass_index_batch(struct MassIndex *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_mass_index_reset(struct MassIndex *handle); + +void wickra_mass_index_free(struct MassIndex *handle); + +struct MatHold *wickra_mat_hold_new(void); + +double wickra_mat_hold_update(struct MatHold *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_mat_hold_batch(struct MatHold *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_mat_hold_reset(struct MatHold *handle); + +void wickra_mat_hold_free(struct MatHold *handle); + +struct MatchingLow *wickra_matching_low_new(void); + +double wickra_matching_low_update(struct MatchingLow *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_matching_low_batch(struct MatchingLow *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_matching_low_reset(struct MatchingLow *handle); + +void wickra_matching_low_free(struct MatchingLow *handle); + +struct MedianPrice *wickra_median_price_new(void); + +double wickra_median_price_update(struct MedianPrice *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_median_price_batch(struct MedianPrice *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_median_price_reset(struct MedianPrice *handle); + +void wickra_median_price_free(struct MedianPrice *handle); + +struct Mfi *wickra_mfi_new(uintptr_t period); + +double wickra_mfi_update(struct Mfi *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_mfi_batch(struct Mfi *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_mfi_reset(struct Mfi *handle); + +void wickra_mfi_free(struct Mfi *handle); + +struct MidPrice *wickra_mid_price_new(uintptr_t period); + +double wickra_mid_price_update(struct MidPrice *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_mid_price_batch(struct MidPrice *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_mid_price_reset(struct MidPrice *handle); + +void wickra_mid_price_free(struct MidPrice *handle); + +struct MinusDi *wickra_minus_di_new(uintptr_t period); + +double wickra_minus_di_update(struct MinusDi *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_minus_di_batch(struct MinusDi *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_minus_di_reset(struct MinusDi *handle); + +void wickra_minus_di_free(struct MinusDi *handle); + +struct MinusDm *wickra_minus_dm_new(uintptr_t period); + +double wickra_minus_dm_update(struct MinusDm *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_minus_dm_batch(struct MinusDm *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_minus_dm_reset(struct MinusDm *handle); + +void wickra_minus_dm_free(struct MinusDm *handle); + +struct MorningDojiStar *wickra_morning_doji_star_new(void); + +double wickra_morning_doji_star_update(struct MorningDojiStar *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_morning_doji_star_batch(struct MorningDojiStar *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_morning_doji_star_reset(struct MorningDojiStar *handle); + +void wickra_morning_doji_star_free(struct MorningDojiStar *handle); + +struct MorningEveningStar *wickra_morning_evening_star_new(void); + +double wickra_morning_evening_star_update(struct MorningEveningStar *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_morning_evening_star_batch(struct MorningEveningStar *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_morning_evening_star_reset(struct MorningEveningStar *handle); + +void wickra_morning_evening_star_free(struct MorningEveningStar *handle); + +struct NakedPoc *wickra_naked_poc_new(uintptr_t session_len, uintptr_t bins); + +double wickra_naked_poc_update(struct NakedPoc *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_naked_poc_batch(struct NakedPoc *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_naked_poc_reset(struct NakedPoc *handle); + +void wickra_naked_poc_free(struct NakedPoc *handle); + +struct Natr *wickra_natr_new(uintptr_t period); + +double wickra_natr_update(struct Natr *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_natr_batch(struct Natr *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_natr_reset(struct Natr *handle); + +void wickra_natr_free(struct Natr *handle); + +struct NewPriceLines *wickra_new_price_lines_new(uintptr_t count); + +double wickra_new_price_lines_update(struct NewPriceLines *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_new_price_lines_batch(struct NewPriceLines *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_new_price_lines_reset(struct NewPriceLines *handle); + +void wickra_new_price_lines_free(struct NewPriceLines *handle); + +struct Nvi *wickra_nvi_new(void); + +double wickra_nvi_update(struct Nvi *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_nvi_batch(struct Nvi *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_nvi_reset(struct Nvi *handle); + +void wickra_nvi_free(struct Nvi *handle); + +struct Obv *wickra_obv_new(void); + +double wickra_obv_update(struct Obv *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_obv_batch(struct Obv *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_obv_reset(struct Obv *handle); + +void wickra_obv_free(struct Obv *handle); + +struct OnNeck *wickra_on_neck_new(void); + +double wickra_on_neck_update(struct OnNeck *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_on_neck_batch(struct OnNeck *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_on_neck_reset(struct OnNeck *handle); + +void wickra_on_neck_free(struct OnNeck *handle); + +struct OpeningMarubozu *wickra_opening_marubozu_new(void); + +double wickra_opening_marubozu_update(struct OpeningMarubozu *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_opening_marubozu_batch(struct OpeningMarubozu *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_opening_marubozu_reset(struct OpeningMarubozu *handle); + +void wickra_opening_marubozu_free(struct OpeningMarubozu *handle); + +struct OvernightGap *wickra_overnight_gap_new(int32_t utc_offset_minutes); + +double wickra_overnight_gap_update(struct OvernightGap *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_overnight_gap_batch(struct OvernightGap *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_overnight_gap_reset(struct OvernightGap *handle); + +void wickra_overnight_gap_free(struct OvernightGap *handle); + +struct ParkinsonVolatility *wickra_parkinson_volatility_new(uintptr_t period, + uintptr_t trading_periods); + +double wickra_parkinson_volatility_update(struct ParkinsonVolatility *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_parkinson_volatility_batch(struct ParkinsonVolatility *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_parkinson_volatility_reset(struct ParkinsonVolatility *handle); + +void wickra_parkinson_volatility_free(struct ParkinsonVolatility *handle); + +struct Pgo *wickra_pgo_new(uintptr_t period); + +double wickra_pgo_update(struct Pgo *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_pgo_batch(struct Pgo *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_pgo_reset(struct Pgo *handle); + +void wickra_pgo_free(struct Pgo *handle); + +struct PiercingDarkCloud *wickra_piercing_dark_cloud_new(void); + +double wickra_piercing_dark_cloud_update(struct PiercingDarkCloud *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_piercing_dark_cloud_batch(struct PiercingDarkCloud *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_piercing_dark_cloud_reset(struct PiercingDarkCloud *handle); + +void wickra_piercing_dark_cloud_free(struct PiercingDarkCloud *handle); + +struct PivotReversal *wickra_pivot_reversal_new(uintptr_t left, uintptr_t right); + +double wickra_pivot_reversal_update(struct PivotReversal *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_pivot_reversal_batch(struct PivotReversal *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_pivot_reversal_reset(struct PivotReversal *handle); + +void wickra_pivot_reversal_free(struct PivotReversal *handle); + +struct PlusDi *wickra_plus_di_new(uintptr_t period); + +double wickra_plus_di_update(struct PlusDi *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_plus_di_batch(struct PlusDi *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_plus_di_reset(struct PlusDi *handle); + +void wickra_plus_di_free(struct PlusDi *handle); + +struct PlusDm *wickra_plus_dm_new(uintptr_t period); + +double wickra_plus_dm_update(struct PlusDm *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_plus_dm_batch(struct PlusDm *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_plus_dm_reset(struct PlusDm *handle); + +void wickra_plus_dm_free(struct PlusDm *handle); + +struct ProfileShape *wickra_profile_shape_new(uintptr_t period, uintptr_t bins); + +double wickra_profile_shape_update(struct ProfileShape *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_profile_shape_batch(struct ProfileShape *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_profile_shape_reset(struct ProfileShape *handle); + +void wickra_profile_shape_free(struct ProfileShape *handle); + +struct ProjectionOscillator *wickra_projection_oscillator_new(uintptr_t period); + +double wickra_projection_oscillator_update(struct ProjectionOscillator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_projection_oscillator_batch(struct ProjectionOscillator *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_projection_oscillator_reset(struct ProjectionOscillator *handle); + +void wickra_projection_oscillator_free(struct ProjectionOscillator *handle); + +struct Psar *wickra_psar_new(double af_start, double af_step, double af_max); + +double wickra_psar_update(struct Psar *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_psar_batch(struct Psar *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_psar_reset(struct Psar *handle); + +void wickra_psar_free(struct Psar *handle); + +struct Pvi *wickra_pvi_new(void); + +double wickra_pvi_update(struct Pvi *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_pvi_batch(struct Pvi *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_pvi_reset(struct Pvi *handle); + +void wickra_pvi_free(struct Pvi *handle); + +struct Qstick *wickra_qstick_new(uintptr_t period); + +double wickra_qstick_update(struct Qstick *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_qstick_batch(struct Qstick *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_qstick_reset(struct Qstick *handle); + +void wickra_qstick_free(struct Qstick *handle); + +struct RectangleRange *wickra_rectangle_range_new(void); + +double wickra_rectangle_range_update(struct RectangleRange *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_rectangle_range_batch(struct RectangleRange *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_rectangle_range_reset(struct RectangleRange *handle); + +void wickra_rectangle_range_free(struct RectangleRange *handle); + +struct RickshawMan *wickra_rickshaw_man_new(void); + +double wickra_rickshaw_man_update(struct RickshawMan *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_rickshaw_man_batch(struct RickshawMan *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_rickshaw_man_reset(struct RickshawMan *handle); + +void wickra_rickshaw_man_free(struct RickshawMan *handle); + +struct RisingThreeMethods *wickra_rising_three_methods_new(void); + +double wickra_rising_three_methods_update(struct RisingThreeMethods *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_rising_three_methods_batch(struct RisingThreeMethods *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_rising_three_methods_reset(struct RisingThreeMethods *handle); + +void wickra_rising_three_methods_free(struct RisingThreeMethods *handle); + +struct RogersSatchellVolatility *wickra_rogers_satchell_volatility_new(uintptr_t period, + uintptr_t trading_periods); + +double wickra_rogers_satchell_volatility_update(struct RogersSatchellVolatility *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_rogers_satchell_volatility_batch(struct RogersSatchellVolatility *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_rogers_satchell_volatility_reset(struct RogersSatchellVolatility *handle); + +void wickra_rogers_satchell_volatility_free(struct RogersSatchellVolatility *handle); + +struct Rvi *wickra_rvi_new(uintptr_t period); + +double wickra_rvi_update(struct Rvi *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_rvi_batch(struct Rvi *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_rvi_reset(struct Rvi *handle); + +void wickra_rvi_free(struct Rvi *handle); + +struct SarExt *wickra_sar_ext_new(double start_value, + double offset_on_reverse, + double accel_init_long, + double accel_long, + double accel_max_long, + double accel_init_short, + double accel_short, + double accel_max_short); + +double wickra_sar_ext_update(struct SarExt *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_sar_ext_batch(struct SarExt *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_sar_ext_reset(struct SarExt *handle); + +void wickra_sar_ext_free(struct SarExt *handle); + +struct SeasonalZScore *wickra_seasonal_z_score_new(int32_t utc_offset_minutes); + +double wickra_seasonal_z_score_update(struct SeasonalZScore *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_seasonal_z_score_batch(struct SeasonalZScore *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_seasonal_z_score_reset(struct SeasonalZScore *handle); + +void wickra_seasonal_z_score_free(struct SeasonalZScore *handle); + +struct SeparatingLines *wickra_separating_lines_new(void); + +double wickra_separating_lines_update(struct SeparatingLines *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_separating_lines_batch(struct SeparatingLines *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_separating_lines_reset(struct SeparatingLines *handle); + +void wickra_separating_lines_free(struct SeparatingLines *handle); + +struct SessionVwap *wickra_session_vwap_new(int32_t utc_offset_minutes); + +double wickra_session_vwap_update(struct SessionVwap *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_session_vwap_batch(struct SessionVwap *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_session_vwap_reset(struct SessionVwap *handle); + +void wickra_session_vwap_free(struct SessionVwap *handle); + +struct Shark *wickra_shark_new(void); + +double wickra_shark_update(struct Shark *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_shark_batch(struct Shark *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_shark_reset(struct Shark *handle); + +void wickra_shark_free(struct Shark *handle); + +struct ShootingStar *wickra_shooting_star_new(void); + +double wickra_shooting_star_update(struct ShootingStar *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_shooting_star_batch(struct ShootingStar *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_shooting_star_reset(struct ShootingStar *handle); + +void wickra_shooting_star_free(struct ShootingStar *handle); + +struct ShortLine *wickra_short_line_new(void); + +double wickra_short_line_update(struct ShortLine *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_short_line_batch(struct ShortLine *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_short_line_reset(struct ShortLine *handle); + +void wickra_short_line_free(struct ShortLine *handle); + +struct SinglePrints *wickra_single_prints_new(uintptr_t period, uintptr_t bins); + +double wickra_single_prints_update(struct SinglePrints *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_single_prints_batch(struct SinglePrints *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_single_prints_reset(struct SinglePrints *handle); + +void wickra_single_prints_free(struct SinglePrints *handle); + +struct Smi *wickra_smi_new(uintptr_t period, uintptr_t d_period, uintptr_t d2_period); + +double wickra_smi_update(struct Smi *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_smi_batch(struct Smi *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_smi_reset(struct Smi *handle); + +void wickra_smi_free(struct Smi *handle); + +struct SpinningTop *wickra_spinning_top_new(void); + +double wickra_spinning_top_update(struct SpinningTop *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_spinning_top_batch(struct SpinningTop *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_spinning_top_reset(struct SpinningTop *handle); + +void wickra_spinning_top_free(struct SpinningTop *handle); + +struct StalledPattern *wickra_stalled_pattern_new(void); + +double wickra_stalled_pattern_update(struct StalledPattern *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_stalled_pattern_batch(struct StalledPattern *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_stalled_pattern_reset(struct StalledPattern *handle); + +void wickra_stalled_pattern_free(struct StalledPattern *handle); + +struct StickSandwich *wickra_stick_sandwich_new(void); + +double wickra_stick_sandwich_update(struct StickSandwich *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_stick_sandwich_batch(struct StickSandwich *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_stick_sandwich_reset(struct StickSandwich *handle); + +void wickra_stick_sandwich_free(struct StickSandwich *handle); + +struct StochasticCci *wickra_stochastic_cci_new(uintptr_t period); + +double wickra_stochastic_cci_update(struct StochasticCci *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_stochastic_cci_batch(struct StochasticCci *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_stochastic_cci_reset(struct StochasticCci *handle); + +void wickra_stochastic_cci_free(struct StochasticCci *handle); + +struct Takuri *wickra_takuri_new(void); + +double wickra_takuri_update(struct Takuri *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_takuri_batch(struct Takuri *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_takuri_reset(struct Takuri *handle); + +void wickra_takuri_free(struct Takuri *handle); + +struct TasukiGap *wickra_tasuki_gap_new(void); + +double wickra_tasuki_gap_update(struct TasukiGap *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_tasuki_gap_batch(struct TasukiGap *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_tasuki_gap_reset(struct TasukiGap *handle); + +void wickra_tasuki_gap_free(struct TasukiGap *handle); + +struct TdCamouflage *wickra_td_camouflage_new(void); + +double wickra_td_camouflage_update(struct TdCamouflage *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_camouflage_batch(struct TdCamouflage *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_camouflage_reset(struct TdCamouflage *handle); + +void wickra_td_camouflage_free(struct TdCamouflage *handle); + +struct TdClop *wickra_td_clop_new(void); + +double wickra_td_clop_update(struct TdClop *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_clop_batch(struct TdClop *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_clop_reset(struct TdClop *handle); + +void wickra_td_clop_free(struct TdClop *handle); + +struct TdClopwin *wickra_td_clopwin_new(void); + +double wickra_td_clopwin_update(struct TdClopwin *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_clopwin_batch(struct TdClopwin *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_clopwin_reset(struct TdClopwin *handle); + +void wickra_td_clopwin_free(struct TdClopwin *handle); + +struct TdCombo *wickra_td_combo_new(uintptr_t setup_lookback, + uintptr_t setup_target, + uintptr_t countdown_lookback, + uintptr_t countdown_target); + +double wickra_td_combo_update(struct TdCombo *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_combo_batch(struct TdCombo *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_combo_reset(struct TdCombo *handle); + +void wickra_td_combo_free(struct TdCombo *handle); + +struct TdCountdown *wickra_td_countdown_new(uintptr_t setup_lookback, + uintptr_t setup_target, + uintptr_t countdown_lookback, + uintptr_t countdown_target); + +double wickra_td_countdown_update(struct TdCountdown *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_countdown_batch(struct TdCountdown *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_countdown_reset(struct TdCountdown *handle); + +void wickra_td_countdown_free(struct TdCountdown *handle); + +struct TdDeMarker *wickra_td_de_marker_new(uintptr_t period); + +double wickra_td_de_marker_update(struct TdDeMarker *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_de_marker_batch(struct TdDeMarker *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_de_marker_reset(struct TdDeMarker *handle); + +void wickra_td_de_marker_free(struct TdDeMarker *handle); + +struct TdDifferential *wickra_td_differential_new(void); + +double wickra_td_differential_update(struct TdDifferential *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_differential_batch(struct TdDifferential *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_differential_reset(struct TdDifferential *handle); + +void wickra_td_differential_free(struct TdDifferential *handle); + +struct TdDWave *wickra_td_d_wave_new(uintptr_t strength); + +double wickra_td_d_wave_update(struct TdDWave *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_d_wave_batch(struct TdDWave *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_d_wave_reset(struct TdDWave *handle); + +void wickra_td_d_wave_free(struct TdDWave *handle); + +struct TdOpen *wickra_td_open_new(void); + +double wickra_td_open_update(struct TdOpen *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_open_batch(struct TdOpen *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_open_reset(struct TdOpen *handle); + +void wickra_td_open_free(struct TdOpen *handle); + +struct TdPressure *wickra_td_pressure_new(uintptr_t period); + +double wickra_td_pressure_update(struct TdPressure *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_pressure_batch(struct TdPressure *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_pressure_reset(struct TdPressure *handle); + +void wickra_td_pressure_free(struct TdPressure *handle); + +struct TdPropulsion *wickra_td_propulsion_new(void); + +double wickra_td_propulsion_update(struct TdPropulsion *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_propulsion_batch(struct TdPropulsion *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_propulsion_reset(struct TdPropulsion *handle); + +void wickra_td_propulsion_free(struct TdPropulsion *handle); + +struct TdRei *wickra_td_rei_new(uintptr_t period); + +double wickra_td_rei_update(struct TdRei *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_rei_batch(struct TdRei *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_rei_reset(struct TdRei *handle); + +void wickra_td_rei_free(struct TdRei *handle); + +struct TdSetup *wickra_td_setup_new(uintptr_t lookback, uintptr_t target); + +double wickra_td_setup_update(struct TdSetup *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_setup_batch(struct TdSetup *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_setup_reset(struct TdSetup *handle); + +void wickra_td_setup_free(struct TdSetup *handle); + +struct TdTrap *wickra_td_trap_new(void); + +double wickra_td_trap_update(struct TdTrap *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_td_trap_batch(struct TdTrap *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_td_trap_reset(struct TdTrap *handle); + +void wickra_td_trap_free(struct TdTrap *handle); + +struct ThreeDrives *wickra_three_drives_new(void); + +double wickra_three_drives_update(struct ThreeDrives *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_three_drives_batch(struct ThreeDrives *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_three_drives_reset(struct ThreeDrives *handle); + +void wickra_three_drives_free(struct ThreeDrives *handle); + +struct ThreeInside *wickra_three_inside_new(void); + +double wickra_three_inside_update(struct ThreeInside *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_three_inside_batch(struct ThreeInside *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_three_inside_reset(struct ThreeInside *handle); + +void wickra_three_inside_free(struct ThreeInside *handle); + +struct ThreeLineBreak *wickra_three_line_break_new(uintptr_t lines); + +double wickra_three_line_break_update(struct ThreeLineBreak *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_three_line_break_batch(struct ThreeLineBreak *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_three_line_break_reset(struct ThreeLineBreak *handle); + +void wickra_three_line_break_free(struct ThreeLineBreak *handle); + +struct ThreeLineStrike *wickra_three_line_strike_new(void); + +double wickra_three_line_strike_update(struct ThreeLineStrike *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_three_line_strike_batch(struct ThreeLineStrike *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_three_line_strike_reset(struct ThreeLineStrike *handle); + +void wickra_three_line_strike_free(struct ThreeLineStrike *handle); + +struct ThreeOutside *wickra_three_outside_new(void); + +double wickra_three_outside_update(struct ThreeOutside *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_three_outside_batch(struct ThreeOutside *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_three_outside_reset(struct ThreeOutside *handle); + +void wickra_three_outside_free(struct ThreeOutside *handle); + +struct ThreeSoldiersOrCrows *wickra_three_soldiers_or_crows_new(void); + +double wickra_three_soldiers_or_crows_update(struct ThreeSoldiersOrCrows *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_three_soldiers_or_crows_batch(struct ThreeSoldiersOrCrows *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_three_soldiers_or_crows_reset(struct ThreeSoldiersOrCrows *handle); + +void wickra_three_soldiers_or_crows_free(struct ThreeSoldiersOrCrows *handle); + +struct ThreeStarsInSouth *wickra_three_stars_in_south_new(void); + +double wickra_three_stars_in_south_update(struct ThreeStarsInSouth *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_three_stars_in_south_batch(struct ThreeStarsInSouth *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_three_stars_in_south_reset(struct ThreeStarsInSouth *handle); + +void wickra_three_stars_in_south_free(struct ThreeStarsInSouth *handle); + +struct Thrusting *wickra_thrusting_new(void); + +double wickra_thrusting_update(struct Thrusting *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_thrusting_batch(struct Thrusting *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_thrusting_reset(struct Thrusting *handle); + +void wickra_thrusting_free(struct Thrusting *handle); + +struct TimeBasedStop *wickra_time_based_stop_new(uintptr_t max_bars); + +double wickra_time_based_stop_update(struct TimeBasedStop *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_time_based_stop_batch(struct TimeBasedStop *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_time_based_stop_reset(struct TimeBasedStop *handle); + +void wickra_time_based_stop_free(struct TimeBasedStop *handle); + +struct TowerTopBottom *wickra_tower_top_bottom_new(void); + +double wickra_tower_top_bottom_update(struct TowerTopBottom *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_tower_top_bottom_batch(struct TowerTopBottom *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_tower_top_bottom_reset(struct TowerTopBottom *handle); + +void wickra_tower_top_bottom_free(struct TowerTopBottom *handle); + +struct TradeVolumeIndex *wickra_trade_volume_index_new(double min_tick); + +double wickra_trade_volume_index_update(struct TradeVolumeIndex *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_trade_volume_index_batch(struct TradeVolumeIndex *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_trade_volume_index_reset(struct TradeVolumeIndex *handle); + +void wickra_trade_volume_index_free(struct TradeVolumeIndex *handle); + +struct Triangle *wickra_triangle_new(void); + +double wickra_triangle_update(struct Triangle *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_triangle_batch(struct Triangle *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_triangle_reset(struct Triangle *handle); + +void wickra_triangle_free(struct Triangle *handle); + +struct TripleTopBottom *wickra_triple_top_bottom_new(void); + +double wickra_triple_top_bottom_update(struct TripleTopBottom *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_triple_top_bottom_batch(struct TripleTopBottom *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_triple_top_bottom_reset(struct TripleTopBottom *handle); + +void wickra_triple_top_bottom_free(struct TripleTopBottom *handle); + +struct Tristar *wickra_tristar_new(void); + +double wickra_tristar_update(struct Tristar *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_tristar_batch(struct Tristar *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_tristar_reset(struct Tristar *handle); + +void wickra_tristar_free(struct Tristar *handle); + +struct TrueRange *wickra_true_range_new(void); + +double wickra_true_range_update(struct TrueRange *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_true_range_batch(struct TrueRange *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_true_range_reset(struct TrueRange *handle); + +void wickra_true_range_free(struct TrueRange *handle); + +struct Tsv *wickra_tsv_new(uintptr_t period); + +double wickra_tsv_update(struct Tsv *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_tsv_batch(struct Tsv *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_tsv_reset(struct Tsv *handle); + +void wickra_tsv_free(struct Tsv *handle); + +struct TtmTrend *wickra_ttm_trend_new(uintptr_t period); + +double wickra_ttm_trend_update(struct TtmTrend *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_ttm_trend_batch(struct TtmTrend *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_ttm_trend_reset(struct TtmTrend *handle); + +void wickra_ttm_trend_free(struct TtmTrend *handle); + +struct TurnOfMonth *wickra_turn_of_month_new(uint32_t n_first, + uint32_t n_last, + int32_t utc_offset_minutes); + +double wickra_turn_of_month_update(struct TurnOfMonth *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_turn_of_month_batch(struct TurnOfMonth *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_turn_of_month_reset(struct TurnOfMonth *handle); + +void wickra_turn_of_month_free(struct TurnOfMonth *handle); + +struct Tweezer *wickra_tweezer_new(void); + +double wickra_tweezer_update(struct Tweezer *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_tweezer_batch(struct Tweezer *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_tweezer_reset(struct Tweezer *handle); + +void wickra_tweezer_free(struct Tweezer *handle); + +struct TwiggsMoneyFlow *wickra_twiggs_money_flow_new(uintptr_t period); + +double wickra_twiggs_money_flow_update(struct TwiggsMoneyFlow *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_twiggs_money_flow_batch(struct TwiggsMoneyFlow *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_twiggs_money_flow_reset(struct TwiggsMoneyFlow *handle); + +void wickra_twiggs_money_flow_free(struct TwiggsMoneyFlow *handle); + +struct TwoCrows *wickra_two_crows_new(void); + +double wickra_two_crows_update(struct TwoCrows *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_two_crows_batch(struct TwoCrows *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_two_crows_reset(struct TwoCrows *handle); + +void wickra_two_crows_free(struct TwoCrows *handle); + +struct TypicalPrice *wickra_typical_price_new(void); + +double wickra_typical_price_update(struct TypicalPrice *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_typical_price_batch(struct TypicalPrice *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_typical_price_reset(struct TypicalPrice *handle); + +void wickra_typical_price_free(struct TypicalPrice *handle); + +struct UltimateOscillator *wickra_ultimate_oscillator_new(uintptr_t short_, + uintptr_t mid, + uintptr_t long_); + +double wickra_ultimate_oscillator_update(struct UltimateOscillator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_ultimate_oscillator_batch(struct UltimateOscillator *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_ultimate_oscillator_reset(struct UltimateOscillator *handle); + +void wickra_ultimate_oscillator_free(struct UltimateOscillator *handle); + +struct UniqueThreeRiver *wickra_unique_three_river_new(void); + +double wickra_unique_three_river_update(struct UniqueThreeRiver *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_unique_three_river_batch(struct UniqueThreeRiver *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_unique_three_river_reset(struct UniqueThreeRiver *handle); + +void wickra_unique_three_river_free(struct UniqueThreeRiver *handle); + +struct UpsideGapThreeMethods *wickra_upside_gap_three_methods_new(void); + +double wickra_upside_gap_three_methods_update(struct UpsideGapThreeMethods *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_upside_gap_three_methods_batch(struct UpsideGapThreeMethods *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_upside_gap_three_methods_reset(struct UpsideGapThreeMethods *handle); + +void wickra_upside_gap_three_methods_free(struct UpsideGapThreeMethods *handle); + +struct UpsideGapTwoCrows *wickra_upside_gap_two_crows_new(void); + +double wickra_upside_gap_two_crows_update(struct UpsideGapTwoCrows *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_upside_gap_two_crows_batch(struct UpsideGapTwoCrows *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_upside_gap_two_crows_reset(struct UpsideGapTwoCrows *handle); + +void wickra_upside_gap_two_crows_free(struct UpsideGapTwoCrows *handle); + +struct VolatilityRatio *wickra_volatility_ratio_new(uintptr_t period); + +double wickra_volatility_ratio_update(struct VolatilityRatio *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_volatility_ratio_batch(struct VolatilityRatio *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_volatility_ratio_reset(struct VolatilityRatio *handle); + +void wickra_volatility_ratio_free(struct VolatilityRatio *handle); + +struct VoltyStop *wickra_volty_stop_new(uintptr_t atr_period, double multiplier); + +double wickra_volty_stop_update(struct VoltyStop *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_volty_stop_batch(struct VoltyStop *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_volty_stop_reset(struct VoltyStop *handle); + +void wickra_volty_stop_free(struct VoltyStop *handle); + +struct VolumeOscillator *wickra_volume_oscillator_new(uintptr_t fast, uintptr_t slow); + +double wickra_volume_oscillator_update(struct VolumeOscillator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_volume_oscillator_batch(struct VolumeOscillator *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_volume_oscillator_reset(struct VolumeOscillator *handle); + +void wickra_volume_oscillator_free(struct VolumeOscillator *handle); + +struct VolumeRsi *wickra_volume_rsi_new(uintptr_t period); + +double wickra_volume_rsi_update(struct VolumeRsi *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_volume_rsi_batch(struct VolumeRsi *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_volume_rsi_reset(struct VolumeRsi *handle); + +void wickra_volume_rsi_free(struct VolumeRsi *handle); + +struct VolumePriceTrend *wickra_volume_price_trend_new(void); + +double wickra_volume_price_trend_update(struct VolumePriceTrend *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_volume_price_trend_batch(struct VolumePriceTrend *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_volume_price_trend_reset(struct VolumePriceTrend *handle); + +void wickra_volume_price_trend_free(struct VolumePriceTrend *handle); + +struct Vwap *wickra_vwap_new(void); + +double wickra_vwap_update(struct Vwap *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_vwap_batch(struct Vwap *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_vwap_reset(struct Vwap *handle); + +void wickra_vwap_free(struct Vwap *handle); + +struct RollingVwap *wickra_rolling_vwap_new(uintptr_t period); + +double wickra_rolling_vwap_update(struct RollingVwap *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_rolling_vwap_batch(struct RollingVwap *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_rolling_vwap_reset(struct RollingVwap *handle); + +void wickra_rolling_vwap_free(struct RollingVwap *handle); + +struct Vwma *wickra_vwma_new(uintptr_t period); + +double wickra_vwma_update(struct Vwma *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_vwma_batch(struct Vwma *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_vwma_reset(struct Vwma *handle); + +void wickra_vwma_free(struct Vwma *handle); + +struct Vzo *wickra_vzo_new(uintptr_t period); + +double wickra_vzo_update(struct Vzo *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_vzo_batch(struct Vzo *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_vzo_reset(struct Vzo *handle); + +void wickra_vzo_free(struct Vzo *handle); + +struct Wad *wickra_wad_new(void); + +double wickra_wad_update(struct Wad *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_wad_batch(struct Wad *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_wad_reset(struct Wad *handle); + +void wickra_wad_free(struct Wad *handle); + +struct Wedge *wickra_wedge_new(void); + +double wickra_wedge_update(struct Wedge *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_wedge_batch(struct Wedge *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_wedge_reset(struct Wedge *handle); + +void wickra_wedge_free(struct Wedge *handle); + +struct WeightedClose *wickra_weighted_close_new(void); + +double wickra_weighted_close_update(struct WeightedClose *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_weighted_close_batch(struct WeightedClose *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_weighted_close_reset(struct WeightedClose *handle); + +void wickra_weighted_close_free(struct WeightedClose *handle); + +struct WickRatio *wickra_wick_ratio_new(void); + +double wickra_wick_ratio_update(struct WickRatio *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_wick_ratio_batch(struct WickRatio *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_wick_ratio_reset(struct WickRatio *handle); + +void wickra_wick_ratio_free(struct WickRatio *handle); + +struct WilliamsR *wickra_williams_r_new(uintptr_t period); + +double wickra_williams_r_update(struct WilliamsR *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_williams_r_batch(struct WilliamsR *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_williams_r_reset(struct WilliamsR *handle); + +void wickra_williams_r_free(struct WilliamsR *handle); + +struct YangZhangVolatility *wickra_yang_zhang_volatility_new(uintptr_t period, + uintptr_t trading_periods); + +double wickra_yang_zhang_volatility_update(struct YangZhangVolatility *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_yang_zhang_volatility_batch(struct YangZhangVolatility *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_yang_zhang_volatility_reset(struct YangZhangVolatility *handle); + +void wickra_yang_zhang_volatility_free(struct YangZhangVolatility *handle); + +struct YoyoExit *wickra_yoyo_exit_new(uintptr_t atr_period, double multiplier); + +double wickra_yoyo_exit_update(struct YoyoExit *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp); + +void wickra_yoyo_exit_batch(struct YoyoExit *handle, + const double *open, + const double *high, + const double *low, + const double *close, + const double *volume, + const int64_t *timestamp, + double *out, + uintptr_t n); + +void wickra_yoyo_exit_reset(struct YoyoExit *handle); + +void wickra_yoyo_exit_free(struct YoyoExit *handle); + +struct AmihudIlliquidity *wickra_amihud_illiquidity_new(uintptr_t period); + +double wickra_amihud_illiquidity_update(struct AmihudIlliquidity *handle, + double price, + double size, + bool is_buy, + int64_t timestamp); + +void wickra_amihud_illiquidity_reset(struct AmihudIlliquidity *handle); + +void wickra_amihud_illiquidity_free(struct AmihudIlliquidity *handle); + +struct CumulativeVolumeDelta *wickra_cumulative_volume_delta_new(void); + +double wickra_cumulative_volume_delta_update(struct CumulativeVolumeDelta *handle, + double price, + double size, + bool is_buy, + int64_t timestamp); + +void wickra_cumulative_volume_delta_reset(struct CumulativeVolumeDelta *handle); + +void wickra_cumulative_volume_delta_free(struct CumulativeVolumeDelta *handle); + +struct Pin *wickra_pin_new(uintptr_t window); + +double wickra_pin_update(struct Pin *handle, + double price, + double size, + bool is_buy, + int64_t timestamp); + +void wickra_pin_reset(struct Pin *handle); + +void wickra_pin_free(struct Pin *handle); + +struct RollMeasure *wickra_roll_measure_new(uintptr_t period); + +double wickra_roll_measure_update(struct RollMeasure *handle, + double price, + double size, + bool is_buy, + int64_t timestamp); + +void wickra_roll_measure_reset(struct RollMeasure *handle); + +void wickra_roll_measure_free(struct RollMeasure *handle); + +struct SignedVolume *wickra_signed_volume_new(void); + +double wickra_signed_volume_update(struct SignedVolume *handle, + double price, + double size, + bool is_buy, + int64_t timestamp); + +void wickra_signed_volume_reset(struct SignedVolume *handle); + +void wickra_signed_volume_free(struct SignedVolume *handle); + +struct TradeImbalance *wickra_trade_imbalance_new(uintptr_t window); + +double wickra_trade_imbalance_update(struct TradeImbalance *handle, + double price, + double size, + bool is_buy, + int64_t timestamp); + +void wickra_trade_imbalance_reset(struct TradeImbalance *handle); + +void wickra_trade_imbalance_free(struct TradeImbalance *handle); + +struct TradeSignAutocorrelation *wickra_trade_sign_autocorrelation_new(uintptr_t period); + +double wickra_trade_sign_autocorrelation_update(struct TradeSignAutocorrelation *handle, + double price, + double size, + bool is_buy, + int64_t timestamp); + +void wickra_trade_sign_autocorrelation_reset(struct TradeSignAutocorrelation *handle); + +void wickra_trade_sign_autocorrelation_free(struct TradeSignAutocorrelation *handle); + +struct Vpin *wickra_vpin_new(double bucket_volume, uintptr_t num_buckets); + +double wickra_vpin_update(struct Vpin *handle, + double price, + double size, + bool is_buy, + int64_t timestamp); + +void wickra_vpin_reset(struct Vpin *handle); + +void wickra_vpin_free(struct Vpin *handle); + +struct EffectiveSpread *wickra_effective_spread_new(void); + +double wickra_effective_spread_update(struct EffectiveSpread *handle, + double price, + double size, + bool is_buy, + int64_t timestamp, + double mid); + +void wickra_effective_spread_reset(struct EffectiveSpread *handle); + +void wickra_effective_spread_free(struct EffectiveSpread *handle); + +struct KylesLambda *wickra_kyles_lambda_new(uintptr_t window); + +double wickra_kyles_lambda_update(struct KylesLambda *handle, + double price, + double size, + bool is_buy, + int64_t timestamp, + double mid); + +void wickra_kyles_lambda_reset(struct KylesLambda *handle); + +void wickra_kyles_lambda_free(struct KylesLambda *handle); + +struct RealizedSpread *wickra_realized_spread_new(uintptr_t horizon); + +double wickra_realized_spread_update(struct RealizedSpread *handle, + double price, + double size, + bool is_buy, + int64_t timestamp, + double mid); + +void wickra_realized_spread_reset(struct RealizedSpread *handle); + +void wickra_realized_spread_free(struct RealizedSpread *handle); + +struct CalendarSpread *wickra_calendar_spread_new(void); + +double wickra_calendar_spread_update(struct CalendarSpread *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_calendar_spread_reset(struct CalendarSpread *handle); + +void wickra_calendar_spread_free(struct CalendarSpread *handle); + +struct EstimatedLeverageRatio *wickra_estimated_leverage_ratio_new(void); + +double wickra_estimated_leverage_ratio_update(struct EstimatedLeverageRatio *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_estimated_leverage_ratio_reset(struct EstimatedLeverageRatio *handle); + +void wickra_estimated_leverage_ratio_free(struct EstimatedLeverageRatio *handle); + +struct FundingBasis *wickra_funding_basis_new(void); + +double wickra_funding_basis_update(struct FundingBasis *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_funding_basis_reset(struct FundingBasis *handle); + +void wickra_funding_basis_free(struct FundingBasis *handle); + +struct FundingImpliedApr *wickra_funding_implied_apr_new(double intervals_per_year); + +double wickra_funding_implied_apr_update(struct FundingImpliedApr *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_funding_implied_apr_reset(struct FundingImpliedApr *handle); + +void wickra_funding_implied_apr_free(struct FundingImpliedApr *handle); + +struct FundingRate *wickra_funding_rate_new(void); + +double wickra_funding_rate_update(struct FundingRate *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_funding_rate_reset(struct FundingRate *handle); + +void wickra_funding_rate_free(struct FundingRate *handle); + +struct FundingRateMean *wickra_funding_rate_mean_new(uintptr_t window); + +double wickra_funding_rate_mean_update(struct FundingRateMean *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_funding_rate_mean_reset(struct FundingRateMean *handle); + +void wickra_funding_rate_mean_free(struct FundingRateMean *handle); + +struct FundingRateZScore *wickra_funding_rate_z_score_new(uintptr_t window); + +double wickra_funding_rate_z_score_update(struct FundingRateZScore *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_funding_rate_z_score_reset(struct FundingRateZScore *handle); + +void wickra_funding_rate_z_score_free(struct FundingRateZScore *handle); + +struct LongShortRatio *wickra_long_short_ratio_new(void); + +double wickra_long_short_ratio_update(struct LongShortRatio *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_long_short_ratio_reset(struct LongShortRatio *handle); + +void wickra_long_short_ratio_free(struct LongShortRatio *handle); + +struct OpenInterestDelta *wickra_open_interest_delta_new(void); + +double wickra_open_interest_delta_update(struct OpenInterestDelta *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_open_interest_delta_reset(struct OpenInterestDelta *handle); + +void wickra_open_interest_delta_free(struct OpenInterestDelta *handle); + +struct OIPriceDivergence *wickra_oi_price_divergence_new(uintptr_t window); + +double wickra_oi_price_divergence_update(struct OIPriceDivergence *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_oi_price_divergence_reset(struct OIPriceDivergence *handle); + +void wickra_oi_price_divergence_free(struct OIPriceDivergence *handle); + +struct OiToVolumeRatio *wickra_oi_to_volume_ratio_new(void); + +double wickra_oi_to_volume_ratio_update(struct OiToVolumeRatio *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_oi_to_volume_ratio_reset(struct OiToVolumeRatio *handle); + +void wickra_oi_to_volume_ratio_free(struct OiToVolumeRatio *handle); + +struct OIWeighted *wickra_oi_weighted_new(void); + +double wickra_oi_weighted_update(struct OIWeighted *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_oi_weighted_reset(struct OIWeighted *handle); + +void wickra_oi_weighted_free(struct OIWeighted *handle); + +struct OpenInterestMomentum *wickra_open_interest_momentum_new(uintptr_t period); + +double wickra_open_interest_momentum_update(struct OpenInterestMomentum *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_open_interest_momentum_reset(struct OpenInterestMomentum *handle); + +void wickra_open_interest_momentum_free(struct OpenInterestMomentum *handle); + +struct PerpetualPremiumIndex *wickra_perpetual_premium_index_new(void); + +double wickra_perpetual_premium_index_update(struct PerpetualPremiumIndex *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_perpetual_premium_index_reset(struct PerpetualPremiumIndex *handle); + +void wickra_perpetual_premium_index_free(struct PerpetualPremiumIndex *handle); + +struct TakerBuySellRatio *wickra_taker_buy_sell_ratio_new(void); + +double wickra_taker_buy_sell_ratio_update(struct TakerBuySellRatio *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_taker_buy_sell_ratio_reset(struct TakerBuySellRatio *handle); + +void wickra_taker_buy_sell_ratio_free(struct TakerBuySellRatio *handle); + +struct TermStructureBasis *wickra_term_structure_basis_new(void); + +double wickra_term_structure_basis_update(struct TermStructureBasis *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp); + +void wickra_term_structure_basis_reset(struct TermStructureBasis *handle); + +void wickra_term_structure_basis_free(struct TermStructureBasis *handle); + +struct DepthSlope *wickra_depth_slope_new(void); + +double wickra_depth_slope_update(struct DepthSlope *handle, + const double *bid_price, + const double *bid_size, + uintptr_t n_bids, + const double *ask_price, + const double *ask_size, + uintptr_t n_asks); + +void wickra_depth_slope_reset(struct DepthSlope *handle); + +void wickra_depth_slope_free(struct DepthSlope *handle); + +struct Microprice *wickra_microprice_new(void); + +double wickra_microprice_update(struct Microprice *handle, + const double *bid_price, + const double *bid_size, + uintptr_t n_bids, + const double *ask_price, + const double *ask_size, + uintptr_t n_asks); + +void wickra_microprice_reset(struct Microprice *handle); + +void wickra_microprice_free(struct Microprice *handle); + +struct OrderBookImbalanceFull *wickra_order_book_imbalance_full_new(void); + +double wickra_order_book_imbalance_full_update(struct OrderBookImbalanceFull *handle, + const double *bid_price, + const double *bid_size, + uintptr_t n_bids, + const double *ask_price, + const double *ask_size, + uintptr_t n_asks); + +void wickra_order_book_imbalance_full_reset(struct OrderBookImbalanceFull *handle); + +void wickra_order_book_imbalance_full_free(struct OrderBookImbalanceFull *handle); + +struct OrderBookImbalanceTop1 *wickra_order_book_imbalance_top1_new(void); + +double wickra_order_book_imbalance_top1_update(struct OrderBookImbalanceTop1 *handle, + const double *bid_price, + const double *bid_size, + uintptr_t n_bids, + const double *ask_price, + const double *ask_size, + uintptr_t n_asks); + +void wickra_order_book_imbalance_top1_reset(struct OrderBookImbalanceTop1 *handle); + +void wickra_order_book_imbalance_top1_free(struct OrderBookImbalanceTop1 *handle); + +struct OrderBookImbalanceTopN *wickra_order_book_imbalance_top_n_new(uintptr_t levels); + +double wickra_order_book_imbalance_top_n_update(struct OrderBookImbalanceTopN *handle, + const double *bid_price, + const double *bid_size, + uintptr_t n_bids, + const double *ask_price, + const double *ask_size, + uintptr_t n_asks); + +void wickra_order_book_imbalance_top_n_reset(struct OrderBookImbalanceTopN *handle); + +void wickra_order_book_imbalance_top_n_free(struct OrderBookImbalanceTopN *handle); + +struct OrderFlowImbalance *wickra_order_flow_imbalance_new(uintptr_t period); + +double wickra_order_flow_imbalance_update(struct OrderFlowImbalance *handle, + const double *bid_price, + const double *bid_size, + uintptr_t n_bids, + const double *ask_price, + const double *ask_size, + uintptr_t n_asks); + +void wickra_order_flow_imbalance_reset(struct OrderFlowImbalance *handle); + +void wickra_order_flow_imbalance_free(struct OrderFlowImbalance *handle); + +struct QuotedSpread *wickra_quoted_spread_new(void); + +double wickra_quoted_spread_update(struct QuotedSpread *handle, + const double *bid_price, + const double *bid_size, + uintptr_t n_bids, + const double *ask_price, + const double *ask_size, + uintptr_t n_asks); + +void wickra_quoted_spread_reset(struct QuotedSpread *handle); + +void wickra_quoted_spread_free(struct QuotedSpread *handle); + +struct AbsoluteBreadthIndex *wickra_absolute_breadth_index_new(void); + +double wickra_absolute_breadth_index_update(struct AbsoluteBreadthIndex *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_absolute_breadth_index_reset(struct AbsoluteBreadthIndex *handle); + +void wickra_absolute_breadth_index_free(struct AbsoluteBreadthIndex *handle); + +struct AdVolumeLine *wickra_ad_volume_line_new(void); + +double wickra_ad_volume_line_update(struct AdVolumeLine *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_ad_volume_line_reset(struct AdVolumeLine *handle); + +void wickra_ad_volume_line_free(struct AdVolumeLine *handle); + +struct AdvanceDecline *wickra_advance_decline_new(void); + +double wickra_advance_decline_update(struct AdvanceDecline *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_advance_decline_reset(struct AdvanceDecline *handle); + +void wickra_advance_decline_free(struct AdvanceDecline *handle); + +struct AdvanceDeclineRatio *wickra_advance_decline_ratio_new(void); + +double wickra_advance_decline_ratio_update(struct AdvanceDeclineRatio *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_advance_decline_ratio_reset(struct AdvanceDeclineRatio *handle); + +void wickra_advance_decline_ratio_free(struct AdvanceDeclineRatio *handle); + +struct BreadthThrust *wickra_breadth_thrust_new(uintptr_t period); + +double wickra_breadth_thrust_update(struct BreadthThrust *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_breadth_thrust_reset(struct BreadthThrust *handle); + +void wickra_breadth_thrust_free(struct BreadthThrust *handle); + +struct BullishPercentIndex *wickra_bullish_percent_index_new(void); + +double wickra_bullish_percent_index_update(struct BullishPercentIndex *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_bullish_percent_index_reset(struct BullishPercentIndex *handle); + +void wickra_bullish_percent_index_free(struct BullishPercentIndex *handle); + +struct CumulativeVolumeIndex *wickra_cumulative_volume_index_new(void); + +double wickra_cumulative_volume_index_update(struct CumulativeVolumeIndex *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_cumulative_volume_index_reset(struct CumulativeVolumeIndex *handle); + +void wickra_cumulative_volume_index_free(struct CumulativeVolumeIndex *handle); + +struct HighLowIndex *wickra_high_low_index_new(uintptr_t period); + +double wickra_high_low_index_update(struct HighLowIndex *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_high_low_index_reset(struct HighLowIndex *handle); + +void wickra_high_low_index_free(struct HighLowIndex *handle); + +struct McClellanOscillator *wickra_mc_clellan_oscillator_new(void); + +double wickra_mc_clellan_oscillator_update(struct McClellanOscillator *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_mc_clellan_oscillator_reset(struct McClellanOscillator *handle); + +void wickra_mc_clellan_oscillator_free(struct McClellanOscillator *handle); + +struct McClellanSummationIndex *wickra_mc_clellan_summation_index_new(void); + +double wickra_mc_clellan_summation_index_update(struct McClellanSummationIndex *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_mc_clellan_summation_index_reset(struct McClellanSummationIndex *handle); + +void wickra_mc_clellan_summation_index_free(struct McClellanSummationIndex *handle); + +struct NewHighsNewLows *wickra_new_highs_new_lows_new(void); + +double wickra_new_highs_new_lows_update(struct NewHighsNewLows *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_new_highs_new_lows_reset(struct NewHighsNewLows *handle); + +void wickra_new_highs_new_lows_free(struct NewHighsNewLows *handle); + +struct PercentAboveMa *wickra_percent_above_ma_new(void); + +double wickra_percent_above_ma_update(struct PercentAboveMa *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_percent_above_ma_reset(struct PercentAboveMa *handle); + +void wickra_percent_above_ma_free(struct PercentAboveMa *handle); + +struct TickIndex *wickra_tick_index_new(void); + +double wickra_tick_index_update(struct TickIndex *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_tick_index_reset(struct TickIndex *handle); + +void wickra_tick_index_free(struct TickIndex *handle); + +struct Trin *wickra_trin_new(void); + +double wickra_trin_update(struct Trin *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_trin_reset(struct Trin *handle); + +void wickra_trin_free(struct Trin *handle); + +struct UpDownVolumeRatio *wickra_up_down_volume_ratio_new(void); + +double wickra_up_down_volume_ratio_update(struct UpDownVolumeRatio *handle, + const double *change, + const double *volume, + const bool *new_high, + const bool *new_low, + const bool *above_ma, + const bool *on_buy_signal, + uintptr_t n, + int64_t timestamp); + +void wickra_up_down_volume_ratio_reset(struct UpDownVolumeRatio *handle); + +void wickra_up_down_volume_ratio_free(struct UpDownVolumeRatio *handle); + +struct AccelerationBands *wickra_acceleration_bands_new(uintptr_t period, double factor); + +bool wickra_acceleration_bands_update(struct AccelerationBands *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraAccelerationBandsOutput *out); + +void wickra_acceleration_bands_reset(struct AccelerationBands *handle); + +void wickra_acceleration_bands_free(struct AccelerationBands *handle); + +struct Adx *wickra_adx_new(uintptr_t period); + +bool wickra_adx_update(struct Adx *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraAdxOutput *out); + +void wickra_adx_reset(struct Adx *handle); + +void wickra_adx_free(struct Adx *handle); + +struct Alligator *wickra_alligator_new(uintptr_t jaw_period, + uintptr_t teeth_period, + uintptr_t lips_period); + +bool wickra_alligator_update(struct Alligator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraAlligatorOutput *out); + +void wickra_alligator_reset(struct Alligator *handle); + +void wickra_alligator_free(struct Alligator *handle); + +struct AndrewsPitchfork *wickra_andrews_pitchfork_new(uintptr_t strength); + +bool wickra_andrews_pitchfork_update(struct AndrewsPitchfork *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraAndrewsPitchforkOutput *out); + +void wickra_andrews_pitchfork_reset(struct AndrewsPitchfork *handle); + +void wickra_andrews_pitchfork_free(struct AndrewsPitchfork *handle); + +struct Aroon *wickra_aroon_new(uintptr_t period); + +bool wickra_aroon_update(struct Aroon *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraAroonOutput *out); + +void wickra_aroon_reset(struct Aroon *handle); + +void wickra_aroon_free(struct Aroon *handle); + +struct AtrBands *wickra_atr_bands_new(uintptr_t period, double multiplier); + +bool wickra_atr_bands_update(struct AtrBands *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraAtrBandsOutput *out); + +void wickra_atr_bands_reset(struct AtrBands *handle); + +void wickra_atr_bands_free(struct AtrBands *handle); + +struct AtrRatchet *wickra_atr_ratchet_new(uintptr_t atr_period, + double start_mult, + double increment); + +bool wickra_atr_ratchet_update(struct AtrRatchet *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraAtrRatchetOutput *out); + +void wickra_atr_ratchet_reset(struct AtrRatchet *handle); + +void wickra_atr_ratchet_free(struct AtrRatchet *handle); + +struct AutoFib *wickra_auto_fib_new(void); + +bool wickra_auto_fib_update(struct AutoFib *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraAutoFibOutput *out); + +void wickra_auto_fib_reset(struct AutoFib *handle); + +void wickra_auto_fib_free(struct AutoFib *handle); + +struct BollingerBands *wickra_bollinger_bands_new(uintptr_t period, double multiplier); + +bool wickra_bollinger_bands_update(struct BollingerBands *handle, + double value, + struct WickraBollingerOutput *out); + +void wickra_bollinger_bands_reset(struct BollingerBands *handle); + +void wickra_bollinger_bands_free(struct BollingerBands *handle); + +struct BomarBands *wickra_bomar_bands_new(uintptr_t period, double coverage); + +bool wickra_bomar_bands_update(struct BomarBands *handle, + double value, + struct WickraBomarBandsOutput *out); + +void wickra_bomar_bands_reset(struct BomarBands *handle); + +void wickra_bomar_bands_free(struct BomarBands *handle); + +struct Camarilla *wickra_camarilla_new(void); + +bool wickra_camarilla_update(struct Camarilla *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraCamarillaPivotsOutput *out); + +void wickra_camarilla_reset(struct Camarilla *handle); + +void wickra_camarilla_free(struct Camarilla *handle); + +struct CandleVolume *wickra_candle_volume_new(uintptr_t period); + +bool wickra_candle_volume_update(struct CandleVolume *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraCandleVolumeOutput *out); + +void wickra_candle_volume_reset(struct CandleVolume *handle); + +void wickra_candle_volume_free(struct CandleVolume *handle); + +struct CentralPivotRange *wickra_central_pivot_range_new(void); + +bool wickra_central_pivot_range_update(struct CentralPivotRange *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraCentralPivotRangeOutput *out); + +void wickra_central_pivot_range_reset(struct CentralPivotRange *handle); + +void wickra_central_pivot_range_free(struct CentralPivotRange *handle); + +struct ChandeKrollStop *wickra_chande_kroll_stop_new(uintptr_t atr_period, + double atr_multiplier, + uintptr_t stop_period); + +bool wickra_chande_kroll_stop_update(struct ChandeKrollStop *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraChandeKrollStopOutput *out); + +void wickra_chande_kroll_stop_reset(struct ChandeKrollStop *handle); + +void wickra_chande_kroll_stop_free(struct ChandeKrollStop *handle); + +struct ChandelierExit *wickra_chandelier_exit_new(uintptr_t period, double multiplier); + +bool wickra_chandelier_exit_update(struct ChandelierExit *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraChandelierExitOutput *out); + +void wickra_chandelier_exit_reset(struct ChandelierExit *handle); + +void wickra_chandelier_exit_free(struct ChandelierExit *handle); + +struct ClassicPivots *wickra_classic_pivots_new(void); + +bool wickra_classic_pivots_update(struct ClassicPivots *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraClassicPivotsOutput *out); + +void wickra_classic_pivots_reset(struct ClassicPivots *handle); + +void wickra_classic_pivots_free(struct ClassicPivots *handle); + +struct Cointegration *wickra_cointegration_new(uintptr_t period, uintptr_t adf_lags); + +bool wickra_cointegration_update(struct Cointegration *handle, + double x, + double y, + struct WickraCointegrationOutput *out); + +void wickra_cointegration_reset(struct Cointegration *handle); + +void wickra_cointegration_free(struct Cointegration *handle); + +struct CompositeProfile *wickra_composite_profile_new(uintptr_t period, + uintptr_t bins, + double value_area_pct); + +bool wickra_composite_profile_update(struct CompositeProfile *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraCompositeProfileOutput *out); + +void wickra_composite_profile_reset(struct CompositeProfile *handle); + +void wickra_composite_profile_free(struct CompositeProfile *handle); + +struct DemarkPivots *wickra_demark_pivots_new(void); + +bool wickra_demark_pivots_update(struct DemarkPivots *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraDemarkPivotsOutput *out); + +void wickra_demark_pivots_reset(struct DemarkPivots *handle); + +void wickra_demark_pivots_free(struct DemarkPivots *handle); + +struct Donchian *wickra_donchian_new(uintptr_t period); + +bool wickra_donchian_update(struct Donchian *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraDonchianOutput *out); + +void wickra_donchian_reset(struct Donchian *handle); + +void wickra_donchian_free(struct Donchian *handle); + +struct DonchianStop *wickra_donchian_stop_new(uintptr_t period); + +bool wickra_donchian_stop_update(struct DonchianStop *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraDonchianStopOutput *out); + +void wickra_donchian_stop_reset(struct DonchianStop *handle); + +void wickra_donchian_stop_free(struct DonchianStop *handle); + +struct DoubleBollinger *wickra_double_bollinger_new(uintptr_t period, + double k_inner, + double k_outer); + +bool wickra_double_bollinger_update(struct DoubleBollinger *handle, + double value, + struct WickraDoubleBollingerOutput *out); + +void wickra_double_bollinger_reset(struct DoubleBollinger *handle); + +void wickra_double_bollinger_free(struct DoubleBollinger *handle); + +struct ElderRay *wickra_elder_ray_new(uintptr_t period); + +bool wickra_elder_ray_update(struct ElderRay *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraElderRayOutput *out); + +void wickra_elder_ray_reset(struct ElderRay *handle); + +void wickra_elder_ray_free(struct ElderRay *handle); + +struct ElderSafeZone *wickra_elder_safe_zone_new(uintptr_t period, double coeff); + +bool wickra_elder_safe_zone_update(struct ElderSafeZone *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraElderSafeZoneOutput *out); + +void wickra_elder_safe_zone_reset(struct ElderSafeZone *handle); + +void wickra_elder_safe_zone_free(struct ElderSafeZone *handle); + +struct Equivolume *wickra_equivolume_new(uintptr_t period); + +bool wickra_equivolume_update(struct Equivolume *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraEquivolumeOutput *out); + +void wickra_equivolume_reset(struct Equivolume *handle); + +void wickra_equivolume_free(struct Equivolume *handle); + +struct FibArcs *wickra_fib_arcs_new(void); + +bool wickra_fib_arcs_update(struct FibArcs *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraFibArcsOutput *out); + +void wickra_fib_arcs_reset(struct FibArcs *handle); + +void wickra_fib_arcs_free(struct FibArcs *handle); + +struct FibChannel *wickra_fib_channel_new(void); + +bool wickra_fib_channel_update(struct FibChannel *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraFibChannelOutput *out); + +void wickra_fib_channel_reset(struct FibChannel *handle); + +void wickra_fib_channel_free(struct FibChannel *handle); + +struct FibConfluence *wickra_fib_confluence_new(void); + +bool wickra_fib_confluence_update(struct FibConfluence *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraFibConfluenceOutput *out); + +void wickra_fib_confluence_reset(struct FibConfluence *handle); + +void wickra_fib_confluence_free(struct FibConfluence *handle); + +struct FibExtension *wickra_fib_extension_new(void); + +bool wickra_fib_extension_update(struct FibExtension *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraFibExtensionOutput *out); + +void wickra_fib_extension_reset(struct FibExtension *handle); + +void wickra_fib_extension_free(struct FibExtension *handle); + +struct FibFan *wickra_fib_fan_new(void); + +bool wickra_fib_fan_update(struct FibFan *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraFibFanOutput *out); + +void wickra_fib_fan_reset(struct FibFan *handle); + +void wickra_fib_fan_free(struct FibFan *handle); + +struct FibProjection *wickra_fib_projection_new(void); + +bool wickra_fib_projection_update(struct FibProjection *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraFibProjectionOutput *out); + +void wickra_fib_projection_reset(struct FibProjection *handle); + +void wickra_fib_projection_free(struct FibProjection *handle); + +struct FibRetracement *wickra_fib_retracement_new(void); + +bool wickra_fib_retracement_update(struct FibRetracement *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraFibRetracementOutput *out); + +void wickra_fib_retracement_reset(struct FibRetracement *handle); + +void wickra_fib_retracement_free(struct FibRetracement *handle); + +struct FibTimeZones *wickra_fib_time_zones_new(void); + +bool wickra_fib_time_zones_update(struct FibTimeZones *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraFibTimeZonesOutput *out); + +void wickra_fib_time_zones_reset(struct FibTimeZones *handle); + +void wickra_fib_time_zones_free(struct FibTimeZones *handle); + +struct FibonacciPivots *wickra_fibonacci_pivots_new(void); + +bool wickra_fibonacci_pivots_update(struct FibonacciPivots *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraFibonacciPivotsOutput *out); + +void wickra_fibonacci_pivots_reset(struct FibonacciPivots *handle); + +void wickra_fibonacci_pivots_free(struct FibonacciPivots *handle); + +struct FractalChaosBands *wickra_fractal_chaos_bands_new(uintptr_t k); + +bool wickra_fractal_chaos_bands_update(struct FractalChaosBands *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraFractalChaosBandsOutput *out); + +void wickra_fractal_chaos_bands_reset(struct FractalChaosBands *handle); + +void wickra_fractal_chaos_bands_free(struct FractalChaosBands *handle); + +struct GatorOscillator *wickra_gator_oscillator_new(uintptr_t jaw_period, + uintptr_t teeth_period, + uintptr_t lips_period); + +bool wickra_gator_oscillator_update(struct GatorOscillator *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraGatorOscillatorOutput *out); + +void wickra_gator_oscillator_reset(struct GatorOscillator *handle); + +void wickra_gator_oscillator_free(struct GatorOscillator *handle); + +struct GoldenPocket *wickra_golden_pocket_new(void); + +bool wickra_golden_pocket_update(struct GoldenPocket *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraGoldenPocketOutput *out); + +void wickra_golden_pocket_reset(struct GoldenPocket *handle); + +void wickra_golden_pocket_free(struct GoldenPocket *handle); + +struct HeikinAshi *wickra_heikin_ashi_new(void); + +bool wickra_heikin_ashi_update(struct HeikinAshi *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraHeikinAshiOutput *out); + +void wickra_heikin_ashi_reset(struct HeikinAshi *handle); + +void wickra_heikin_ashi_free(struct HeikinAshi *handle); + +struct HighLowVolumeNodes *wickra_high_low_volume_nodes_new(uintptr_t period, uintptr_t bins); + +bool wickra_high_low_volume_nodes_update(struct HighLowVolumeNodes *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraHighLowVolumeNodesOutput *out); + +void wickra_high_low_volume_nodes_reset(struct HighLowVolumeNodes *handle); + +void wickra_high_low_volume_nodes_free(struct HighLowVolumeNodes *handle); + +struct HtPhasor *wickra_ht_phasor_new(void); + +bool wickra_ht_phasor_update(struct HtPhasor *handle, + double value, + struct WickraHtPhasorOutput *out); + +void wickra_ht_phasor_reset(struct HtPhasor *handle); + +void wickra_ht_phasor_free(struct HtPhasor *handle); + +struct HurstChannel *wickra_hurst_channel_new(uintptr_t period, double multiplier); + +bool wickra_hurst_channel_update(struct HurstChannel *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraHurstChannelOutput *out); + +void wickra_hurst_channel_reset(struct HurstChannel *handle); + +void wickra_hurst_channel_free(struct HurstChannel *handle); + +struct Ichimoku *wickra_ichimoku_new(uintptr_t tenkan_period, + uintptr_t kijun_period, + uintptr_t senkou_b_period, + uintptr_t displacement); + +bool wickra_ichimoku_update(struct Ichimoku *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraIchimokuOutput *out); + +void wickra_ichimoku_reset(struct Ichimoku *handle); + +void wickra_ichimoku_free(struct Ichimoku *handle); + +struct InitialBalance *wickra_initial_balance_new(uintptr_t period); + +bool wickra_initial_balance_update(struct InitialBalance *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraInitialBalanceOutput *out); + +void wickra_initial_balance_reset(struct InitialBalance *handle); + +void wickra_initial_balance_free(struct InitialBalance *handle); + +struct KalmanHedgeRatio *wickra_kalman_hedge_ratio_new(double delta, double observation_var); + +bool wickra_kalman_hedge_ratio_update(struct KalmanHedgeRatio *handle, + double x, + double y, + struct WickraKalmanHedgeRatioOutput *out); + +void wickra_kalman_hedge_ratio_reset(struct KalmanHedgeRatio *handle); + +void wickra_kalman_hedge_ratio_free(struct KalmanHedgeRatio *handle); + +struct KaseDevStop *wickra_kase_dev_stop_new(uintptr_t period, double dev); + +bool wickra_kase_dev_stop_update(struct KaseDevStop *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraKaseDevStopOutput *out); + +void wickra_kase_dev_stop_reset(struct KaseDevStop *handle); + +void wickra_kase_dev_stop_free(struct KaseDevStop *handle); + +struct KasePermissionStochastic *wickra_kase_permission_stochastic_new(uintptr_t length, + uintptr_t smooth); + +bool wickra_kase_permission_stochastic_update(struct KasePermissionStochastic *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraKasePermissionStochasticOutput *out); + +void wickra_kase_permission_stochastic_reset(struct KasePermissionStochastic *handle); + +void wickra_kase_permission_stochastic_free(struct KasePermissionStochastic *handle); + +struct Keltner *wickra_keltner_new(uintptr_t ema_period, uintptr_t atr_period, double multiplier); + +bool wickra_keltner_update(struct Keltner *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraKeltnerOutput *out); + +void wickra_keltner_reset(struct Keltner *handle); + +void wickra_keltner_free(struct Keltner *handle); + +struct Kst *wickra_kst_new(uintptr_t roc1, + uintptr_t roc2, + uintptr_t roc3, + uintptr_t roc4, + uintptr_t sma1, + uintptr_t sma2, + uintptr_t sma3, + uintptr_t sma4, + uintptr_t signal); + +bool wickra_kst_update(struct Kst *handle, double value, struct WickraKstOutput *out); + +void wickra_kst_reset(struct Kst *handle); + +void wickra_kst_free(struct Kst *handle); + +struct LeadLagCrossCorrelation *wickra_lead_lag_cross_correlation_new(uintptr_t window, + uintptr_t max_lag); + +bool wickra_lead_lag_cross_correlation_update(struct LeadLagCrossCorrelation *handle, + double x, + double y, + struct WickraLeadLagCrossCorrelationOutput *out); + +void wickra_lead_lag_cross_correlation_reset(struct LeadLagCrossCorrelation *handle); + +void wickra_lead_lag_cross_correlation_free(struct LeadLagCrossCorrelation *handle); + +struct LinRegChannel *wickra_lin_reg_channel_new(uintptr_t period, double multiplier); + +bool wickra_lin_reg_channel_update(struct LinRegChannel *handle, + double value, + struct WickraLinRegChannelOutput *out); + +void wickra_lin_reg_channel_reset(struct LinRegChannel *handle); + +void wickra_lin_reg_channel_free(struct LinRegChannel *handle); + +struct LiquidationFeatures *wickra_liquidation_features_new(void); + +bool wickra_liquidation_features_update(struct LiquidationFeatures *handle, + double funding_rate, + double mark_price, + double index_price, + double futures_price, + double open_interest, + double long_size, + double short_size, + double taker_buy_volume, + double taker_sell_volume, + double long_liquidation, + double short_liquidation, + int64_t timestamp, + struct WickraLiquidationFeaturesOutput *out); + +void wickra_liquidation_features_reset(struct LiquidationFeatures *handle); + +void wickra_liquidation_features_free(struct LiquidationFeatures *handle); + +struct MaEnvelope *wickra_ma_envelope_new(uintptr_t period, double percent); + +bool wickra_ma_envelope_update(struct MaEnvelope *handle, + double value, + struct WickraMaEnvelopeOutput *out); + +void wickra_ma_envelope_reset(struct MaEnvelope *handle); + +void wickra_ma_envelope_free(struct MaEnvelope *handle); + +struct MacdIndicator *wickra_macd_indicator_new(uintptr_t fast, uintptr_t slow, uintptr_t signal); + +bool wickra_macd_indicator_update(struct MacdIndicator *handle, + double value, + struct WickraMacdOutput *out); + +void wickra_macd_indicator_reset(struct MacdIndicator *handle); + +void wickra_macd_indicator_free(struct MacdIndicator *handle); + +struct MacdFix *wickra_macd_fix_new(uintptr_t signal); + +bool wickra_macd_fix_update(struct MacdFix *handle, double value, struct WickraMacdOutput *out); + +void wickra_macd_fix_reset(struct MacdFix *handle); + +void wickra_macd_fix_free(struct MacdFix *handle); + +struct Mama *wickra_mama_new(double fast_limit, double slow_limit); + +bool wickra_mama_update(struct Mama *handle, double value, struct WickraMamaOutput *out); + +void wickra_mama_reset(struct Mama *handle); + +void wickra_mama_free(struct Mama *handle); + +struct MedianChannel *wickra_median_channel_new(uintptr_t period, double multiplier); + +bool wickra_median_channel_update(struct MedianChannel *handle, + double value, + struct WickraMedianChannelOutput *out); + +void wickra_median_channel_reset(struct MedianChannel *handle); + +void wickra_median_channel_free(struct MedianChannel *handle); + +struct ModifiedMaStop *wickra_modified_ma_stop_new(uintptr_t period); + +bool wickra_modified_ma_stop_update(struct ModifiedMaStop *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraModifiedMaStopOutput *out); + +void wickra_modified_ma_stop_reset(struct ModifiedMaStop *handle); + +void wickra_modified_ma_stop_free(struct ModifiedMaStop *handle); + +struct MurreyMathLines *wickra_murrey_math_lines_new(uintptr_t period); + +bool wickra_murrey_math_lines_update(struct MurreyMathLines *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraMurreyMathLinesOutput *out); + +void wickra_murrey_math_lines_reset(struct MurreyMathLines *handle); + +void wickra_murrey_math_lines_free(struct MurreyMathLines *handle); + +struct Nrtr *wickra_nrtr_new(double pct); + +bool wickra_nrtr_update(struct Nrtr *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraNrtrOutput *out); + +void wickra_nrtr_reset(struct Nrtr *handle); + +void wickra_nrtr_free(struct Nrtr *handle); + +struct OpeningRange *wickra_opening_range_new(uintptr_t period); + +bool wickra_opening_range_update(struct OpeningRange *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraOpeningRangeOutput *out); + +void wickra_opening_range_reset(struct OpeningRange *handle); + +void wickra_opening_range_free(struct OpeningRange *handle); + +struct OvernightIntradayReturn *wickra_overnight_intraday_return_new(int32_t utc_offset_minutes); + +bool wickra_overnight_intraday_return_update(struct OvernightIntradayReturn *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraOvernightIntradayReturnOutput *out); + +void wickra_overnight_intraday_return_reset(struct OvernightIntradayReturn *handle); + +void wickra_overnight_intraday_return_free(struct OvernightIntradayReturn *handle); + +struct ProjectionBands *wickra_projection_bands_new(uintptr_t period); + +bool wickra_projection_bands_update(struct ProjectionBands *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraProjectionBandsOutput *out); + +void wickra_projection_bands_reset(struct ProjectionBands *handle); + +void wickra_projection_bands_free(struct ProjectionBands *handle); + +struct Qqe *wickra_qqe_new(uintptr_t rsi_period, uintptr_t smoothing, double factor); + +bool wickra_qqe_update(struct Qqe *handle, double value, struct WickraQqeOutput *out); + +void wickra_qqe_reset(struct Qqe *handle); + +void wickra_qqe_free(struct Qqe *handle); + +struct QuartileBands *wickra_quartile_bands_new(uintptr_t period); + +bool wickra_quartile_bands_update(struct QuartileBands *handle, + double value, + struct WickraQuartileBandsOutput *out); + +void wickra_quartile_bands_reset(struct QuartileBands *handle); + +void wickra_quartile_bands_free(struct QuartileBands *handle); + +struct RelativeStrengthAB *wickra_relative_strength_ab_new(uintptr_t ma_period, + uintptr_t rsi_period); + +bool wickra_relative_strength_ab_update(struct RelativeStrengthAB *handle, + double x, + double y, + struct WickraRelativeStrengthOutput *out); + +void wickra_relative_strength_ab_reset(struct RelativeStrengthAB *handle); + +void wickra_relative_strength_ab_free(struct RelativeStrengthAB *handle); + +struct Rwi *wickra_rwi_new(uintptr_t period); + +bool wickra_rwi_update(struct Rwi *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraRwiOutput *out); + +void wickra_rwi_reset(struct Rwi *handle); + +void wickra_rwi_free(struct Rwi *handle); + +struct SessionHighLow *wickra_session_high_low_new(int32_t utc_offset_minutes); + +bool wickra_session_high_low_update(struct SessionHighLow *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraSessionHighLowOutput *out); + +void wickra_session_high_low_reset(struct SessionHighLow *handle); + +void wickra_session_high_low_free(struct SessionHighLow *handle); + +struct SessionRange *wickra_session_range_new(int32_t utc_offset_minutes); + +bool wickra_session_range_update(struct SessionRange *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraSessionRangeOutput *out); + +void wickra_session_range_reset(struct SessionRange *handle); + +void wickra_session_range_free(struct SessionRange *handle); + +struct SmoothedHeikinAshi *wickra_smoothed_heikin_ashi_new(uintptr_t period); + +bool wickra_smoothed_heikin_ashi_update(struct SmoothedHeikinAshi *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraSmoothedHeikinAshiOutput *out); + +void wickra_smoothed_heikin_ashi_reset(struct SmoothedHeikinAshi *handle); + +void wickra_smoothed_heikin_ashi_free(struct SmoothedHeikinAshi *handle); + +struct SpreadBollingerBands *wickra_spread_bollinger_bands_new(uintptr_t period, double num_std); + +bool wickra_spread_bollinger_bands_update(struct SpreadBollingerBands *handle, + double x, + double y, + struct WickraSpreadBollingerBandsOutput *out); + +void wickra_spread_bollinger_bands_reset(struct SpreadBollingerBands *handle); + +void wickra_spread_bollinger_bands_free(struct SpreadBollingerBands *handle); + +struct StandardErrorBands *wickra_standard_error_bands_new(uintptr_t period, double multiplier); + +bool wickra_standard_error_bands_update(struct StandardErrorBands *handle, + double value, + struct WickraStandardErrorBandsOutput *out); + +void wickra_standard_error_bands_reset(struct StandardErrorBands *handle); + +void wickra_standard_error_bands_free(struct StandardErrorBands *handle); + +struct StarcBands *wickra_starc_bands_new(uintptr_t sma_period, + uintptr_t atr_period, + double multiplier); + +bool wickra_starc_bands_update(struct StarcBands *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraStarcBandsOutput *out); + +void wickra_starc_bands_reset(struct StarcBands *handle); + +void wickra_starc_bands_free(struct StarcBands *handle); + +struct Stochastic *wickra_stochastic_new(uintptr_t k_period, uintptr_t d_period); + +bool wickra_stochastic_update(struct Stochastic *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraStochasticOutput *out); + +void wickra_stochastic_reset(struct Stochastic *handle); + +void wickra_stochastic_free(struct Stochastic *handle); + +struct SuperTrend *wickra_super_trend_new(uintptr_t atr_period, double multiplier); + +bool wickra_super_trend_update(struct SuperTrend *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraSuperTrendOutput *out); + +void wickra_super_trend_reset(struct SuperTrend *handle); + +void wickra_super_trend_free(struct SuperTrend *handle); + +struct TdLines *wickra_td_lines_new(uintptr_t lookback, uintptr_t target); + +bool wickra_td_lines_update(struct TdLines *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraTdLinesOutput *out); + +void wickra_td_lines_reset(struct TdLines *handle); + +void wickra_td_lines_free(struct TdLines *handle); + +struct TdMovingAverage *wickra_td_moving_average_new(uintptr_t period_st1, uintptr_t period_st2); + +bool wickra_td_moving_average_update(struct TdMovingAverage *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraTdMovingAverageOutput *out); + +void wickra_td_moving_average_reset(struct TdMovingAverage *handle); + +void wickra_td_moving_average_free(struct TdMovingAverage *handle); + +struct TdRangeProjection *wickra_td_range_projection_new(void); + +bool wickra_td_range_projection_update(struct TdRangeProjection *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraTdRangeProjectionOutput *out); + +void wickra_td_range_projection_reset(struct TdRangeProjection *handle); + +void wickra_td_range_projection_free(struct TdRangeProjection *handle); + +struct TdRiskLevel *wickra_td_risk_level_new(uintptr_t lookback, uintptr_t target); + +bool wickra_td_risk_level_update(struct TdRiskLevel *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraTdRiskLevelOutput *out); + +void wickra_td_risk_level_reset(struct TdRiskLevel *handle); + +void wickra_td_risk_level_free(struct TdRiskLevel *handle); + +struct TdSequential *wickra_td_sequential_new(uintptr_t setup_lookback, + uintptr_t setup_target, + uintptr_t countdown_lookback, + uintptr_t countdown_target); + +bool wickra_td_sequential_update(struct TdSequential *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraTdSequentialOutput *out); + +void wickra_td_sequential_reset(struct TdSequential *handle); + +void wickra_td_sequential_free(struct TdSequential *handle); + +struct TtmSqueeze *wickra_ttm_squeeze_new(uintptr_t period, double bb_mult, double kc_mult); + +bool wickra_ttm_squeeze_update(struct TtmSqueeze *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraTtmSqueezeOutput *out); + +void wickra_ttm_squeeze_reset(struct TtmSqueeze *handle); + +void wickra_ttm_squeeze_free(struct TtmSqueeze *handle); + +struct ValueArea *wickra_value_area_new(uintptr_t period, + uintptr_t bin_count, + double value_area_pct); + +bool wickra_value_area_update(struct ValueArea *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraValueAreaOutput *out); + +void wickra_value_area_reset(struct ValueArea *handle); + +void wickra_value_area_free(struct ValueArea *handle); + +struct VolatilityCone *wickra_volatility_cone_new(uintptr_t window, uintptr_t lookback); + +bool wickra_volatility_cone_update(struct VolatilityCone *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraVolatilityConeOutput *out); + +void wickra_volatility_cone_reset(struct VolatilityCone *handle); + +void wickra_volatility_cone_free(struct VolatilityCone *handle); + +struct VolumeWeightedMacd *wickra_volume_weighted_macd_new(uintptr_t fast, + uintptr_t slow, + uintptr_t signal); + +bool wickra_volume_weighted_macd_update(struct VolumeWeightedMacd *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraVolumeWeightedMacdOutput *out); + +void wickra_volume_weighted_macd_reset(struct VolumeWeightedMacd *handle); + +void wickra_volume_weighted_macd_free(struct VolumeWeightedMacd *handle); + +struct VolumeWeightedSr *wickra_volume_weighted_sr_new(uintptr_t period); + +bool wickra_volume_weighted_sr_update(struct VolumeWeightedSr *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraVolumeWeightedSrOutput *out); + +void wickra_volume_weighted_sr_reset(struct VolumeWeightedSr *handle); + +void wickra_volume_weighted_sr_free(struct VolumeWeightedSr *handle); + +struct Vortex *wickra_vortex_new(uintptr_t period); + +bool wickra_vortex_update(struct Vortex *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraVortexOutput *out); + +void wickra_vortex_reset(struct Vortex *handle); + +void wickra_vortex_free(struct Vortex *handle); + +struct VwapStdDevBands *wickra_vwap_std_dev_bands_new(double multiplier); + +bool wickra_vwap_std_dev_bands_update(struct VwapStdDevBands *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraVwapStdDevBandsOutput *out); + +void wickra_vwap_std_dev_bands_reset(struct VwapStdDevBands *handle); + +void wickra_vwap_std_dev_bands_free(struct VwapStdDevBands *handle); + +struct WaveTrend *wickra_wave_trend_new(uintptr_t channel_period, + uintptr_t average_period, + uintptr_t signal_period); + +bool wickra_wave_trend_update(struct WaveTrend *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraWaveTrendOutput *out); + +void wickra_wave_trend_reset(struct WaveTrend *handle); + +void wickra_wave_trend_free(struct WaveTrend *handle); + +struct WilliamsFractals *wickra_williams_fractals_new(void); + +bool wickra_williams_fractals_update(struct WilliamsFractals *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraWilliamsFractalsOutput *out); + +void wickra_williams_fractals_reset(struct WilliamsFractals *handle); + +void wickra_williams_fractals_free(struct WilliamsFractals *handle); + +struct WoodiePivots *wickra_woodie_pivots_new(void); + +bool wickra_woodie_pivots_update(struct WoodiePivots *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraWoodiePivotsOutput *out); + +void wickra_woodie_pivots_reset(struct WoodiePivots *handle); + +void wickra_woodie_pivots_free(struct WoodiePivots *handle); + +struct ZeroLagMacd *wickra_zero_lag_macd_new(uintptr_t fast, uintptr_t slow, uintptr_t signal); + +bool wickra_zero_lag_macd_update(struct ZeroLagMacd *handle, + double value, + struct WickraZeroLagMacdOutput *out); + +void wickra_zero_lag_macd_reset(struct ZeroLagMacd *handle); + +void wickra_zero_lag_macd_free(struct ZeroLagMacd *handle); + +struct ZigZag *wickra_zig_zag_new(double threshold); + +bool wickra_zig_zag_update(struct ZigZag *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraZigZagOutput *out); + +void wickra_zig_zag_reset(struct ZigZag *handle); + +void wickra_zig_zag_free(struct ZigZag *handle); + +struct DayOfWeekProfile *wickra_day_of_week_profile_new(int32_t utc_offset_minutes); + +intptr_t wickra_day_of_week_profile_update(struct DayOfWeekProfile *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + double *values, + uintptr_t cap); + +void wickra_day_of_week_profile_reset(struct DayOfWeekProfile *handle); + +void wickra_day_of_week_profile_free(struct DayOfWeekProfile *handle); + +struct IntradayVolatilityProfile *wickra_intraday_volatility_profile_new(uintptr_t buckets, + int32_t utc_offset_minutes); + +intptr_t wickra_intraday_volatility_profile_update(struct IntradayVolatilityProfile *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + double *values, + uintptr_t cap); + +void wickra_intraday_volatility_profile_reset(struct IntradayVolatilityProfile *handle); + +void wickra_intraday_volatility_profile_free(struct IntradayVolatilityProfile *handle); + +struct TimeOfDayReturnProfile *wickra_time_of_day_return_profile_new(uintptr_t buckets, + int32_t utc_offset_minutes); + +intptr_t wickra_time_of_day_return_profile_update(struct TimeOfDayReturnProfile *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + double *values, + uintptr_t cap); + +void wickra_time_of_day_return_profile_reset(struct TimeOfDayReturnProfile *handle); + +void wickra_time_of_day_return_profile_free(struct TimeOfDayReturnProfile *handle); + +struct TpoProfile *wickra_tpo_profile_new(uintptr_t period, uintptr_t bin_count); + +intptr_t wickra_tpo_profile_update(struct TpoProfile *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraTpoProfileOutputScalars *scalars, + double *values, + uintptr_t cap); + +void wickra_tpo_profile_reset(struct TpoProfile *handle); + +void wickra_tpo_profile_free(struct TpoProfile *handle); + +struct VolumeByTimeProfile *wickra_volume_by_time_profile_new(uintptr_t buckets, + int32_t utc_offset_minutes); + +intptr_t wickra_volume_by_time_profile_update(struct VolumeByTimeProfile *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + double *values, + uintptr_t cap); + +void wickra_volume_by_time_profile_reset(struct VolumeByTimeProfile *handle); + +void wickra_volume_by_time_profile_free(struct VolumeByTimeProfile *handle); + +struct VolumeProfile *wickra_volume_profile_new(uintptr_t period, uintptr_t bin_count); + +intptr_t wickra_volume_profile_update(struct VolumeProfile *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraVolumeProfileOutputScalars *scalars, + double *values, + uintptr_t cap); + +void wickra_volume_profile_reset(struct VolumeProfile *handle); + +void wickra_volume_profile_free(struct VolumeProfile *handle); + +struct DollarBars *wickra_dollar_bars_new(double dollar_per_bar); + +uintptr_t wickra_dollar_bars_update(struct DollarBars *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraDollarBar *out, + uintptr_t cap); + +void wickra_dollar_bars_reset(struct DollarBars *handle); + +void wickra_dollar_bars_free(struct DollarBars *handle); + +struct ImbalanceBars *wickra_imbalance_bars_new(double threshold); + +uintptr_t wickra_imbalance_bars_update(struct ImbalanceBars *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraImbalanceBar *out, + uintptr_t cap); + +void wickra_imbalance_bars_reset(struct ImbalanceBars *handle); + +void wickra_imbalance_bars_free(struct ImbalanceBars *handle); + +struct KagiBars *wickra_kagi_bars_new(double reversal); + +uintptr_t wickra_kagi_bars_update(struct KagiBars *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraKagiBar *out, + uintptr_t cap); + +void wickra_kagi_bars_reset(struct KagiBars *handle); + +void wickra_kagi_bars_free(struct KagiBars *handle); + +struct PointAndFigureBars *wickra_point_and_figure_bars_new(double box_size, uintptr_t reversal); + +uintptr_t wickra_point_and_figure_bars_update(struct PointAndFigureBars *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraPnfColumn *out, + uintptr_t cap); + +void wickra_point_and_figure_bars_reset(struct PointAndFigureBars *handle); + +void wickra_point_and_figure_bars_free(struct PointAndFigureBars *handle); + +struct RangeBars *wickra_range_bars_new(double range); + +uintptr_t wickra_range_bars_update(struct RangeBars *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraRangeBar *out, + uintptr_t cap); + +void wickra_range_bars_reset(struct RangeBars *handle); + +void wickra_range_bars_free(struct RangeBars *handle); + +struct RenkoBars *wickra_renko_bars_new(double box_size); + +uintptr_t wickra_renko_bars_update(struct RenkoBars *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraRenkoBrick *out, + uintptr_t cap); + +void wickra_renko_bars_reset(struct RenkoBars *handle); + +void wickra_renko_bars_free(struct RenkoBars *handle); + +struct RunBars *wickra_run_bars_new(uintptr_t run_length); + +uintptr_t wickra_run_bars_update(struct RunBars *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraRunBar *out, + uintptr_t cap); + +void wickra_run_bars_reset(struct RunBars *handle); + +void wickra_run_bars_free(struct RunBars *handle); + +struct ThreeLineBreakBars *wickra_three_line_break_bars_new(uintptr_t lines); + +uintptr_t wickra_three_line_break_bars_update(struct ThreeLineBreakBars *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraLineBreakBar *out, + uintptr_t cap); + +void wickra_three_line_break_bars_reset(struct ThreeLineBreakBars *handle); + +void wickra_three_line_break_bars_free(struct ThreeLineBreakBars *handle); + +struct TickBars *wickra_tick_bars_new(uintptr_t ticks); + +uintptr_t wickra_tick_bars_update(struct TickBars *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraTickBar *out, + uintptr_t cap); + +void wickra_tick_bars_reset(struct TickBars *handle); + +void wickra_tick_bars_free(struct TickBars *handle); + +struct VolumeBars *wickra_volume_bars_new(double volume_per_bar); + +uintptr_t wickra_volume_bars_update(struct VolumeBars *handle, + double open, + double high, + double low, + double close, + double volume, + int64_t timestamp, + struct WickraVolumeBar *out, + uintptr_t cap); + +void wickra_volume_bars_reset(struct VolumeBars *handle); + +void wickra_volume_bars_free(struct VolumeBars *handle); + +struct MacdExt *wickra_macd_ext_new(uintptr_t fast, + uint8_t fast_type, + uintptr_t slow, + uint8_t slow_type, + uintptr_t signal, + uint8_t signal_type); + +bool wickra_macd_ext_update(struct MacdExt *handle, double value, struct WickraMacdOutput *out); + +void wickra_macd_ext_reset(struct MacdExt *handle); + +void wickra_macd_ext_free(struct MacdExt *handle); + +struct Footprint *wickra_footprint_new(double tick_size); + +intptr_t wickra_footprint_update(struct Footprint *handle, + double price, + double size, + bool is_buy, + int64_t timestamp, + struct WickraFootprintLevel *out, + uintptr_t cap); + +void wickra_footprint_reset(struct Footprint *handle); + +void wickra_footprint_free(struct Footprint *handle); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif /* WICKRA_H */ diff --git a/bindings/c/include/wickra.hpp b/bindings/c/include/wickra.hpp new file mode 100644 index 00000000..f508f561 --- /dev/null +++ b/bindings/c/include/wickra.hpp @@ -0,0 +1,66 @@ +// Optional C++ convenience layer over the Wickra C ABI (`wickra.h`). +// +// The C ABI hands out raw handles that must be released exactly once with the +// matching `wickra__free`. `wickra::Handle` wraps that in a move-only RAII +// owner so the free happens automatically at scope exit: +// +// #include "wickra.hpp" +// +// wickra::Handle sma(wickra_sma_new(14)); +// if (sma) { +// double v = wickra_sma_update(sma.get(), 42.0); // NaN during warmup +// } +// // sma is freed here +// +// This is header-only and adds no runtime cost beyond the C calls themselves. + +#ifndef WICKRA_HPP +#define WICKRA_HPP + +#include "wickra.h" + +#include + +namespace wickra { + +/// Move-only RAII owner of a Wickra handle. `T` is the opaque indicator type and +/// `Free` its `wickra__free` function. +template +class Handle { +public: + explicit Handle(T *ptr) noexcept : ptr_(ptr) {} + + ~Handle() { + if (ptr_ != nullptr) { + Free(ptr_); + } + } + + Handle(const Handle &) = delete; + Handle &operator=(const Handle &) = delete; + + Handle(Handle &&other) noexcept : ptr_(std::exchange(other.ptr_, nullptr)) {} + + Handle &operator=(Handle &&other) noexcept { + if (this != &other) { + if (ptr_ != nullptr) { + Free(ptr_); + } + ptr_ = std::exchange(other.ptr_, nullptr); + } + return *this; + } + + /// The raw handle, for passing to the `wickra__*` functions. + T *get() const noexcept { return ptr_; } + + /// True if the handle is non-null (construction succeeded). + explicit operator bool() const noexcept { return ptr_ != nullptr; } + +private: + T *ptr_; +}; + +} // namespace wickra + +#endif // WICKRA_HPP diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs new file mode 100644 index 00000000..8e29d4ac --- /dev/null +++ b/bindings/c/src/lib.rs @@ -0,0 +1,44291 @@ +//! C ABI for Wickra — the hub every C-capable language (C, C++, Go, C#, Java, R) +//! links against. Each indicator is exposed as `extern "C"` functions over an +//! opaque handle: +//! +//! - `wickra__new(...)` — construct; returns `NULL` on invalid parameters. +//! - `wickra__update(h, ...)` — feed one point; returns the output, or `NaN` +//! while warming up / if `h` is `NULL` / if the inputs are invalid. +//! - `wickra__batch(h, ..., out, n)` — write one output per input into the +//! caller-owned `out` buffer (length `n`), `NaN` at warmup positions. +//! - `wickra__reset(h)` — clear all state. +//! - `wickra__free(h)` — destroy the handle. Every `_new` must be paired +//! with exactly one `_free`; there is no RAII across the C boundary. +//! +//! GENERATED by the `ScriptHelpers` `capi` wrapper from the Rust core. Do not edit +//! by hand — edit the generator and regenerate, then commit this file and +//! `include/wickra.h`. + +use core::ptr; +use core::slice; + +use wickra_core::{ + AbandonedBaby, Abcd, AbsoluteBreadthIndex, AccelerationBands, AcceleratorOscillator, + AdOscillator, AdVolumeLine, AdaptiveCci, AdaptiveCycle, AdaptiveLaguerreFilter, AdaptiveRsi, + Adl, AdvanceBlock, AdvanceDecline, AdvanceDeclineRatio, Adx, Adxr, Alligator, Alma, Alpha, + AmihudIlliquidity, AnchoredRsi, AnchoredVwap, AndrewsPitchfork, Apo, Aroon, AroonOscillator, + Atr, AtrBands, AtrRatchet, AtrTrailingStop, AutoFib, Autocorrelation, + AutocorrelationPeriodogram, AverageDailyRange, AverageDrawdown, AvgPrice, AwesomeOscillator, + AwesomeOscillatorHistogram, BalanceOfPower, BandpassFilter, BarBuilder, Bat, BeltHold, Beta, + BetaNeutralSpread, BetterVolume, BipowerVariation, BodySizePct, BollingerBands, + BollingerBandwidth, BomarBands, BreadthThrust, Breakaway, BullishPercentIndex, BurkeRatio, + Butterfly, CalendarSpread, CalmarRatio, Camarilla, Candle, CandleVolume, Cci, CenterOfGravity, + CentralPivotRange, Cfo, ChaikinMoneyFlow, ChaikinOscillator, ChaikinVolatility, + ChandeKrollStop, ChandelierExit, ChoppinessIndex, ClassicPivots, CloseVsOpen, ClosingMarubozu, + Cmo, CoefficientOfVariation, Cointegration, CommonSenseRatio, CompositeProfile, + ConcealingBabySwallow, ConditionalValueAtRisk, ConnorsRsi, Coppock, CorrelationTrendIndicator, + Counterattack, Crab, CrossSection, CumulativeVolumeDelta, CumulativeVolumeIndex, CupAndHandle, + CyberneticCycle, Cypher, DayOfWeekProfile, Decycler, DecyclerOscillator, Dema, DemandIndex, + DemarkPivots, DepthSlope, DerivativeOscillator, DerivativesTick, DetrendedStdDev, + DisparityIndex, DistanceSsd, Doji, DojiStar, DollarBars, Donchian, DonchianStop, + DoubleBollinger, DoubleTopBottom, DownsideGapThreeMethods, Dpo, DragonflyDoji, + DrawdownDuration, DumplingTop, Dx, DynamicMomentumIndex, EaseOfMovement, EffectiveSpread, + EhlersStochastic, Ehma, ElderImpulse, ElderRay, ElderSafeZone, Ema, EmpiricalModeDecomposition, + Engulfing, Equivolume, EstimatedLeverageRatio, EvenBetterSinewave, EveningDojiStar, Evwma, + EwmaVolatility, Expectancy, FallingThreeMethods, Fama, FibArcs, FibChannel, FibConfluence, + FibExtension, FibFan, FibProjection, FibRetracement, FibTimeZones, FibonacciPivots, FisherRsi, + FisherTransform, FlagPennant, Footprint, ForceIndex, FractalChaosBands, Frama, FryPanBottom, + FundingBasis, FundingImpliedApr, FundingRate, FundingRateMean, FundingRateZScore, + GainLossRatio, GainToPainRatio, GapSideBySideWhite, Garch11, GarmanKlassVolatility, Gartley, + GatorOscillator, GeneralizedDema, GeometricMa, GoldenPocket, GrangerCausality, GravestoneDoji, + Hammer, HangingMan, Harami, HaramiCross, HasbrouckInformationShare, HeadAndShoulders, + HeikinAshi, HeikinAshiOscillator, HiLoActivator, HighLowIndex, HighLowRange, + HighLowVolumeNodes, HighWave, HighpassFilter, Hikkake, HikkakeModified, HilbertDominantCycle, + HistoricalVolatility, Hma, HoltWinters, HomingPigeon, HtDcPhase, HtPhasor, HtTrendMode, + HurstChannel, HurstExponent, Ichimoku, IdenticalThreeCrows, ImbalanceBars, InNeck, Indicator, + Inertia, InformationRatio, InitialBalance, InstantaneousTrendline, IntradayIntensity, + IntradayMomentumIndex, IntradayVolatilityProfile, InverseFisherTransform, InvertedHammer, + JarqueBera, Jma, JumpIndicator, KRatio, KagiBars, KalmanHedgeRatio, Kama, KaseDevStop, + KasePermissionStochastic, KellyCriterion, Keltner, KendallTau, Kicking, KickingByLength, Kst, + Kurtosis, Kvo, KylesLambda, LadderBottom, LaguerreRsi, LeadLagCrossCorrelation, Level, + LinRegAngle, LinRegChannel, LinRegIntercept, LinRegSlope, LinearRegression, + LiquidationFeatures, LogReturn, LongLeggedDoji, LongLine, LongShortRatio, M2Measure, + MaEnvelope, MaType, MacdExt, MacdFix, MacdHistogram, MacdIndicator, Mama, + MarketFacilitationIndex, MartinRatio, Marubozu, MassIndex, MatHold, MatchingLow, MaxDrawdown, + McClellanOscillator, McClellanSummationIndex, McGinleyDynamic, MedianAbsoluteDeviation, + MedianChannel, MedianMa, MedianPrice, Member, Mfi, Microprice, MidPoint, MidPrice, MinusDi, + MinusDm, ModifiedMaStop, Mom, MorningDojiStar, MorningEveningStar, MurreyMathLines, NakedPoc, + Natr, NewHighsNewLows, NewPriceLines, Nrtr, Nvi, OIPriceDivergence, OIWeighted, Obv, + OiToVolumeRatio, OmegaRatio, OnNeck, OpenInterestDelta, OpenInterestMomentum, OpeningMarubozu, + OpeningRange, OrderBook, OrderBookImbalanceFull, OrderBookImbalanceTop1, + OrderBookImbalanceTopN, OrderFlowImbalance, OuHalfLife, OvernightGap, OvernightIntradayReturn, + PainIndex, PairSpreadZScore, PairwiseBeta, ParkinsonVolatility, PearsonCorrelation, + PercentAboveMa, PercentB, PercentageTrailingStop, PerpetualPremiumIndex, Pgo, + PiercingDarkCloud, Pin, PivotReversal, PlusDi, PlusDm, Pmo, PointAndFigureBars, + PolarizedFractalEfficiency, Ppo, PpoHistogram, ProfileShape, ProfitFactor, ProjectionBands, + ProjectionOscillator, Psar, Pvi, Qqe, Qstick, QuartileBands, QuotedSpread, RSquared, RangeBars, + RealizedSpread, RealizedVolatility, RecoveryFactor, RectangleRange, Reflex, RegimeLabel, + RelativeStrengthAB, RenkoBars, RenkoTrailingStop, RickshawMan, RisingThreeMethods, Rmi, Roc, + Rocp, Rocr, Rocr100, RogersSatchellVolatility, RollMeasure, RollingCorrelation, + RollingCovariance, RollingIqr, RollingMinMaxScaler, RollingPercentileRank, RollingQuantile, + RollingVwap, RoofingFilter, Rsi, Rsx, RunBars, Rvi, RviVolatility, Rwi, SampleEntropy, SarExt, + SeasonalZScore, SeparatingLines, SessionHighLow, SessionRange, SessionVwap, ShannonEntropy, + Shark, SharpeRatio, ShootingStar, ShortLine, Side, SignedVolume, SineWave, SineWeightedMa, + SinglePrints, Skewness, Sma, Smi, Smma, SmoothedHeikinAshi, SortinoRatio, SpearmanCorrelation, + SpinningTop, SpreadAr1Coefficient, SpreadBollingerBands, SpreadHurst, StalledPattern, + StandardError, StandardErrorBands, StarcBands, Stc, StdDev, StepTrailingStop, SterlingRatio, + StickSandwich, StochRsi, Stochastic, StochasticCci, SuperSmoother, SuperTrend, TailRatio, + TakerBuySellRatio, Takuri, TasukiGap, TdCamouflage, TdClop, TdClopwin, TdCombo, TdCountdown, + TdDWave, TdDeMarker, TdDifferential, TdLines, TdMovingAverage, TdOpen, TdPressure, + TdPropulsion, TdRangeProjection, TdRei, TdRiskLevel, TdSequential, TdSetup, TdTrap, Tema, + TermStructureBasis, ThreeDrives, ThreeInside, ThreeLineBreak, ThreeLineBreakBars, + ThreeLineStrike, ThreeOutside, ThreeSoldiersOrCrows, ThreeStarsInSouth, Thrusting, TickBars, + TickIndex, Tii, TimeBasedStop, TimeOfDayReturnProfile, TowerTopBottom, TpoProfile, Trade, + TradeImbalance, TradeQuote, TradeSignAutocorrelation, TradeVolumeIndex, TrendLabel, + TrendStrengthIndex, Trendflex, TreynorRatio, Triangle, Trima, Trin, TripleTopBottom, Tristar, + Trix, TrueRange, Tsf, TsfOscillator, Tsi, Tsv, TtmSqueeze, TtmTrend, TurnOfMonth, Tweezer, + TwiggsMoneyFlow, TwoCrows, TypicalPrice, UlcerIndex, UltimateOscillator, UniqueThreeRiver, + UniversalOscillator, UpDownVolumeRatio, UpsideGapThreeMethods, UpsideGapTwoCrows, + UpsidePotentialRatio, ValueArea, ValueAtRisk, Variance, VarianceRatio, + VerticalHorizontalFilter, Vidya, VolatilityCone, VolatilityOfVolatility, VolatilityRatio, + VoltyStop, VolumeBars, VolumeByTimeProfile, VolumeOscillator, VolumePriceTrend, VolumeProfile, + VolumeRsi, VolumeWeightedMacd, VolumeWeightedSr, Vortex, Vpin, Vwap, VwapStdDevBands, Vwma, + Vzo, Wad, WavePm, WaveTrend, Wedge, WeightedClose, WickRatio, WilliamsFractals, WilliamsR, + WinRate, Wma, WoodiePivots, YangZhangVolatility, YoyoExit, ZScore, ZeroLagMacd, ZigZag, Zlema, + T3, +}; + +// ===== Scalar indicators (f64 -> f64) ===== + +/// Create a `AdaptiveCycle` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_adaptive_cycle_free`. +#[no_mangle] +pub extern "C" fn wickra_adaptive_cycle_new() -> *mut AdaptiveCycle { + Box::into_raw(Box::new(AdaptiveCycle::new())) +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_adaptive_cycle_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_cycle_update( + handle: *mut AdaptiveCycle, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_adaptive_cycle_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_cycle_batch( + handle: *mut AdaptiveCycle, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_adaptive_cycle_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_cycle_reset(handle: *mut AdaptiveCycle) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_adaptive_cycle_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_adaptive_cycle_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_cycle_free(handle: *mut AdaptiveCycle) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AdaptiveLaguerreFilter` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_adaptive_laguerre_filter_free`. +#[no_mangle] +pub extern "C" fn wickra_adaptive_laguerre_filter_new( + period: usize, +) -> *mut AdaptiveLaguerreFilter { + match AdaptiveLaguerreFilter::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_adaptive_laguerre_filter_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_laguerre_filter_update( + handle: *mut AdaptiveLaguerreFilter, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_adaptive_laguerre_filter_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_laguerre_filter_batch( + handle: *mut AdaptiveLaguerreFilter, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_adaptive_laguerre_filter_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_laguerre_filter_reset( + handle: *mut AdaptiveLaguerreFilter, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_adaptive_laguerre_filter_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_adaptive_laguerre_filter_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_laguerre_filter_free(handle: *mut AdaptiveLaguerreFilter) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AdaptiveRsi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_adaptive_rsi_free`. +#[no_mangle] +pub extern "C" fn wickra_adaptive_rsi_new(period: usize) -> *mut AdaptiveRsi { + match AdaptiveRsi::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_adaptive_rsi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_rsi_update(handle: *mut AdaptiveRsi, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_adaptive_rsi_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_rsi_batch( + handle: *mut AdaptiveRsi, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_adaptive_rsi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_rsi_reset(handle: *mut AdaptiveRsi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_adaptive_rsi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_adaptive_rsi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_rsi_free(handle: *mut AdaptiveRsi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Alma` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_alma_free`. +#[no_mangle] +pub extern "C" fn wickra_alma_new(period: usize, offset: f64, sigma: f64) -> *mut Alma { + match Alma::new(period, offset, sigma) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_alma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_alma_update(handle: *mut Alma, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_alma_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_alma_batch( + handle: *mut Alma, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_alma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_alma_reset(handle: *mut Alma) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_alma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_alma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_alma_free(handle: *mut Alma) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AnchoredRsi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_anchored_rsi_free`. +#[no_mangle] +pub extern "C" fn wickra_anchored_rsi_new() -> *mut AnchoredRsi { + Box::into_raw(Box::new(AnchoredRsi::new())) +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_anchored_rsi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_anchored_rsi_update(handle: *mut AnchoredRsi, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_anchored_rsi_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_anchored_rsi_batch( + handle: *mut AnchoredRsi, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_anchored_rsi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_anchored_rsi_reset(handle: *mut AnchoredRsi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_anchored_rsi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_anchored_rsi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_anchored_rsi_free(handle: *mut AnchoredRsi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Apo` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_apo_free`. +#[no_mangle] +pub extern "C" fn wickra_apo_new(fast: usize, slow: usize) -> *mut Apo { + match Apo::new(fast, slow) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_apo_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_apo_update(handle: *mut Apo, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_apo_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_apo_batch( + handle: *mut Apo, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_apo_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_apo_reset(handle: *mut Apo) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_apo_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_apo_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_apo_free(handle: *mut Apo) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Autocorrelation` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_autocorrelation_free`. +#[no_mangle] +pub extern "C" fn wickra_autocorrelation_new(period: usize, lag: usize) -> *mut Autocorrelation { + match Autocorrelation::new(period, lag) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_autocorrelation_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_autocorrelation_update( + handle: *mut Autocorrelation, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_autocorrelation_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_autocorrelation_batch( + handle: *mut Autocorrelation, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_autocorrelation_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_autocorrelation_reset(handle: *mut Autocorrelation) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_autocorrelation_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_autocorrelation_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_autocorrelation_free(handle: *mut Autocorrelation) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AutocorrelationPeriodogram` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_autocorrelation_periodogram_free`. +#[no_mangle] +pub extern "C" fn wickra_autocorrelation_periodogram_new( + min_period: usize, + max_period: usize, +) -> *mut AutocorrelationPeriodogram { + match AutocorrelationPeriodogram::new(min_period, max_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_autocorrelation_periodogram_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_autocorrelation_periodogram_update( + handle: *mut AutocorrelationPeriodogram, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_autocorrelation_periodogram_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_autocorrelation_periodogram_batch( + handle: *mut AutocorrelationPeriodogram, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_autocorrelation_periodogram_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_autocorrelation_periodogram_reset( + handle: *mut AutocorrelationPeriodogram, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_autocorrelation_periodogram_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_autocorrelation_periodogram_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_autocorrelation_periodogram_free( + handle: *mut AutocorrelationPeriodogram, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AverageDrawdown` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_average_drawdown_free`. +#[no_mangle] +pub extern "C" fn wickra_average_drawdown_new(period: usize) -> *mut AverageDrawdown { + match AverageDrawdown::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_average_drawdown_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_average_drawdown_update( + handle: *mut AverageDrawdown, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_average_drawdown_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_average_drawdown_batch( + handle: *mut AverageDrawdown, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_average_drawdown_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_average_drawdown_reset(handle: *mut AverageDrawdown) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_average_drawdown_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_average_drawdown_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_average_drawdown_free(handle: *mut AverageDrawdown) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BandpassFilter` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_bandpass_filter_free`. +#[no_mangle] +pub extern "C" fn wickra_bandpass_filter_new(period: usize, bandwidth: f64) -> *mut BandpassFilter { + match BandpassFilter::new(period, bandwidth) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_bandpass_filter_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bandpass_filter_update( + handle: *mut BandpassFilter, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_bandpass_filter_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_bandpass_filter_batch( + handle: *mut BandpassFilter, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_bandpass_filter_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bandpass_filter_reset(handle: *mut BandpassFilter) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_bandpass_filter_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_bandpass_filter_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bandpass_filter_free(handle: *mut BandpassFilter) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BipowerVariation` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_bipower_variation_free`. +#[no_mangle] +pub extern "C" fn wickra_bipower_variation_new(period: usize) -> *mut BipowerVariation { + match BipowerVariation::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_bipower_variation_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bipower_variation_update( + handle: *mut BipowerVariation, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_bipower_variation_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_bipower_variation_batch( + handle: *mut BipowerVariation, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_bipower_variation_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bipower_variation_reset(handle: *mut BipowerVariation) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_bipower_variation_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_bipower_variation_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bipower_variation_free(handle: *mut BipowerVariation) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BollingerBandwidth` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_bollinger_bandwidth_free`. +#[no_mangle] +pub extern "C" fn wickra_bollinger_bandwidth_new( + period: usize, + multiplier: f64, +) -> *mut BollingerBandwidth { + match BollingerBandwidth::new(period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_bollinger_bandwidth_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bollinger_bandwidth_update( + handle: *mut BollingerBandwidth, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_bollinger_bandwidth_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_bollinger_bandwidth_batch( + handle: *mut BollingerBandwidth, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_bollinger_bandwidth_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bollinger_bandwidth_reset(handle: *mut BollingerBandwidth) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_bollinger_bandwidth_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_bollinger_bandwidth_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bollinger_bandwidth_free(handle: *mut BollingerBandwidth) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BurkeRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_burke_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_burke_ratio_new(period: usize) -> *mut BurkeRatio { + match BurkeRatio::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_burke_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_burke_ratio_update(handle: *mut BurkeRatio, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_burke_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_burke_ratio_batch( + handle: *mut BurkeRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_burke_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_burke_ratio_reset(handle: *mut BurkeRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_burke_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_burke_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_burke_ratio_free(handle: *mut BurkeRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CalmarRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_calmar_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_calmar_ratio_new(period: usize) -> *mut CalmarRatio { + match CalmarRatio::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_calmar_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_calmar_ratio_update(handle: *mut CalmarRatio, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_calmar_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_calmar_ratio_batch( + handle: *mut CalmarRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_calmar_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_calmar_ratio_reset(handle: *mut CalmarRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_calmar_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_calmar_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_calmar_ratio_free(handle: *mut CalmarRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CenterOfGravity` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_center_of_gravity_free`. +#[no_mangle] +pub extern "C" fn wickra_center_of_gravity_new(period: usize) -> *mut CenterOfGravity { + match CenterOfGravity::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_center_of_gravity_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_center_of_gravity_update( + handle: *mut CenterOfGravity, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_center_of_gravity_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_center_of_gravity_batch( + handle: *mut CenterOfGravity, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_center_of_gravity_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_center_of_gravity_reset(handle: *mut CenterOfGravity) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_center_of_gravity_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_center_of_gravity_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_center_of_gravity_free(handle: *mut CenterOfGravity) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Cfo` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_cfo_free`. +#[no_mangle] +pub extern "C" fn wickra_cfo_new(period: usize) -> *mut Cfo { + match Cfo::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_cfo_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cfo_update(handle: *mut Cfo, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_cfo_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_cfo_batch( + handle: *mut Cfo, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_cfo_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cfo_reset(handle: *mut Cfo) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_cfo_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_cfo_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cfo_free(handle: *mut Cfo) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Cmo` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_cmo_free`. +#[no_mangle] +pub extern "C" fn wickra_cmo_new(period: usize) -> *mut Cmo { + match Cmo::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_cmo_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cmo_update(handle: *mut Cmo, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_cmo_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_cmo_batch( + handle: *mut Cmo, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_cmo_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cmo_reset(handle: *mut Cmo) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_cmo_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_cmo_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cmo_free(handle: *mut Cmo) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CoefficientOfVariation` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_coefficient_of_variation_free`. +#[no_mangle] +pub extern "C" fn wickra_coefficient_of_variation_new( + period: usize, +) -> *mut CoefficientOfVariation { + match CoefficientOfVariation::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_coefficient_of_variation_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_coefficient_of_variation_update( + handle: *mut CoefficientOfVariation, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_coefficient_of_variation_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_coefficient_of_variation_batch( + handle: *mut CoefficientOfVariation, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_coefficient_of_variation_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_coefficient_of_variation_reset( + handle: *mut CoefficientOfVariation, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_coefficient_of_variation_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_coefficient_of_variation_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_coefficient_of_variation_free(handle: *mut CoefficientOfVariation) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CommonSenseRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_common_sense_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_common_sense_ratio_new(period: usize) -> *mut CommonSenseRatio { + match CommonSenseRatio::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_common_sense_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_common_sense_ratio_update( + handle: *mut CommonSenseRatio, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_common_sense_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_common_sense_ratio_batch( + handle: *mut CommonSenseRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_common_sense_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_common_sense_ratio_reset(handle: *mut CommonSenseRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_common_sense_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_common_sense_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_common_sense_ratio_free(handle: *mut CommonSenseRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ConditionalValueAtRisk` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_conditional_value_at_risk_free`. +#[no_mangle] +pub extern "C" fn wickra_conditional_value_at_risk_new( + period: usize, + confidence: f64, +) -> *mut ConditionalValueAtRisk { + match ConditionalValueAtRisk::new(period, confidence) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_conditional_value_at_risk_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_conditional_value_at_risk_update( + handle: *mut ConditionalValueAtRisk, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_conditional_value_at_risk_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_conditional_value_at_risk_batch( + handle: *mut ConditionalValueAtRisk, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_conditional_value_at_risk_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_conditional_value_at_risk_reset( + handle: *mut ConditionalValueAtRisk, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_conditional_value_at_risk_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_conditional_value_at_risk_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_conditional_value_at_risk_free( + handle: *mut ConditionalValueAtRisk, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ConnorsRsi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_connors_rsi_free`. +#[no_mangle] +pub extern "C" fn wickra_connors_rsi_new( + period_rsi: usize, + period_streak: usize, + period_rank: usize, +) -> *mut ConnorsRsi { + match ConnorsRsi::new(period_rsi, period_streak, period_rank) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_connors_rsi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_connors_rsi_update(handle: *mut ConnorsRsi, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_connors_rsi_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_connors_rsi_batch( + handle: *mut ConnorsRsi, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_connors_rsi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_connors_rsi_reset(handle: *mut ConnorsRsi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_connors_rsi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_connors_rsi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_connors_rsi_free(handle: *mut ConnorsRsi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Coppock` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_coppock_free`. +#[no_mangle] +pub extern "C" fn wickra_coppock_new( + roc_long_period: usize, + roc_short_period: usize, + wma_period: usize, +) -> *mut Coppock { + match Coppock::new(roc_long_period, roc_short_period, wma_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_coppock_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_coppock_update(handle: *mut Coppock, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_coppock_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_coppock_batch( + handle: *mut Coppock, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_coppock_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_coppock_reset(handle: *mut Coppock) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_coppock_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_coppock_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_coppock_free(handle: *mut Coppock) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CorrelationTrendIndicator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_correlation_trend_indicator_free`. +#[no_mangle] +pub extern "C" fn wickra_correlation_trend_indicator_new( + period: usize, +) -> *mut CorrelationTrendIndicator { + match CorrelationTrendIndicator::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_correlation_trend_indicator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_correlation_trend_indicator_update( + handle: *mut CorrelationTrendIndicator, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_correlation_trend_indicator_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_correlation_trend_indicator_batch( + handle: *mut CorrelationTrendIndicator, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_correlation_trend_indicator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_correlation_trend_indicator_reset( + handle: *mut CorrelationTrendIndicator, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_correlation_trend_indicator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_correlation_trend_indicator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_correlation_trend_indicator_free( + handle: *mut CorrelationTrendIndicator, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CyberneticCycle` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_cybernetic_cycle_free`. +#[no_mangle] +pub extern "C" fn wickra_cybernetic_cycle_new(period: usize) -> *mut CyberneticCycle { + match CyberneticCycle::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_cybernetic_cycle_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cybernetic_cycle_update( + handle: *mut CyberneticCycle, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_cybernetic_cycle_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_cybernetic_cycle_batch( + handle: *mut CyberneticCycle, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_cybernetic_cycle_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cybernetic_cycle_reset(handle: *mut CyberneticCycle) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_cybernetic_cycle_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_cybernetic_cycle_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cybernetic_cycle_free(handle: *mut CyberneticCycle) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Decycler` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_decycler_free`. +#[no_mangle] +pub extern "C" fn wickra_decycler_new(period: usize) -> *mut Decycler { + match Decycler::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_decycler_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_decycler_update(handle: *mut Decycler, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_decycler_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_decycler_batch( + handle: *mut Decycler, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_decycler_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_decycler_reset(handle: *mut Decycler) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_decycler_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_decycler_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_decycler_free(handle: *mut Decycler) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DecyclerOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_decycler_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_decycler_oscillator_new( + fast: usize, + slow: usize, +) -> *mut DecyclerOscillator { + match DecyclerOscillator::new(fast, slow) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_decycler_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_decycler_oscillator_update( + handle: *mut DecyclerOscillator, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_decycler_oscillator_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_decycler_oscillator_batch( + handle: *mut DecyclerOscillator, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_decycler_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_decycler_oscillator_reset(handle: *mut DecyclerOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_decycler_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_decycler_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_decycler_oscillator_free(handle: *mut DecyclerOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Dema` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_dema_free`. +#[no_mangle] +pub extern "C" fn wickra_dema_new(period: usize) -> *mut Dema { + match Dema::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_dema_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dema_update(handle: *mut Dema, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_dema_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_dema_batch( + handle: *mut Dema, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_dema_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dema_reset(handle: *mut Dema) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_dema_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_dema_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dema_free(handle: *mut Dema) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DerivativeOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_derivative_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_derivative_oscillator_new( + rsi_period: usize, + smooth1: usize, + smooth2: usize, + signal_period: usize, +) -> *mut DerivativeOscillator { + match DerivativeOscillator::new(rsi_period, smooth1, smooth2, signal_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_derivative_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_derivative_oscillator_update( + handle: *mut DerivativeOscillator, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_derivative_oscillator_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_derivative_oscillator_batch( + handle: *mut DerivativeOscillator, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_derivative_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_derivative_oscillator_reset(handle: *mut DerivativeOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_derivative_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_derivative_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_derivative_oscillator_free(handle: *mut DerivativeOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DetrendedStdDev` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_detrended_std_dev_free`. +#[no_mangle] +pub extern "C" fn wickra_detrended_std_dev_new(period: usize) -> *mut DetrendedStdDev { + match DetrendedStdDev::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_detrended_std_dev_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_detrended_std_dev_update( + handle: *mut DetrendedStdDev, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_detrended_std_dev_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_detrended_std_dev_batch( + handle: *mut DetrendedStdDev, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_detrended_std_dev_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_detrended_std_dev_reset(handle: *mut DetrendedStdDev) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_detrended_std_dev_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_detrended_std_dev_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_detrended_std_dev_free(handle: *mut DetrendedStdDev) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DisparityIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_disparity_index_free`. +#[no_mangle] +pub extern "C" fn wickra_disparity_index_new(period: usize) -> *mut DisparityIndex { + match DisparityIndex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_disparity_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_disparity_index_update( + handle: *mut DisparityIndex, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_disparity_index_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_disparity_index_batch( + handle: *mut DisparityIndex, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_disparity_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_disparity_index_reset(handle: *mut DisparityIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_disparity_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_disparity_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_disparity_index_free(handle: *mut DisparityIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Dpo` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_dpo_free`. +#[no_mangle] +pub extern "C" fn wickra_dpo_new(period: usize) -> *mut Dpo { + match Dpo::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_dpo_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dpo_update(handle: *mut Dpo, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_dpo_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_dpo_batch( + handle: *mut Dpo, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_dpo_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dpo_reset(handle: *mut Dpo) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_dpo_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_dpo_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dpo_free(handle: *mut Dpo) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DrawdownDuration` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_drawdown_duration_free`. +#[no_mangle] +pub extern "C" fn wickra_drawdown_duration_new() -> *mut DrawdownDuration { + Box::into_raw(Box::new(DrawdownDuration::new())) +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_drawdown_duration_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_drawdown_duration_update( + handle: *mut DrawdownDuration, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).map_or(f64::NAN, f64::from), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_drawdown_duration_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_drawdown_duration_batch( + handle: *mut DrawdownDuration, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).map_or(f64::NAN, f64::from); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_drawdown_duration_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_drawdown_duration_reset(handle: *mut DrawdownDuration) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_drawdown_duration_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_drawdown_duration_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_drawdown_duration_free(handle: *mut DrawdownDuration) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DynamicMomentumIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_dynamic_momentum_index_free`. +#[no_mangle] +pub extern "C" fn wickra_dynamic_momentum_index_new(period: usize) -> *mut DynamicMomentumIndex { + match DynamicMomentumIndex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_dynamic_momentum_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dynamic_momentum_index_update( + handle: *mut DynamicMomentumIndex, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_dynamic_momentum_index_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_dynamic_momentum_index_batch( + handle: *mut DynamicMomentumIndex, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_dynamic_momentum_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dynamic_momentum_index_reset(handle: *mut DynamicMomentumIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_dynamic_momentum_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_dynamic_momentum_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dynamic_momentum_index_free(handle: *mut DynamicMomentumIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `EhlersStochastic` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ehlers_stochastic_free`. +#[no_mangle] +pub extern "C" fn wickra_ehlers_stochastic_new(period: usize) -> *mut EhlersStochastic { + match EhlersStochastic::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ehlers_stochastic_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ehlers_stochastic_update( + handle: *mut EhlersStochastic, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_ehlers_stochastic_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_ehlers_stochastic_batch( + handle: *mut EhlersStochastic, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ehlers_stochastic_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ehlers_stochastic_reset(handle: *mut EhlersStochastic) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ehlers_stochastic_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ehlers_stochastic_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ehlers_stochastic_free(handle: *mut EhlersStochastic) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Ehma` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ehma_free`. +#[no_mangle] +pub extern "C" fn wickra_ehma_new(period: usize) -> *mut Ehma { + match Ehma::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ehma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ehma_update(handle: *mut Ehma, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_ehma_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_ehma_batch( + handle: *mut Ehma, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ehma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ehma_reset(handle: *mut Ehma) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ehma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ehma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ehma_free(handle: *mut Ehma) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ElderImpulse` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_elder_impulse_free`. +#[no_mangle] +pub extern "C" fn wickra_elder_impulse_new( + ema_period: usize, + macd_fast: usize, + macd_slow: usize, + macd_signal: usize, +) -> *mut ElderImpulse { + match ElderImpulse::new(ema_period, macd_fast, macd_slow, macd_signal) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_elder_impulse_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_elder_impulse_update(handle: *mut ElderImpulse, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_elder_impulse_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_elder_impulse_batch( + handle: *mut ElderImpulse, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_elder_impulse_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_elder_impulse_reset(handle: *mut ElderImpulse) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_elder_impulse_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_elder_impulse_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_elder_impulse_free(handle: *mut ElderImpulse) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Ema` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ema_free`. +#[no_mangle] +pub extern "C" fn wickra_ema_new(period: usize) -> *mut Ema { + match Ema::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ema_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ema_update(handle: *mut Ema, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_ema_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_ema_batch( + handle: *mut Ema, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ema_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ema_reset(handle: *mut Ema) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ema_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ema_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ema_free(handle: *mut Ema) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `EmpiricalModeDecomposition` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_empirical_mode_decomposition_free`. +#[no_mangle] +pub extern "C" fn wickra_empirical_mode_decomposition_new( + period: usize, + fraction: f64, +) -> *mut EmpiricalModeDecomposition { + match EmpiricalModeDecomposition::new(period, fraction) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_empirical_mode_decomposition_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_empirical_mode_decomposition_update( + handle: *mut EmpiricalModeDecomposition, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_empirical_mode_decomposition_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_empirical_mode_decomposition_batch( + handle: *mut EmpiricalModeDecomposition, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_empirical_mode_decomposition_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_empirical_mode_decomposition_reset( + handle: *mut EmpiricalModeDecomposition, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_empirical_mode_decomposition_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_empirical_mode_decomposition_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_empirical_mode_decomposition_free( + handle: *mut EmpiricalModeDecomposition, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `EvenBetterSinewave` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_even_better_sinewave_free`. +#[no_mangle] +pub extern "C" fn wickra_even_better_sinewave_new( + hp_period: usize, + ssf_length: usize, +) -> *mut EvenBetterSinewave { + match EvenBetterSinewave::new(hp_period, ssf_length) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_even_better_sinewave_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_even_better_sinewave_update( + handle: *mut EvenBetterSinewave, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_even_better_sinewave_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_even_better_sinewave_batch( + handle: *mut EvenBetterSinewave, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_even_better_sinewave_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_even_better_sinewave_reset(handle: *mut EvenBetterSinewave) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_even_better_sinewave_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_even_better_sinewave_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_even_better_sinewave_free(handle: *mut EvenBetterSinewave) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `EwmaVolatility` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ewma_volatility_free`. +#[no_mangle] +pub extern "C" fn wickra_ewma_volatility_new(lambda: f64) -> *mut EwmaVolatility { + match EwmaVolatility::new(lambda) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ewma_volatility_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ewma_volatility_update( + handle: *mut EwmaVolatility, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_ewma_volatility_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_ewma_volatility_batch( + handle: *mut EwmaVolatility, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ewma_volatility_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ewma_volatility_reset(handle: *mut EwmaVolatility) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ewma_volatility_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ewma_volatility_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ewma_volatility_free(handle: *mut EwmaVolatility) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Expectancy` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_expectancy_free`. +#[no_mangle] +pub extern "C" fn wickra_expectancy_new(period: usize) -> *mut Expectancy { + match Expectancy::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_expectancy_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_expectancy_update(handle: *mut Expectancy, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_expectancy_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_expectancy_batch( + handle: *mut Expectancy, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_expectancy_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_expectancy_reset(handle: *mut Expectancy) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_expectancy_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_expectancy_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_expectancy_free(handle: *mut Expectancy) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Fama` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fama_free`. +#[no_mangle] +pub extern "C" fn wickra_fama_new(fast_limit: f64, slow_limit: f64) -> *mut Fama { + match Fama::new(fast_limit, slow_limit) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_fama_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fama_update(handle: *mut Fama, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_fama_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_fama_batch( + handle: *mut Fama, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fama_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fama_reset(handle: *mut Fama) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fama_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fama_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fama_free(handle: *mut Fama) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FisherRsi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fisher_rsi_free`. +#[no_mangle] +pub extern "C" fn wickra_fisher_rsi_new(period: usize) -> *mut FisherRsi { + match FisherRsi::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_fisher_rsi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fisher_rsi_update(handle: *mut FisherRsi, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_fisher_rsi_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_fisher_rsi_batch( + handle: *mut FisherRsi, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fisher_rsi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fisher_rsi_reset(handle: *mut FisherRsi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fisher_rsi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fisher_rsi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fisher_rsi_free(handle: *mut FisherRsi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FisherTransform` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fisher_transform_free`. +#[no_mangle] +pub extern "C" fn wickra_fisher_transform_new(period: usize) -> *mut FisherTransform { + match FisherTransform::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_fisher_transform_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fisher_transform_update( + handle: *mut FisherTransform, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_fisher_transform_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_fisher_transform_batch( + handle: *mut FisherTransform, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fisher_transform_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fisher_transform_reset(handle: *mut FisherTransform) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fisher_transform_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fisher_transform_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fisher_transform_free(handle: *mut FisherTransform) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Frama` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_frama_free`. +#[no_mangle] +pub extern "C" fn wickra_frama_new(period: usize) -> *mut Frama { + match Frama::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_frama_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_frama_update(handle: *mut Frama, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_frama_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_frama_batch( + handle: *mut Frama, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_frama_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_frama_reset(handle: *mut Frama) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_frama_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_frama_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_frama_free(handle: *mut Frama) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `GainLossRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_gain_loss_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_gain_loss_ratio_new(period: usize) -> *mut GainLossRatio { + match GainLossRatio::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_gain_loss_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gain_loss_ratio_update( + handle: *mut GainLossRatio, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_gain_loss_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_gain_loss_ratio_batch( + handle: *mut GainLossRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_gain_loss_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gain_loss_ratio_reset(handle: *mut GainLossRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_gain_loss_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_gain_loss_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gain_loss_ratio_free(handle: *mut GainLossRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `GainToPainRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_gain_to_pain_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_gain_to_pain_ratio_new(period: usize) -> *mut GainToPainRatio { + match GainToPainRatio::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_gain_to_pain_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gain_to_pain_ratio_update( + handle: *mut GainToPainRatio, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_gain_to_pain_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_gain_to_pain_ratio_batch( + handle: *mut GainToPainRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_gain_to_pain_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gain_to_pain_ratio_reset(handle: *mut GainToPainRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_gain_to_pain_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_gain_to_pain_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gain_to_pain_ratio_free(handle: *mut GainToPainRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Garch11` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_garch11_free`. +#[no_mangle] +pub extern "C" fn wickra_garch11_new(omega: f64, alpha: f64, beta: f64) -> *mut Garch11 { + match Garch11::new(omega, alpha, beta) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_garch11_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_garch11_update(handle: *mut Garch11, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_garch11_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_garch11_batch( + handle: *mut Garch11, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_garch11_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_garch11_reset(handle: *mut Garch11) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_garch11_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_garch11_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_garch11_free(handle: *mut Garch11) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `GeneralizedDema` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_generalized_dema_free`. +#[no_mangle] +pub extern "C" fn wickra_generalized_dema_new(period: usize, v: f64) -> *mut GeneralizedDema { + match GeneralizedDema::new(period, v) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_generalized_dema_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_generalized_dema_update( + handle: *mut GeneralizedDema, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_generalized_dema_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_generalized_dema_batch( + handle: *mut GeneralizedDema, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_generalized_dema_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_generalized_dema_reset(handle: *mut GeneralizedDema) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_generalized_dema_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_generalized_dema_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_generalized_dema_free(handle: *mut GeneralizedDema) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `GeometricMa` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_geometric_ma_free`. +#[no_mangle] +pub extern "C" fn wickra_geometric_ma_new(period: usize) -> *mut GeometricMa { + match GeometricMa::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_geometric_ma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_geometric_ma_update(handle: *mut GeometricMa, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_geometric_ma_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_geometric_ma_batch( + handle: *mut GeometricMa, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_geometric_ma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_geometric_ma_reset(handle: *mut GeometricMa) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_geometric_ma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_geometric_ma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_geometric_ma_free(handle: *mut GeometricMa) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HighpassFilter` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_highpass_filter_free`. +#[no_mangle] +pub extern "C" fn wickra_highpass_filter_new(period: usize) -> *mut HighpassFilter { + match HighpassFilter::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_highpass_filter_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_highpass_filter_update( + handle: *mut HighpassFilter, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_highpass_filter_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_highpass_filter_batch( + handle: *mut HighpassFilter, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_highpass_filter_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_highpass_filter_reset(handle: *mut HighpassFilter) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_highpass_filter_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_highpass_filter_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_highpass_filter_free(handle: *mut HighpassFilter) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HilbertDominantCycle` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_hilbert_dominant_cycle_free`. +#[no_mangle] +pub extern "C" fn wickra_hilbert_dominant_cycle_new() -> *mut HilbertDominantCycle { + Box::into_raw(Box::new(HilbertDominantCycle::new())) +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_hilbert_dominant_cycle_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hilbert_dominant_cycle_update( + handle: *mut HilbertDominantCycle, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_hilbert_dominant_cycle_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_hilbert_dominant_cycle_batch( + handle: *mut HilbertDominantCycle, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_hilbert_dominant_cycle_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hilbert_dominant_cycle_reset(handle: *mut HilbertDominantCycle) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_hilbert_dominant_cycle_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_hilbert_dominant_cycle_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hilbert_dominant_cycle_free(handle: *mut HilbertDominantCycle) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HistoricalVolatility` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_historical_volatility_free`. +#[no_mangle] +pub extern "C" fn wickra_historical_volatility_new( + period: usize, + trading_periods: usize, +) -> *mut HistoricalVolatility { + match HistoricalVolatility::new(period, trading_periods) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_historical_volatility_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_historical_volatility_update( + handle: *mut HistoricalVolatility, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_historical_volatility_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_historical_volatility_batch( + handle: *mut HistoricalVolatility, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_historical_volatility_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_historical_volatility_reset(handle: *mut HistoricalVolatility) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_historical_volatility_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_historical_volatility_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_historical_volatility_free(handle: *mut HistoricalVolatility) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Hma` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_hma_free`. +#[no_mangle] +pub extern "C" fn wickra_hma_new(period: usize) -> *mut Hma { + match Hma::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_hma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hma_update(handle: *mut Hma, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_hma_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_hma_batch( + handle: *mut Hma, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_hma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hma_reset(handle: *mut Hma) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_hma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_hma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hma_free(handle: *mut Hma) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HoltWinters` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_holt_winters_free`. +#[no_mangle] +pub extern "C" fn wickra_holt_winters_new(alpha: f64, beta: f64) -> *mut HoltWinters { + match HoltWinters::new(alpha, beta) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_holt_winters_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_holt_winters_update(handle: *mut HoltWinters, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_holt_winters_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_holt_winters_batch( + handle: *mut HoltWinters, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_holt_winters_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_holt_winters_reset(handle: *mut HoltWinters) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_holt_winters_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_holt_winters_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_holt_winters_free(handle: *mut HoltWinters) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HtDcPhase` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ht_dc_phase_free`. +#[no_mangle] +pub extern "C" fn wickra_ht_dc_phase_new() -> *mut HtDcPhase { + Box::into_raw(Box::new(HtDcPhase::new())) +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ht_dc_phase_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ht_dc_phase_update(handle: *mut HtDcPhase, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_ht_dc_phase_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_ht_dc_phase_batch( + handle: *mut HtDcPhase, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ht_dc_phase_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ht_dc_phase_reset(handle: *mut HtDcPhase) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ht_dc_phase_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ht_dc_phase_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ht_dc_phase_free(handle: *mut HtDcPhase) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HtTrendMode` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ht_trend_mode_free`. +#[no_mangle] +pub extern "C" fn wickra_ht_trend_mode_new() -> *mut HtTrendMode { + Box::into_raw(Box::new(HtTrendMode::new())) +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ht_trend_mode_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ht_trend_mode_update(handle: *mut HtTrendMode, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_ht_trend_mode_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_ht_trend_mode_batch( + handle: *mut HtTrendMode, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ht_trend_mode_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ht_trend_mode_reset(handle: *mut HtTrendMode) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ht_trend_mode_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ht_trend_mode_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ht_trend_mode_free(handle: *mut HtTrendMode) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HurstExponent` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_hurst_exponent_free`. +#[no_mangle] +pub extern "C" fn wickra_hurst_exponent_new(period: usize, chunks: usize) -> *mut HurstExponent { + match HurstExponent::new(period, chunks) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_hurst_exponent_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hurst_exponent_update( + handle: *mut HurstExponent, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_hurst_exponent_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_hurst_exponent_batch( + handle: *mut HurstExponent, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_hurst_exponent_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hurst_exponent_reset(handle: *mut HurstExponent) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_hurst_exponent_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_hurst_exponent_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hurst_exponent_free(handle: *mut HurstExponent) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `InstantaneousTrendline` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_instantaneous_trendline_free`. +#[no_mangle] +pub extern "C" fn wickra_instantaneous_trendline_new(period: usize) -> *mut InstantaneousTrendline { + match InstantaneousTrendline::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_instantaneous_trendline_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_instantaneous_trendline_update( + handle: *mut InstantaneousTrendline, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_instantaneous_trendline_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_instantaneous_trendline_batch( + handle: *mut InstantaneousTrendline, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_instantaneous_trendline_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_instantaneous_trendline_reset(handle: *mut InstantaneousTrendline) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_instantaneous_trendline_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_instantaneous_trendline_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_instantaneous_trendline_free(handle: *mut InstantaneousTrendline) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `InverseFisherTransform` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_inverse_fisher_transform_free`. +#[no_mangle] +pub extern "C" fn wickra_inverse_fisher_transform_new(scale: f64) -> *mut InverseFisherTransform { + match InverseFisherTransform::new(scale) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_inverse_fisher_transform_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_inverse_fisher_transform_update( + handle: *mut InverseFisherTransform, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_inverse_fisher_transform_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_inverse_fisher_transform_batch( + handle: *mut InverseFisherTransform, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_inverse_fisher_transform_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_inverse_fisher_transform_reset( + handle: *mut InverseFisherTransform, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_inverse_fisher_transform_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_inverse_fisher_transform_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_inverse_fisher_transform_free(handle: *mut InverseFisherTransform) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `JarqueBera` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_jarque_bera_free`. +#[no_mangle] +pub extern "C" fn wickra_jarque_bera_new(period: usize) -> *mut JarqueBera { + match JarqueBera::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_jarque_bera_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_jarque_bera_update(handle: *mut JarqueBera, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_jarque_bera_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_jarque_bera_batch( + handle: *mut JarqueBera, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_jarque_bera_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_jarque_bera_reset(handle: *mut JarqueBera) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_jarque_bera_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_jarque_bera_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_jarque_bera_free(handle: *mut JarqueBera) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Jma` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_jma_free`. +#[no_mangle] +pub extern "C" fn wickra_jma_new(period: usize, phase: f64, power: u32) -> *mut Jma { + match Jma::new(period, phase, power) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_jma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_jma_update(handle: *mut Jma, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_jma_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_jma_batch( + handle: *mut Jma, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_jma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_jma_reset(handle: *mut Jma) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_jma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_jma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_jma_free(handle: *mut Jma) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `JumpIndicator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_jump_indicator_free`. +#[no_mangle] +pub extern "C" fn wickra_jump_indicator_new(period: usize, threshold: f64) -> *mut JumpIndicator { + match JumpIndicator::new(period, threshold) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_jump_indicator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_jump_indicator_update( + handle: *mut JumpIndicator, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_jump_indicator_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_jump_indicator_batch( + handle: *mut JumpIndicator, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_jump_indicator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_jump_indicator_reset(handle: *mut JumpIndicator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_jump_indicator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_jump_indicator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_jump_indicator_free(handle: *mut JumpIndicator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `KRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_k_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_k_ratio_new(period: usize) -> *mut KRatio { + match KRatio::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_k_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_k_ratio_update(handle: *mut KRatio, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_k_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_k_ratio_batch( + handle: *mut KRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_k_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_k_ratio_reset(handle: *mut KRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_k_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_k_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_k_ratio_free(handle: *mut KRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Kama` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kama_free`. +#[no_mangle] +pub extern "C" fn wickra_kama_new(er_period: usize, fast: usize, slow: usize) -> *mut Kama { + match Kama::new(er_period, fast, slow) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_kama_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kama_update(handle: *mut Kama, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_kama_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_kama_batch( + handle: *mut Kama, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kama_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kama_reset(handle: *mut Kama) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kama_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kama_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kama_free(handle: *mut Kama) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `KellyCriterion` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kelly_criterion_free`. +#[no_mangle] +pub extern "C" fn wickra_kelly_criterion_new(period: usize) -> *mut KellyCriterion { + match KellyCriterion::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_kelly_criterion_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kelly_criterion_update( + handle: *mut KellyCriterion, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_kelly_criterion_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_kelly_criterion_batch( + handle: *mut KellyCriterion, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kelly_criterion_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kelly_criterion_reset(handle: *mut KellyCriterion) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kelly_criterion_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kelly_criterion_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kelly_criterion_free(handle: *mut KellyCriterion) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Kurtosis` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kurtosis_free`. +#[no_mangle] +pub extern "C" fn wickra_kurtosis_new(period: usize) -> *mut Kurtosis { + match Kurtosis::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_kurtosis_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kurtosis_update(handle: *mut Kurtosis, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_kurtosis_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_kurtosis_batch( + handle: *mut Kurtosis, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kurtosis_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kurtosis_reset(handle: *mut Kurtosis) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kurtosis_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kurtosis_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kurtosis_free(handle: *mut Kurtosis) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LaguerreRsi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_laguerre_rsi_free`. +#[no_mangle] +pub extern "C" fn wickra_laguerre_rsi_new(gamma: f64) -> *mut LaguerreRsi { + match LaguerreRsi::new(gamma) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_laguerre_rsi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_laguerre_rsi_update(handle: *mut LaguerreRsi, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_laguerre_rsi_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_laguerre_rsi_batch( + handle: *mut LaguerreRsi, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_laguerre_rsi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_laguerre_rsi_reset(handle: *mut LaguerreRsi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_laguerre_rsi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_laguerre_rsi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_laguerre_rsi_free(handle: *mut LaguerreRsi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LinearRegression` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_linear_regression_free`. +#[no_mangle] +pub extern "C" fn wickra_linear_regression_new(period: usize) -> *mut LinearRegression { + match LinearRegression::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_linear_regression_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_linear_regression_update( + handle: *mut LinearRegression, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_linear_regression_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_linear_regression_batch( + handle: *mut LinearRegression, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_linear_regression_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_linear_regression_reset(handle: *mut LinearRegression) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_linear_regression_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_linear_regression_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_linear_regression_free(handle: *mut LinearRegression) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LinRegAngle` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_lin_reg_angle_free`. +#[no_mangle] +pub extern "C" fn wickra_lin_reg_angle_new(period: usize) -> *mut LinRegAngle { + match LinRegAngle::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_lin_reg_angle_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_angle_update(handle: *mut LinRegAngle, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_lin_reg_angle_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_angle_batch( + handle: *mut LinRegAngle, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_lin_reg_angle_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_angle_reset(handle: *mut LinRegAngle) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_lin_reg_angle_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_lin_reg_angle_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_angle_free(handle: *mut LinRegAngle) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LinRegIntercept` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_lin_reg_intercept_free`. +#[no_mangle] +pub extern "C" fn wickra_lin_reg_intercept_new(period: usize) -> *mut LinRegIntercept { + match LinRegIntercept::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_lin_reg_intercept_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_intercept_update( + handle: *mut LinRegIntercept, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_lin_reg_intercept_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_intercept_batch( + handle: *mut LinRegIntercept, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_lin_reg_intercept_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_intercept_reset(handle: *mut LinRegIntercept) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_lin_reg_intercept_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_lin_reg_intercept_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_intercept_free(handle: *mut LinRegIntercept) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LinRegSlope` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_lin_reg_slope_free`. +#[no_mangle] +pub extern "C" fn wickra_lin_reg_slope_new(period: usize) -> *mut LinRegSlope { + match LinRegSlope::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_lin_reg_slope_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_slope_update(handle: *mut LinRegSlope, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_lin_reg_slope_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_slope_batch( + handle: *mut LinRegSlope, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_lin_reg_slope_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_slope_reset(handle: *mut LinRegSlope) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_lin_reg_slope_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_lin_reg_slope_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_slope_free(handle: *mut LinRegSlope) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LogReturn` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_log_return_free`. +#[no_mangle] +pub extern "C" fn wickra_log_return_new(period: usize) -> *mut LogReturn { + match LogReturn::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_log_return_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_log_return_update(handle: *mut LogReturn, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_log_return_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_log_return_batch( + handle: *mut LogReturn, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_log_return_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_log_return_reset(handle: *mut LogReturn) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_log_return_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_log_return_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_log_return_free(handle: *mut LogReturn) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `M2Measure` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_m2_measure_free`. +#[no_mangle] +pub extern "C" fn wickra_m2_measure_new( + period: usize, + risk_free: f64, + benchmark_stddev: f64, +) -> *mut M2Measure { + match M2Measure::new(period, risk_free, benchmark_stddev) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_m2_measure_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_m2_measure_update(handle: *mut M2Measure, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_m2_measure_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_m2_measure_batch( + handle: *mut M2Measure, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_m2_measure_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_m2_measure_reset(handle: *mut M2Measure) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_m2_measure_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_m2_measure_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_m2_measure_free(handle: *mut M2Measure) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MacdHistogram` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_macd_histogram_free`. +#[no_mangle] +pub extern "C" fn wickra_macd_histogram_new( + fast: usize, + slow: usize, + signal: usize, +) -> *mut MacdHistogram { + match MacdHistogram::new(fast, slow, signal) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_macd_histogram_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_histogram_update( + handle: *mut MacdHistogram, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_macd_histogram_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_histogram_batch( + handle: *mut MacdHistogram, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_macd_histogram_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_histogram_reset(handle: *mut MacdHistogram) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_macd_histogram_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_macd_histogram_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_histogram_free(handle: *mut MacdHistogram) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MartinRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_martin_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_martin_ratio_new(period: usize) -> *mut MartinRatio { + match MartinRatio::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_martin_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_martin_ratio_update(handle: *mut MartinRatio, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_martin_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_martin_ratio_batch( + handle: *mut MartinRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_martin_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_martin_ratio_reset(handle: *mut MartinRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_martin_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_martin_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_martin_ratio_free(handle: *mut MartinRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MaxDrawdown` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_max_drawdown_free`. +#[no_mangle] +pub extern "C" fn wickra_max_drawdown_new(period: usize) -> *mut MaxDrawdown { + match MaxDrawdown::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_max_drawdown_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_max_drawdown_update(handle: *mut MaxDrawdown, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_max_drawdown_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_max_drawdown_batch( + handle: *mut MaxDrawdown, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_max_drawdown_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_max_drawdown_reset(handle: *mut MaxDrawdown) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_max_drawdown_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_max_drawdown_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_max_drawdown_free(handle: *mut MaxDrawdown) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `McGinleyDynamic` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_mc_ginley_dynamic_free`. +#[no_mangle] +pub extern "C" fn wickra_mc_ginley_dynamic_new(period: usize) -> *mut McGinleyDynamic { + match McGinleyDynamic::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_mc_ginley_dynamic_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mc_ginley_dynamic_update( + handle: *mut McGinleyDynamic, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_mc_ginley_dynamic_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_mc_ginley_dynamic_batch( + handle: *mut McGinleyDynamic, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_mc_ginley_dynamic_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mc_ginley_dynamic_reset(handle: *mut McGinleyDynamic) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_mc_ginley_dynamic_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_mc_ginley_dynamic_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mc_ginley_dynamic_free(handle: *mut McGinleyDynamic) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MedianAbsoluteDeviation` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_median_absolute_deviation_free`. +#[no_mangle] +pub extern "C" fn wickra_median_absolute_deviation_new( + period: usize, +) -> *mut MedianAbsoluteDeviation { + match MedianAbsoluteDeviation::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_median_absolute_deviation_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_absolute_deviation_update( + handle: *mut MedianAbsoluteDeviation, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_median_absolute_deviation_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_absolute_deviation_batch( + handle: *mut MedianAbsoluteDeviation, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_median_absolute_deviation_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_absolute_deviation_reset( + handle: *mut MedianAbsoluteDeviation, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_median_absolute_deviation_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_median_absolute_deviation_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_absolute_deviation_free( + handle: *mut MedianAbsoluteDeviation, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MedianMa` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_median_ma_free`. +#[no_mangle] +pub extern "C" fn wickra_median_ma_new(period: usize) -> *mut MedianMa { + match MedianMa::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_median_ma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_ma_update(handle: *mut MedianMa, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_median_ma_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_ma_batch( + handle: *mut MedianMa, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_median_ma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_ma_reset(handle: *mut MedianMa) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_median_ma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_median_ma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_ma_free(handle: *mut MedianMa) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MidPoint` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_mid_point_free`. +#[no_mangle] +pub extern "C" fn wickra_mid_point_new(period: usize) -> *mut MidPoint { + match MidPoint::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_mid_point_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mid_point_update(handle: *mut MidPoint, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_mid_point_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_mid_point_batch( + handle: *mut MidPoint, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_mid_point_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mid_point_reset(handle: *mut MidPoint) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_mid_point_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_mid_point_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mid_point_free(handle: *mut MidPoint) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Mom` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_mom_free`. +#[no_mangle] +pub extern "C" fn wickra_mom_new(period: usize) -> *mut Mom { + match Mom::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_mom_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mom_update(handle: *mut Mom, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_mom_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_mom_batch( + handle: *mut Mom, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_mom_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mom_reset(handle: *mut Mom) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_mom_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_mom_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mom_free(handle: *mut Mom) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OmegaRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_omega_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_omega_ratio_new(period: usize, threshold: f64) -> *mut OmegaRatio { + match OmegaRatio::new(period, threshold) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_omega_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_omega_ratio_update(handle: *mut OmegaRatio, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_omega_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_omega_ratio_batch( + handle: *mut OmegaRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_omega_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_omega_ratio_reset(handle: *mut OmegaRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_omega_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_omega_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_omega_ratio_free(handle: *mut OmegaRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PainIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_pain_index_free`. +#[no_mangle] +pub extern "C" fn wickra_pain_index_new(period: usize) -> *mut PainIndex { + match PainIndex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_pain_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pain_index_update(handle: *mut PainIndex, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_pain_index_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_pain_index_batch( + handle: *mut PainIndex, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_pain_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pain_index_reset(handle: *mut PainIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_pain_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_pain_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pain_index_free(handle: *mut PainIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PercentB` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_percent_b_free`. +#[no_mangle] +pub extern "C" fn wickra_percent_b_new(period: usize, multiplier: f64) -> *mut PercentB { + match PercentB::new(period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_percent_b_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_percent_b_update(handle: *mut PercentB, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_percent_b_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_percent_b_batch( + handle: *mut PercentB, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_percent_b_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_percent_b_reset(handle: *mut PercentB) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_percent_b_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_percent_b_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_percent_b_free(handle: *mut PercentB) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PercentageTrailingStop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_percentage_trailing_stop_free`. +#[no_mangle] +pub extern "C" fn wickra_percentage_trailing_stop_new(percent: f64) -> *mut PercentageTrailingStop { + match PercentageTrailingStop::new(percent) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_percentage_trailing_stop_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_percentage_trailing_stop_update( + handle: *mut PercentageTrailingStop, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_percentage_trailing_stop_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_percentage_trailing_stop_batch( + handle: *mut PercentageTrailingStop, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_percentage_trailing_stop_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_percentage_trailing_stop_reset( + handle: *mut PercentageTrailingStop, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_percentage_trailing_stop_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_percentage_trailing_stop_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_percentage_trailing_stop_free(handle: *mut PercentageTrailingStop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Pmo` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_pmo_free`. +#[no_mangle] +pub extern "C" fn wickra_pmo_new(smoothing1: usize, smoothing2: usize) -> *mut Pmo { + match Pmo::new(smoothing1, smoothing2) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_pmo_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pmo_update(handle: *mut Pmo, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_pmo_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_pmo_batch( + handle: *mut Pmo, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_pmo_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pmo_reset(handle: *mut Pmo) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_pmo_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_pmo_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pmo_free(handle: *mut Pmo) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PolarizedFractalEfficiency` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_polarized_fractal_efficiency_free`. +#[no_mangle] +pub extern "C" fn wickra_polarized_fractal_efficiency_new( + period: usize, + smoothing: usize, +) -> *mut PolarizedFractalEfficiency { + match PolarizedFractalEfficiency::new(period, smoothing) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_polarized_fractal_efficiency_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_polarized_fractal_efficiency_update( + handle: *mut PolarizedFractalEfficiency, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_polarized_fractal_efficiency_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_polarized_fractal_efficiency_batch( + handle: *mut PolarizedFractalEfficiency, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_polarized_fractal_efficiency_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_polarized_fractal_efficiency_reset( + handle: *mut PolarizedFractalEfficiency, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_polarized_fractal_efficiency_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_polarized_fractal_efficiency_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_polarized_fractal_efficiency_free( + handle: *mut PolarizedFractalEfficiency, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Ppo` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ppo_free`. +#[no_mangle] +pub extern "C" fn wickra_ppo_new(fast: usize, slow: usize) -> *mut Ppo { + match Ppo::new(fast, slow) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ppo_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ppo_update(handle: *mut Ppo, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_ppo_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_ppo_batch( + handle: *mut Ppo, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ppo_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ppo_reset(handle: *mut Ppo) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ppo_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ppo_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ppo_free(handle: *mut Ppo) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PpoHistogram` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ppo_histogram_free`. +#[no_mangle] +pub extern "C" fn wickra_ppo_histogram_new( + fast: usize, + slow: usize, + signal: usize, +) -> *mut PpoHistogram { + match PpoHistogram::new(fast, slow, signal) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ppo_histogram_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ppo_histogram_update(handle: *mut PpoHistogram, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_ppo_histogram_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_ppo_histogram_batch( + handle: *mut PpoHistogram, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ppo_histogram_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ppo_histogram_reset(handle: *mut PpoHistogram) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ppo_histogram_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ppo_histogram_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ppo_histogram_free(handle: *mut PpoHistogram) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ProfitFactor` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_profit_factor_free`. +#[no_mangle] +pub extern "C" fn wickra_profit_factor_new(period: usize) -> *mut ProfitFactor { + match ProfitFactor::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_profit_factor_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_profit_factor_update(handle: *mut ProfitFactor, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_profit_factor_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_profit_factor_batch( + handle: *mut ProfitFactor, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_profit_factor_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_profit_factor_reset(handle: *mut ProfitFactor) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_profit_factor_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_profit_factor_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_profit_factor_free(handle: *mut ProfitFactor) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RSquared` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_r_squared_free`. +#[no_mangle] +pub extern "C" fn wickra_r_squared_new(period: usize) -> *mut RSquared { + match RSquared::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_r_squared_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_r_squared_update(handle: *mut RSquared, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_r_squared_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_r_squared_batch( + handle: *mut RSquared, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_r_squared_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_r_squared_reset(handle: *mut RSquared) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_r_squared_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_r_squared_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_r_squared_free(handle: *mut RSquared) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RealizedVolatility` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_realized_volatility_free`. +#[no_mangle] +pub extern "C" fn wickra_realized_volatility_new(period: usize) -> *mut RealizedVolatility { + match RealizedVolatility::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_realized_volatility_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_realized_volatility_update( + handle: *mut RealizedVolatility, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_realized_volatility_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_realized_volatility_batch( + handle: *mut RealizedVolatility, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_realized_volatility_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_realized_volatility_reset(handle: *mut RealizedVolatility) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_realized_volatility_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_realized_volatility_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_realized_volatility_free(handle: *mut RealizedVolatility) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RecoveryFactor` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_recovery_factor_free`. +#[no_mangle] +pub extern "C" fn wickra_recovery_factor_new() -> *mut RecoveryFactor { + Box::into_raw(Box::new(RecoveryFactor::new())) +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_recovery_factor_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_recovery_factor_update( + handle: *mut RecoveryFactor, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_recovery_factor_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_recovery_factor_batch( + handle: *mut RecoveryFactor, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_recovery_factor_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_recovery_factor_reset(handle: *mut RecoveryFactor) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_recovery_factor_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_recovery_factor_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_recovery_factor_free(handle: *mut RecoveryFactor) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Reflex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_reflex_free`. +#[no_mangle] +pub extern "C" fn wickra_reflex_new(period: usize) -> *mut Reflex { + match Reflex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_reflex_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_reflex_update(handle: *mut Reflex, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_reflex_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_reflex_batch( + handle: *mut Reflex, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_reflex_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_reflex_reset(handle: *mut Reflex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_reflex_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_reflex_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_reflex_free(handle: *mut Reflex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RegimeLabel` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_regime_label_free`. +#[no_mangle] +pub extern "C" fn wickra_regime_label_new(vol_period: usize, lookback: usize) -> *mut RegimeLabel { + match RegimeLabel::new(vol_period, lookback) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_regime_label_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_regime_label_update(handle: *mut RegimeLabel, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_regime_label_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_regime_label_batch( + handle: *mut RegimeLabel, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_regime_label_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_regime_label_reset(handle: *mut RegimeLabel) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_regime_label_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_regime_label_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_regime_label_free(handle: *mut RegimeLabel) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RenkoTrailingStop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_renko_trailing_stop_free`. +#[no_mangle] +pub extern "C" fn wickra_renko_trailing_stop_new(block_size: f64) -> *mut RenkoTrailingStop { + match RenkoTrailingStop::new(block_size) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_renko_trailing_stop_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_renko_trailing_stop_update( + handle: *mut RenkoTrailingStop, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_renko_trailing_stop_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_renko_trailing_stop_batch( + handle: *mut RenkoTrailingStop, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_renko_trailing_stop_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_renko_trailing_stop_reset(handle: *mut RenkoTrailingStop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_renko_trailing_stop_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_renko_trailing_stop_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_renko_trailing_stop_free(handle: *mut RenkoTrailingStop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Rmi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rmi_free`. +#[no_mangle] +pub extern "C" fn wickra_rmi_new(period: usize, momentum: usize) -> *mut Rmi { + match Rmi::new(period, momentum) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rmi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rmi_update(handle: *mut Rmi, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rmi_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rmi_batch( + handle: *mut Rmi, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rmi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rmi_reset(handle: *mut Rmi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rmi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rmi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rmi_free(handle: *mut Rmi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Roc` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_roc_free`. +#[no_mangle] +pub extern "C" fn wickra_roc_new(period: usize) -> *mut Roc { + match Roc::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_roc_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_roc_update(handle: *mut Roc, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_roc_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_roc_batch( + handle: *mut Roc, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_roc_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_roc_reset(handle: *mut Roc) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_roc_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_roc_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_roc_free(handle: *mut Roc) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Rocp` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rocp_free`. +#[no_mangle] +pub extern "C" fn wickra_rocp_new(period: usize) -> *mut Rocp { + match Rocp::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rocp_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocp_update(handle: *mut Rocp, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rocp_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocp_batch( + handle: *mut Rocp, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rocp_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocp_reset(handle: *mut Rocp) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rocp_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rocp_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocp_free(handle: *mut Rocp) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Rocr` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rocr_free`. +#[no_mangle] +pub extern "C" fn wickra_rocr_new(period: usize) -> *mut Rocr { + match Rocr::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rocr_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocr_update(handle: *mut Rocr, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rocr_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocr_batch( + handle: *mut Rocr, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rocr_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocr_reset(handle: *mut Rocr) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rocr_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rocr_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocr_free(handle: *mut Rocr) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Rocr100` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rocr100_free`. +#[no_mangle] +pub extern "C" fn wickra_rocr100_new(period: usize) -> *mut Rocr100 { + match Rocr100::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rocr100_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocr100_update(handle: *mut Rocr100, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rocr100_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocr100_batch( + handle: *mut Rocr100, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rocr100_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocr100_reset(handle: *mut Rocr100) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rocr100_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rocr100_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rocr100_free(handle: *mut Rocr100) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RollingIqr` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rolling_iqr_free`. +#[no_mangle] +pub extern "C" fn wickra_rolling_iqr_new(period: usize) -> *mut RollingIqr { + match RollingIqr::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rolling_iqr_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_iqr_update(handle: *mut RollingIqr, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rolling_iqr_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_iqr_batch( + handle: *mut RollingIqr, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rolling_iqr_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_iqr_reset(handle: *mut RollingIqr) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rolling_iqr_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rolling_iqr_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_iqr_free(handle: *mut RollingIqr) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RollingMinMaxScaler` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rolling_min_max_scaler_free`. +#[no_mangle] +pub extern "C" fn wickra_rolling_min_max_scaler_new(period: usize) -> *mut RollingMinMaxScaler { + match RollingMinMaxScaler::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rolling_min_max_scaler_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_min_max_scaler_update( + handle: *mut RollingMinMaxScaler, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rolling_min_max_scaler_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_min_max_scaler_batch( + handle: *mut RollingMinMaxScaler, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rolling_min_max_scaler_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_min_max_scaler_reset(handle: *mut RollingMinMaxScaler) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rolling_min_max_scaler_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rolling_min_max_scaler_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_min_max_scaler_free(handle: *mut RollingMinMaxScaler) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RollingPercentileRank` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rolling_percentile_rank_free`. +#[no_mangle] +pub extern "C" fn wickra_rolling_percentile_rank_new(period: usize) -> *mut RollingPercentileRank { + match RollingPercentileRank::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rolling_percentile_rank_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_percentile_rank_update( + handle: *mut RollingPercentileRank, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rolling_percentile_rank_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_percentile_rank_batch( + handle: *mut RollingPercentileRank, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rolling_percentile_rank_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_percentile_rank_reset(handle: *mut RollingPercentileRank) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rolling_percentile_rank_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rolling_percentile_rank_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_percentile_rank_free(handle: *mut RollingPercentileRank) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RollingQuantile` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rolling_quantile_free`. +#[no_mangle] +pub extern "C" fn wickra_rolling_quantile_new( + period: usize, + quantile: f64, +) -> *mut RollingQuantile { + match RollingQuantile::new(period, quantile) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rolling_quantile_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_quantile_update( + handle: *mut RollingQuantile, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rolling_quantile_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_quantile_batch( + handle: *mut RollingQuantile, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rolling_quantile_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_quantile_reset(handle: *mut RollingQuantile) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rolling_quantile_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rolling_quantile_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_quantile_free(handle: *mut RollingQuantile) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RoofingFilter` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_roofing_filter_free`. +#[no_mangle] +pub extern "C" fn wickra_roofing_filter_new( + lp_period: usize, + hp_period: usize, +) -> *mut RoofingFilter { + match RoofingFilter::new(lp_period, hp_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_roofing_filter_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_roofing_filter_update( + handle: *mut RoofingFilter, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_roofing_filter_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_roofing_filter_batch( + handle: *mut RoofingFilter, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_roofing_filter_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_roofing_filter_reset(handle: *mut RoofingFilter) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_roofing_filter_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_roofing_filter_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_roofing_filter_free(handle: *mut RoofingFilter) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Rsi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rsi_free`. +#[no_mangle] +pub extern "C" fn wickra_rsi_new(period: usize) -> *mut Rsi { + match Rsi::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rsi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rsi_update(handle: *mut Rsi, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rsi_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rsi_batch( + handle: *mut Rsi, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rsi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rsi_reset(handle: *mut Rsi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rsi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rsi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rsi_free(handle: *mut Rsi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Rsx` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rsx_free`. +#[no_mangle] +pub extern "C" fn wickra_rsx_new(length: usize) -> *mut Rsx { + match Rsx::new(length) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rsx_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rsx_update(handle: *mut Rsx, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rsx_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rsx_batch( + handle: *mut Rsx, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rsx_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rsx_reset(handle: *mut Rsx) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rsx_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rsx_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rsx_free(handle: *mut Rsx) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RviVolatility` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rvi_volatility_free`. +#[no_mangle] +pub extern "C" fn wickra_rvi_volatility_new(period: usize) -> *mut RviVolatility { + match RviVolatility::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rvi_volatility_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rvi_volatility_update( + handle: *mut RviVolatility, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rvi_volatility_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rvi_volatility_batch( + handle: *mut RviVolatility, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rvi_volatility_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rvi_volatility_reset(handle: *mut RviVolatility) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rvi_volatility_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rvi_volatility_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rvi_volatility_free(handle: *mut RviVolatility) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SampleEntropy` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_sample_entropy_free`. +#[no_mangle] +pub extern "C" fn wickra_sample_entropy_new( + period: usize, + m: usize, + r_factor: f64, +) -> *mut SampleEntropy { + match SampleEntropy::new(period, m, r_factor) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_sample_entropy_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sample_entropy_update( + handle: *mut SampleEntropy, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_sample_entropy_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_sample_entropy_batch( + handle: *mut SampleEntropy, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_sample_entropy_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sample_entropy_reset(handle: *mut SampleEntropy) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_sample_entropy_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_sample_entropy_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sample_entropy_free(handle: *mut SampleEntropy) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ShannonEntropy` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_shannon_entropy_free`. +#[no_mangle] +pub extern "C" fn wickra_shannon_entropy_new(period: usize, bins: usize) -> *mut ShannonEntropy { + match ShannonEntropy::new(period, bins) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_shannon_entropy_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_shannon_entropy_update( + handle: *mut ShannonEntropy, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_shannon_entropy_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_shannon_entropy_batch( + handle: *mut ShannonEntropy, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_shannon_entropy_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_shannon_entropy_reset(handle: *mut ShannonEntropy) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_shannon_entropy_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_shannon_entropy_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_shannon_entropy_free(handle: *mut ShannonEntropy) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SharpeRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_sharpe_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_sharpe_ratio_new(period: usize, risk_free: f64) -> *mut SharpeRatio { + match SharpeRatio::new(period, risk_free) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_sharpe_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sharpe_ratio_update(handle: *mut SharpeRatio, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_sharpe_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_sharpe_ratio_batch( + handle: *mut SharpeRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_sharpe_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sharpe_ratio_reset(handle: *mut SharpeRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_sharpe_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_sharpe_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sharpe_ratio_free(handle: *mut SharpeRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SineWave` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_sine_wave_free`. +#[no_mangle] +pub extern "C" fn wickra_sine_wave_new() -> *mut SineWave { + Box::into_raw(Box::new(SineWave::new())) +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_sine_wave_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sine_wave_update(handle: *mut SineWave, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_sine_wave_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_sine_wave_batch( + handle: *mut SineWave, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_sine_wave_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sine_wave_reset(handle: *mut SineWave) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_sine_wave_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_sine_wave_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sine_wave_free(handle: *mut SineWave) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SineWeightedMa` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_sine_weighted_ma_free`. +#[no_mangle] +pub extern "C" fn wickra_sine_weighted_ma_new(period: usize) -> *mut SineWeightedMa { + match SineWeightedMa::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_sine_weighted_ma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sine_weighted_ma_update( + handle: *mut SineWeightedMa, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_sine_weighted_ma_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_sine_weighted_ma_batch( + handle: *mut SineWeightedMa, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_sine_weighted_ma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sine_weighted_ma_reset(handle: *mut SineWeightedMa) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_sine_weighted_ma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_sine_weighted_ma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sine_weighted_ma_free(handle: *mut SineWeightedMa) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Skewness` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_skewness_free`. +#[no_mangle] +pub extern "C" fn wickra_skewness_new(period: usize) -> *mut Skewness { + match Skewness::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_skewness_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_skewness_update(handle: *mut Skewness, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_skewness_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_skewness_batch( + handle: *mut Skewness, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_skewness_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_skewness_reset(handle: *mut Skewness) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_skewness_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_skewness_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_skewness_free(handle: *mut Skewness) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Sma` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_sma_free`. +#[no_mangle] +pub extern "C" fn wickra_sma_new(period: usize) -> *mut Sma { + match Sma::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_sma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sma_update(handle: *mut Sma, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_sma_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_sma_batch( + handle: *mut Sma, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_sma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sma_reset(handle: *mut Sma) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_sma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_sma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sma_free(handle: *mut Sma) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Smma` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_smma_free`. +#[no_mangle] +pub extern "C" fn wickra_smma_new(period: usize) -> *mut Smma { + match Smma::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_smma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_smma_update(handle: *mut Smma, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_smma_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_smma_batch( + handle: *mut Smma, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_smma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_smma_reset(handle: *mut Smma) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_smma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_smma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_smma_free(handle: *mut Smma) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SortinoRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_sortino_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_sortino_ratio_new(period: usize, mar: f64) -> *mut SortinoRatio { + match SortinoRatio::new(period, mar) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_sortino_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sortino_ratio_update(handle: *mut SortinoRatio, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_sortino_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_sortino_ratio_batch( + handle: *mut SortinoRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_sortino_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sortino_ratio_reset(handle: *mut SortinoRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_sortino_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_sortino_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sortino_ratio_free(handle: *mut SortinoRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `StandardError` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_standard_error_free`. +#[no_mangle] +pub extern "C" fn wickra_standard_error_new(period: usize) -> *mut StandardError { + match StandardError::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_standard_error_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_standard_error_update( + handle: *mut StandardError, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_standard_error_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_standard_error_batch( + handle: *mut StandardError, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_standard_error_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_standard_error_reset(handle: *mut StandardError) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_standard_error_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_standard_error_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_standard_error_free(handle: *mut StandardError) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Stc` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_stc_free`. +#[no_mangle] +pub extern "C" fn wickra_stc_new( + fast: usize, + slow: usize, + schaff_period: usize, + factor: f64, +) -> *mut Stc { + match Stc::new(fast, slow, schaff_period, factor) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_stc_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stc_update(handle: *mut Stc, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_stc_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_stc_batch( + handle: *mut Stc, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_stc_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stc_reset(handle: *mut Stc) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_stc_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_stc_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stc_free(handle: *mut Stc) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `StdDev` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_std_dev_free`. +#[no_mangle] +pub extern "C" fn wickra_std_dev_new(period: usize) -> *mut StdDev { + match StdDev::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_std_dev_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_std_dev_update(handle: *mut StdDev, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_std_dev_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_std_dev_batch( + handle: *mut StdDev, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_std_dev_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_std_dev_reset(handle: *mut StdDev) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_std_dev_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_std_dev_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_std_dev_free(handle: *mut StdDev) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `StepTrailingStop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_step_trailing_stop_free`. +#[no_mangle] +pub extern "C" fn wickra_step_trailing_stop_new(step_size: f64) -> *mut StepTrailingStop { + match StepTrailingStop::new(step_size) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_step_trailing_stop_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_step_trailing_stop_update( + handle: *mut StepTrailingStop, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_step_trailing_stop_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_step_trailing_stop_batch( + handle: *mut StepTrailingStop, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_step_trailing_stop_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_step_trailing_stop_reset(handle: *mut StepTrailingStop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_step_trailing_stop_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_step_trailing_stop_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_step_trailing_stop_free(handle: *mut StepTrailingStop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SterlingRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_sterling_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_sterling_ratio_new(period: usize) -> *mut SterlingRatio { + match SterlingRatio::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_sterling_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sterling_ratio_update( + handle: *mut SterlingRatio, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_sterling_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_sterling_ratio_batch( + handle: *mut SterlingRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_sterling_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sterling_ratio_reset(handle: *mut SterlingRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_sterling_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_sterling_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sterling_ratio_free(handle: *mut SterlingRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `StochRsi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_stoch_rsi_free`. +#[no_mangle] +pub extern "C" fn wickra_stoch_rsi_new(rsi_period: usize, stoch_period: usize) -> *mut StochRsi { + match StochRsi::new(rsi_period, stoch_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_stoch_rsi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stoch_rsi_update(handle: *mut StochRsi, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_stoch_rsi_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_stoch_rsi_batch( + handle: *mut StochRsi, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_stoch_rsi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stoch_rsi_reset(handle: *mut StochRsi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_stoch_rsi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_stoch_rsi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stoch_rsi_free(handle: *mut StochRsi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SuperSmoother` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_super_smoother_free`. +#[no_mangle] +pub extern "C" fn wickra_super_smoother_new(period: usize) -> *mut SuperSmoother { + match SuperSmoother::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_super_smoother_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_super_smoother_update( + handle: *mut SuperSmoother, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_super_smoother_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_super_smoother_batch( + handle: *mut SuperSmoother, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_super_smoother_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_super_smoother_reset(handle: *mut SuperSmoother) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_super_smoother_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_super_smoother_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_super_smoother_free(handle: *mut SuperSmoother) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `T3` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_t3_free`. +#[no_mangle] +pub extern "C" fn wickra_t3_new(period: usize, v: f64) -> *mut T3 { + match T3::new(period, v) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_t3_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_t3_update(handle: *mut T3, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_t3_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_t3_batch( + handle: *mut T3, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_t3_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_t3_reset(handle: *mut T3) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_t3_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_t3_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_t3_free(handle: *mut T3) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TailRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tail_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_tail_ratio_new(period: usize) -> *mut TailRatio { + match TailRatio::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_tail_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tail_ratio_update(handle: *mut TailRatio, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_tail_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_tail_ratio_batch( + handle: *mut TailRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tail_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tail_ratio_reset(handle: *mut TailRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tail_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tail_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tail_ratio_free(handle: *mut TailRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Tema` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tema_free`. +#[no_mangle] +pub extern "C" fn wickra_tema_new(period: usize) -> *mut Tema { + match Tema::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_tema_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tema_update(handle: *mut Tema, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_tema_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_tema_batch( + handle: *mut Tema, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tema_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tema_reset(handle: *mut Tema) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tema_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tema_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tema_free(handle: *mut Tema) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Tii` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tii_free`. +#[no_mangle] +pub extern "C" fn wickra_tii_new(sma_period: usize, dev_period: usize) -> *mut Tii { + match Tii::new(sma_period, dev_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_tii_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tii_update(handle: *mut Tii, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_tii_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_tii_batch( + handle: *mut Tii, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tii_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tii_reset(handle: *mut Tii) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tii_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tii_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tii_free(handle: *mut Tii) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TrendLabel` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_trend_label_free`. +#[no_mangle] +pub extern "C" fn wickra_trend_label_new(period: usize) -> *mut TrendLabel { + match TrendLabel::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_trend_label_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trend_label_update(handle: *mut TrendLabel, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_trend_label_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_trend_label_batch( + handle: *mut TrendLabel, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_trend_label_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trend_label_reset(handle: *mut TrendLabel) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_trend_label_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_trend_label_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trend_label_free(handle: *mut TrendLabel) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TrendStrengthIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_trend_strength_index_free`. +#[no_mangle] +pub extern "C" fn wickra_trend_strength_index_new(period: usize) -> *mut TrendStrengthIndex { + match TrendStrengthIndex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_trend_strength_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trend_strength_index_update( + handle: *mut TrendStrengthIndex, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_trend_strength_index_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_trend_strength_index_batch( + handle: *mut TrendStrengthIndex, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_trend_strength_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trend_strength_index_reset(handle: *mut TrendStrengthIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_trend_strength_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_trend_strength_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trend_strength_index_free(handle: *mut TrendStrengthIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Trendflex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_trendflex_free`. +#[no_mangle] +pub extern "C" fn wickra_trendflex_new(period: usize) -> *mut Trendflex { + match Trendflex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_trendflex_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trendflex_update(handle: *mut Trendflex, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_trendflex_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_trendflex_batch( + handle: *mut Trendflex, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_trendflex_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trendflex_reset(handle: *mut Trendflex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_trendflex_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_trendflex_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trendflex_free(handle: *mut Trendflex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Trima` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_trima_free`. +#[no_mangle] +pub extern "C" fn wickra_trima_new(period: usize) -> *mut Trima { + match Trima::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_trima_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trima_update(handle: *mut Trima, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_trima_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_trima_batch( + handle: *mut Trima, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_trima_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trima_reset(handle: *mut Trima) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_trima_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_trima_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trima_free(handle: *mut Trima) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Trix` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_trix_free`. +#[no_mangle] +pub extern "C" fn wickra_trix_new(period: usize) -> *mut Trix { + match Trix::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_trix_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trix_update(handle: *mut Trix, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_trix_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_trix_batch( + handle: *mut Trix, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_trix_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trix_reset(handle: *mut Trix) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_trix_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_trix_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trix_free(handle: *mut Trix) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Tsf` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tsf_free`. +#[no_mangle] +pub extern "C" fn wickra_tsf_new(period: usize) -> *mut Tsf { + match Tsf::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_tsf_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsf_update(handle: *mut Tsf, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_tsf_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsf_batch( + handle: *mut Tsf, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tsf_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsf_reset(handle: *mut Tsf) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tsf_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tsf_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsf_free(handle: *mut Tsf) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TsfOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tsf_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_tsf_oscillator_new(period: usize) -> *mut TsfOscillator { + match TsfOscillator::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_tsf_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsf_oscillator_update( + handle: *mut TsfOscillator, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_tsf_oscillator_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsf_oscillator_batch( + handle: *mut TsfOscillator, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tsf_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsf_oscillator_reset(handle: *mut TsfOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tsf_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tsf_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsf_oscillator_free(handle: *mut TsfOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Tsi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tsi_free`. +#[no_mangle] +pub extern "C" fn wickra_tsi_new(long: usize, short: usize) -> *mut Tsi { + match Tsi::new(long, short) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_tsi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsi_update(handle: *mut Tsi, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_tsi_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsi_batch( + handle: *mut Tsi, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tsi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsi_reset(handle: *mut Tsi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tsi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tsi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsi_free(handle: *mut Tsi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `UlcerIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ulcer_index_free`. +#[no_mangle] +pub extern "C" fn wickra_ulcer_index_new(period: usize) -> *mut UlcerIndex { + match UlcerIndex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ulcer_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ulcer_index_update(handle: *mut UlcerIndex, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_ulcer_index_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_ulcer_index_batch( + handle: *mut UlcerIndex, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ulcer_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ulcer_index_reset(handle: *mut UlcerIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ulcer_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ulcer_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ulcer_index_free(handle: *mut UlcerIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `UniversalOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_universal_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_universal_oscillator_new(period: usize) -> *mut UniversalOscillator { + match UniversalOscillator::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_universal_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_universal_oscillator_update( + handle: *mut UniversalOscillator, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_universal_oscillator_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_universal_oscillator_batch( + handle: *mut UniversalOscillator, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_universal_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_universal_oscillator_reset(handle: *mut UniversalOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_universal_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_universal_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_universal_oscillator_free(handle: *mut UniversalOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `UpsidePotentialRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_upside_potential_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_upside_potential_ratio_new( + period: usize, + mar: f64, +) -> *mut UpsidePotentialRatio { + match UpsidePotentialRatio::new(period, mar) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_upside_potential_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_potential_ratio_update( + handle: *mut UpsidePotentialRatio, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_upside_potential_ratio_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_potential_ratio_batch( + handle: *mut UpsidePotentialRatio, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_upside_potential_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_potential_ratio_reset(handle: *mut UpsidePotentialRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_upside_potential_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_upside_potential_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_potential_ratio_free(handle: *mut UpsidePotentialRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ValueAtRisk` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_value_at_risk_free`. +#[no_mangle] +pub extern "C" fn wickra_value_at_risk_new(period: usize, confidence: f64) -> *mut ValueAtRisk { + match ValueAtRisk::new(period, confidence) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_value_at_risk_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_value_at_risk_update(handle: *mut ValueAtRisk, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_value_at_risk_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_value_at_risk_batch( + handle: *mut ValueAtRisk, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_value_at_risk_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_value_at_risk_reset(handle: *mut ValueAtRisk) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_value_at_risk_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_value_at_risk_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_value_at_risk_free(handle: *mut ValueAtRisk) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Variance` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_variance_free`. +#[no_mangle] +pub extern "C" fn wickra_variance_new(period: usize) -> *mut Variance { + match Variance::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_variance_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_variance_update(handle: *mut Variance, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_variance_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_variance_batch( + handle: *mut Variance, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_variance_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_variance_reset(handle: *mut Variance) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_variance_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_variance_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_variance_free(handle: *mut Variance) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VerticalHorizontalFilter` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_vertical_horizontal_filter_free`. +#[no_mangle] +pub extern "C" fn wickra_vertical_horizontal_filter_new( + period: usize, +) -> *mut VerticalHorizontalFilter { + match VerticalHorizontalFilter::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_vertical_horizontal_filter_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vertical_horizontal_filter_update( + handle: *mut VerticalHorizontalFilter, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_vertical_horizontal_filter_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_vertical_horizontal_filter_batch( + handle: *mut VerticalHorizontalFilter, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_vertical_horizontal_filter_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vertical_horizontal_filter_reset( + handle: *mut VerticalHorizontalFilter, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_vertical_horizontal_filter_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_vertical_horizontal_filter_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vertical_horizontal_filter_free( + handle: *mut VerticalHorizontalFilter, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Vidya` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_vidya_free`. +#[no_mangle] +pub extern "C" fn wickra_vidya_new(period: usize, cmo_period: usize) -> *mut Vidya { + match Vidya::new(period, cmo_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_vidya_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vidya_update(handle: *mut Vidya, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_vidya_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_vidya_batch( + handle: *mut Vidya, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_vidya_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vidya_reset(handle: *mut Vidya) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_vidya_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_vidya_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vidya_free(handle: *mut Vidya) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VolatilityOfVolatility` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volatility_of_volatility_free`. +#[no_mangle] +pub extern "C" fn wickra_volatility_of_volatility_new( + vol_window: usize, + vov_window: usize, +) -> *mut VolatilityOfVolatility { + match VolatilityOfVolatility::new(vol_window, vov_window) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_volatility_of_volatility_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volatility_of_volatility_update( + handle: *mut VolatilityOfVolatility, + value: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_volatility_of_volatility_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_volatility_of_volatility_batch( + handle: *mut VolatilityOfVolatility, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volatility_of_volatility_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volatility_of_volatility_reset( + handle: *mut VolatilityOfVolatility, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volatility_of_volatility_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volatility_of_volatility_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volatility_of_volatility_free(handle: *mut VolatilityOfVolatility) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `WavePm` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_wave_pm_free`. +#[no_mangle] +pub extern "C" fn wickra_wave_pm_new(length: usize, smoothing: usize) -> *mut WavePm { + match WavePm::new(length, smoothing) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_wave_pm_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wave_pm_update(handle: *mut WavePm, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_wave_pm_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_wave_pm_batch( + handle: *mut WavePm, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_wave_pm_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wave_pm_reset(handle: *mut WavePm) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_wave_pm_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_wave_pm_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wave_pm_free(handle: *mut WavePm) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `WinRate` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_win_rate_free`. +#[no_mangle] +pub extern "C" fn wickra_win_rate_new(period: usize) -> *mut WinRate { + match WinRate::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_win_rate_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_win_rate_update(handle: *mut WinRate, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_win_rate_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_win_rate_batch( + handle: *mut WinRate, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_win_rate_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_win_rate_reset(handle: *mut WinRate) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_win_rate_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_win_rate_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_win_rate_free(handle: *mut WinRate) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Wma` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_wma_free`. +#[no_mangle] +pub extern "C" fn wickra_wma_new(period: usize) -> *mut Wma { + match Wma::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_wma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wma_update(handle: *mut Wma, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_wma_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_wma_batch( + handle: *mut Wma, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_wma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wma_reset(handle: *mut Wma) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_wma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_wma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wma_free(handle: *mut Wma) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ZScore` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_z_score_free`. +#[no_mangle] +pub extern "C" fn wickra_z_score_new(period: usize) -> *mut ZScore { + match ZScore::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_z_score_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_z_score_update(handle: *mut ZScore, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_z_score_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_z_score_batch( + handle: *mut ZScore, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_z_score_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_z_score_reset(handle: *mut ZScore) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_z_score_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_z_score_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_z_score_free(handle: *mut ZScore) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Zlema` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_zlema_free`. +#[no_mangle] +pub extern "C" fn wickra_zlema_new(period: usize) -> *mut Zlema { + match Zlema::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_zlema_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_zlema_update(handle: *mut Zlema, value: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update(value).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_zlema_new`, not freed); `input`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_zlema_batch( + handle: *mut Zlema, + input: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || input.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let inputs = slice::from_raw_parts(input, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (slot, &value) in outputs.iter_mut().zip(inputs) { + *slot = ind.update(value).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_zlema_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_zlema_reset(handle: *mut Zlema) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_zlema_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_zlema_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_zlema_free(handle: *mut Zlema) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +// ===== Pairwise indicators ((f64, f64) -> f64) ===== + +/// Create a `Alpha` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_alpha_free`. +#[no_mangle] +pub extern "C" fn wickra_alpha_new(period: usize, risk_free: f64) -> *mut Alpha { + match Alpha::new(period, risk_free) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_alpha_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_alpha_update(handle: *mut Alpha, x: f64, y: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_alpha_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_alpha_batch( + handle: *mut Alpha, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_alpha_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_alpha_reset(handle: *mut Alpha) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_alpha_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_alpha_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_alpha_free(handle: *mut Alpha) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Beta` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_beta_free`. +#[no_mangle] +pub extern "C" fn wickra_beta_new(period: usize) -> *mut Beta { + match Beta::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_beta_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_beta_update(handle: *mut Beta, x: f64, y: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_beta_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_beta_batch( + handle: *mut Beta, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_beta_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_beta_reset(handle: *mut Beta) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_beta_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_beta_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_beta_free(handle: *mut Beta) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BetaNeutralSpread` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_beta_neutral_spread_free`. +#[no_mangle] +pub extern "C" fn wickra_beta_neutral_spread_new(period: usize) -> *mut BetaNeutralSpread { + match BetaNeutralSpread::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_beta_neutral_spread_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_beta_neutral_spread_update( + handle: *mut BetaNeutralSpread, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_beta_neutral_spread_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_beta_neutral_spread_batch( + handle: *mut BetaNeutralSpread, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_beta_neutral_spread_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_beta_neutral_spread_reset(handle: *mut BetaNeutralSpread) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_beta_neutral_spread_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_beta_neutral_spread_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_beta_neutral_spread_free(handle: *mut BetaNeutralSpread) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DistanceSsd` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_distance_ssd_free`. +#[no_mangle] +pub extern "C" fn wickra_distance_ssd_new(period: usize) -> *mut DistanceSsd { + match DistanceSsd::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_distance_ssd_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_distance_ssd_update( + handle: *mut DistanceSsd, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_distance_ssd_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_distance_ssd_batch( + handle: *mut DistanceSsd, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_distance_ssd_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_distance_ssd_reset(handle: *mut DistanceSsd) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_distance_ssd_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_distance_ssd_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_distance_ssd_free(handle: *mut DistanceSsd) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `GrangerCausality` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_granger_causality_free`. +#[no_mangle] +pub extern "C" fn wickra_granger_causality_new(period: usize, lag: usize) -> *mut GrangerCausality { + match GrangerCausality::new(period, lag) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_granger_causality_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_granger_causality_update( + handle: *mut GrangerCausality, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_granger_causality_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_granger_causality_batch( + handle: *mut GrangerCausality, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_granger_causality_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_granger_causality_reset(handle: *mut GrangerCausality) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_granger_causality_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_granger_causality_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_granger_causality_free(handle: *mut GrangerCausality) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HasbrouckInformationShare` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_hasbrouck_information_share_free`. +#[no_mangle] +pub extern "C" fn wickra_hasbrouck_information_share_new( + period: usize, +) -> *mut HasbrouckInformationShare { + match HasbrouckInformationShare::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_hasbrouck_information_share_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hasbrouck_information_share_update( + handle: *mut HasbrouckInformationShare, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_hasbrouck_information_share_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_hasbrouck_information_share_batch( + handle: *mut HasbrouckInformationShare, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_hasbrouck_information_share_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hasbrouck_information_share_reset( + handle: *mut HasbrouckInformationShare, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_hasbrouck_information_share_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_hasbrouck_information_share_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hasbrouck_information_share_free( + handle: *mut HasbrouckInformationShare, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `InformationRatio` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_information_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_information_ratio_new(period: usize) -> *mut InformationRatio { + match InformationRatio::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_information_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_information_ratio_update( + handle: *mut InformationRatio, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_information_ratio_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_information_ratio_batch( + handle: *mut InformationRatio, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_information_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_information_ratio_reset(handle: *mut InformationRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_information_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_information_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_information_ratio_free(handle: *mut InformationRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `KendallTau` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kendall_tau_free`. +#[no_mangle] +pub extern "C" fn wickra_kendall_tau_new(period: usize) -> *mut KendallTau { + match KendallTau::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_kendall_tau_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kendall_tau_update(handle: *mut KendallTau, x: f64, y: f64) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_kendall_tau_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_kendall_tau_batch( + handle: *mut KendallTau, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kendall_tau_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kendall_tau_reset(handle: *mut KendallTau) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kendall_tau_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kendall_tau_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kendall_tau_free(handle: *mut KendallTau) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OuHalfLife` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ou_half_life_free`. +#[no_mangle] +pub extern "C" fn wickra_ou_half_life_new(period: usize) -> *mut OuHalfLife { + match OuHalfLife::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ou_half_life_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ou_half_life_update( + handle: *mut OuHalfLife, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_ou_half_life_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_ou_half_life_batch( + handle: *mut OuHalfLife, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ou_half_life_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ou_half_life_reset(handle: *mut OuHalfLife) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ou_half_life_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ou_half_life_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ou_half_life_free(handle: *mut OuHalfLife) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PairSpreadZScore` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_pair_spread_z_score_free`. +#[no_mangle] +pub extern "C" fn wickra_pair_spread_z_score_new( + beta_period: usize, + z_period: usize, +) -> *mut PairSpreadZScore { + match PairSpreadZScore::new(beta_period, z_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_pair_spread_z_score_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pair_spread_z_score_update( + handle: *mut PairSpreadZScore, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_pair_spread_z_score_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_pair_spread_z_score_batch( + handle: *mut PairSpreadZScore, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_pair_spread_z_score_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pair_spread_z_score_reset(handle: *mut PairSpreadZScore) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_pair_spread_z_score_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_pair_spread_z_score_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pair_spread_z_score_free(handle: *mut PairSpreadZScore) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PairwiseBeta` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_pairwise_beta_free`. +#[no_mangle] +pub extern "C" fn wickra_pairwise_beta_new(period: usize) -> *mut PairwiseBeta { + match PairwiseBeta::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_pairwise_beta_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pairwise_beta_update( + handle: *mut PairwiseBeta, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_pairwise_beta_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_pairwise_beta_batch( + handle: *mut PairwiseBeta, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_pairwise_beta_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pairwise_beta_reset(handle: *mut PairwiseBeta) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_pairwise_beta_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_pairwise_beta_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pairwise_beta_free(handle: *mut PairwiseBeta) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PearsonCorrelation` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_pearson_correlation_free`. +#[no_mangle] +pub extern "C" fn wickra_pearson_correlation_new(period: usize) -> *mut PearsonCorrelation { + match PearsonCorrelation::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_pearson_correlation_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pearson_correlation_update( + handle: *mut PearsonCorrelation, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_pearson_correlation_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_pearson_correlation_batch( + handle: *mut PearsonCorrelation, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_pearson_correlation_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pearson_correlation_reset(handle: *mut PearsonCorrelation) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_pearson_correlation_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_pearson_correlation_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pearson_correlation_free(handle: *mut PearsonCorrelation) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RollingCorrelation` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rolling_correlation_free`. +#[no_mangle] +pub extern "C" fn wickra_rolling_correlation_new(period: usize) -> *mut RollingCorrelation { + match RollingCorrelation::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rolling_correlation_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_correlation_update( + handle: *mut RollingCorrelation, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rolling_correlation_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_correlation_batch( + handle: *mut RollingCorrelation, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rolling_correlation_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_correlation_reset(handle: *mut RollingCorrelation) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rolling_correlation_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rolling_correlation_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_correlation_free(handle: *mut RollingCorrelation) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RollingCovariance` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rolling_covariance_free`. +#[no_mangle] +pub extern "C" fn wickra_rolling_covariance_new(period: usize) -> *mut RollingCovariance { + match RollingCovariance::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rolling_covariance_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_covariance_update( + handle: *mut RollingCovariance, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_rolling_covariance_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_covariance_batch( + handle: *mut RollingCovariance, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rolling_covariance_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_covariance_reset(handle: *mut RollingCovariance) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rolling_covariance_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rolling_covariance_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_covariance_free(handle: *mut RollingCovariance) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SpearmanCorrelation` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_spearman_correlation_free`. +#[no_mangle] +pub extern "C" fn wickra_spearman_correlation_new(period: usize) -> *mut SpearmanCorrelation { + match SpearmanCorrelation::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_spearman_correlation_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spearman_correlation_update( + handle: *mut SpearmanCorrelation, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_spearman_correlation_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_spearman_correlation_batch( + handle: *mut SpearmanCorrelation, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_spearman_correlation_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spearman_correlation_reset(handle: *mut SpearmanCorrelation) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_spearman_correlation_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_spearman_correlation_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spearman_correlation_free(handle: *mut SpearmanCorrelation) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SpreadAr1Coefficient` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_spread_ar1_coefficient_free`. +#[no_mangle] +pub extern "C" fn wickra_spread_ar1_coefficient_new(period: usize) -> *mut SpreadAr1Coefficient { + match SpreadAr1Coefficient::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_spread_ar1_coefficient_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spread_ar1_coefficient_update( + handle: *mut SpreadAr1Coefficient, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_spread_ar1_coefficient_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_spread_ar1_coefficient_batch( + handle: *mut SpreadAr1Coefficient, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_spread_ar1_coefficient_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spread_ar1_coefficient_reset(handle: *mut SpreadAr1Coefficient) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_spread_ar1_coefficient_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_spread_ar1_coefficient_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spread_ar1_coefficient_free(handle: *mut SpreadAr1Coefficient) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SpreadHurst` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_spread_hurst_free`. +#[no_mangle] +pub extern "C" fn wickra_spread_hurst_new(period: usize) -> *mut SpreadHurst { + match SpreadHurst::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_spread_hurst_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spread_hurst_update( + handle: *mut SpreadHurst, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_spread_hurst_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_spread_hurst_batch( + handle: *mut SpreadHurst, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_spread_hurst_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spread_hurst_reset(handle: *mut SpreadHurst) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_spread_hurst_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_spread_hurst_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spread_hurst_free(handle: *mut SpreadHurst) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TreynorRatio` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_treynor_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_treynor_ratio_new(period: usize, risk_free: f64) -> *mut TreynorRatio { + match TreynorRatio::new(period, risk_free) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_treynor_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_treynor_ratio_update( + handle: *mut TreynorRatio, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_treynor_ratio_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_treynor_ratio_batch( + handle: *mut TreynorRatio, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_treynor_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_treynor_ratio_reset(handle: *mut TreynorRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_treynor_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_treynor_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_treynor_ratio_free(handle: *mut TreynorRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VarianceRatio` pairwise indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_variance_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_variance_ratio_new(period: usize, q: usize) -> *mut VarianceRatio { + match VarianceRatio::new(period, q) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_variance_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_variance_ratio_update( + handle: *mut VarianceRatio, + x: f64, + y: f64, +) -> f64 { + match handle.as_mut() { + Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), + None => f64::NAN, + } +} + +/// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). +/// +/// # Safety +/// `handle` valid (from `wickra_variance_ratio_new`, not freed); `x`/`y`/`out` cover `n` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_variance_ratio_batch( + handle: *mut VarianceRatio, + x: *const f64, + y: *const f64, + out: *mut f64, + n: usize, +) { + if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { + return; + } + let ind = &mut *handle; + let xs = slice::from_raw_parts(x, n); + let ys = slice::from_raw_parts(y, n); + let outputs = slice::from_raw_parts_mut(out, n); + for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { + *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_variance_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_variance_ratio_reset(handle: *mut VarianceRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_variance_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_variance_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_variance_ratio_free(handle: *mut VarianceRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +// ===== Candle indicators (OHLCV -> f64; includes candlestick patterns) ===== + +/// Create a `AbandonedBaby` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_abandoned_baby_free`. +#[no_mangle] +pub extern "C" fn wickra_abandoned_baby_new() -> *mut AbandonedBaby { + Box::into_raw(Box::new(AbandonedBaby::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_abandoned_baby_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_abandoned_baby_update( + handle: *mut AbandonedBaby, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_abandoned_baby_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_abandoned_baby_batch( + handle: *mut AbandonedBaby, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_abandoned_baby_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_abandoned_baby_reset(handle: *mut AbandonedBaby) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_abandoned_baby_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_abandoned_baby_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_abandoned_baby_free(handle: *mut AbandonedBaby) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Abcd` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_abcd_free`. +#[no_mangle] +pub extern "C" fn wickra_abcd_new() -> *mut Abcd { + Box::into_raw(Box::new(Abcd::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_abcd_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_abcd_update( + handle: *mut Abcd, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_abcd_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_abcd_batch( + handle: *mut Abcd, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_abcd_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_abcd_reset(handle: *mut Abcd) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_abcd_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_abcd_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_abcd_free(handle: *mut Abcd) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AcceleratorOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_accelerator_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_accelerator_oscillator_new( + ao_fast: usize, + ao_slow: usize, + signal_period: usize, +) -> *mut AcceleratorOscillator { + match AcceleratorOscillator::new(ao_fast, ao_slow, signal_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_accelerator_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_accelerator_oscillator_update( + handle: *mut AcceleratorOscillator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_accelerator_oscillator_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_accelerator_oscillator_batch( + handle: *mut AcceleratorOscillator, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_accelerator_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_accelerator_oscillator_reset(handle: *mut AcceleratorOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_accelerator_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_accelerator_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_accelerator_oscillator_free(handle: *mut AcceleratorOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AdOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ad_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_ad_oscillator_new() -> *mut AdOscillator { + Box::into_raw(Box::new(AdOscillator::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ad_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ad_oscillator_update( + handle: *mut AdOscillator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_ad_oscillator_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_ad_oscillator_batch( + handle: *mut AdOscillator, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ad_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ad_oscillator_reset(handle: *mut AdOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ad_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ad_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ad_oscillator_free(handle: *mut AdOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AdaptiveCci` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_adaptive_cci_free`. +#[no_mangle] +pub extern "C" fn wickra_adaptive_cci_new(period: usize) -> *mut AdaptiveCci { + match AdaptiveCci::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_adaptive_cci_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_cci_update( + handle: *mut AdaptiveCci, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_adaptive_cci_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_cci_batch( + handle: *mut AdaptiveCci, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_adaptive_cci_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_cci_reset(handle: *mut AdaptiveCci) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_adaptive_cci_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_adaptive_cci_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adaptive_cci_free(handle: *mut AdaptiveCci) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Adl` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_adl_free`. +#[no_mangle] +pub extern "C" fn wickra_adl_new() -> *mut Adl { + Box::into_raw(Box::new(Adl::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_adl_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adl_update( + handle: *mut Adl, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_adl_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_adl_batch( + handle: *mut Adl, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_adl_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adl_reset(handle: *mut Adl) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_adl_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_adl_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adl_free(handle: *mut Adl) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AdvanceBlock` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_advance_block_free`. +#[no_mangle] +pub extern "C" fn wickra_advance_block_new() -> *mut AdvanceBlock { + Box::into_raw(Box::new(AdvanceBlock::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_advance_block_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_advance_block_update( + handle: *mut AdvanceBlock, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_advance_block_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_advance_block_batch( + handle: *mut AdvanceBlock, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_advance_block_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_advance_block_reset(handle: *mut AdvanceBlock) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_advance_block_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_advance_block_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_advance_block_free(handle: *mut AdvanceBlock) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Adxr` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_adxr_free`. +#[no_mangle] +pub extern "C" fn wickra_adxr_new(period: usize) -> *mut Adxr { + match Adxr::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_adxr_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adxr_update( + handle: *mut Adxr, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_adxr_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_adxr_batch( + handle: *mut Adxr, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_adxr_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adxr_reset(handle: *mut Adxr) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_adxr_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_adxr_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adxr_free(handle: *mut Adxr) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AnchoredVwap` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_anchored_vwap_free`. +#[no_mangle] +pub extern "C" fn wickra_anchored_vwap_new() -> *mut AnchoredVwap { + Box::into_raw(Box::new(AnchoredVwap::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_anchored_vwap_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_anchored_vwap_update( + handle: *mut AnchoredVwap, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_anchored_vwap_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_anchored_vwap_batch( + handle: *mut AnchoredVwap, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_anchored_vwap_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_anchored_vwap_reset(handle: *mut AnchoredVwap) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_anchored_vwap_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_anchored_vwap_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_anchored_vwap_free(handle: *mut AnchoredVwap) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AroonOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_aroon_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_aroon_oscillator_new(period: usize) -> *mut AroonOscillator { + match AroonOscillator::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_aroon_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_aroon_oscillator_update( + handle: *mut AroonOscillator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_aroon_oscillator_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_aroon_oscillator_batch( + handle: *mut AroonOscillator, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_aroon_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_aroon_oscillator_reset(handle: *mut AroonOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_aroon_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_aroon_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_aroon_oscillator_free(handle: *mut AroonOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Atr` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_atr_free`. +#[no_mangle] +pub extern "C" fn wickra_atr_new(period: usize) -> *mut Atr { + match Atr::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_atr_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_update( + handle: *mut Atr, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_atr_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_batch( + handle: *mut Atr, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_atr_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_reset(handle: *mut Atr) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_atr_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_atr_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_free(handle: *mut Atr) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AtrTrailingStop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_atr_trailing_stop_free`. +#[no_mangle] +pub extern "C" fn wickra_atr_trailing_stop_new( + atr_period: usize, + multiplier: f64, +) -> *mut AtrTrailingStop { + match AtrTrailingStop::new(atr_period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_atr_trailing_stop_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_trailing_stop_update( + handle: *mut AtrTrailingStop, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_atr_trailing_stop_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_trailing_stop_batch( + handle: *mut AtrTrailingStop, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_atr_trailing_stop_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_trailing_stop_reset(handle: *mut AtrTrailingStop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_atr_trailing_stop_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_atr_trailing_stop_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_trailing_stop_free(handle: *mut AtrTrailingStop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AverageDailyRange` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_average_daily_range_free`. +#[no_mangle] +pub extern "C" fn wickra_average_daily_range_new( + period: usize, + utc_offset_minutes: i32, +) -> *mut AverageDailyRange { + match AverageDailyRange::new(period, utc_offset_minutes) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_average_daily_range_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_average_daily_range_update( + handle: *mut AverageDailyRange, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_average_daily_range_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_average_daily_range_batch( + handle: *mut AverageDailyRange, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_average_daily_range_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_average_daily_range_reset(handle: *mut AverageDailyRange) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_average_daily_range_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_average_daily_range_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_average_daily_range_free(handle: *mut AverageDailyRange) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AvgPrice` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_avg_price_free`. +#[no_mangle] +pub extern "C" fn wickra_avg_price_new() -> *mut AvgPrice { + Box::into_raw(Box::new(AvgPrice::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_avg_price_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_avg_price_update( + handle: *mut AvgPrice, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_avg_price_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_avg_price_batch( + handle: *mut AvgPrice, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_avg_price_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_avg_price_reset(handle: *mut AvgPrice) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_avg_price_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_avg_price_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_avg_price_free(handle: *mut AvgPrice) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AwesomeOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_awesome_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_awesome_oscillator_new( + fast: usize, + slow: usize, +) -> *mut AwesomeOscillator { + match AwesomeOscillator::new(fast, slow) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_awesome_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_awesome_oscillator_update( + handle: *mut AwesomeOscillator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_awesome_oscillator_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_awesome_oscillator_batch( + handle: *mut AwesomeOscillator, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_awesome_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_awesome_oscillator_reset(handle: *mut AwesomeOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_awesome_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_awesome_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_awesome_oscillator_free(handle: *mut AwesomeOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AwesomeOscillatorHistogram` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_awesome_oscillator_histogram_free`. +#[no_mangle] +pub extern "C" fn wickra_awesome_oscillator_histogram_new( + fast: usize, + slow: usize, + sma_period: usize, +) -> *mut AwesomeOscillatorHistogram { + match AwesomeOscillatorHistogram::new(fast, slow, sma_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_awesome_oscillator_histogram_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_awesome_oscillator_histogram_update( + handle: *mut AwesomeOscillatorHistogram, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_awesome_oscillator_histogram_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_awesome_oscillator_histogram_batch( + handle: *mut AwesomeOscillatorHistogram, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_awesome_oscillator_histogram_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_awesome_oscillator_histogram_reset( + handle: *mut AwesomeOscillatorHistogram, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_awesome_oscillator_histogram_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_awesome_oscillator_histogram_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_awesome_oscillator_histogram_free( + handle: *mut AwesomeOscillatorHistogram, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BalanceOfPower` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_balance_of_power_free`. +#[no_mangle] +pub extern "C" fn wickra_balance_of_power_new() -> *mut BalanceOfPower { + Box::into_raw(Box::new(BalanceOfPower::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_balance_of_power_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_balance_of_power_update( + handle: *mut BalanceOfPower, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_balance_of_power_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_balance_of_power_batch( + handle: *mut BalanceOfPower, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_balance_of_power_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_balance_of_power_reset(handle: *mut BalanceOfPower) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_balance_of_power_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_balance_of_power_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_balance_of_power_free(handle: *mut BalanceOfPower) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Bat` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_bat_free`. +#[no_mangle] +pub extern "C" fn wickra_bat_new() -> *mut Bat { + Box::into_raw(Box::new(Bat::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_bat_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bat_update( + handle: *mut Bat, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_bat_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_bat_batch( + handle: *mut Bat, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_bat_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bat_reset(handle: *mut Bat) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_bat_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_bat_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bat_free(handle: *mut Bat) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BeltHold` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_belt_hold_free`. +#[no_mangle] +pub extern "C" fn wickra_belt_hold_new() -> *mut BeltHold { + Box::into_raw(Box::new(BeltHold::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_belt_hold_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_belt_hold_update( + handle: *mut BeltHold, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_belt_hold_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_belt_hold_batch( + handle: *mut BeltHold, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_belt_hold_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_belt_hold_reset(handle: *mut BeltHold) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_belt_hold_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_belt_hold_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_belt_hold_free(handle: *mut BeltHold) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BetterVolume` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_better_volume_free`. +#[no_mangle] +pub extern "C" fn wickra_better_volume_new(period: usize) -> *mut BetterVolume { + match BetterVolume::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_better_volume_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_better_volume_update( + handle: *mut BetterVolume, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_better_volume_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_better_volume_batch( + handle: *mut BetterVolume, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_better_volume_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_better_volume_reset(handle: *mut BetterVolume) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_better_volume_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_better_volume_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_better_volume_free(handle: *mut BetterVolume) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BodySizePct` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_body_size_pct_free`. +#[no_mangle] +pub extern "C" fn wickra_body_size_pct_new() -> *mut BodySizePct { + Box::into_raw(Box::new(BodySizePct::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_body_size_pct_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_body_size_pct_update( + handle: *mut BodySizePct, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_body_size_pct_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_body_size_pct_batch( + handle: *mut BodySizePct, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_body_size_pct_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_body_size_pct_reset(handle: *mut BodySizePct) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_body_size_pct_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_body_size_pct_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_body_size_pct_free(handle: *mut BodySizePct) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Breakaway` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_breakaway_free`. +#[no_mangle] +pub extern "C" fn wickra_breakaway_new() -> *mut Breakaway { + Box::into_raw(Box::new(Breakaway::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_breakaway_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_breakaway_update( + handle: *mut Breakaway, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_breakaway_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_breakaway_batch( + handle: *mut Breakaway, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_breakaway_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_breakaway_reset(handle: *mut Breakaway) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_breakaway_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_breakaway_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_breakaway_free(handle: *mut Breakaway) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Butterfly` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_butterfly_free`. +#[no_mangle] +pub extern "C" fn wickra_butterfly_new() -> *mut Butterfly { + Box::into_raw(Box::new(Butterfly::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_butterfly_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_butterfly_update( + handle: *mut Butterfly, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_butterfly_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_butterfly_batch( + handle: *mut Butterfly, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_butterfly_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_butterfly_reset(handle: *mut Butterfly) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_butterfly_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_butterfly_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_butterfly_free(handle: *mut Butterfly) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Cci` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_cci_free`. +#[no_mangle] +pub extern "C" fn wickra_cci_new(period: usize) -> *mut Cci { + match Cci::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_cci_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cci_update( + handle: *mut Cci, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_cci_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_cci_batch( + handle: *mut Cci, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_cci_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cci_reset(handle: *mut Cci) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_cci_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_cci_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cci_free(handle: *mut Cci) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ChaikinOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_chaikin_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_chaikin_oscillator_new( + fast: usize, + slow: usize, +) -> *mut ChaikinOscillator { + match ChaikinOscillator::new(fast, slow) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_chaikin_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_oscillator_update( + handle: *mut ChaikinOscillator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_chaikin_oscillator_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_oscillator_batch( + handle: *mut ChaikinOscillator, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_chaikin_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_oscillator_reset(handle: *mut ChaikinOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_chaikin_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_chaikin_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_oscillator_free(handle: *mut ChaikinOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ChaikinVolatility` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_chaikin_volatility_free`. +#[no_mangle] +pub extern "C" fn wickra_chaikin_volatility_new( + ema_period: usize, + roc_period: usize, +) -> *mut ChaikinVolatility { + match ChaikinVolatility::new(ema_period, roc_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_chaikin_volatility_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_volatility_update( + handle: *mut ChaikinVolatility, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_chaikin_volatility_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_volatility_batch( + handle: *mut ChaikinVolatility, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_chaikin_volatility_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_volatility_reset(handle: *mut ChaikinVolatility) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_chaikin_volatility_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_chaikin_volatility_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_volatility_free(handle: *mut ChaikinVolatility) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ChoppinessIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_choppiness_index_free`. +#[no_mangle] +pub extern "C" fn wickra_choppiness_index_new(period: usize) -> *mut ChoppinessIndex { + match ChoppinessIndex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_choppiness_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_choppiness_index_update( + handle: *mut ChoppinessIndex, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_choppiness_index_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_choppiness_index_batch( + handle: *mut ChoppinessIndex, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_choppiness_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_choppiness_index_reset(handle: *mut ChoppinessIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_choppiness_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_choppiness_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_choppiness_index_free(handle: *mut ChoppinessIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CloseVsOpen` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_close_vs_open_free`. +#[no_mangle] +pub extern "C" fn wickra_close_vs_open_new() -> *mut CloseVsOpen { + Box::into_raw(Box::new(CloseVsOpen::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_close_vs_open_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_close_vs_open_update( + handle: *mut CloseVsOpen, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_close_vs_open_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_close_vs_open_batch( + handle: *mut CloseVsOpen, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_close_vs_open_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_close_vs_open_reset(handle: *mut CloseVsOpen) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_close_vs_open_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_close_vs_open_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_close_vs_open_free(handle: *mut CloseVsOpen) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ClosingMarubozu` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_closing_marubozu_free`. +#[no_mangle] +pub extern "C" fn wickra_closing_marubozu_new() -> *mut ClosingMarubozu { + Box::into_raw(Box::new(ClosingMarubozu::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_closing_marubozu_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_closing_marubozu_update( + handle: *mut ClosingMarubozu, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_closing_marubozu_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_closing_marubozu_batch( + handle: *mut ClosingMarubozu, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_closing_marubozu_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_closing_marubozu_reset(handle: *mut ClosingMarubozu) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_closing_marubozu_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_closing_marubozu_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_closing_marubozu_free(handle: *mut ClosingMarubozu) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ChaikinMoneyFlow` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_chaikin_money_flow_free`. +#[no_mangle] +pub extern "C" fn wickra_chaikin_money_flow_new(period: usize) -> *mut ChaikinMoneyFlow { + match ChaikinMoneyFlow::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_chaikin_money_flow_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_money_flow_update( + handle: *mut ChaikinMoneyFlow, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_chaikin_money_flow_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_money_flow_batch( + handle: *mut ChaikinMoneyFlow, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_chaikin_money_flow_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_money_flow_reset(handle: *mut ChaikinMoneyFlow) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_chaikin_money_flow_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_chaikin_money_flow_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chaikin_money_flow_free(handle: *mut ChaikinMoneyFlow) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ConcealingBabySwallow` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_concealing_baby_swallow_free`. +#[no_mangle] +pub extern "C" fn wickra_concealing_baby_swallow_new() -> *mut ConcealingBabySwallow { + Box::into_raw(Box::new(ConcealingBabySwallow::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_concealing_baby_swallow_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_concealing_baby_swallow_update( + handle: *mut ConcealingBabySwallow, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_concealing_baby_swallow_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_concealing_baby_swallow_batch( + handle: *mut ConcealingBabySwallow, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_concealing_baby_swallow_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_concealing_baby_swallow_reset(handle: *mut ConcealingBabySwallow) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_concealing_baby_swallow_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_concealing_baby_swallow_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_concealing_baby_swallow_free(handle: *mut ConcealingBabySwallow) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Counterattack` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_counterattack_free`. +#[no_mangle] +pub extern "C" fn wickra_counterattack_new() -> *mut Counterattack { + Box::into_raw(Box::new(Counterattack::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_counterattack_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_counterattack_update( + handle: *mut Counterattack, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_counterattack_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_counterattack_batch( + handle: *mut Counterattack, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_counterattack_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_counterattack_reset(handle: *mut Counterattack) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_counterattack_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_counterattack_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_counterattack_free(handle: *mut Counterattack) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Crab` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_crab_free`. +#[no_mangle] +pub extern "C" fn wickra_crab_new() -> *mut Crab { + Box::into_raw(Box::new(Crab::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_crab_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_crab_update( + handle: *mut Crab, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_crab_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_crab_batch( + handle: *mut Crab, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_crab_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_crab_reset(handle: *mut Crab) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_crab_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_crab_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_crab_free(handle: *mut Crab) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CupAndHandle` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_cup_and_handle_free`. +#[no_mangle] +pub extern "C" fn wickra_cup_and_handle_new() -> *mut CupAndHandle { + Box::into_raw(Box::new(CupAndHandle::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_cup_and_handle_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cup_and_handle_update( + handle: *mut CupAndHandle, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_cup_and_handle_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_cup_and_handle_batch( + handle: *mut CupAndHandle, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_cup_and_handle_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cup_and_handle_reset(handle: *mut CupAndHandle) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_cup_and_handle_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_cup_and_handle_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cup_and_handle_free(handle: *mut CupAndHandle) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Cypher` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_cypher_free`. +#[no_mangle] +pub extern "C" fn wickra_cypher_new() -> *mut Cypher { + Box::into_raw(Box::new(Cypher::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_cypher_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cypher_update( + handle: *mut Cypher, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_cypher_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_cypher_batch( + handle: *mut Cypher, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_cypher_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cypher_reset(handle: *mut Cypher) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_cypher_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_cypher_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cypher_free(handle: *mut Cypher) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DemandIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_demand_index_free`. +#[no_mangle] +pub extern "C" fn wickra_demand_index_new(period: usize) -> *mut DemandIndex { + match DemandIndex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_demand_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_demand_index_update( + handle: *mut DemandIndex, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_demand_index_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_demand_index_batch( + handle: *mut DemandIndex, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_demand_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_demand_index_reset(handle: *mut DemandIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_demand_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_demand_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_demand_index_free(handle: *mut DemandIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Doji` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_doji_free`. +#[no_mangle] +pub extern "C" fn wickra_doji_new() -> *mut Doji { + Box::into_raw(Box::new(Doji::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_doji_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_doji_update( + handle: *mut Doji, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_doji_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_doji_batch( + handle: *mut Doji, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_doji_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_doji_reset(handle: *mut Doji) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_doji_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_doji_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_doji_free(handle: *mut Doji) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DojiStar` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_doji_star_free`. +#[no_mangle] +pub extern "C" fn wickra_doji_star_new() -> *mut DojiStar { + Box::into_raw(Box::new(DojiStar::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_doji_star_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_doji_star_update( + handle: *mut DojiStar, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_doji_star_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_doji_star_batch( + handle: *mut DojiStar, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_doji_star_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_doji_star_reset(handle: *mut DojiStar) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_doji_star_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_doji_star_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_doji_star_free(handle: *mut DojiStar) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DoubleTopBottom` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_double_top_bottom_free`. +#[no_mangle] +pub extern "C" fn wickra_double_top_bottom_new() -> *mut DoubleTopBottom { + Box::into_raw(Box::new(DoubleTopBottom::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_double_top_bottom_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_double_top_bottom_update( + handle: *mut DoubleTopBottom, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_double_top_bottom_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_double_top_bottom_batch( + handle: *mut DoubleTopBottom, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_double_top_bottom_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_double_top_bottom_reset(handle: *mut DoubleTopBottom) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_double_top_bottom_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_double_top_bottom_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_double_top_bottom_free(handle: *mut DoubleTopBottom) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DownsideGapThreeMethods` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_downside_gap_three_methods_free`. +#[no_mangle] +pub extern "C" fn wickra_downside_gap_three_methods_new() -> *mut DownsideGapThreeMethods { + Box::into_raw(Box::new(DownsideGapThreeMethods::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_downside_gap_three_methods_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_downside_gap_three_methods_update( + handle: *mut DownsideGapThreeMethods, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_downside_gap_three_methods_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_downside_gap_three_methods_batch( + handle: *mut DownsideGapThreeMethods, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_downside_gap_three_methods_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_downside_gap_three_methods_reset( + handle: *mut DownsideGapThreeMethods, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_downside_gap_three_methods_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_downside_gap_three_methods_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_downside_gap_three_methods_free( + handle: *mut DownsideGapThreeMethods, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DragonflyDoji` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_dragonfly_doji_free`. +#[no_mangle] +pub extern "C" fn wickra_dragonfly_doji_new() -> *mut DragonflyDoji { + Box::into_raw(Box::new(DragonflyDoji::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_dragonfly_doji_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dragonfly_doji_update( + handle: *mut DragonflyDoji, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_dragonfly_doji_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_dragonfly_doji_batch( + handle: *mut DragonflyDoji, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_dragonfly_doji_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dragonfly_doji_reset(handle: *mut DragonflyDoji) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_dragonfly_doji_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_dragonfly_doji_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dragonfly_doji_free(handle: *mut DragonflyDoji) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DumplingTop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_dumpling_top_free`. +#[no_mangle] +pub extern "C" fn wickra_dumpling_top_new(period: usize) -> *mut DumplingTop { + match DumplingTop::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_dumpling_top_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dumpling_top_update( + handle: *mut DumplingTop, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_dumpling_top_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_dumpling_top_batch( + handle: *mut DumplingTop, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_dumpling_top_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dumpling_top_reset(handle: *mut DumplingTop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_dumpling_top_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_dumpling_top_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dumpling_top_free(handle: *mut DumplingTop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Dx` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_dx_free`. +#[no_mangle] +pub extern "C" fn wickra_dx_new(period: usize) -> *mut Dx { + match Dx::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_dx_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dx_update( + handle: *mut Dx, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_dx_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_dx_batch( + handle: *mut Dx, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_dx_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dx_reset(handle: *mut Dx) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_dx_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_dx_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dx_free(handle: *mut Dx) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `EaseOfMovement` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ease_of_movement_free`. +#[no_mangle] +pub extern "C" fn wickra_ease_of_movement_new(period: usize) -> *mut EaseOfMovement { + match EaseOfMovement::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ease_of_movement_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ease_of_movement_update( + handle: *mut EaseOfMovement, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_ease_of_movement_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_ease_of_movement_batch( + handle: *mut EaseOfMovement, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ease_of_movement_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ease_of_movement_reset(handle: *mut EaseOfMovement) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ease_of_movement_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ease_of_movement_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ease_of_movement_free(handle: *mut EaseOfMovement) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Engulfing` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_engulfing_free`. +#[no_mangle] +pub extern "C" fn wickra_engulfing_new() -> *mut Engulfing { + Box::into_raw(Box::new(Engulfing::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_engulfing_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_engulfing_update( + handle: *mut Engulfing, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_engulfing_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_engulfing_batch( + handle: *mut Engulfing, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_engulfing_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_engulfing_reset(handle: *mut Engulfing) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_engulfing_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_engulfing_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_engulfing_free(handle: *mut Engulfing) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `EveningDojiStar` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_evening_doji_star_free`. +#[no_mangle] +pub extern "C" fn wickra_evening_doji_star_new() -> *mut EveningDojiStar { + Box::into_raw(Box::new(EveningDojiStar::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_evening_doji_star_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_evening_doji_star_update( + handle: *mut EveningDojiStar, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_evening_doji_star_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_evening_doji_star_batch( + handle: *mut EveningDojiStar, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_evening_doji_star_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_evening_doji_star_reset(handle: *mut EveningDojiStar) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_evening_doji_star_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_evening_doji_star_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_evening_doji_star_free(handle: *mut EveningDojiStar) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Evwma` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_evwma_free`. +#[no_mangle] +pub extern "C" fn wickra_evwma_new(period: usize) -> *mut Evwma { + match Evwma::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_evwma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_evwma_update( + handle: *mut Evwma, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_evwma_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_evwma_batch( + handle: *mut Evwma, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_evwma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_evwma_reset(handle: *mut Evwma) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_evwma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_evwma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_evwma_free(handle: *mut Evwma) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FallingThreeMethods` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_falling_three_methods_free`. +#[no_mangle] +pub extern "C" fn wickra_falling_three_methods_new() -> *mut FallingThreeMethods { + Box::into_raw(Box::new(FallingThreeMethods::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_falling_three_methods_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_falling_three_methods_update( + handle: *mut FallingThreeMethods, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_falling_three_methods_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_falling_three_methods_batch( + handle: *mut FallingThreeMethods, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_falling_three_methods_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_falling_three_methods_reset(handle: *mut FallingThreeMethods) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_falling_three_methods_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_falling_three_methods_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_falling_three_methods_free(handle: *mut FallingThreeMethods) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FlagPennant` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_flag_pennant_free`. +#[no_mangle] +pub extern "C" fn wickra_flag_pennant_new() -> *mut FlagPennant { + Box::into_raw(Box::new(FlagPennant::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_flag_pennant_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_flag_pennant_update( + handle: *mut FlagPennant, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_flag_pennant_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_flag_pennant_batch( + handle: *mut FlagPennant, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_flag_pennant_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_flag_pennant_reset(handle: *mut FlagPennant) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_flag_pennant_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_flag_pennant_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_flag_pennant_free(handle: *mut FlagPennant) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ForceIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_force_index_free`. +#[no_mangle] +pub extern "C" fn wickra_force_index_new(period: usize) -> *mut ForceIndex { + match ForceIndex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_force_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_force_index_update( + handle: *mut ForceIndex, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_force_index_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_force_index_batch( + handle: *mut ForceIndex, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_force_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_force_index_reset(handle: *mut ForceIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_force_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_force_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_force_index_free(handle: *mut ForceIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FryPanBottom` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fry_pan_bottom_free`. +#[no_mangle] +pub extern "C" fn wickra_fry_pan_bottom_new(period: usize) -> *mut FryPanBottom { + match FryPanBottom::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_fry_pan_bottom_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fry_pan_bottom_update( + handle: *mut FryPanBottom, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_fry_pan_bottom_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_fry_pan_bottom_batch( + handle: *mut FryPanBottom, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fry_pan_bottom_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fry_pan_bottom_reset(handle: *mut FryPanBottom) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fry_pan_bottom_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fry_pan_bottom_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fry_pan_bottom_free(handle: *mut FryPanBottom) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `GapSideBySideWhite` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_gap_side_by_side_white_free`. +#[no_mangle] +pub extern "C" fn wickra_gap_side_by_side_white_new() -> *mut GapSideBySideWhite { + Box::into_raw(Box::new(GapSideBySideWhite::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_gap_side_by_side_white_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gap_side_by_side_white_update( + handle: *mut GapSideBySideWhite, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_gap_side_by_side_white_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_gap_side_by_side_white_batch( + handle: *mut GapSideBySideWhite, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_gap_side_by_side_white_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gap_side_by_side_white_reset(handle: *mut GapSideBySideWhite) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_gap_side_by_side_white_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_gap_side_by_side_white_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gap_side_by_side_white_free(handle: *mut GapSideBySideWhite) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `GarmanKlassVolatility` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_garman_klass_volatility_free`. +#[no_mangle] +pub extern "C" fn wickra_garman_klass_volatility_new( + period: usize, + trading_periods: usize, +) -> *mut GarmanKlassVolatility { + match GarmanKlassVolatility::new(period, trading_periods) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_garman_klass_volatility_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_garman_klass_volatility_update( + handle: *mut GarmanKlassVolatility, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_garman_klass_volatility_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_garman_klass_volatility_batch( + handle: *mut GarmanKlassVolatility, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_garman_klass_volatility_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_garman_klass_volatility_reset(handle: *mut GarmanKlassVolatility) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_garman_klass_volatility_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_garman_klass_volatility_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_garman_klass_volatility_free(handle: *mut GarmanKlassVolatility) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Gartley` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_gartley_free`. +#[no_mangle] +pub extern "C" fn wickra_gartley_new() -> *mut Gartley { + Box::into_raw(Box::new(Gartley::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_gartley_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gartley_update( + handle: *mut Gartley, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_gartley_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_gartley_batch( + handle: *mut Gartley, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_gartley_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gartley_reset(handle: *mut Gartley) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_gartley_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_gartley_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gartley_free(handle: *mut Gartley) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `GravestoneDoji` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_gravestone_doji_free`. +#[no_mangle] +pub extern "C" fn wickra_gravestone_doji_new() -> *mut GravestoneDoji { + Box::into_raw(Box::new(GravestoneDoji::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_gravestone_doji_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gravestone_doji_update( + handle: *mut GravestoneDoji, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_gravestone_doji_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_gravestone_doji_batch( + handle: *mut GravestoneDoji, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_gravestone_doji_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gravestone_doji_reset(handle: *mut GravestoneDoji) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_gravestone_doji_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_gravestone_doji_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gravestone_doji_free(handle: *mut GravestoneDoji) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Hammer` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_hammer_free`. +#[no_mangle] +pub extern "C" fn wickra_hammer_new() -> *mut Hammer { + Box::into_raw(Box::new(Hammer::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_hammer_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hammer_update( + handle: *mut Hammer, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_hammer_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_hammer_batch( + handle: *mut Hammer, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_hammer_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hammer_reset(handle: *mut Hammer) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_hammer_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_hammer_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hammer_free(handle: *mut Hammer) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HangingMan` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_hanging_man_free`. +#[no_mangle] +pub extern "C" fn wickra_hanging_man_new() -> *mut HangingMan { + Box::into_raw(Box::new(HangingMan::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_hanging_man_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hanging_man_update( + handle: *mut HangingMan, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_hanging_man_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_hanging_man_batch( + handle: *mut HangingMan, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_hanging_man_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hanging_man_reset(handle: *mut HangingMan) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_hanging_man_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_hanging_man_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hanging_man_free(handle: *mut HangingMan) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Harami` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_harami_free`. +#[no_mangle] +pub extern "C" fn wickra_harami_new() -> *mut Harami { + Box::into_raw(Box::new(Harami::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_harami_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_harami_update( + handle: *mut Harami, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_harami_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_harami_batch( + handle: *mut Harami, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_harami_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_harami_reset(handle: *mut Harami) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_harami_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_harami_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_harami_free(handle: *mut Harami) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HaramiCross` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_harami_cross_free`. +#[no_mangle] +pub extern "C" fn wickra_harami_cross_new() -> *mut HaramiCross { + Box::into_raw(Box::new(HaramiCross::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_harami_cross_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_harami_cross_update( + handle: *mut HaramiCross, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_harami_cross_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_harami_cross_batch( + handle: *mut HaramiCross, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_harami_cross_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_harami_cross_reset(handle: *mut HaramiCross) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_harami_cross_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_harami_cross_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_harami_cross_free(handle: *mut HaramiCross) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HeadAndShoulders` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_head_and_shoulders_free`. +#[no_mangle] +pub extern "C" fn wickra_head_and_shoulders_new() -> *mut HeadAndShoulders { + Box::into_raw(Box::new(HeadAndShoulders::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_head_and_shoulders_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_head_and_shoulders_update( + handle: *mut HeadAndShoulders, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_head_and_shoulders_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_head_and_shoulders_batch( + handle: *mut HeadAndShoulders, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_head_and_shoulders_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_head_and_shoulders_reset(handle: *mut HeadAndShoulders) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_head_and_shoulders_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_head_and_shoulders_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_head_and_shoulders_free(handle: *mut HeadAndShoulders) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HeikinAshiOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_heikin_ashi_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_heikin_ashi_oscillator_new(period: usize) -> *mut HeikinAshiOscillator { + match HeikinAshiOscillator::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_heikin_ashi_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_heikin_ashi_oscillator_update( + handle: *mut HeikinAshiOscillator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_heikin_ashi_oscillator_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_heikin_ashi_oscillator_batch( + handle: *mut HeikinAshiOscillator, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_heikin_ashi_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_heikin_ashi_oscillator_reset(handle: *mut HeikinAshiOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_heikin_ashi_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_heikin_ashi_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_heikin_ashi_oscillator_free(handle: *mut HeikinAshiOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HighLowRange` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_high_low_range_free`. +#[no_mangle] +pub extern "C" fn wickra_high_low_range_new() -> *mut HighLowRange { + Box::into_raw(Box::new(HighLowRange::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_high_low_range_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_low_range_update( + handle: *mut HighLowRange, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_high_low_range_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_low_range_batch( + handle: *mut HighLowRange, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_high_low_range_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_low_range_reset(handle: *mut HighLowRange) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_high_low_range_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_high_low_range_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_low_range_free(handle: *mut HighLowRange) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HighWave` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_high_wave_free`. +#[no_mangle] +pub extern "C" fn wickra_high_wave_new() -> *mut HighWave { + Box::into_raw(Box::new(HighWave::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_high_wave_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_wave_update( + handle: *mut HighWave, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_high_wave_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_wave_batch( + handle: *mut HighWave, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_high_wave_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_wave_reset(handle: *mut HighWave) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_high_wave_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_high_wave_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_wave_free(handle: *mut HighWave) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Hikkake` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_hikkake_free`. +#[no_mangle] +pub extern "C" fn wickra_hikkake_new() -> *mut Hikkake { + Box::into_raw(Box::new(Hikkake::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_hikkake_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hikkake_update( + handle: *mut Hikkake, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_hikkake_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_hikkake_batch( + handle: *mut Hikkake, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_hikkake_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hikkake_reset(handle: *mut Hikkake) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_hikkake_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_hikkake_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hikkake_free(handle: *mut Hikkake) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HikkakeModified` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_hikkake_modified_free`. +#[no_mangle] +pub extern "C" fn wickra_hikkake_modified_new() -> *mut HikkakeModified { + Box::into_raw(Box::new(HikkakeModified::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_hikkake_modified_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hikkake_modified_update( + handle: *mut HikkakeModified, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_hikkake_modified_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_hikkake_modified_batch( + handle: *mut HikkakeModified, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_hikkake_modified_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hikkake_modified_reset(handle: *mut HikkakeModified) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_hikkake_modified_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_hikkake_modified_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hikkake_modified_free(handle: *mut HikkakeModified) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HiLoActivator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_hi_lo_activator_free`. +#[no_mangle] +pub extern "C" fn wickra_hi_lo_activator_new(period: usize) -> *mut HiLoActivator { + match HiLoActivator::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_hi_lo_activator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hi_lo_activator_update( + handle: *mut HiLoActivator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_hi_lo_activator_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_hi_lo_activator_batch( + handle: *mut HiLoActivator, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_hi_lo_activator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hi_lo_activator_reset(handle: *mut HiLoActivator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_hi_lo_activator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_hi_lo_activator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hi_lo_activator_free(handle: *mut HiLoActivator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HomingPigeon` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_homing_pigeon_free`. +#[no_mangle] +pub extern "C" fn wickra_homing_pigeon_new() -> *mut HomingPigeon { + Box::into_raw(Box::new(HomingPigeon::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_homing_pigeon_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_homing_pigeon_update( + handle: *mut HomingPigeon, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_homing_pigeon_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_homing_pigeon_batch( + handle: *mut HomingPigeon, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_homing_pigeon_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_homing_pigeon_reset(handle: *mut HomingPigeon) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_homing_pigeon_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_homing_pigeon_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_homing_pigeon_free(handle: *mut HomingPigeon) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `IdenticalThreeCrows` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_identical_three_crows_free`. +#[no_mangle] +pub extern "C" fn wickra_identical_three_crows_new() -> *mut IdenticalThreeCrows { + Box::into_raw(Box::new(IdenticalThreeCrows::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_identical_three_crows_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_identical_three_crows_update( + handle: *mut IdenticalThreeCrows, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_identical_three_crows_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_identical_three_crows_batch( + handle: *mut IdenticalThreeCrows, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_identical_three_crows_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_identical_three_crows_reset(handle: *mut IdenticalThreeCrows) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_identical_three_crows_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_identical_three_crows_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_identical_three_crows_free(handle: *mut IdenticalThreeCrows) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `InNeck` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_in_neck_free`. +#[no_mangle] +pub extern "C" fn wickra_in_neck_new() -> *mut InNeck { + Box::into_raw(Box::new(InNeck::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_in_neck_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_in_neck_update( + handle: *mut InNeck, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_in_neck_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_in_neck_batch( + handle: *mut InNeck, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_in_neck_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_in_neck_reset(handle: *mut InNeck) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_in_neck_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_in_neck_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_in_neck_free(handle: *mut InNeck) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Inertia` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_inertia_free`. +#[no_mangle] +pub extern "C" fn wickra_inertia_new(rvi_period: usize, linreg_period: usize) -> *mut Inertia { + match Inertia::new(rvi_period, linreg_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_inertia_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_inertia_update( + handle: *mut Inertia, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_inertia_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_inertia_batch( + handle: *mut Inertia, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_inertia_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_inertia_reset(handle: *mut Inertia) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_inertia_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_inertia_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_inertia_free(handle: *mut Inertia) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `IntradayIntensity` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_intraday_intensity_free`. +#[no_mangle] +pub extern "C" fn wickra_intraday_intensity_new() -> *mut IntradayIntensity { + Box::into_raw(Box::new(IntradayIntensity::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_intraday_intensity_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_intraday_intensity_update( + handle: *mut IntradayIntensity, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_intraday_intensity_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_intraday_intensity_batch( + handle: *mut IntradayIntensity, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_intraday_intensity_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_intraday_intensity_reset(handle: *mut IntradayIntensity) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_intraday_intensity_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_intraday_intensity_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_intraday_intensity_free(handle: *mut IntradayIntensity) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `IntradayMomentumIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_intraday_momentum_index_free`. +#[no_mangle] +pub extern "C" fn wickra_intraday_momentum_index_new(period: usize) -> *mut IntradayMomentumIndex { + match IntradayMomentumIndex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_intraday_momentum_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_intraday_momentum_index_update( + handle: *mut IntradayMomentumIndex, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_intraday_momentum_index_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_intraday_momentum_index_batch( + handle: *mut IntradayMomentumIndex, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_intraday_momentum_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_intraday_momentum_index_reset(handle: *mut IntradayMomentumIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_intraday_momentum_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_intraday_momentum_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_intraday_momentum_index_free(handle: *mut IntradayMomentumIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `InvertedHammer` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_inverted_hammer_free`. +#[no_mangle] +pub extern "C" fn wickra_inverted_hammer_new() -> *mut InvertedHammer { + Box::into_raw(Box::new(InvertedHammer::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_inverted_hammer_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_inverted_hammer_update( + handle: *mut InvertedHammer, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_inverted_hammer_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_inverted_hammer_batch( + handle: *mut InvertedHammer, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_inverted_hammer_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_inverted_hammer_reset(handle: *mut InvertedHammer) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_inverted_hammer_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_inverted_hammer_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_inverted_hammer_free(handle: *mut InvertedHammer) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Kicking` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kicking_free`. +#[no_mangle] +pub extern "C" fn wickra_kicking_new() -> *mut Kicking { + Box::into_raw(Box::new(Kicking::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_kicking_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kicking_update( + handle: *mut Kicking, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_kicking_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_kicking_batch( + handle: *mut Kicking, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kicking_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kicking_reset(handle: *mut Kicking) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kicking_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kicking_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kicking_free(handle: *mut Kicking) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `KickingByLength` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kicking_by_length_free`. +#[no_mangle] +pub extern "C" fn wickra_kicking_by_length_new() -> *mut KickingByLength { + Box::into_raw(Box::new(KickingByLength::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_kicking_by_length_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kicking_by_length_update( + handle: *mut KickingByLength, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_kicking_by_length_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_kicking_by_length_batch( + handle: *mut KickingByLength, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kicking_by_length_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kicking_by_length_reset(handle: *mut KickingByLength) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kicking_by_length_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kicking_by_length_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kicking_by_length_free(handle: *mut KickingByLength) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Kvo` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kvo_free`. +#[no_mangle] +pub extern "C" fn wickra_kvo_new(fast: usize, slow: usize) -> *mut Kvo { + match Kvo::new(fast, slow) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_kvo_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kvo_update( + handle: *mut Kvo, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_kvo_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_kvo_batch( + handle: *mut Kvo, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kvo_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kvo_reset(handle: *mut Kvo) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kvo_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kvo_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kvo_free(handle: *mut Kvo) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LadderBottom` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ladder_bottom_free`. +#[no_mangle] +pub extern "C" fn wickra_ladder_bottom_new() -> *mut LadderBottom { + Box::into_raw(Box::new(LadderBottom::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ladder_bottom_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ladder_bottom_update( + handle: *mut LadderBottom, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_ladder_bottom_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_ladder_bottom_batch( + handle: *mut LadderBottom, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ladder_bottom_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ladder_bottom_reset(handle: *mut LadderBottom) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ladder_bottom_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ladder_bottom_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ladder_bottom_free(handle: *mut LadderBottom) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LongLeggedDoji` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_long_legged_doji_free`. +#[no_mangle] +pub extern "C" fn wickra_long_legged_doji_new() -> *mut LongLeggedDoji { + Box::into_raw(Box::new(LongLeggedDoji::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_long_legged_doji_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_long_legged_doji_update( + handle: *mut LongLeggedDoji, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_long_legged_doji_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_long_legged_doji_batch( + handle: *mut LongLeggedDoji, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_long_legged_doji_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_long_legged_doji_reset(handle: *mut LongLeggedDoji) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_long_legged_doji_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_long_legged_doji_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_long_legged_doji_free(handle: *mut LongLeggedDoji) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LongLine` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_long_line_free`. +#[no_mangle] +pub extern "C" fn wickra_long_line_new() -> *mut LongLine { + Box::into_raw(Box::new(LongLine::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_long_line_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_long_line_update( + handle: *mut LongLine, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_long_line_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_long_line_batch( + handle: *mut LongLine, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_long_line_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_long_line_reset(handle: *mut LongLine) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_long_line_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_long_line_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_long_line_free(handle: *mut LongLine) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MarketFacilitationIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_market_facilitation_index_free`. +#[no_mangle] +pub extern "C" fn wickra_market_facilitation_index_new() -> *mut MarketFacilitationIndex { + Box::into_raw(Box::new(MarketFacilitationIndex::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_market_facilitation_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_market_facilitation_index_update( + handle: *mut MarketFacilitationIndex, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_market_facilitation_index_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_market_facilitation_index_batch( + handle: *mut MarketFacilitationIndex, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_market_facilitation_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_market_facilitation_index_reset( + handle: *mut MarketFacilitationIndex, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_market_facilitation_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_market_facilitation_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_market_facilitation_index_free( + handle: *mut MarketFacilitationIndex, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Marubozu` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_marubozu_free`. +#[no_mangle] +pub extern "C" fn wickra_marubozu_new() -> *mut Marubozu { + Box::into_raw(Box::new(Marubozu::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_marubozu_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_marubozu_update( + handle: *mut Marubozu, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_marubozu_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_marubozu_batch( + handle: *mut Marubozu, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_marubozu_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_marubozu_reset(handle: *mut Marubozu) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_marubozu_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_marubozu_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_marubozu_free(handle: *mut Marubozu) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MassIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_mass_index_free`. +#[no_mangle] +pub extern "C" fn wickra_mass_index_new(ema_period: usize, sum_period: usize) -> *mut MassIndex { + match MassIndex::new(ema_period, sum_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_mass_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mass_index_update( + handle: *mut MassIndex, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_mass_index_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_mass_index_batch( + handle: *mut MassIndex, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_mass_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mass_index_reset(handle: *mut MassIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_mass_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_mass_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mass_index_free(handle: *mut MassIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MatHold` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_mat_hold_free`. +#[no_mangle] +pub extern "C" fn wickra_mat_hold_new() -> *mut MatHold { + Box::into_raw(Box::new(MatHold::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_mat_hold_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mat_hold_update( + handle: *mut MatHold, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_mat_hold_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_mat_hold_batch( + handle: *mut MatHold, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_mat_hold_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mat_hold_reset(handle: *mut MatHold) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_mat_hold_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_mat_hold_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mat_hold_free(handle: *mut MatHold) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MatchingLow` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_matching_low_free`. +#[no_mangle] +pub extern "C" fn wickra_matching_low_new() -> *mut MatchingLow { + Box::into_raw(Box::new(MatchingLow::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_matching_low_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_matching_low_update( + handle: *mut MatchingLow, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_matching_low_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_matching_low_batch( + handle: *mut MatchingLow, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_matching_low_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_matching_low_reset(handle: *mut MatchingLow) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_matching_low_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_matching_low_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_matching_low_free(handle: *mut MatchingLow) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MedianPrice` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_median_price_free`. +#[no_mangle] +pub extern "C" fn wickra_median_price_new() -> *mut MedianPrice { + Box::into_raw(Box::new(MedianPrice::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_median_price_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_price_update( + handle: *mut MedianPrice, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_median_price_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_price_batch( + handle: *mut MedianPrice, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_median_price_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_price_reset(handle: *mut MedianPrice) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_median_price_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_median_price_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_price_free(handle: *mut MedianPrice) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Mfi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_mfi_free`. +#[no_mangle] +pub extern "C" fn wickra_mfi_new(period: usize) -> *mut Mfi { + match Mfi::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_mfi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mfi_update( + handle: *mut Mfi, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_mfi_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_mfi_batch( + handle: *mut Mfi, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_mfi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mfi_reset(handle: *mut Mfi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_mfi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_mfi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mfi_free(handle: *mut Mfi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MidPrice` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_mid_price_free`. +#[no_mangle] +pub extern "C" fn wickra_mid_price_new(period: usize) -> *mut MidPrice { + match MidPrice::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_mid_price_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mid_price_update( + handle: *mut MidPrice, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_mid_price_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_mid_price_batch( + handle: *mut MidPrice, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_mid_price_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mid_price_reset(handle: *mut MidPrice) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_mid_price_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_mid_price_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mid_price_free(handle: *mut MidPrice) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MinusDi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_minus_di_free`. +#[no_mangle] +pub extern "C" fn wickra_minus_di_new(period: usize) -> *mut MinusDi { + match MinusDi::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_minus_di_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_minus_di_update( + handle: *mut MinusDi, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_minus_di_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_minus_di_batch( + handle: *mut MinusDi, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_minus_di_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_minus_di_reset(handle: *mut MinusDi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_minus_di_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_minus_di_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_minus_di_free(handle: *mut MinusDi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MinusDm` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_minus_dm_free`. +#[no_mangle] +pub extern "C" fn wickra_minus_dm_new(period: usize) -> *mut MinusDm { + match MinusDm::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_minus_dm_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_minus_dm_update( + handle: *mut MinusDm, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_minus_dm_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_minus_dm_batch( + handle: *mut MinusDm, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_minus_dm_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_minus_dm_reset(handle: *mut MinusDm) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_minus_dm_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_minus_dm_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_minus_dm_free(handle: *mut MinusDm) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MorningDojiStar` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_morning_doji_star_free`. +#[no_mangle] +pub extern "C" fn wickra_morning_doji_star_new() -> *mut MorningDojiStar { + Box::into_raw(Box::new(MorningDojiStar::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_morning_doji_star_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_morning_doji_star_update( + handle: *mut MorningDojiStar, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_morning_doji_star_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_morning_doji_star_batch( + handle: *mut MorningDojiStar, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_morning_doji_star_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_morning_doji_star_reset(handle: *mut MorningDojiStar) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_morning_doji_star_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_morning_doji_star_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_morning_doji_star_free(handle: *mut MorningDojiStar) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MorningEveningStar` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_morning_evening_star_free`. +#[no_mangle] +pub extern "C" fn wickra_morning_evening_star_new() -> *mut MorningEveningStar { + Box::into_raw(Box::new(MorningEveningStar::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_morning_evening_star_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_morning_evening_star_update( + handle: *mut MorningEveningStar, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_morning_evening_star_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_morning_evening_star_batch( + handle: *mut MorningEveningStar, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_morning_evening_star_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_morning_evening_star_reset(handle: *mut MorningEveningStar) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_morning_evening_star_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_morning_evening_star_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_morning_evening_star_free(handle: *mut MorningEveningStar) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `NakedPoc` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_naked_poc_free`. +#[no_mangle] +pub extern "C" fn wickra_naked_poc_new(session_len: usize, bins: usize) -> *mut NakedPoc { + match NakedPoc::new(session_len, bins) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_naked_poc_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_naked_poc_update( + handle: *mut NakedPoc, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_naked_poc_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_naked_poc_batch( + handle: *mut NakedPoc, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_naked_poc_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_naked_poc_reset(handle: *mut NakedPoc) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_naked_poc_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_naked_poc_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_naked_poc_free(handle: *mut NakedPoc) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Natr` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_natr_free`. +#[no_mangle] +pub extern "C" fn wickra_natr_new(period: usize) -> *mut Natr { + match Natr::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_natr_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_natr_update( + handle: *mut Natr, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_natr_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_natr_batch( + handle: *mut Natr, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_natr_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_natr_reset(handle: *mut Natr) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_natr_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_natr_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_natr_free(handle: *mut Natr) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `NewPriceLines` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_new_price_lines_free`. +#[no_mangle] +pub extern "C" fn wickra_new_price_lines_new(count: usize) -> *mut NewPriceLines { + match NewPriceLines::new(count) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_new_price_lines_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_new_price_lines_update( + handle: *mut NewPriceLines, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_new_price_lines_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_new_price_lines_batch( + handle: *mut NewPriceLines, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_new_price_lines_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_new_price_lines_reset(handle: *mut NewPriceLines) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_new_price_lines_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_new_price_lines_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_new_price_lines_free(handle: *mut NewPriceLines) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Nvi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_nvi_free`. +#[no_mangle] +pub extern "C" fn wickra_nvi_new() -> *mut Nvi { + Box::into_raw(Box::new(Nvi::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_nvi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_nvi_update( + handle: *mut Nvi, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_nvi_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_nvi_batch( + handle: *mut Nvi, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_nvi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_nvi_reset(handle: *mut Nvi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_nvi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_nvi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_nvi_free(handle: *mut Nvi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Obv` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_obv_free`. +#[no_mangle] +pub extern "C" fn wickra_obv_new() -> *mut Obv { + Box::into_raw(Box::new(Obv::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_obv_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_obv_update( + handle: *mut Obv, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_obv_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_obv_batch( + handle: *mut Obv, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_obv_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_obv_reset(handle: *mut Obv) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_obv_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_obv_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_obv_free(handle: *mut Obv) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OnNeck` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_on_neck_free`. +#[no_mangle] +pub extern "C" fn wickra_on_neck_new() -> *mut OnNeck { + Box::into_raw(Box::new(OnNeck::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_on_neck_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_on_neck_update( + handle: *mut OnNeck, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_on_neck_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_on_neck_batch( + handle: *mut OnNeck, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_on_neck_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_on_neck_reset(handle: *mut OnNeck) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_on_neck_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_on_neck_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_on_neck_free(handle: *mut OnNeck) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OpeningMarubozu` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_opening_marubozu_free`. +#[no_mangle] +pub extern "C" fn wickra_opening_marubozu_new() -> *mut OpeningMarubozu { + Box::into_raw(Box::new(OpeningMarubozu::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_opening_marubozu_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_opening_marubozu_update( + handle: *mut OpeningMarubozu, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_opening_marubozu_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_opening_marubozu_batch( + handle: *mut OpeningMarubozu, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_opening_marubozu_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_opening_marubozu_reset(handle: *mut OpeningMarubozu) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_opening_marubozu_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_opening_marubozu_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_opening_marubozu_free(handle: *mut OpeningMarubozu) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OvernightGap` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_overnight_gap_free`. +#[no_mangle] +pub extern "C" fn wickra_overnight_gap_new(utc_offset_minutes: i32) -> *mut OvernightGap { + Box::into_raw(Box::new(OvernightGap::new(utc_offset_minutes))) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_overnight_gap_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_overnight_gap_update( + handle: *mut OvernightGap, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_overnight_gap_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_overnight_gap_batch( + handle: *mut OvernightGap, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_overnight_gap_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_overnight_gap_reset(handle: *mut OvernightGap) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_overnight_gap_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_overnight_gap_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_overnight_gap_free(handle: *mut OvernightGap) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ParkinsonVolatility` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_parkinson_volatility_free`. +#[no_mangle] +pub extern "C" fn wickra_parkinson_volatility_new( + period: usize, + trading_periods: usize, +) -> *mut ParkinsonVolatility { + match ParkinsonVolatility::new(period, trading_periods) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_parkinson_volatility_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_parkinson_volatility_update( + handle: *mut ParkinsonVolatility, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_parkinson_volatility_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_parkinson_volatility_batch( + handle: *mut ParkinsonVolatility, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_parkinson_volatility_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_parkinson_volatility_reset(handle: *mut ParkinsonVolatility) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_parkinson_volatility_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_parkinson_volatility_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_parkinson_volatility_free(handle: *mut ParkinsonVolatility) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Pgo` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_pgo_free`. +#[no_mangle] +pub extern "C" fn wickra_pgo_new(period: usize) -> *mut Pgo { + match Pgo::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_pgo_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pgo_update( + handle: *mut Pgo, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_pgo_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_pgo_batch( + handle: *mut Pgo, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_pgo_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pgo_reset(handle: *mut Pgo) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_pgo_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_pgo_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pgo_free(handle: *mut Pgo) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PiercingDarkCloud` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_piercing_dark_cloud_free`. +#[no_mangle] +pub extern "C" fn wickra_piercing_dark_cloud_new() -> *mut PiercingDarkCloud { + Box::into_raw(Box::new(PiercingDarkCloud::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_piercing_dark_cloud_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_piercing_dark_cloud_update( + handle: *mut PiercingDarkCloud, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_piercing_dark_cloud_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_piercing_dark_cloud_batch( + handle: *mut PiercingDarkCloud, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_piercing_dark_cloud_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_piercing_dark_cloud_reset(handle: *mut PiercingDarkCloud) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_piercing_dark_cloud_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_piercing_dark_cloud_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_piercing_dark_cloud_free(handle: *mut PiercingDarkCloud) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PivotReversal` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_pivot_reversal_free`. +#[no_mangle] +pub extern "C" fn wickra_pivot_reversal_new(left: usize, right: usize) -> *mut PivotReversal { + match PivotReversal::new(left, right) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_pivot_reversal_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pivot_reversal_update( + handle: *mut PivotReversal, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_pivot_reversal_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_pivot_reversal_batch( + handle: *mut PivotReversal, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_pivot_reversal_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pivot_reversal_reset(handle: *mut PivotReversal) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_pivot_reversal_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_pivot_reversal_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pivot_reversal_free(handle: *mut PivotReversal) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PlusDi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_plus_di_free`. +#[no_mangle] +pub extern "C" fn wickra_plus_di_new(period: usize) -> *mut PlusDi { + match PlusDi::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_plus_di_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_plus_di_update( + handle: *mut PlusDi, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_plus_di_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_plus_di_batch( + handle: *mut PlusDi, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_plus_di_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_plus_di_reset(handle: *mut PlusDi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_plus_di_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_plus_di_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_plus_di_free(handle: *mut PlusDi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PlusDm` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_plus_dm_free`. +#[no_mangle] +pub extern "C" fn wickra_plus_dm_new(period: usize) -> *mut PlusDm { + match PlusDm::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_plus_dm_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_plus_dm_update( + handle: *mut PlusDm, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_plus_dm_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_plus_dm_batch( + handle: *mut PlusDm, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_plus_dm_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_plus_dm_reset(handle: *mut PlusDm) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_plus_dm_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_plus_dm_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_plus_dm_free(handle: *mut PlusDm) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ProfileShape` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_profile_shape_free`. +#[no_mangle] +pub extern "C" fn wickra_profile_shape_new(period: usize, bins: usize) -> *mut ProfileShape { + match ProfileShape::new(period, bins) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_profile_shape_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_profile_shape_update( + handle: *mut ProfileShape, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_profile_shape_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_profile_shape_batch( + handle: *mut ProfileShape, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_profile_shape_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_profile_shape_reset(handle: *mut ProfileShape) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_profile_shape_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_profile_shape_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_profile_shape_free(handle: *mut ProfileShape) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ProjectionOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_projection_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_projection_oscillator_new(period: usize) -> *mut ProjectionOscillator { + match ProjectionOscillator::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_projection_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_projection_oscillator_update( + handle: *mut ProjectionOscillator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_projection_oscillator_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_projection_oscillator_batch( + handle: *mut ProjectionOscillator, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_projection_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_projection_oscillator_reset(handle: *mut ProjectionOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_projection_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_projection_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_projection_oscillator_free(handle: *mut ProjectionOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Psar` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_psar_free`. +#[no_mangle] +pub extern "C" fn wickra_psar_new(af_start: f64, af_step: f64, af_max: f64) -> *mut Psar { + match Psar::new(af_start, af_step, af_max) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_psar_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_psar_update( + handle: *mut Psar, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_psar_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_psar_batch( + handle: *mut Psar, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_psar_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_psar_reset(handle: *mut Psar) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_psar_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_psar_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_psar_free(handle: *mut Psar) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Pvi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_pvi_free`. +#[no_mangle] +pub extern "C" fn wickra_pvi_new() -> *mut Pvi { + Box::into_raw(Box::new(Pvi::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_pvi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pvi_update( + handle: *mut Pvi, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_pvi_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_pvi_batch( + handle: *mut Pvi, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_pvi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pvi_reset(handle: *mut Pvi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_pvi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_pvi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pvi_free(handle: *mut Pvi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Qstick` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_qstick_free`. +#[no_mangle] +pub extern "C" fn wickra_qstick_new(period: usize) -> *mut Qstick { + match Qstick::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_qstick_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_qstick_update( + handle: *mut Qstick, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_qstick_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_qstick_batch( + handle: *mut Qstick, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_qstick_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_qstick_reset(handle: *mut Qstick) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_qstick_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_qstick_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_qstick_free(handle: *mut Qstick) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RectangleRange` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rectangle_range_free`. +#[no_mangle] +pub extern "C" fn wickra_rectangle_range_new() -> *mut RectangleRange { + Box::into_raw(Box::new(RectangleRange::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rectangle_range_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rectangle_range_update( + handle: *mut RectangleRange, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_rectangle_range_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_rectangle_range_batch( + handle: *mut RectangleRange, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rectangle_range_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rectangle_range_reset(handle: *mut RectangleRange) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rectangle_range_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rectangle_range_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rectangle_range_free(handle: *mut RectangleRange) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RickshawMan` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rickshaw_man_free`. +#[no_mangle] +pub extern "C" fn wickra_rickshaw_man_new() -> *mut RickshawMan { + Box::into_raw(Box::new(RickshawMan::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rickshaw_man_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rickshaw_man_update( + handle: *mut RickshawMan, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_rickshaw_man_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_rickshaw_man_batch( + handle: *mut RickshawMan, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rickshaw_man_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rickshaw_man_reset(handle: *mut RickshawMan) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rickshaw_man_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rickshaw_man_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rickshaw_man_free(handle: *mut RickshawMan) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RisingThreeMethods` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rising_three_methods_free`. +#[no_mangle] +pub extern "C" fn wickra_rising_three_methods_new() -> *mut RisingThreeMethods { + Box::into_raw(Box::new(RisingThreeMethods::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rising_three_methods_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rising_three_methods_update( + handle: *mut RisingThreeMethods, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_rising_three_methods_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_rising_three_methods_batch( + handle: *mut RisingThreeMethods, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rising_three_methods_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rising_three_methods_reset(handle: *mut RisingThreeMethods) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rising_three_methods_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rising_three_methods_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rising_three_methods_free(handle: *mut RisingThreeMethods) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RogersSatchellVolatility` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rogers_satchell_volatility_free`. +#[no_mangle] +pub extern "C" fn wickra_rogers_satchell_volatility_new( + period: usize, + trading_periods: usize, +) -> *mut RogersSatchellVolatility { + match RogersSatchellVolatility::new(period, trading_periods) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rogers_satchell_volatility_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rogers_satchell_volatility_update( + handle: *mut RogersSatchellVolatility, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_rogers_satchell_volatility_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_rogers_satchell_volatility_batch( + handle: *mut RogersSatchellVolatility, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rogers_satchell_volatility_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rogers_satchell_volatility_reset( + handle: *mut RogersSatchellVolatility, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rogers_satchell_volatility_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rogers_satchell_volatility_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rogers_satchell_volatility_free( + handle: *mut RogersSatchellVolatility, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Rvi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rvi_free`. +#[no_mangle] +pub extern "C" fn wickra_rvi_new(period: usize) -> *mut Rvi { + match Rvi::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rvi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rvi_update( + handle: *mut Rvi, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_rvi_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_rvi_batch( + handle: *mut Rvi, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rvi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rvi_reset(handle: *mut Rvi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rvi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rvi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rvi_free(handle: *mut Rvi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SarExt` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_sar_ext_free`. +#[no_mangle] +pub extern "C" fn wickra_sar_ext_new( + start_value: f64, + offset_on_reverse: f64, + accel_init_long: f64, + accel_long: f64, + accel_max_long: f64, + accel_init_short: f64, + accel_short: f64, + accel_max_short: f64, +) -> *mut SarExt { + match SarExt::new( + start_value, + offset_on_reverse, + accel_init_long, + accel_long, + accel_max_long, + accel_init_short, + accel_short, + accel_max_short, + ) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_sar_ext_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sar_ext_update( + handle: *mut SarExt, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_sar_ext_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_sar_ext_batch( + handle: *mut SarExt, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_sar_ext_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sar_ext_reset(handle: *mut SarExt) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_sar_ext_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_sar_ext_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_sar_ext_free(handle: *mut SarExt) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SeasonalZScore` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_seasonal_z_score_free`. +#[no_mangle] +pub extern "C" fn wickra_seasonal_z_score_new(utc_offset_minutes: i32) -> *mut SeasonalZScore { + Box::into_raw(Box::new(SeasonalZScore::new(utc_offset_minutes))) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_seasonal_z_score_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_seasonal_z_score_update( + handle: *mut SeasonalZScore, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_seasonal_z_score_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_seasonal_z_score_batch( + handle: *mut SeasonalZScore, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_seasonal_z_score_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_seasonal_z_score_reset(handle: *mut SeasonalZScore) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_seasonal_z_score_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_seasonal_z_score_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_seasonal_z_score_free(handle: *mut SeasonalZScore) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SeparatingLines` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_separating_lines_free`. +#[no_mangle] +pub extern "C" fn wickra_separating_lines_new() -> *mut SeparatingLines { + Box::into_raw(Box::new(SeparatingLines::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_separating_lines_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_separating_lines_update( + handle: *mut SeparatingLines, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_separating_lines_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_separating_lines_batch( + handle: *mut SeparatingLines, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_separating_lines_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_separating_lines_reset(handle: *mut SeparatingLines) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_separating_lines_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_separating_lines_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_separating_lines_free(handle: *mut SeparatingLines) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SessionVwap` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_session_vwap_free`. +#[no_mangle] +pub extern "C" fn wickra_session_vwap_new(utc_offset_minutes: i32) -> *mut SessionVwap { + Box::into_raw(Box::new(SessionVwap::new(utc_offset_minutes))) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_session_vwap_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_session_vwap_update( + handle: *mut SessionVwap, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_session_vwap_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_session_vwap_batch( + handle: *mut SessionVwap, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_session_vwap_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_session_vwap_reset(handle: *mut SessionVwap) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_session_vwap_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_session_vwap_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_session_vwap_free(handle: *mut SessionVwap) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Shark` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_shark_free`. +#[no_mangle] +pub extern "C" fn wickra_shark_new() -> *mut Shark { + Box::into_raw(Box::new(Shark::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_shark_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_shark_update( + handle: *mut Shark, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_shark_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_shark_batch( + handle: *mut Shark, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_shark_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_shark_reset(handle: *mut Shark) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_shark_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_shark_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_shark_free(handle: *mut Shark) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ShootingStar` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_shooting_star_free`. +#[no_mangle] +pub extern "C" fn wickra_shooting_star_new() -> *mut ShootingStar { + Box::into_raw(Box::new(ShootingStar::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_shooting_star_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_shooting_star_update( + handle: *mut ShootingStar, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_shooting_star_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_shooting_star_batch( + handle: *mut ShootingStar, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_shooting_star_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_shooting_star_reset(handle: *mut ShootingStar) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_shooting_star_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_shooting_star_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_shooting_star_free(handle: *mut ShootingStar) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ShortLine` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_short_line_free`. +#[no_mangle] +pub extern "C" fn wickra_short_line_new() -> *mut ShortLine { + Box::into_raw(Box::new(ShortLine::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_short_line_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_short_line_update( + handle: *mut ShortLine, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_short_line_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_short_line_batch( + handle: *mut ShortLine, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_short_line_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_short_line_reset(handle: *mut ShortLine) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_short_line_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_short_line_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_short_line_free(handle: *mut ShortLine) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SinglePrints` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_single_prints_free`. +#[no_mangle] +pub extern "C" fn wickra_single_prints_new(period: usize, bins: usize) -> *mut SinglePrints { + match SinglePrints::new(period, bins) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_single_prints_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_single_prints_update( + handle: *mut SinglePrints, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_single_prints_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_single_prints_batch( + handle: *mut SinglePrints, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_single_prints_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_single_prints_reset(handle: *mut SinglePrints) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_single_prints_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_single_prints_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_single_prints_free(handle: *mut SinglePrints) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Smi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_smi_free`. +#[no_mangle] +pub extern "C" fn wickra_smi_new(period: usize, d_period: usize, d2_period: usize) -> *mut Smi { + match Smi::new(period, d_period, d2_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_smi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_smi_update( + handle: *mut Smi, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_smi_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_smi_batch( + handle: *mut Smi, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_smi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_smi_reset(handle: *mut Smi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_smi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_smi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_smi_free(handle: *mut Smi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SpinningTop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_spinning_top_free`. +#[no_mangle] +pub extern "C" fn wickra_spinning_top_new() -> *mut SpinningTop { + Box::into_raw(Box::new(SpinningTop::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_spinning_top_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spinning_top_update( + handle: *mut SpinningTop, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_spinning_top_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_spinning_top_batch( + handle: *mut SpinningTop, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_spinning_top_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spinning_top_reset(handle: *mut SpinningTop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_spinning_top_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_spinning_top_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spinning_top_free(handle: *mut SpinningTop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `StalledPattern` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_stalled_pattern_free`. +#[no_mangle] +pub extern "C" fn wickra_stalled_pattern_new() -> *mut StalledPattern { + Box::into_raw(Box::new(StalledPattern::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_stalled_pattern_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stalled_pattern_update( + handle: *mut StalledPattern, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_stalled_pattern_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_stalled_pattern_batch( + handle: *mut StalledPattern, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_stalled_pattern_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stalled_pattern_reset(handle: *mut StalledPattern) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_stalled_pattern_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_stalled_pattern_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stalled_pattern_free(handle: *mut StalledPattern) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `StickSandwich` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_stick_sandwich_free`. +#[no_mangle] +pub extern "C" fn wickra_stick_sandwich_new() -> *mut StickSandwich { + Box::into_raw(Box::new(StickSandwich::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_stick_sandwich_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stick_sandwich_update( + handle: *mut StickSandwich, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_stick_sandwich_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_stick_sandwich_batch( + handle: *mut StickSandwich, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_stick_sandwich_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stick_sandwich_reset(handle: *mut StickSandwich) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_stick_sandwich_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_stick_sandwich_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stick_sandwich_free(handle: *mut StickSandwich) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `StochasticCci` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_stochastic_cci_free`. +#[no_mangle] +pub extern "C" fn wickra_stochastic_cci_new(period: usize) -> *mut StochasticCci { + match StochasticCci::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_stochastic_cci_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stochastic_cci_update( + handle: *mut StochasticCci, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_stochastic_cci_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_stochastic_cci_batch( + handle: *mut StochasticCci, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_stochastic_cci_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stochastic_cci_reset(handle: *mut StochasticCci) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_stochastic_cci_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_stochastic_cci_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stochastic_cci_free(handle: *mut StochasticCci) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Takuri` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_takuri_free`. +#[no_mangle] +pub extern "C" fn wickra_takuri_new() -> *mut Takuri { + Box::into_raw(Box::new(Takuri::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_takuri_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_takuri_update( + handle: *mut Takuri, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_takuri_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_takuri_batch( + handle: *mut Takuri, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_takuri_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_takuri_reset(handle: *mut Takuri) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_takuri_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_takuri_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_takuri_free(handle: *mut Takuri) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TasukiGap` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tasuki_gap_free`. +#[no_mangle] +pub extern "C" fn wickra_tasuki_gap_new() -> *mut TasukiGap { + Box::into_raw(Box::new(TasukiGap::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_tasuki_gap_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tasuki_gap_update( + handle: *mut TasukiGap, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_tasuki_gap_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_tasuki_gap_batch( + handle: *mut TasukiGap, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tasuki_gap_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tasuki_gap_reset(handle: *mut TasukiGap) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tasuki_gap_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tasuki_gap_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tasuki_gap_free(handle: *mut TasukiGap) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdCamouflage` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_camouflage_free`. +#[no_mangle] +pub extern "C" fn wickra_td_camouflage_new() -> *mut TdCamouflage { + Box::into_raw(Box::new(TdCamouflage::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_camouflage_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_camouflage_update( + handle: *mut TdCamouflage, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_camouflage_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_camouflage_batch( + handle: *mut TdCamouflage, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_camouflage_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_camouflage_reset(handle: *mut TdCamouflage) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_camouflage_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_camouflage_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_camouflage_free(handle: *mut TdCamouflage) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdClop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_clop_free`. +#[no_mangle] +pub extern "C" fn wickra_td_clop_new() -> *mut TdClop { + Box::into_raw(Box::new(TdClop::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_clop_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_clop_update( + handle: *mut TdClop, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_clop_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_clop_batch( + handle: *mut TdClop, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_clop_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_clop_reset(handle: *mut TdClop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_clop_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_clop_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_clop_free(handle: *mut TdClop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdClopwin` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_clopwin_free`. +#[no_mangle] +pub extern "C" fn wickra_td_clopwin_new() -> *mut TdClopwin { + Box::into_raw(Box::new(TdClopwin::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_clopwin_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_clopwin_update( + handle: *mut TdClopwin, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_clopwin_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_clopwin_batch( + handle: *mut TdClopwin, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_clopwin_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_clopwin_reset(handle: *mut TdClopwin) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_clopwin_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_clopwin_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_clopwin_free(handle: *mut TdClopwin) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdCombo` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_combo_free`. +#[no_mangle] +pub extern "C" fn wickra_td_combo_new( + setup_lookback: usize, + setup_target: usize, + countdown_lookback: usize, + countdown_target: usize, +) -> *mut TdCombo { + match TdCombo::new( + setup_lookback, + setup_target, + countdown_lookback, + countdown_target, + ) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_combo_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_combo_update( + handle: *mut TdCombo, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_combo_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_combo_batch( + handle: *mut TdCombo, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_combo_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_combo_reset(handle: *mut TdCombo) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_combo_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_combo_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_combo_free(handle: *mut TdCombo) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdCountdown` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_countdown_free`. +#[no_mangle] +pub extern "C" fn wickra_td_countdown_new( + setup_lookback: usize, + setup_target: usize, + countdown_lookback: usize, + countdown_target: usize, +) -> *mut TdCountdown { + match TdCountdown::new( + setup_lookback, + setup_target, + countdown_lookback, + countdown_target, + ) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_countdown_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_countdown_update( + handle: *mut TdCountdown, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_countdown_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_countdown_batch( + handle: *mut TdCountdown, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_countdown_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_countdown_reset(handle: *mut TdCountdown) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_countdown_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_countdown_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_countdown_free(handle: *mut TdCountdown) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdDeMarker` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_de_marker_free`. +#[no_mangle] +pub extern "C" fn wickra_td_de_marker_new(period: usize) -> *mut TdDeMarker { + match TdDeMarker::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_de_marker_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_de_marker_update( + handle: *mut TdDeMarker, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_de_marker_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_de_marker_batch( + handle: *mut TdDeMarker, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_de_marker_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_de_marker_reset(handle: *mut TdDeMarker) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_de_marker_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_de_marker_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_de_marker_free(handle: *mut TdDeMarker) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdDifferential` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_differential_free`. +#[no_mangle] +pub extern "C" fn wickra_td_differential_new() -> *mut TdDifferential { + Box::into_raw(Box::new(TdDifferential::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_differential_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_differential_update( + handle: *mut TdDifferential, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_differential_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_differential_batch( + handle: *mut TdDifferential, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_differential_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_differential_reset(handle: *mut TdDifferential) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_differential_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_differential_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_differential_free(handle: *mut TdDifferential) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdDWave` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_d_wave_free`. +#[no_mangle] +pub extern "C" fn wickra_td_d_wave_new(strength: usize) -> *mut TdDWave { + match TdDWave::new(strength) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_d_wave_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_d_wave_update( + handle: *mut TdDWave, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_d_wave_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_d_wave_batch( + handle: *mut TdDWave, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_d_wave_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_d_wave_reset(handle: *mut TdDWave) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_d_wave_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_d_wave_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_d_wave_free(handle: *mut TdDWave) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdOpen` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_open_free`. +#[no_mangle] +pub extern "C" fn wickra_td_open_new() -> *mut TdOpen { + Box::into_raw(Box::new(TdOpen::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_open_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_open_update( + handle: *mut TdOpen, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_open_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_open_batch( + handle: *mut TdOpen, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_open_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_open_reset(handle: *mut TdOpen) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_open_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_open_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_open_free(handle: *mut TdOpen) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdPressure` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_pressure_free`. +#[no_mangle] +pub extern "C" fn wickra_td_pressure_new(period: usize) -> *mut TdPressure { + match TdPressure::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_pressure_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_pressure_update( + handle: *mut TdPressure, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_pressure_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_pressure_batch( + handle: *mut TdPressure, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_pressure_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_pressure_reset(handle: *mut TdPressure) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_pressure_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_pressure_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_pressure_free(handle: *mut TdPressure) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdPropulsion` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_propulsion_free`. +#[no_mangle] +pub extern "C" fn wickra_td_propulsion_new() -> *mut TdPropulsion { + Box::into_raw(Box::new(TdPropulsion::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_propulsion_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_propulsion_update( + handle: *mut TdPropulsion, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_propulsion_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_propulsion_batch( + handle: *mut TdPropulsion, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_propulsion_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_propulsion_reset(handle: *mut TdPropulsion) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_propulsion_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_propulsion_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_propulsion_free(handle: *mut TdPropulsion) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdRei` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_rei_free`. +#[no_mangle] +pub extern "C" fn wickra_td_rei_new(period: usize) -> *mut TdRei { + match TdRei::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_rei_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_rei_update( + handle: *mut TdRei, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_rei_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_rei_batch( + handle: *mut TdRei, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_rei_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_rei_reset(handle: *mut TdRei) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_rei_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_rei_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_rei_free(handle: *mut TdRei) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdSetup` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_setup_free`. +#[no_mangle] +pub extern "C" fn wickra_td_setup_new(lookback: usize, target: usize) -> *mut TdSetup { + match TdSetup::new(lookback, target) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_setup_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_setup_update( + handle: *mut TdSetup, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_setup_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_setup_batch( + handle: *mut TdSetup, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_setup_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_setup_reset(handle: *mut TdSetup) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_setup_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_setup_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_setup_free(handle: *mut TdSetup) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdTrap` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_trap_free`. +#[no_mangle] +pub extern "C" fn wickra_td_trap_new() -> *mut TdTrap { + Box::into_raw(Box::new(TdTrap::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_td_trap_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_trap_update( + handle: *mut TdTrap, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_td_trap_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_trap_batch( + handle: *mut TdTrap, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_trap_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_trap_reset(handle: *mut TdTrap) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_trap_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_trap_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_trap_free(handle: *mut TdTrap) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ThreeDrives` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_three_drives_free`. +#[no_mangle] +pub extern "C" fn wickra_three_drives_new() -> *mut ThreeDrives { + Box::into_raw(Box::new(ThreeDrives::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_three_drives_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_drives_update( + handle: *mut ThreeDrives, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_three_drives_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_drives_batch( + handle: *mut ThreeDrives, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_three_drives_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_drives_reset(handle: *mut ThreeDrives) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_three_drives_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_three_drives_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_drives_free(handle: *mut ThreeDrives) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ThreeInside` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_three_inside_free`. +#[no_mangle] +pub extern "C" fn wickra_three_inside_new() -> *mut ThreeInside { + Box::into_raw(Box::new(ThreeInside::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_three_inside_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_inside_update( + handle: *mut ThreeInside, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_three_inside_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_inside_batch( + handle: *mut ThreeInside, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_three_inside_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_inside_reset(handle: *mut ThreeInside) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_three_inside_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_three_inside_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_inside_free(handle: *mut ThreeInside) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ThreeLineBreak` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_three_line_break_free`. +#[no_mangle] +pub extern "C" fn wickra_three_line_break_new(lines: usize) -> *mut ThreeLineBreak { + match ThreeLineBreak::new(lines) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_three_line_break_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_line_break_update( + handle: *mut ThreeLineBreak, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_three_line_break_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_line_break_batch( + handle: *mut ThreeLineBreak, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_three_line_break_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_line_break_reset(handle: *mut ThreeLineBreak) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_three_line_break_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_three_line_break_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_line_break_free(handle: *mut ThreeLineBreak) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ThreeLineStrike` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_three_line_strike_free`. +#[no_mangle] +pub extern "C" fn wickra_three_line_strike_new() -> *mut ThreeLineStrike { + Box::into_raw(Box::new(ThreeLineStrike::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_three_line_strike_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_line_strike_update( + handle: *mut ThreeLineStrike, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_three_line_strike_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_line_strike_batch( + handle: *mut ThreeLineStrike, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_three_line_strike_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_line_strike_reset(handle: *mut ThreeLineStrike) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_three_line_strike_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_three_line_strike_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_line_strike_free(handle: *mut ThreeLineStrike) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ThreeOutside` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_three_outside_free`. +#[no_mangle] +pub extern "C" fn wickra_three_outside_new() -> *mut ThreeOutside { + Box::into_raw(Box::new(ThreeOutside::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_three_outside_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_outside_update( + handle: *mut ThreeOutside, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_three_outside_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_outside_batch( + handle: *mut ThreeOutside, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_three_outside_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_outside_reset(handle: *mut ThreeOutside) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_three_outside_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_three_outside_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_outside_free(handle: *mut ThreeOutside) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ThreeSoldiersOrCrows` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_three_soldiers_or_crows_free`. +#[no_mangle] +pub extern "C" fn wickra_three_soldiers_or_crows_new() -> *mut ThreeSoldiersOrCrows { + Box::into_raw(Box::new(ThreeSoldiersOrCrows::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_three_soldiers_or_crows_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_soldiers_or_crows_update( + handle: *mut ThreeSoldiersOrCrows, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_three_soldiers_or_crows_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_soldiers_or_crows_batch( + handle: *mut ThreeSoldiersOrCrows, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_three_soldiers_or_crows_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_soldiers_or_crows_reset(handle: *mut ThreeSoldiersOrCrows) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_three_soldiers_or_crows_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_three_soldiers_or_crows_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_soldiers_or_crows_free(handle: *mut ThreeSoldiersOrCrows) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ThreeStarsInSouth` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_three_stars_in_south_free`. +#[no_mangle] +pub extern "C" fn wickra_three_stars_in_south_new() -> *mut ThreeStarsInSouth { + Box::into_raw(Box::new(ThreeStarsInSouth::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_three_stars_in_south_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_stars_in_south_update( + handle: *mut ThreeStarsInSouth, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_three_stars_in_south_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_stars_in_south_batch( + handle: *mut ThreeStarsInSouth, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_three_stars_in_south_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_stars_in_south_reset(handle: *mut ThreeStarsInSouth) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_three_stars_in_south_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_three_stars_in_south_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_stars_in_south_free(handle: *mut ThreeStarsInSouth) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Thrusting` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_thrusting_free`. +#[no_mangle] +pub extern "C" fn wickra_thrusting_new() -> *mut Thrusting { + Box::into_raw(Box::new(Thrusting::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_thrusting_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_thrusting_update( + handle: *mut Thrusting, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_thrusting_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_thrusting_batch( + handle: *mut Thrusting, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_thrusting_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_thrusting_reset(handle: *mut Thrusting) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_thrusting_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_thrusting_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_thrusting_free(handle: *mut Thrusting) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TimeBasedStop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_time_based_stop_free`. +#[no_mangle] +pub extern "C" fn wickra_time_based_stop_new(max_bars: usize) -> *mut TimeBasedStop { + match TimeBasedStop::new(max_bars) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_time_based_stop_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_time_based_stop_update( + handle: *mut TimeBasedStop, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_time_based_stop_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_time_based_stop_batch( + handle: *mut TimeBasedStop, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_time_based_stop_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_time_based_stop_reset(handle: *mut TimeBasedStop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_time_based_stop_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_time_based_stop_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_time_based_stop_free(handle: *mut TimeBasedStop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TowerTopBottom` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tower_top_bottom_free`. +#[no_mangle] +pub extern "C" fn wickra_tower_top_bottom_new() -> *mut TowerTopBottom { + Box::into_raw(Box::new(TowerTopBottom::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_tower_top_bottom_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tower_top_bottom_update( + handle: *mut TowerTopBottom, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_tower_top_bottom_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_tower_top_bottom_batch( + handle: *mut TowerTopBottom, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tower_top_bottom_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tower_top_bottom_reset(handle: *mut TowerTopBottom) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tower_top_bottom_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tower_top_bottom_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tower_top_bottom_free(handle: *mut TowerTopBottom) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TradeVolumeIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_trade_volume_index_free`. +#[no_mangle] +pub extern "C" fn wickra_trade_volume_index_new(min_tick: f64) -> *mut TradeVolumeIndex { + match TradeVolumeIndex::new(min_tick) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_trade_volume_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trade_volume_index_update( + handle: *mut TradeVolumeIndex, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_trade_volume_index_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_trade_volume_index_batch( + handle: *mut TradeVolumeIndex, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_trade_volume_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trade_volume_index_reset(handle: *mut TradeVolumeIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_trade_volume_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_trade_volume_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trade_volume_index_free(handle: *mut TradeVolumeIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Triangle` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_triangle_free`. +#[no_mangle] +pub extern "C" fn wickra_triangle_new() -> *mut Triangle { + Box::into_raw(Box::new(Triangle::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_triangle_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_triangle_update( + handle: *mut Triangle, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_triangle_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_triangle_batch( + handle: *mut Triangle, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_triangle_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_triangle_reset(handle: *mut Triangle) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_triangle_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_triangle_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_triangle_free(handle: *mut Triangle) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TripleTopBottom` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_triple_top_bottom_free`. +#[no_mangle] +pub extern "C" fn wickra_triple_top_bottom_new() -> *mut TripleTopBottom { + Box::into_raw(Box::new(TripleTopBottom::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_triple_top_bottom_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_triple_top_bottom_update( + handle: *mut TripleTopBottom, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_triple_top_bottom_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_triple_top_bottom_batch( + handle: *mut TripleTopBottom, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_triple_top_bottom_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_triple_top_bottom_reset(handle: *mut TripleTopBottom) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_triple_top_bottom_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_triple_top_bottom_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_triple_top_bottom_free(handle: *mut TripleTopBottom) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Tristar` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tristar_free`. +#[no_mangle] +pub extern "C" fn wickra_tristar_new() -> *mut Tristar { + Box::into_raw(Box::new(Tristar::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_tristar_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tristar_update( + handle: *mut Tristar, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_tristar_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_tristar_batch( + handle: *mut Tristar, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tristar_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tristar_reset(handle: *mut Tristar) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tristar_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tristar_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tristar_free(handle: *mut Tristar) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TrueRange` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_true_range_free`. +#[no_mangle] +pub extern "C" fn wickra_true_range_new() -> *mut TrueRange { + Box::into_raw(Box::new(TrueRange::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_true_range_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_true_range_update( + handle: *mut TrueRange, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_true_range_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_true_range_batch( + handle: *mut TrueRange, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_true_range_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_true_range_reset(handle: *mut TrueRange) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_true_range_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_true_range_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_true_range_free(handle: *mut TrueRange) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Tsv` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tsv_free`. +#[no_mangle] +pub extern "C" fn wickra_tsv_new(period: usize) -> *mut Tsv { + match Tsv::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_tsv_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsv_update( + handle: *mut Tsv, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_tsv_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsv_batch( + handle: *mut Tsv, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tsv_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsv_reset(handle: *mut Tsv) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tsv_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tsv_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tsv_free(handle: *mut Tsv) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TtmTrend` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ttm_trend_free`. +#[no_mangle] +pub extern "C" fn wickra_ttm_trend_new(period: usize) -> *mut TtmTrend { + match TtmTrend::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ttm_trend_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ttm_trend_update( + handle: *mut TtmTrend, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_ttm_trend_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_ttm_trend_batch( + handle: *mut TtmTrend, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ttm_trend_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ttm_trend_reset(handle: *mut TtmTrend) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ttm_trend_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ttm_trend_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ttm_trend_free(handle: *mut TtmTrend) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TurnOfMonth` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_turn_of_month_free`. +#[no_mangle] +pub extern "C" fn wickra_turn_of_month_new( + n_first: u32, + n_last: u32, + utc_offset_minutes: i32, +) -> *mut TurnOfMonth { + match TurnOfMonth::new(n_first, n_last, utc_offset_minutes) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_turn_of_month_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_turn_of_month_update( + handle: *mut TurnOfMonth, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_turn_of_month_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_turn_of_month_batch( + handle: *mut TurnOfMonth, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_turn_of_month_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_turn_of_month_reset(handle: *mut TurnOfMonth) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_turn_of_month_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_turn_of_month_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_turn_of_month_free(handle: *mut TurnOfMonth) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Tweezer` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tweezer_free`. +#[no_mangle] +pub extern "C" fn wickra_tweezer_new() -> *mut Tweezer { + Box::into_raw(Box::new(Tweezer::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_tweezer_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tweezer_update( + handle: *mut Tweezer, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_tweezer_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_tweezer_batch( + handle: *mut Tweezer, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tweezer_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tweezer_reset(handle: *mut Tweezer) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tweezer_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tweezer_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tweezer_free(handle: *mut Tweezer) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TwiggsMoneyFlow` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_twiggs_money_flow_free`. +#[no_mangle] +pub extern "C" fn wickra_twiggs_money_flow_new(period: usize) -> *mut TwiggsMoneyFlow { + match TwiggsMoneyFlow::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_twiggs_money_flow_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_twiggs_money_flow_update( + handle: *mut TwiggsMoneyFlow, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_twiggs_money_flow_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_twiggs_money_flow_batch( + handle: *mut TwiggsMoneyFlow, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_twiggs_money_flow_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_twiggs_money_flow_reset(handle: *mut TwiggsMoneyFlow) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_twiggs_money_flow_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_twiggs_money_flow_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_twiggs_money_flow_free(handle: *mut TwiggsMoneyFlow) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TwoCrows` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_two_crows_free`. +#[no_mangle] +pub extern "C" fn wickra_two_crows_new() -> *mut TwoCrows { + Box::into_raw(Box::new(TwoCrows::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_two_crows_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_two_crows_update( + handle: *mut TwoCrows, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_two_crows_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_two_crows_batch( + handle: *mut TwoCrows, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_two_crows_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_two_crows_reset(handle: *mut TwoCrows) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_two_crows_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_two_crows_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_two_crows_free(handle: *mut TwoCrows) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TypicalPrice` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_typical_price_free`. +#[no_mangle] +pub extern "C" fn wickra_typical_price_new() -> *mut TypicalPrice { + Box::into_raw(Box::new(TypicalPrice::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_typical_price_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_typical_price_update( + handle: *mut TypicalPrice, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_typical_price_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_typical_price_batch( + handle: *mut TypicalPrice, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_typical_price_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_typical_price_reset(handle: *mut TypicalPrice) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_typical_price_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_typical_price_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_typical_price_free(handle: *mut TypicalPrice) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `UltimateOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ultimate_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_ultimate_oscillator_new( + short: usize, + mid: usize, + long: usize, +) -> *mut UltimateOscillator { + match UltimateOscillator::new(short, mid, long) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_ultimate_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ultimate_oscillator_update( + handle: *mut UltimateOscillator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_ultimate_oscillator_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_ultimate_oscillator_batch( + handle: *mut UltimateOscillator, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ultimate_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ultimate_oscillator_reset(handle: *mut UltimateOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ultimate_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ultimate_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ultimate_oscillator_free(handle: *mut UltimateOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `UniqueThreeRiver` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_unique_three_river_free`. +#[no_mangle] +pub extern "C" fn wickra_unique_three_river_new() -> *mut UniqueThreeRiver { + Box::into_raw(Box::new(UniqueThreeRiver::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_unique_three_river_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_unique_three_river_update( + handle: *mut UniqueThreeRiver, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_unique_three_river_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_unique_three_river_batch( + handle: *mut UniqueThreeRiver, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_unique_three_river_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_unique_three_river_reset(handle: *mut UniqueThreeRiver) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_unique_three_river_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_unique_three_river_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_unique_three_river_free(handle: *mut UniqueThreeRiver) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `UpsideGapThreeMethods` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_upside_gap_three_methods_free`. +#[no_mangle] +pub extern "C" fn wickra_upside_gap_three_methods_new() -> *mut UpsideGapThreeMethods { + Box::into_raw(Box::new(UpsideGapThreeMethods::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_upside_gap_three_methods_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_gap_three_methods_update( + handle: *mut UpsideGapThreeMethods, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_upside_gap_three_methods_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_gap_three_methods_batch( + handle: *mut UpsideGapThreeMethods, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_upside_gap_three_methods_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_gap_three_methods_reset(handle: *mut UpsideGapThreeMethods) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_upside_gap_three_methods_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_upside_gap_three_methods_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_gap_three_methods_free(handle: *mut UpsideGapThreeMethods) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `UpsideGapTwoCrows` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_upside_gap_two_crows_free`. +#[no_mangle] +pub extern "C" fn wickra_upside_gap_two_crows_new() -> *mut UpsideGapTwoCrows { + Box::into_raw(Box::new(UpsideGapTwoCrows::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_upside_gap_two_crows_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_gap_two_crows_update( + handle: *mut UpsideGapTwoCrows, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_upside_gap_two_crows_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_gap_two_crows_batch( + handle: *mut UpsideGapTwoCrows, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_upside_gap_two_crows_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_gap_two_crows_reset(handle: *mut UpsideGapTwoCrows) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_upside_gap_two_crows_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_upside_gap_two_crows_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_upside_gap_two_crows_free(handle: *mut UpsideGapTwoCrows) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VolatilityRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volatility_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_volatility_ratio_new(period: usize) -> *mut VolatilityRatio { + match VolatilityRatio::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_volatility_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volatility_ratio_update( + handle: *mut VolatilityRatio, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_volatility_ratio_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_volatility_ratio_batch( + handle: *mut VolatilityRatio, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volatility_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volatility_ratio_reset(handle: *mut VolatilityRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volatility_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volatility_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volatility_ratio_free(handle: *mut VolatilityRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VoltyStop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volty_stop_free`. +#[no_mangle] +pub extern "C" fn wickra_volty_stop_new(atr_period: usize, multiplier: f64) -> *mut VoltyStop { + match VoltyStop::new(atr_period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_volty_stop_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volty_stop_update( + handle: *mut VoltyStop, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_volty_stop_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_volty_stop_batch( + handle: *mut VoltyStop, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volty_stop_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volty_stop_reset(handle: *mut VoltyStop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volty_stop_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volty_stop_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volty_stop_free(handle: *mut VoltyStop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VolumeOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volume_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_volume_oscillator_new(fast: usize, slow: usize) -> *mut VolumeOscillator { + match VolumeOscillator::new(fast, slow) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_volume_oscillator_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_oscillator_update( + handle: *mut VolumeOscillator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_volume_oscillator_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_oscillator_batch( + handle: *mut VolumeOscillator, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volume_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_oscillator_reset(handle: *mut VolumeOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volume_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volume_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_oscillator_free(handle: *mut VolumeOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VolumeRsi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volume_rsi_free`. +#[no_mangle] +pub extern "C" fn wickra_volume_rsi_new(period: usize) -> *mut VolumeRsi { + match VolumeRsi::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_volume_rsi_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_rsi_update( + handle: *mut VolumeRsi, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_volume_rsi_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_rsi_batch( + handle: *mut VolumeRsi, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volume_rsi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_rsi_reset(handle: *mut VolumeRsi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volume_rsi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volume_rsi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_rsi_free(handle: *mut VolumeRsi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VolumePriceTrend` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volume_price_trend_free`. +#[no_mangle] +pub extern "C" fn wickra_volume_price_trend_new() -> *mut VolumePriceTrend { + Box::into_raw(Box::new(VolumePriceTrend::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_volume_price_trend_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_price_trend_update( + handle: *mut VolumePriceTrend, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_volume_price_trend_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_price_trend_batch( + handle: *mut VolumePriceTrend, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volume_price_trend_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_price_trend_reset(handle: *mut VolumePriceTrend) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volume_price_trend_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volume_price_trend_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_price_trend_free(handle: *mut VolumePriceTrend) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Vwap` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_vwap_free`. +#[no_mangle] +pub extern "C" fn wickra_vwap_new() -> *mut Vwap { + Box::into_raw(Box::new(Vwap::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_vwap_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vwap_update( + handle: *mut Vwap, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_vwap_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_vwap_batch( + handle: *mut Vwap, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_vwap_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vwap_reset(handle: *mut Vwap) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_vwap_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_vwap_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vwap_free(handle: *mut Vwap) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RollingVwap` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rolling_vwap_free`. +#[no_mangle] +pub extern "C" fn wickra_rolling_vwap_new(period: usize) -> *mut RollingVwap { + match RollingVwap::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_rolling_vwap_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_vwap_update( + handle: *mut RollingVwap, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_rolling_vwap_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_vwap_batch( + handle: *mut RollingVwap, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rolling_vwap_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_vwap_reset(handle: *mut RollingVwap) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rolling_vwap_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rolling_vwap_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rolling_vwap_free(handle: *mut RollingVwap) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Vwma` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_vwma_free`. +#[no_mangle] +pub extern "C" fn wickra_vwma_new(period: usize) -> *mut Vwma { + match Vwma::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_vwma_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vwma_update( + handle: *mut Vwma, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_vwma_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_vwma_batch( + handle: *mut Vwma, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_vwma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vwma_reset(handle: *mut Vwma) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_vwma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_vwma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vwma_free(handle: *mut Vwma) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Vzo` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_vzo_free`. +#[no_mangle] +pub extern "C" fn wickra_vzo_new(period: usize) -> *mut Vzo { + match Vzo::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_vzo_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vzo_update( + handle: *mut Vzo, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_vzo_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_vzo_batch( + handle: *mut Vzo, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_vzo_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vzo_reset(handle: *mut Vzo) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_vzo_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_vzo_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vzo_free(handle: *mut Vzo) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Wad` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_wad_free`. +#[no_mangle] +pub extern "C" fn wickra_wad_new() -> *mut Wad { + Box::into_raw(Box::new(Wad::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_wad_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wad_update( + handle: *mut Wad, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_wad_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_wad_batch( + handle: *mut Wad, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_wad_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wad_reset(handle: *mut Wad) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_wad_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_wad_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wad_free(handle: *mut Wad) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Wedge` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_wedge_free`. +#[no_mangle] +pub extern "C" fn wickra_wedge_new() -> *mut Wedge { + Box::into_raw(Box::new(Wedge::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_wedge_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wedge_update( + handle: *mut Wedge, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_wedge_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_wedge_batch( + handle: *mut Wedge, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_wedge_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wedge_reset(handle: *mut Wedge) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_wedge_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_wedge_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wedge_free(handle: *mut Wedge) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `WeightedClose` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_weighted_close_free`. +#[no_mangle] +pub extern "C" fn wickra_weighted_close_new() -> *mut WeightedClose { + Box::into_raw(Box::new(WeightedClose::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_weighted_close_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_weighted_close_update( + handle: *mut WeightedClose, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_weighted_close_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_weighted_close_batch( + handle: *mut WeightedClose, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_weighted_close_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_weighted_close_reset(handle: *mut WeightedClose) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_weighted_close_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_weighted_close_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_weighted_close_free(handle: *mut WeightedClose) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `WickRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_wick_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_wick_ratio_new() -> *mut WickRatio { + Box::into_raw(Box::new(WickRatio::new())) +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_wick_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wick_ratio_update( + handle: *mut WickRatio, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_wick_ratio_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_wick_ratio_batch( + handle: *mut WickRatio, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_wick_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wick_ratio_reset(handle: *mut WickRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_wick_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_wick_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wick_ratio_free(handle: *mut WickRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `WilliamsR` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_williams_r_free`. +#[no_mangle] +pub extern "C" fn wickra_williams_r_new(period: usize) -> *mut WilliamsR { + match WilliamsR::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_williams_r_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_williams_r_update( + handle: *mut WilliamsR, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_williams_r_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_williams_r_batch( + handle: *mut WilliamsR, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_williams_r_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_williams_r_reset(handle: *mut WilliamsR) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_williams_r_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_williams_r_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_williams_r_free(handle: *mut WilliamsR) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `YangZhangVolatility` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_yang_zhang_volatility_free`. +#[no_mangle] +pub extern "C" fn wickra_yang_zhang_volatility_new( + period: usize, + trading_periods: usize, +) -> *mut YangZhangVolatility { + match YangZhangVolatility::new(period, trading_periods) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_yang_zhang_volatility_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_yang_zhang_volatility_update( + handle: *mut YangZhangVolatility, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_yang_zhang_volatility_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_yang_zhang_volatility_batch( + handle: *mut YangZhangVolatility, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_yang_zhang_volatility_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_yang_zhang_volatility_reset(handle: *mut YangZhangVolatility) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_yang_zhang_volatility_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_yang_zhang_volatility_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_yang_zhang_volatility_free(handle: *mut YangZhangVolatility) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `YoyoExit` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_yoyo_exit_free`. +#[no_mangle] +pub extern "C" fn wickra_yoyo_exit_new(atr_period: usize, multiplier: f64) -> *mut YoyoExit { + match YoyoExit::new(atr_period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` +/// handle / if the candle is invalid (e.g. `high < low`). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_yoyo_exit_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_yoyo_exit_update( + handle: *mut YoyoExit, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match Candle::new(open, high, low, close, volume, timestamp) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into +/// `out[0..n]` (`NaN` at warmup or on an invalid candle). +/// +/// # Safety +/// `handle` valid (from `wickra_yoyo_exit_new`, not freed); every input pointer and `out` +/// cover `n` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_yoyo_exit_batch( + handle: *mut YoyoExit, + open: *const f64, + high: *const f64, + low: *const f64, + close: *const f64, + volume: *const f64, + timestamp: *const i64, + out: *mut f64, + n: usize, +) { + if handle.is_null() + || open.is_null() + || high.is_null() + || low.is_null() + || close.is_null() + || volume.is_null() + || timestamp.is_null() + || out.is_null() + { + return; + } + let ind = &mut *handle; + let opens = slice::from_raw_parts(open, n); + let highs = slice::from_raw_parts(high, n); + let lows = slice::from_raw_parts(low, n); + let closes = slice::from_raw_parts(close, n); + let volumes = slice::from_raw_parts(volume, n); + let timestamps = slice::from_raw_parts(timestamp, n); + let outputs = slice::from_raw_parts_mut(out, n); + for (idx, slot) in outputs.iter_mut().enumerate() { + *slot = match Candle::new( + opens[idx], + highs[idx], + lows[idx], + closes[idx], + volumes[idx], + timestamps[idx], + ) { + Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + }; + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_yoyo_exit_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_yoyo_exit_reset(handle: *mut YoyoExit) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_yoyo_exit_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_yoyo_exit_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_yoyo_exit_free(handle: *mut YoyoExit) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +// ===== Trade-input indicators (microstructure) ===== + +/// Create a `AmihudIlliquidity` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_amihud_illiquidity_free`. +#[no_mangle] +pub extern "C" fn wickra_amihud_illiquidity_new(period: usize) -> *mut AmihudIlliquidity { + match AmihudIlliquidity::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / +/// if the trade is invalid (non-positive price, negative size). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_amihud_illiquidity_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_amihud_illiquidity_update( + handle: *mut AmihudIlliquidity, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + match Trade::new(price, size, side, timestamp) { + Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_amihud_illiquidity_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_amihud_illiquidity_reset(handle: *mut AmihudIlliquidity) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_amihud_illiquidity_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_amihud_illiquidity_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_amihud_illiquidity_free(handle: *mut AmihudIlliquidity) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CumulativeVolumeDelta` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_cumulative_volume_delta_free`. +#[no_mangle] +pub extern "C" fn wickra_cumulative_volume_delta_new() -> *mut CumulativeVolumeDelta { + Box::into_raw(Box::new(CumulativeVolumeDelta::new())) +} + +/// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / +/// if the trade is invalid (non-positive price, negative size). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_cumulative_volume_delta_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cumulative_volume_delta_update( + handle: *mut CumulativeVolumeDelta, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + match Trade::new(price, size, side, timestamp) { + Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_cumulative_volume_delta_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cumulative_volume_delta_reset(handle: *mut CumulativeVolumeDelta) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_cumulative_volume_delta_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_cumulative_volume_delta_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cumulative_volume_delta_free(handle: *mut CumulativeVolumeDelta) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Pin` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_pin_free`. +#[no_mangle] +pub extern "C" fn wickra_pin_new(window: usize) -> *mut Pin { + match Pin::new(window) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / +/// if the trade is invalid (non-positive price, negative size). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_pin_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pin_update( + handle: *mut Pin, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + match Trade::new(price, size, side, timestamp) { + Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_pin_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pin_reset(handle: *mut Pin) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_pin_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_pin_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_pin_free(handle: *mut Pin) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RollMeasure` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_roll_measure_free`. +#[no_mangle] +pub extern "C" fn wickra_roll_measure_new(period: usize) -> *mut RollMeasure { + match RollMeasure::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / +/// if the trade is invalid (non-positive price, negative size). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_roll_measure_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_roll_measure_update( + handle: *mut RollMeasure, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + match Trade::new(price, size, side, timestamp) { + Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_roll_measure_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_roll_measure_reset(handle: *mut RollMeasure) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_roll_measure_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_roll_measure_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_roll_measure_free(handle: *mut RollMeasure) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SignedVolume` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_signed_volume_free`. +#[no_mangle] +pub extern "C" fn wickra_signed_volume_new() -> *mut SignedVolume { + Box::into_raw(Box::new(SignedVolume::new())) +} + +/// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / +/// if the trade is invalid (non-positive price, negative size). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_signed_volume_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_signed_volume_update( + handle: *mut SignedVolume, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + match Trade::new(price, size, side, timestamp) { + Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_signed_volume_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_signed_volume_reset(handle: *mut SignedVolume) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_signed_volume_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_signed_volume_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_signed_volume_free(handle: *mut SignedVolume) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TradeImbalance` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_trade_imbalance_free`. +#[no_mangle] +pub extern "C" fn wickra_trade_imbalance_new(window: usize) -> *mut TradeImbalance { + match TradeImbalance::new(window) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / +/// if the trade is invalid (non-positive price, negative size). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_trade_imbalance_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trade_imbalance_update( + handle: *mut TradeImbalance, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + match Trade::new(price, size, side, timestamp) { + Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_trade_imbalance_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trade_imbalance_reset(handle: *mut TradeImbalance) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_trade_imbalance_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_trade_imbalance_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trade_imbalance_free(handle: *mut TradeImbalance) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TradeSignAutocorrelation` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_trade_sign_autocorrelation_free`. +#[no_mangle] +pub extern "C" fn wickra_trade_sign_autocorrelation_new( + period: usize, +) -> *mut TradeSignAutocorrelation { + match TradeSignAutocorrelation::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / +/// if the trade is invalid (non-positive price, negative size). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_trade_sign_autocorrelation_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trade_sign_autocorrelation_update( + handle: *mut TradeSignAutocorrelation, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + match Trade::new(price, size, side, timestamp) { + Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_trade_sign_autocorrelation_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trade_sign_autocorrelation_reset( + handle: *mut TradeSignAutocorrelation, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_trade_sign_autocorrelation_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_trade_sign_autocorrelation_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trade_sign_autocorrelation_free( + handle: *mut TradeSignAutocorrelation, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Vpin` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_vpin_free`. +#[no_mangle] +pub extern "C" fn wickra_vpin_new(bucket_volume: f64, num_buckets: usize) -> *mut Vpin { + match Vpin::new(bucket_volume, num_buckets) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / +/// if the trade is invalid (non-positive price, negative size). +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_vpin_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vpin_update( + handle: *mut Vpin, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + match Trade::new(price, size, side, timestamp) { + Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_vpin_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vpin_reset(handle: *mut Vpin) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_vpin_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_vpin_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vpin_free(handle: *mut Vpin) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +// ===== Trade+quote-input indicators (microstructure) ===== + +/// Create a `EffectiveSpread` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_effective_spread_free`. +#[no_mangle] +pub extern "C" fn wickra_effective_spread_new() -> *mut EffectiveSpread { + Box::into_raw(Box::new(EffectiveSpread::new())) +} + +/// Feed one trade with the prevailing mid price; returns the output, or `NaN` during +/// warmup / on a `NULL` handle / if the trade or mid is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_effective_spread_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_effective_spread_update( + handle: *mut EffectiveSpread, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, + mid: f64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + let Ok(trade) = Trade::new(price, size, side, timestamp) else { + return f64::NAN; + }; + match TradeQuote::new(trade, mid) { + Ok(quote) => ind.update(quote).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_effective_spread_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_effective_spread_reset(handle: *mut EffectiveSpread) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_effective_spread_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_effective_spread_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_effective_spread_free(handle: *mut EffectiveSpread) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `KylesLambda` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kyles_lambda_free`. +#[no_mangle] +pub extern "C" fn wickra_kyles_lambda_new(window: usize) -> *mut KylesLambda { + match KylesLambda::new(window) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one trade with the prevailing mid price; returns the output, or `NaN` during +/// warmup / on a `NULL` handle / if the trade or mid is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_kyles_lambda_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kyles_lambda_update( + handle: *mut KylesLambda, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, + mid: f64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + let Ok(trade) = Trade::new(price, size, side, timestamp) else { + return f64::NAN; + }; + match TradeQuote::new(trade, mid) { + Ok(quote) => ind.update(quote).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kyles_lambda_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kyles_lambda_reset(handle: *mut KylesLambda) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kyles_lambda_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kyles_lambda_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kyles_lambda_free(handle: *mut KylesLambda) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RealizedSpread` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_realized_spread_free`. +#[no_mangle] +pub extern "C" fn wickra_realized_spread_new(horizon: usize) -> *mut RealizedSpread { + match RealizedSpread::new(horizon) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one trade with the prevailing mid price; returns the output, or `NaN` during +/// warmup / on a `NULL` handle / if the trade or mid is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_realized_spread_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_realized_spread_update( + handle: *mut RealizedSpread, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, + mid: f64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + let Ok(trade) = Trade::new(price, size, side, timestamp) else { + return f64::NAN; + }; + match TradeQuote::new(trade, mid) { + Ok(quote) => ind.update(quote).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_realized_spread_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_realized_spread_reset(handle: *mut RealizedSpread) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_realized_spread_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_realized_spread_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_realized_spread_free(handle: *mut RealizedSpread) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +// ===== Derivatives-tick-input indicators ===== + +/// Create a `CalendarSpread` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_calendar_spread_free`. +#[no_mangle] +pub extern "C" fn wickra_calendar_spread_new() -> *mut CalendarSpread { + Box::into_raw(Box::new(CalendarSpread::new())) +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_calendar_spread_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_calendar_spread_update( + handle: *mut CalendarSpread, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_calendar_spread_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_calendar_spread_reset(handle: *mut CalendarSpread) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_calendar_spread_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_calendar_spread_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_calendar_spread_free(handle: *mut CalendarSpread) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `EstimatedLeverageRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_estimated_leverage_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_estimated_leverage_ratio_new() -> *mut EstimatedLeverageRatio { + Box::into_raw(Box::new(EstimatedLeverageRatio::new())) +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_estimated_leverage_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_estimated_leverage_ratio_update( + handle: *mut EstimatedLeverageRatio, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_estimated_leverage_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_estimated_leverage_ratio_reset( + handle: *mut EstimatedLeverageRatio, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_estimated_leverage_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_estimated_leverage_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_estimated_leverage_ratio_free(handle: *mut EstimatedLeverageRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FundingBasis` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_funding_basis_free`. +#[no_mangle] +pub extern "C" fn wickra_funding_basis_new() -> *mut FundingBasis { + Box::into_raw(Box::new(FundingBasis::new())) +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_funding_basis_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_basis_update( + handle: *mut FundingBasis, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_funding_basis_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_basis_reset(handle: *mut FundingBasis) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_funding_basis_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_funding_basis_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_basis_free(handle: *mut FundingBasis) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FundingImpliedApr` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_funding_implied_apr_free`. +#[no_mangle] +pub extern "C" fn wickra_funding_implied_apr_new( + intervals_per_year: f64, +) -> *mut FundingImpliedApr { + match FundingImpliedApr::new(intervals_per_year) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_funding_implied_apr_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_implied_apr_update( + handle: *mut FundingImpliedApr, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_funding_implied_apr_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_implied_apr_reset(handle: *mut FundingImpliedApr) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_funding_implied_apr_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_funding_implied_apr_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_implied_apr_free(handle: *mut FundingImpliedApr) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FundingRate` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_funding_rate_free`. +#[no_mangle] +pub extern "C" fn wickra_funding_rate_new() -> *mut FundingRate { + Box::into_raw(Box::new(FundingRate::new())) +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_funding_rate_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_rate_update( + handle: *mut FundingRate, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_funding_rate_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_rate_reset(handle: *mut FundingRate) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_funding_rate_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_funding_rate_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_rate_free(handle: *mut FundingRate) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FundingRateMean` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_funding_rate_mean_free`. +#[no_mangle] +pub extern "C" fn wickra_funding_rate_mean_new(window: usize) -> *mut FundingRateMean { + match FundingRateMean::new(window) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_funding_rate_mean_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_rate_mean_update( + handle: *mut FundingRateMean, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_funding_rate_mean_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_rate_mean_reset(handle: *mut FundingRateMean) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_funding_rate_mean_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_funding_rate_mean_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_rate_mean_free(handle: *mut FundingRateMean) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FundingRateZScore` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_funding_rate_z_score_free`. +#[no_mangle] +pub extern "C" fn wickra_funding_rate_z_score_new(window: usize) -> *mut FundingRateZScore { + match FundingRateZScore::new(window) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_funding_rate_z_score_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_rate_z_score_update( + handle: *mut FundingRateZScore, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_funding_rate_z_score_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_rate_z_score_reset(handle: *mut FundingRateZScore) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_funding_rate_z_score_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_funding_rate_z_score_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_funding_rate_z_score_free(handle: *mut FundingRateZScore) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LongShortRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_long_short_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_long_short_ratio_new() -> *mut LongShortRatio { + Box::into_raw(Box::new(LongShortRatio::new())) +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_long_short_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_long_short_ratio_update( + handle: *mut LongShortRatio, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_long_short_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_long_short_ratio_reset(handle: *mut LongShortRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_long_short_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_long_short_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_long_short_ratio_free(handle: *mut LongShortRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OpenInterestDelta` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_open_interest_delta_free`. +#[no_mangle] +pub extern "C" fn wickra_open_interest_delta_new() -> *mut OpenInterestDelta { + Box::into_raw(Box::new(OpenInterestDelta::new())) +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_open_interest_delta_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_open_interest_delta_update( + handle: *mut OpenInterestDelta, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_open_interest_delta_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_open_interest_delta_reset(handle: *mut OpenInterestDelta) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_open_interest_delta_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_open_interest_delta_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_open_interest_delta_free(handle: *mut OpenInterestDelta) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OIPriceDivergence` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_oi_price_divergence_free`. +#[no_mangle] +pub extern "C" fn wickra_oi_price_divergence_new(window: usize) -> *mut OIPriceDivergence { + match OIPriceDivergence::new(window) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_oi_price_divergence_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_oi_price_divergence_update( + handle: *mut OIPriceDivergence, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_oi_price_divergence_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_oi_price_divergence_reset(handle: *mut OIPriceDivergence) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_oi_price_divergence_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_oi_price_divergence_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_oi_price_divergence_free(handle: *mut OIPriceDivergence) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OiToVolumeRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_oi_to_volume_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_oi_to_volume_ratio_new() -> *mut OiToVolumeRatio { + Box::into_raw(Box::new(OiToVolumeRatio::new())) +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_oi_to_volume_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_oi_to_volume_ratio_update( + handle: *mut OiToVolumeRatio, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_oi_to_volume_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_oi_to_volume_ratio_reset(handle: *mut OiToVolumeRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_oi_to_volume_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_oi_to_volume_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_oi_to_volume_ratio_free(handle: *mut OiToVolumeRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OIWeighted` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_oi_weighted_free`. +#[no_mangle] +pub extern "C" fn wickra_oi_weighted_new() -> *mut OIWeighted { + Box::into_raw(Box::new(OIWeighted::new())) +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_oi_weighted_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_oi_weighted_update( + handle: *mut OIWeighted, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_oi_weighted_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_oi_weighted_reset(handle: *mut OIWeighted) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_oi_weighted_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_oi_weighted_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_oi_weighted_free(handle: *mut OIWeighted) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OpenInterestMomentum` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_open_interest_momentum_free`. +#[no_mangle] +pub extern "C" fn wickra_open_interest_momentum_new(period: usize) -> *mut OpenInterestMomentum { + match OpenInterestMomentum::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_open_interest_momentum_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_open_interest_momentum_update( + handle: *mut OpenInterestMomentum, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_open_interest_momentum_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_open_interest_momentum_reset(handle: *mut OpenInterestMomentum) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_open_interest_momentum_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_open_interest_momentum_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_open_interest_momentum_free(handle: *mut OpenInterestMomentum) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PerpetualPremiumIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_perpetual_premium_index_free`. +#[no_mangle] +pub extern "C" fn wickra_perpetual_premium_index_new() -> *mut PerpetualPremiumIndex { + Box::into_raw(Box::new(PerpetualPremiumIndex::new())) +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_perpetual_premium_index_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_perpetual_premium_index_update( + handle: *mut PerpetualPremiumIndex, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_perpetual_premium_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_perpetual_premium_index_reset(handle: *mut PerpetualPremiumIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_perpetual_premium_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_perpetual_premium_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_perpetual_premium_index_free(handle: *mut PerpetualPremiumIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TakerBuySellRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_taker_buy_sell_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_taker_buy_sell_ratio_new() -> *mut TakerBuySellRatio { + Box::into_raw(Box::new(TakerBuySellRatio::new())) +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_taker_buy_sell_ratio_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_taker_buy_sell_ratio_update( + handle: *mut TakerBuySellRatio, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_taker_buy_sell_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_taker_buy_sell_ratio_reset(handle: *mut TakerBuySellRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_taker_buy_sell_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_taker_buy_sell_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_taker_buy_sell_ratio_free(handle: *mut TakerBuySellRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TermStructureBasis` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_term_structure_basis_free`. +#[no_mangle] +pub extern "C" fn wickra_term_structure_basis_new() -> *mut TermStructureBasis { + Box::into_raw(Box::new(TermStructureBasis::new())) +} + +/// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a +/// `NULL` handle / if the tick is invalid. +/// +/// # Safety +/// `handle` must be a valid pointer from `wickra_term_structure_basis_new` (not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_term_structure_basis_update( + handle: *mut TermStructureBasis, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + match DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) { + Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_term_structure_basis_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_term_structure_basis_reset(handle: *mut TermStructureBasis) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_term_structure_basis_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_term_structure_basis_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_term_structure_basis_free(handle: *mut TermStructureBasis) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +// ===== Order-book-input indicators (microstructure) ===== + +/// Create a `DepthSlope` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_depth_slope_free`. +#[no_mangle] +pub extern "C" fn wickra_depth_slope_new() -> *mut DepthSlope { + Box::into_raw(Box::new(DepthSlope::new())) +} + +/// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the +/// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_depth_slope_new`, not freed) or `NULL`. `bid_price`/`bid_size` +/// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. +#[no_mangle] +pub unsafe extern "C" fn wickra_depth_slope_update( + handle: *mut DepthSlope, + bid_price: *const f64, + bid_size: *const f64, + n_bids: usize, + ask_price: *const f64, + ask_size: *const f64, + n_asks: usize, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { + return f64::NAN; + } + let bid_px = slice::from_raw_parts(bid_price, n_bids); + let bid_sz = slice::from_raw_parts(bid_size, n_bids); + let ask_px = slice::from_raw_parts(ask_price, n_asks); + let ask_sz = slice::from_raw_parts(ask_size, n_asks); + let mut bids = Vec::with_capacity(n_bids); + for (&px, &sz) in bid_px.iter().zip(bid_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + bids.push(level); + } + let mut asks = Vec::with_capacity(n_asks); + for (&px, &sz) in ask_px.iter().zip(ask_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + asks.push(level); + } + match OrderBook::new(bids, asks) { + Ok(book) => ind.update(book).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_depth_slope_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_depth_slope_reset(handle: *mut DepthSlope) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_depth_slope_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_depth_slope_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_depth_slope_free(handle: *mut DepthSlope) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Microprice` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_microprice_free`. +#[no_mangle] +pub extern "C" fn wickra_microprice_new() -> *mut Microprice { + Box::into_raw(Box::new(Microprice::new())) +} + +/// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the +/// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_microprice_new`, not freed) or `NULL`. `bid_price`/`bid_size` +/// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. +#[no_mangle] +pub unsafe extern "C" fn wickra_microprice_update( + handle: *mut Microprice, + bid_price: *const f64, + bid_size: *const f64, + n_bids: usize, + ask_price: *const f64, + ask_size: *const f64, + n_asks: usize, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { + return f64::NAN; + } + let bid_px = slice::from_raw_parts(bid_price, n_bids); + let bid_sz = slice::from_raw_parts(bid_size, n_bids); + let ask_px = slice::from_raw_parts(ask_price, n_asks); + let ask_sz = slice::from_raw_parts(ask_size, n_asks); + let mut bids = Vec::with_capacity(n_bids); + for (&px, &sz) in bid_px.iter().zip(bid_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + bids.push(level); + } + let mut asks = Vec::with_capacity(n_asks); + for (&px, &sz) in ask_px.iter().zip(ask_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + asks.push(level); + } + match OrderBook::new(bids, asks) { + Ok(book) => ind.update(book).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_microprice_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_microprice_reset(handle: *mut Microprice) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_microprice_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_microprice_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_microprice_free(handle: *mut Microprice) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OrderBookImbalanceFull` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_order_book_imbalance_full_free`. +#[no_mangle] +pub extern "C" fn wickra_order_book_imbalance_full_new() -> *mut OrderBookImbalanceFull { + Box::into_raw(Box::new(OrderBookImbalanceFull::new())) +} + +/// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the +/// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_order_book_imbalance_full_new`, not freed) or `NULL`. `bid_price`/`bid_size` +/// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_book_imbalance_full_update( + handle: *mut OrderBookImbalanceFull, + bid_price: *const f64, + bid_size: *const f64, + n_bids: usize, + ask_price: *const f64, + ask_size: *const f64, + n_asks: usize, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { + return f64::NAN; + } + let bid_px = slice::from_raw_parts(bid_price, n_bids); + let bid_sz = slice::from_raw_parts(bid_size, n_bids); + let ask_px = slice::from_raw_parts(ask_price, n_asks); + let ask_sz = slice::from_raw_parts(ask_size, n_asks); + let mut bids = Vec::with_capacity(n_bids); + for (&px, &sz) in bid_px.iter().zip(bid_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + bids.push(level); + } + let mut asks = Vec::with_capacity(n_asks); + for (&px, &sz) in ask_px.iter().zip(ask_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + asks.push(level); + } + match OrderBook::new(bids, asks) { + Ok(book) => ind.update(book).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_order_book_imbalance_full_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_book_imbalance_full_reset( + handle: *mut OrderBookImbalanceFull, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_order_book_imbalance_full_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_order_book_imbalance_full_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_book_imbalance_full_free( + handle: *mut OrderBookImbalanceFull, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OrderBookImbalanceTop1` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_order_book_imbalance_top1_free`. +#[no_mangle] +pub extern "C" fn wickra_order_book_imbalance_top1_new() -> *mut OrderBookImbalanceTop1 { + Box::into_raw(Box::new(OrderBookImbalanceTop1::new())) +} + +/// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the +/// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_order_book_imbalance_top1_new`, not freed) or `NULL`. `bid_price`/`bid_size` +/// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_book_imbalance_top1_update( + handle: *mut OrderBookImbalanceTop1, + bid_price: *const f64, + bid_size: *const f64, + n_bids: usize, + ask_price: *const f64, + ask_size: *const f64, + n_asks: usize, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { + return f64::NAN; + } + let bid_px = slice::from_raw_parts(bid_price, n_bids); + let bid_sz = slice::from_raw_parts(bid_size, n_bids); + let ask_px = slice::from_raw_parts(ask_price, n_asks); + let ask_sz = slice::from_raw_parts(ask_size, n_asks); + let mut bids = Vec::with_capacity(n_bids); + for (&px, &sz) in bid_px.iter().zip(bid_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + bids.push(level); + } + let mut asks = Vec::with_capacity(n_asks); + for (&px, &sz) in ask_px.iter().zip(ask_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + asks.push(level); + } + match OrderBook::new(bids, asks) { + Ok(book) => ind.update(book).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_order_book_imbalance_top1_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_book_imbalance_top1_reset( + handle: *mut OrderBookImbalanceTop1, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_order_book_imbalance_top1_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_order_book_imbalance_top1_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_book_imbalance_top1_free( + handle: *mut OrderBookImbalanceTop1, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OrderBookImbalanceTopN` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_order_book_imbalance_top_n_free`. +#[no_mangle] +pub extern "C" fn wickra_order_book_imbalance_top_n_new( + levels: usize, +) -> *mut OrderBookImbalanceTopN { + match OrderBookImbalanceTopN::new(levels) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the +/// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_order_book_imbalance_top_n_new`, not freed) or `NULL`. `bid_price`/`bid_size` +/// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_book_imbalance_top_n_update( + handle: *mut OrderBookImbalanceTopN, + bid_price: *const f64, + bid_size: *const f64, + n_bids: usize, + ask_price: *const f64, + ask_size: *const f64, + n_asks: usize, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { + return f64::NAN; + } + let bid_px = slice::from_raw_parts(bid_price, n_bids); + let bid_sz = slice::from_raw_parts(bid_size, n_bids); + let ask_px = slice::from_raw_parts(ask_price, n_asks); + let ask_sz = slice::from_raw_parts(ask_size, n_asks); + let mut bids = Vec::with_capacity(n_bids); + for (&px, &sz) in bid_px.iter().zip(bid_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + bids.push(level); + } + let mut asks = Vec::with_capacity(n_asks); + for (&px, &sz) in ask_px.iter().zip(ask_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + asks.push(level); + } + match OrderBook::new(bids, asks) { + Ok(book) => ind.update(book).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_order_book_imbalance_top_n_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_book_imbalance_top_n_reset( + handle: *mut OrderBookImbalanceTopN, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_order_book_imbalance_top_n_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_order_book_imbalance_top_n_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_book_imbalance_top_n_free( + handle: *mut OrderBookImbalanceTopN, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OrderFlowImbalance` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_order_flow_imbalance_free`. +#[no_mangle] +pub extern "C" fn wickra_order_flow_imbalance_new(period: usize) -> *mut OrderFlowImbalance { + match OrderFlowImbalance::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the +/// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_order_flow_imbalance_new`, not freed) or `NULL`. `bid_price`/`bid_size` +/// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_flow_imbalance_update( + handle: *mut OrderFlowImbalance, + bid_price: *const f64, + bid_size: *const f64, + n_bids: usize, + ask_price: *const f64, + ask_size: *const f64, + n_asks: usize, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { + return f64::NAN; + } + let bid_px = slice::from_raw_parts(bid_price, n_bids); + let bid_sz = slice::from_raw_parts(bid_size, n_bids); + let ask_px = slice::from_raw_parts(ask_price, n_asks); + let ask_sz = slice::from_raw_parts(ask_size, n_asks); + let mut bids = Vec::with_capacity(n_bids); + for (&px, &sz) in bid_px.iter().zip(bid_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + bids.push(level); + } + let mut asks = Vec::with_capacity(n_asks); + for (&px, &sz) in ask_px.iter().zip(ask_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + asks.push(level); + } + match OrderBook::new(bids, asks) { + Ok(book) => ind.update(book).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_order_flow_imbalance_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_flow_imbalance_reset(handle: *mut OrderFlowImbalance) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_order_flow_imbalance_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_order_flow_imbalance_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_order_flow_imbalance_free(handle: *mut OrderFlowImbalance) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `QuotedSpread` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_quoted_spread_free`. +#[no_mangle] +pub extern "C" fn wickra_quoted_spread_new() -> *mut QuotedSpread { + Box::into_raw(Box::new(QuotedSpread::new())) +} + +/// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the +/// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_quoted_spread_new`, not freed) or `NULL`. `bid_price`/`bid_size` +/// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. +#[no_mangle] +pub unsafe extern "C" fn wickra_quoted_spread_update( + handle: *mut QuotedSpread, + bid_price: *const f64, + bid_size: *const f64, + n_bids: usize, + ask_price: *const f64, + ask_size: *const f64, + n_asks: usize, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { + return f64::NAN; + } + let bid_px = slice::from_raw_parts(bid_price, n_bids); + let bid_sz = slice::from_raw_parts(bid_size, n_bids); + let ask_px = slice::from_raw_parts(ask_price, n_asks); + let ask_sz = slice::from_raw_parts(ask_size, n_asks); + let mut bids = Vec::with_capacity(n_bids); + for (&px, &sz) in bid_px.iter().zip(bid_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + bids.push(level); + } + let mut asks = Vec::with_capacity(n_asks); + for (&px, &sz) in ask_px.iter().zip(ask_sz) { + let Ok(level) = Level::new(px, sz) else { + return f64::NAN; + }; + asks.push(level); + } + match OrderBook::new(bids, asks) { + Ok(book) => ind.update(book).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_quoted_spread_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_quoted_spread_reset(handle: *mut QuotedSpread) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_quoted_spread_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_quoted_spread_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_quoted_spread_free(handle: *mut QuotedSpread) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +// ===== Cross-section-input indicators (market breadth) ===== + +/// Create a `AbsoluteBreadthIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_absolute_breadth_index_free`. +#[no_mangle] +pub extern "C" fn wickra_absolute_breadth_index_new() -> *mut AbsoluteBreadthIndex { + Box::into_raw(Box::new(AbsoluteBreadthIndex::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_absolute_breadth_index_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_absolute_breadth_index_update( + handle: *mut AbsoluteBreadthIndex, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_absolute_breadth_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_absolute_breadth_index_reset(handle: *mut AbsoluteBreadthIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_absolute_breadth_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_absolute_breadth_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_absolute_breadth_index_free(handle: *mut AbsoluteBreadthIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AdVolumeLine` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ad_volume_line_free`. +#[no_mangle] +pub extern "C" fn wickra_ad_volume_line_new() -> *mut AdVolumeLine { + Box::into_raw(Box::new(AdVolumeLine::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_ad_volume_line_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_ad_volume_line_update( + handle: *mut AdVolumeLine, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ad_volume_line_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ad_volume_line_reset(handle: *mut AdVolumeLine) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ad_volume_line_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ad_volume_line_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ad_volume_line_free(handle: *mut AdVolumeLine) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AdvanceDecline` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_advance_decline_free`. +#[no_mangle] +pub extern "C" fn wickra_advance_decline_new() -> *mut AdvanceDecline { + Box::into_raw(Box::new(AdvanceDecline::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_advance_decline_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_advance_decline_update( + handle: *mut AdvanceDecline, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_advance_decline_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_advance_decline_reset(handle: *mut AdvanceDecline) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_advance_decline_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_advance_decline_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_advance_decline_free(handle: *mut AdvanceDecline) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AdvanceDeclineRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_advance_decline_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_advance_decline_ratio_new() -> *mut AdvanceDeclineRatio { + Box::into_raw(Box::new(AdvanceDeclineRatio::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_advance_decline_ratio_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_advance_decline_ratio_update( + handle: *mut AdvanceDeclineRatio, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_advance_decline_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_advance_decline_ratio_reset(handle: *mut AdvanceDeclineRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_advance_decline_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_advance_decline_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_advance_decline_ratio_free(handle: *mut AdvanceDeclineRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BreadthThrust` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_breadth_thrust_free`. +#[no_mangle] +pub extern "C" fn wickra_breadth_thrust_new(period: usize) -> *mut BreadthThrust { + match BreadthThrust::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_breadth_thrust_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_breadth_thrust_update( + handle: *mut BreadthThrust, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_breadth_thrust_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_breadth_thrust_reset(handle: *mut BreadthThrust) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_breadth_thrust_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_breadth_thrust_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_breadth_thrust_free(handle: *mut BreadthThrust) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BullishPercentIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_bullish_percent_index_free`. +#[no_mangle] +pub extern "C" fn wickra_bullish_percent_index_new() -> *mut BullishPercentIndex { + Box::into_raw(Box::new(BullishPercentIndex::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_bullish_percent_index_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_bullish_percent_index_update( + handle: *mut BullishPercentIndex, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_bullish_percent_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bullish_percent_index_reset(handle: *mut BullishPercentIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_bullish_percent_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_bullish_percent_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bullish_percent_index_free(handle: *mut BullishPercentIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CumulativeVolumeIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_cumulative_volume_index_free`. +#[no_mangle] +pub extern "C" fn wickra_cumulative_volume_index_new() -> *mut CumulativeVolumeIndex { + Box::into_raw(Box::new(CumulativeVolumeIndex::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_cumulative_volume_index_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_cumulative_volume_index_update( + handle: *mut CumulativeVolumeIndex, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_cumulative_volume_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cumulative_volume_index_reset(handle: *mut CumulativeVolumeIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_cumulative_volume_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_cumulative_volume_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cumulative_volume_index_free(handle: *mut CumulativeVolumeIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HighLowIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_high_low_index_free`. +#[no_mangle] +pub extern "C" fn wickra_high_low_index_new(period: usize) -> *mut HighLowIndex { + match HighLowIndex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_high_low_index_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_high_low_index_update( + handle: *mut HighLowIndex, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_high_low_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_low_index_reset(handle: *mut HighLowIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_high_low_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_high_low_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_low_index_free(handle: *mut HighLowIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `McClellanOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_mc_clellan_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_mc_clellan_oscillator_new() -> *mut McClellanOscillator { + Box::into_raw(Box::new(McClellanOscillator::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_mc_clellan_oscillator_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_mc_clellan_oscillator_update( + handle: *mut McClellanOscillator, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_mc_clellan_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mc_clellan_oscillator_reset(handle: *mut McClellanOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_mc_clellan_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_mc_clellan_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mc_clellan_oscillator_free(handle: *mut McClellanOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `McClellanSummationIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_mc_clellan_summation_index_free`. +#[no_mangle] +pub extern "C" fn wickra_mc_clellan_summation_index_new() -> *mut McClellanSummationIndex { + Box::into_raw(Box::new(McClellanSummationIndex::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_mc_clellan_summation_index_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_mc_clellan_summation_index_update( + handle: *mut McClellanSummationIndex, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_mc_clellan_summation_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mc_clellan_summation_index_reset( + handle: *mut McClellanSummationIndex, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_mc_clellan_summation_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_mc_clellan_summation_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mc_clellan_summation_index_free( + handle: *mut McClellanSummationIndex, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `NewHighsNewLows` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_new_highs_new_lows_free`. +#[no_mangle] +pub extern "C" fn wickra_new_highs_new_lows_new() -> *mut NewHighsNewLows { + Box::into_raw(Box::new(NewHighsNewLows::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_new_highs_new_lows_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_new_highs_new_lows_update( + handle: *mut NewHighsNewLows, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_new_highs_new_lows_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_new_highs_new_lows_reset(handle: *mut NewHighsNewLows) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_new_highs_new_lows_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_new_highs_new_lows_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_new_highs_new_lows_free(handle: *mut NewHighsNewLows) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PercentAboveMa` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_percent_above_ma_free`. +#[no_mangle] +pub extern "C" fn wickra_percent_above_ma_new() -> *mut PercentAboveMa { + Box::into_raw(Box::new(PercentAboveMa::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_percent_above_ma_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_percent_above_ma_update( + handle: *mut PercentAboveMa, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_percent_above_ma_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_percent_above_ma_reset(handle: *mut PercentAboveMa) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_percent_above_ma_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_percent_above_ma_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_percent_above_ma_free(handle: *mut PercentAboveMa) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TickIndex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tick_index_free`. +#[no_mangle] +pub extern "C" fn wickra_tick_index_new() -> *mut TickIndex { + Box::into_raw(Box::new(TickIndex::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_tick_index_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_tick_index_update( + handle: *mut TickIndex, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tick_index_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tick_index_reset(handle: *mut TickIndex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tick_index_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tick_index_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tick_index_free(handle: *mut TickIndex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Trin` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_trin_free`. +#[no_mangle] +pub extern "C" fn wickra_trin_new() -> *mut Trin { + Box::into_raw(Box::new(Trin::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_trin_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_trin_update( + handle: *mut Trin, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_trin_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trin_reset(handle: *mut Trin) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_trin_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_trin_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_trin_free(handle: *mut Trin) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `UpDownVolumeRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_up_down_volume_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_up_down_volume_ratio_new() -> *mut UpDownVolumeRatio { + Box::into_raw(Box::new(UpDownVolumeRatio::new())) +} + +/// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, +/// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. +/// +/// # Safety +/// `handle` valid (from `wickra_up_down_volume_ratio_new`, not freed) or `NULL`. Every member array +/// covers `n` elements. +#[no_mangle] +#[allow(clippy::needless_range_loop)] +pub unsafe extern "C" fn wickra_up_down_volume_ratio_update( + handle: *mut UpDownVolumeRatio, + change: *const f64, + volume: *const f64, + new_high: *const bool, + new_low: *const bool, + above_ma: *const bool, + on_buy_signal: *const bool, + n: usize, + timestamp: i64, +) -> f64 { + let Some(ind) = handle.as_mut() else { + return f64::NAN; + }; + if change.is_null() + || volume.is_null() + || new_high.is_null() + || new_low.is_null() + || above_ma.is_null() + || on_buy_signal.is_null() + { + return f64::NAN; + } + let changes = slice::from_raw_parts(change, n); + let volumes = slice::from_raw_parts(volume, n); + let new_highs = slice::from_raw_parts(new_high, n); + let new_lows = slice::from_raw_parts(new_low, n); + let above_mas = slice::from_raw_parts(above_ma, n); + let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); + let mut members = Vec::with_capacity(n); + for idx in 0..n { + let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); + member.above_ma = above_mas[idx]; + member.on_buy_signal = on_buy_signals[idx]; + members.push(member); + } + match CrossSection::new(members, timestamp) { + Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), + Err(_) => f64::NAN, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_up_down_volume_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_up_down_volume_ratio_reset(handle: *mut UpDownVolumeRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_up_down_volume_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_up_down_volume_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_up_down_volume_ratio_free(handle: *mut UpDownVolumeRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +// ===== Multi-output indicators (fixed-size struct via out-param) ===== + +/// C-ABI view of `AccelerationBandsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraAccelerationBandsOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `AdxOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraAdxOutput { + pub plus_di: f64, + pub minus_di: f64, + pub adx: f64, +} + +/// C-ABI view of `AlligatorOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraAlligatorOutput { + pub jaw: f64, + pub teeth: f64, + pub lips: f64, +} + +/// C-ABI view of `AndrewsPitchforkOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraAndrewsPitchforkOutput { + pub median: f64, + pub upper: f64, + pub lower: f64, +} + +/// C-ABI view of `AroonOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraAroonOutput { + pub up: f64, + pub down: f64, +} + +/// C-ABI view of `AtrBandsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraAtrBandsOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `AtrRatchetOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraAtrRatchetOutput { + pub value: f64, + pub direction: f64, +} + +/// C-ABI view of `AutoFibOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraAutoFibOutput { + pub level_0: f64, + pub level_236: f64, + pub level_382: f64, + pub level_500: f64, + pub level_618: f64, + pub level_786: f64, + pub level_1000: f64, +} + +/// C-ABI view of `BollingerOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraBollingerOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, + pub stddev: f64, +} + +/// C-ABI view of `BomarBandsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraBomarBandsOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `CamarillaPivotsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraCamarillaPivotsOutput { + pub pp: f64, + pub r1: f64, + pub r2: f64, + pub r3: f64, + pub r4: f64, + pub s1: f64, + pub s2: f64, + pub s3: f64, + pub s4: f64, +} + +/// C-ABI view of `CandleVolumeOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraCandleVolumeOutput { + pub body: f64, + pub width: f64, +} + +/// C-ABI view of `CentralPivotRangeOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraCentralPivotRangeOutput { + pub pivot: f64, + pub tc: f64, + pub bc: f64, +} + +/// C-ABI view of `ChandeKrollStopOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraChandeKrollStopOutput { + pub stop_long: f64, + pub stop_short: f64, +} + +/// C-ABI view of `ChandelierExitOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraChandelierExitOutput { + pub long_stop: f64, + pub short_stop: f64, +} + +/// C-ABI view of `ClassicPivotsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraClassicPivotsOutput { + pub pp: f64, + pub r1: f64, + pub r2: f64, + pub r3: f64, + pub s1: f64, + pub s2: f64, + pub s3: f64, +} + +/// C-ABI view of `CointegrationOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraCointegrationOutput { + pub hedge_ratio: f64, + pub spread: f64, + pub adf_stat: f64, +} + +/// C-ABI view of `CompositeProfileOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraCompositeProfileOutput { + pub poc: f64, + pub vah: f64, + pub val: f64, +} + +/// C-ABI view of `DemarkPivotsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraDemarkPivotsOutput { + pub pp: f64, + pub r1: f64, + pub s1: f64, +} + +/// C-ABI view of `DonchianOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraDonchianOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `DonchianStopOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraDonchianStopOutput { + pub stop_long: f64, + pub stop_short: f64, +} + +/// C-ABI view of `DoubleBollingerOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraDoubleBollingerOutput { + pub upper_outer: f64, + pub upper_inner: f64, + pub middle: f64, + pub lower_inner: f64, + pub lower_outer: f64, +} + +/// C-ABI view of `ElderRayOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraElderRayOutput { + pub bull_power: f64, + pub bear_power: f64, +} + +/// C-ABI view of `ElderSafeZoneOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraElderSafeZoneOutput { + pub value: f64, + pub direction: f64, +} + +/// C-ABI view of `EquivolumeOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraEquivolumeOutput { + pub height: f64, + pub width: f64, +} + +/// C-ABI view of `FibArcsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraFibArcsOutput { + pub arc_382: f64, + pub arc_500: f64, + pub arc_618: f64, +} + +/// C-ABI view of `FibChannelOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraFibChannelOutput { + pub base: f64, + pub level_618: f64, + pub level_1000: f64, + pub level_1618: f64, +} + +/// C-ABI view of `FibConfluenceOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraFibConfluenceOutput { + pub price: f64, + pub strength: f64, +} + +/// C-ABI view of `FibExtensionOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraFibExtensionOutput { + pub level_1272: f64, + pub level_1414: f64, + pub level_1618: f64, + pub level_2000: f64, + pub level_2618: f64, +} + +/// C-ABI view of `FibFanOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraFibFanOutput { + pub fan_382: f64, + pub fan_500: f64, + pub fan_618: f64, +} + +/// C-ABI view of `FibProjectionOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraFibProjectionOutput { + pub level_618: f64, + pub level_1000: f64, + pub level_1618: f64, + pub level_2618: f64, +} + +/// C-ABI view of `FibRetracementOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraFibRetracementOutput { + pub level_0: f64, + pub level_236: f64, + pub level_382: f64, + pub level_500: f64, + pub level_618: f64, + pub level_786: f64, + pub level_1000: f64, +} + +/// C-ABI view of `FibTimeZonesOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraFibTimeZonesOutput { + pub on_zone: f64, + pub bars_to_next: f64, +} + +/// C-ABI view of `FibonacciPivotsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraFibonacciPivotsOutput { + pub pp: f64, + pub r1: f64, + pub r2: f64, + pub r3: f64, + pub s1: f64, + pub s2: f64, + pub s3: f64, +} + +/// C-ABI view of `FractalChaosBandsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraFractalChaosBandsOutput { + pub upper: f64, + pub lower: f64, +} + +/// C-ABI view of `GatorOscillatorOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraGatorOscillatorOutput { + pub upper: f64, + pub lower: f64, +} + +/// C-ABI view of `GoldenPocketOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraGoldenPocketOutput { + pub low: f64, + pub mid: f64, + pub high: f64, +} + +/// C-ABI view of `HeikinAshiOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraHeikinAshiOutput { + pub open: f64, + pub high: f64, + pub low: f64, + pub close: f64, +} + +/// C-ABI view of `HighLowVolumeNodesOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraHighLowVolumeNodesOutput { + pub hvn: f64, + pub lvn: f64, +} + +/// C-ABI view of `HtPhasorOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraHtPhasorOutput { + pub inphase: f64, + pub quadrature: f64, +} + +/// C-ABI view of `HurstChannelOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraHurstChannelOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `IchimokuOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraIchimokuOutput { + pub tenkan: f64, + pub kijun: f64, + pub senkou_a: f64, + pub senkou_b: f64, + pub chikou: f64, +} + +/// C-ABI view of `InitialBalanceOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraInitialBalanceOutput { + pub high: f64, + pub low: f64, +} + +/// C-ABI view of `KalmanHedgeRatioOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraKalmanHedgeRatioOutput { + pub hedge_ratio: f64, + pub intercept: f64, + pub spread: f64, +} + +/// C-ABI view of `KaseDevStopOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraKaseDevStopOutput { + pub value: f64, + pub direction: f64, +} + +/// C-ABI view of `KasePermissionStochasticOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraKasePermissionStochasticOutput { + pub fast: f64, + pub slow: f64, +} + +/// C-ABI view of `KeltnerOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraKeltnerOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `KstOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraKstOutput { + pub kst: f64, + pub signal: f64, +} + +/// C-ABI view of `LeadLagCrossCorrelationOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraLeadLagCrossCorrelationOutput { + pub lag: i64, + pub correlation: f64, +} + +/// C-ABI view of `LinRegChannelOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraLinRegChannelOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `LiquidationFeaturesOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraLiquidationFeaturesOutput { + pub long: f64, + pub short: f64, + pub net: f64, + pub total: f64, + pub imbalance: f64, +} + +/// C-ABI view of `MaEnvelopeOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraMaEnvelopeOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `MacdOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraMacdOutput { + pub macd: f64, + pub signal: f64, + pub histogram: f64, +} + +/// C-ABI view of `MamaOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraMamaOutput { + pub mama: f64, + pub fama: f64, +} + +/// C-ABI view of `MedianChannelOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraMedianChannelOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `ModifiedMaStopOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraModifiedMaStopOutput { + pub value: f64, + pub direction: f64, +} + +/// C-ABI view of `MurreyMathLinesOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraMurreyMathLinesOutput { + pub mm8_8: f64, + pub mm7_8: f64, + pub mm6_8: f64, + pub mm5_8: f64, + pub mm4_8: f64, + pub mm3_8: f64, + pub mm2_8: f64, + pub mm1_8: f64, + pub mm0_8: f64, +} + +/// C-ABI view of `NrtrOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraNrtrOutput { + pub value: f64, + pub direction: f64, +} + +/// C-ABI view of `OpeningRangeOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraOpeningRangeOutput { + pub high: f64, + pub low: f64, + pub breakout_distance: f64, +} + +/// C-ABI view of `OvernightIntradayReturnOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraOvernightIntradayReturnOutput { + pub overnight: f64, + pub intraday: f64, +} + +/// C-ABI view of `ProjectionBandsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraProjectionBandsOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `QqeOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraQqeOutput { + pub rsi_ma: f64, + pub trailing_line: f64, +} + +/// C-ABI view of `QuartileBandsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraQuartileBandsOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `RelativeStrengthOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraRelativeStrengthOutput { + pub ratio: f64, + pub ratio_ma: f64, + pub ratio_rsi: f64, +} + +/// C-ABI view of `RwiOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraRwiOutput { + pub high: f64, + pub low: f64, +} + +/// C-ABI view of `SessionHighLowOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraSessionHighLowOutput { + pub high: f64, + pub low: f64, +} + +/// C-ABI view of `SessionRangeOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraSessionRangeOutput { + pub asia: f64, + pub eu: f64, + pub us: f64, +} + +/// C-ABI view of `SmoothedHeikinAshiOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraSmoothedHeikinAshiOutput { + pub open: f64, + pub high: f64, + pub low: f64, + pub close: f64, +} + +/// C-ABI view of `SpreadBollingerBandsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraSpreadBollingerBandsOutput { + pub middle: f64, + pub upper: f64, + pub lower: f64, + pub percent_b: f64, +} + +/// C-ABI view of `StandardErrorBandsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraStandardErrorBandsOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `StarcBandsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraStarcBandsOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, +} + +/// C-ABI view of `StochasticOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraStochasticOutput { + pub k: f64, + pub d: f64, +} + +/// C-ABI view of `SuperTrendOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraSuperTrendOutput { + pub value: f64, + pub direction: f64, +} + +/// C-ABI view of `TdLinesOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraTdLinesOutput { + pub resistance: f64, + pub support: f64, +} + +/// C-ABI view of `TdMovingAverageOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraTdMovingAverageOutput { + pub st1: f64, + pub st2: f64, +} + +/// C-ABI view of `TdRangeProjectionOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraTdRangeProjectionOutput { + pub high: f64, + pub low: f64, +} + +/// C-ABI view of `TdRiskLevelOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraTdRiskLevelOutput { + pub buy_risk: f64, + pub sell_risk: f64, +} + +/// C-ABI view of `TdSequentialOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraTdSequentialOutput { + pub setup: f64, + pub countdown: f64, + pub direction: f64, +} + +/// C-ABI view of `TtmSqueezeOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraTtmSqueezeOutput { + pub squeeze: f64, + pub momentum: f64, +} + +/// C-ABI view of `ValueAreaOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraValueAreaOutput { + pub poc: f64, + pub vah: f64, + pub val: f64, +} + +/// C-ABI view of `VolatilityConeOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraVolatilityConeOutput { + pub current: f64, + pub min: f64, + pub median: f64, + pub max: f64, + pub percentile: f64, +} + +/// C-ABI view of `VolumeWeightedMacdOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraVolumeWeightedMacdOutput { + pub macd: f64, + pub signal: f64, + pub histogram: f64, +} + +/// C-ABI view of `VolumeWeightedSrOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraVolumeWeightedSrOutput { + pub support: f64, + pub resistance: f64, +} + +/// C-ABI view of `VortexOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraVortexOutput { + pub plus: f64, + pub minus: f64, +} + +/// C-ABI view of `VwapStdDevBandsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraVwapStdDevBandsOutput { + pub upper: f64, + pub middle: f64, + pub lower: f64, + pub stddev: f64, +} + +/// C-ABI view of `WaveTrendOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraWaveTrendOutput { + pub wt1: f64, + pub wt2: f64, +} + +/// C-ABI view of `WilliamsFractalsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraWilliamsFractalsOutput { + pub up: f64, + pub down: f64, +} + +/// C-ABI view of `WoodiePivotsOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraWoodiePivotsOutput { + pub pp: f64, + pub r1: f64, + pub r2: f64, + pub s1: f64, + pub s2: f64, +} + +/// C-ABI view of `ZeroLagMacdOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraZeroLagMacdOutput { + pub macd: f64, + pub signal: f64, + pub histogram: f64, +} + +/// C-ABI view of `ZigZagOutput` (fixed-size; `Option` fields surface as `NaN`). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraZigZagOutput { + pub swing: f64, + pub direction: f64, +} + +/// Create a `AccelerationBands` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_acceleration_bands_free`. +#[no_mangle] +pub extern "C" fn wickra_acceleration_bands_new( + period: usize, + factor: f64, +) -> *mut AccelerationBands { + match AccelerationBands::new(period, factor) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_acceleration_bands_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_acceleration_bands_update( + handle: *mut AccelerationBands, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraAccelerationBandsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraAccelerationBandsOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_acceleration_bands_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_acceleration_bands_reset(handle: *mut AccelerationBands) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_acceleration_bands_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_acceleration_bands_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_acceleration_bands_free(handle: *mut AccelerationBands) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Adx` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_adx_free`. +#[no_mangle] +pub extern "C" fn wickra_adx_new(period: usize) -> *mut Adx { + match Adx::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_adx_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adx_update( + handle: *mut Adx, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraAdxOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraAdxOutput { + plus_di: out_val.plus_di, + minus_di: out_val.minus_di, + adx: out_val.adx, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_adx_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adx_reset(handle: *mut Adx) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_adx_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_adx_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_adx_free(handle: *mut Adx) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Alligator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_alligator_free`. +#[no_mangle] +pub extern "C" fn wickra_alligator_new( + jaw_period: usize, + teeth_period: usize, + lips_period: usize, +) -> *mut Alligator { + match Alligator::new(jaw_period, teeth_period, lips_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_alligator_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_alligator_update( + handle: *mut Alligator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraAlligatorOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraAlligatorOutput { + jaw: out_val.jaw, + teeth: out_val.teeth, + lips: out_val.lips, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_alligator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_alligator_reset(handle: *mut Alligator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_alligator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_alligator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_alligator_free(handle: *mut Alligator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AndrewsPitchfork` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_andrews_pitchfork_free`. +#[no_mangle] +pub extern "C" fn wickra_andrews_pitchfork_new(strength: usize) -> *mut AndrewsPitchfork { + match AndrewsPitchfork::new(strength) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_andrews_pitchfork_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_andrews_pitchfork_update( + handle: *mut AndrewsPitchfork, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraAndrewsPitchforkOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraAndrewsPitchforkOutput { + median: out_val.median, + upper: out_val.upper, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_andrews_pitchfork_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_andrews_pitchfork_reset(handle: *mut AndrewsPitchfork) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_andrews_pitchfork_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_andrews_pitchfork_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_andrews_pitchfork_free(handle: *mut AndrewsPitchfork) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Aroon` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_aroon_free`. +#[no_mangle] +pub extern "C" fn wickra_aroon_new(period: usize) -> *mut Aroon { + match Aroon::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_aroon_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_aroon_update( + handle: *mut Aroon, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraAroonOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraAroonOutput { + up: out_val.up, + down: out_val.down, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_aroon_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_aroon_reset(handle: *mut Aroon) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_aroon_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_aroon_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_aroon_free(handle: *mut Aroon) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AtrBands` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_atr_bands_free`. +#[no_mangle] +pub extern "C" fn wickra_atr_bands_new(period: usize, multiplier: f64) -> *mut AtrBands { + match AtrBands::new(period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_atr_bands_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_bands_update( + handle: *mut AtrBands, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraAtrBandsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraAtrBandsOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_atr_bands_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_bands_reset(handle: *mut AtrBands) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_atr_bands_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_atr_bands_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_bands_free(handle: *mut AtrBands) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AtrRatchet` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_atr_ratchet_free`. +#[no_mangle] +pub extern "C" fn wickra_atr_ratchet_new( + atr_period: usize, + start_mult: f64, + increment: f64, +) -> *mut AtrRatchet { + match AtrRatchet::new(atr_period, start_mult, increment) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_atr_ratchet_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_ratchet_update( + handle: *mut AtrRatchet, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraAtrRatchetOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraAtrRatchetOutput { + value: out_val.value, + direction: out_val.direction, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_atr_ratchet_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_ratchet_reset(handle: *mut AtrRatchet) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_atr_ratchet_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_atr_ratchet_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_atr_ratchet_free(handle: *mut AtrRatchet) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `AutoFib` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_auto_fib_free`. +#[no_mangle] +pub extern "C" fn wickra_auto_fib_new() -> *mut AutoFib { + Box::into_raw(Box::new(AutoFib::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_auto_fib_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_auto_fib_update( + handle: *mut AutoFib, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraAutoFibOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraAutoFibOutput { + level_0: out_val.level_0, + level_236: out_val.level_236, + level_382: out_val.level_382, + level_500: out_val.level_500, + level_618: out_val.level_618, + level_786: out_val.level_786, + level_1000: out_val.level_1000, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_auto_fib_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_auto_fib_reset(handle: *mut AutoFib) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_auto_fib_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_auto_fib_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_auto_fib_free(handle: *mut AutoFib) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BollingerBands` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_bollinger_bands_free`. +#[no_mangle] +pub extern "C" fn wickra_bollinger_bands_new( + period: usize, + multiplier: f64, +) -> *mut BollingerBands { + match BollingerBands::new(period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_bollinger_bands_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bollinger_bands_update( + handle: *mut BollingerBands, + value: f64, + out: *mut WickraBollingerOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraBollingerOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + stddev: out_val.stddev, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_bollinger_bands_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bollinger_bands_reset(handle: *mut BollingerBands) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_bollinger_bands_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_bollinger_bands_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bollinger_bands_free(handle: *mut BollingerBands) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `BomarBands` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_bomar_bands_free`. +#[no_mangle] +pub extern "C" fn wickra_bomar_bands_new(period: usize, coverage: f64) -> *mut BomarBands { + match BomarBands::new(period, coverage) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_bomar_bands_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bomar_bands_update( + handle: *mut BomarBands, + value: f64, + out: *mut WickraBomarBandsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraBomarBandsOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_bomar_bands_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bomar_bands_reset(handle: *mut BomarBands) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_bomar_bands_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_bomar_bands_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_bomar_bands_free(handle: *mut BomarBands) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Camarilla` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_camarilla_free`. +#[no_mangle] +pub extern "C" fn wickra_camarilla_new() -> *mut Camarilla { + Box::into_raw(Box::new(Camarilla::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_camarilla_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_camarilla_update( + handle: *mut Camarilla, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraCamarillaPivotsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraCamarillaPivotsOutput { + pp: out_val.pp, + r1: out_val.r1, + r2: out_val.r2, + r3: out_val.r3, + r4: out_val.r4, + s1: out_val.s1, + s2: out_val.s2, + s3: out_val.s3, + s4: out_val.s4, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_camarilla_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_camarilla_reset(handle: *mut Camarilla) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_camarilla_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_camarilla_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_camarilla_free(handle: *mut Camarilla) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CandleVolume` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_candle_volume_free`. +#[no_mangle] +pub extern "C" fn wickra_candle_volume_new(period: usize) -> *mut CandleVolume { + match CandleVolume::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_candle_volume_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_candle_volume_update( + handle: *mut CandleVolume, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraCandleVolumeOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraCandleVolumeOutput { + body: out_val.body, + width: out_val.width, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_candle_volume_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_candle_volume_reset(handle: *mut CandleVolume) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_candle_volume_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_candle_volume_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_candle_volume_free(handle: *mut CandleVolume) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CentralPivotRange` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_central_pivot_range_free`. +#[no_mangle] +pub extern "C" fn wickra_central_pivot_range_new() -> *mut CentralPivotRange { + Box::into_raw(Box::new(CentralPivotRange::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_central_pivot_range_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_central_pivot_range_update( + handle: *mut CentralPivotRange, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraCentralPivotRangeOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraCentralPivotRangeOutput { + pivot: out_val.pivot, + tc: out_val.tc, + bc: out_val.bc, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_central_pivot_range_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_central_pivot_range_reset(handle: *mut CentralPivotRange) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_central_pivot_range_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_central_pivot_range_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_central_pivot_range_free(handle: *mut CentralPivotRange) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ChandeKrollStop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_chande_kroll_stop_free`. +#[no_mangle] +pub extern "C" fn wickra_chande_kroll_stop_new( + atr_period: usize, + atr_multiplier: f64, + stop_period: usize, +) -> *mut ChandeKrollStop { + match ChandeKrollStop::new(atr_period, atr_multiplier, stop_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_chande_kroll_stop_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chande_kroll_stop_update( + handle: *mut ChandeKrollStop, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraChandeKrollStopOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraChandeKrollStopOutput { + stop_long: out_val.stop_long, + stop_short: out_val.stop_short, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_chande_kroll_stop_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chande_kroll_stop_reset(handle: *mut ChandeKrollStop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_chande_kroll_stop_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_chande_kroll_stop_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chande_kroll_stop_free(handle: *mut ChandeKrollStop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ChandelierExit` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_chandelier_exit_free`. +#[no_mangle] +pub extern "C" fn wickra_chandelier_exit_new( + period: usize, + multiplier: f64, +) -> *mut ChandelierExit { + match ChandelierExit::new(period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_chandelier_exit_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chandelier_exit_update( + handle: *mut ChandelierExit, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraChandelierExitOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraChandelierExitOutput { + long_stop: out_val.long_stop, + short_stop: out_val.short_stop, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_chandelier_exit_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chandelier_exit_reset(handle: *mut ChandelierExit) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_chandelier_exit_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_chandelier_exit_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_chandelier_exit_free(handle: *mut ChandelierExit) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ClassicPivots` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_classic_pivots_free`. +#[no_mangle] +pub extern "C" fn wickra_classic_pivots_new() -> *mut ClassicPivots { + Box::into_raw(Box::new(ClassicPivots::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_classic_pivots_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_classic_pivots_update( + handle: *mut ClassicPivots, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraClassicPivotsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraClassicPivotsOutput { + pp: out_val.pp, + r1: out_val.r1, + r2: out_val.r2, + r3: out_val.r3, + s1: out_val.s1, + s2: out_val.s2, + s3: out_val.s3, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_classic_pivots_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_classic_pivots_reset(handle: *mut ClassicPivots) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_classic_pivots_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_classic_pivots_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_classic_pivots_free(handle: *mut ClassicPivots) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Cointegration` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_cointegration_free`. +#[no_mangle] +pub extern "C" fn wickra_cointegration_new(period: usize, adf_lags: usize) -> *mut Cointegration { + match Cointegration::new(period, adf_lags) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_cointegration_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cointegration_update( + handle: *mut Cointegration, + x: f64, + y: f64, + out: *mut WickraCointegrationOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update((x, y)) { + Some(out_val) => { + *out = WickraCointegrationOutput { + hedge_ratio: out_val.hedge_ratio, + spread: out_val.spread, + adf_stat: out_val.adf_stat, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_cointegration_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cointegration_reset(handle: *mut Cointegration) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_cointegration_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_cointegration_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_cointegration_free(handle: *mut Cointegration) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `CompositeProfile` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_composite_profile_free`. +#[no_mangle] +pub extern "C" fn wickra_composite_profile_new( + period: usize, + bins: usize, + value_area_pct: f64, +) -> *mut CompositeProfile { + match CompositeProfile::new(period, bins, value_area_pct) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_composite_profile_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_composite_profile_update( + handle: *mut CompositeProfile, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraCompositeProfileOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraCompositeProfileOutput { + poc: out_val.poc, + vah: out_val.vah, + val: out_val.val, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_composite_profile_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_composite_profile_reset(handle: *mut CompositeProfile) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_composite_profile_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_composite_profile_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_composite_profile_free(handle: *mut CompositeProfile) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DemarkPivots` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_demark_pivots_free`. +#[no_mangle] +pub extern "C" fn wickra_demark_pivots_new() -> *mut DemarkPivots { + Box::into_raw(Box::new(DemarkPivots::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_demark_pivots_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_demark_pivots_update( + handle: *mut DemarkPivots, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraDemarkPivotsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraDemarkPivotsOutput { + pp: out_val.pp, + r1: out_val.r1, + s1: out_val.s1, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_demark_pivots_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_demark_pivots_reset(handle: *mut DemarkPivots) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_demark_pivots_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_demark_pivots_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_demark_pivots_free(handle: *mut DemarkPivots) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Donchian` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_donchian_free`. +#[no_mangle] +pub extern "C" fn wickra_donchian_new(period: usize) -> *mut Donchian { + match Donchian::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_donchian_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_donchian_update( + handle: *mut Donchian, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraDonchianOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraDonchianOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_donchian_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_donchian_reset(handle: *mut Donchian) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_donchian_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_donchian_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_donchian_free(handle: *mut Donchian) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DonchianStop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_donchian_stop_free`. +#[no_mangle] +pub extern "C" fn wickra_donchian_stop_new(period: usize) -> *mut DonchianStop { + match DonchianStop::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_donchian_stop_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_donchian_stop_update( + handle: *mut DonchianStop, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraDonchianStopOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraDonchianStopOutput { + stop_long: out_val.stop_long, + stop_short: out_val.stop_short, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_donchian_stop_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_donchian_stop_reset(handle: *mut DonchianStop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_donchian_stop_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_donchian_stop_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_donchian_stop_free(handle: *mut DonchianStop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `DoubleBollinger` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_double_bollinger_free`. +#[no_mangle] +pub extern "C" fn wickra_double_bollinger_new( + period: usize, + k_inner: f64, + k_outer: f64, +) -> *mut DoubleBollinger { + match DoubleBollinger::new(period, k_inner, k_outer) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_double_bollinger_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_double_bollinger_update( + handle: *mut DoubleBollinger, + value: f64, + out: *mut WickraDoubleBollingerOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraDoubleBollingerOutput { + upper_outer: out_val.upper_outer, + upper_inner: out_val.upper_inner, + middle: out_val.middle, + lower_inner: out_val.lower_inner, + lower_outer: out_val.lower_outer, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_double_bollinger_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_double_bollinger_reset(handle: *mut DoubleBollinger) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_double_bollinger_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_double_bollinger_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_double_bollinger_free(handle: *mut DoubleBollinger) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ElderRay` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_elder_ray_free`. +#[no_mangle] +pub extern "C" fn wickra_elder_ray_new(period: usize) -> *mut ElderRay { + match ElderRay::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_elder_ray_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_elder_ray_update( + handle: *mut ElderRay, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraElderRayOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraElderRayOutput { + bull_power: out_val.bull_power, + bear_power: out_val.bear_power, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_elder_ray_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_elder_ray_reset(handle: *mut ElderRay) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_elder_ray_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_elder_ray_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_elder_ray_free(handle: *mut ElderRay) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ElderSafeZone` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_elder_safe_zone_free`. +#[no_mangle] +pub extern "C" fn wickra_elder_safe_zone_new(period: usize, coeff: f64) -> *mut ElderSafeZone { + match ElderSafeZone::new(period, coeff) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_elder_safe_zone_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_elder_safe_zone_update( + handle: *mut ElderSafeZone, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraElderSafeZoneOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraElderSafeZoneOutput { + value: out_val.value, + direction: out_val.direction, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_elder_safe_zone_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_elder_safe_zone_reset(handle: *mut ElderSafeZone) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_elder_safe_zone_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_elder_safe_zone_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_elder_safe_zone_free(handle: *mut ElderSafeZone) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Equivolume` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_equivolume_free`. +#[no_mangle] +pub extern "C" fn wickra_equivolume_new(period: usize) -> *mut Equivolume { + match Equivolume::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_equivolume_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_equivolume_update( + handle: *mut Equivolume, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraEquivolumeOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraEquivolumeOutput { + height: out_val.height, + width: out_val.width, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_equivolume_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_equivolume_reset(handle: *mut Equivolume) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_equivolume_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_equivolume_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_equivolume_free(handle: *mut Equivolume) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FibArcs` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fib_arcs_free`. +#[no_mangle] +pub extern "C" fn wickra_fib_arcs_new() -> *mut FibArcs { + Box::into_raw(Box::new(FibArcs::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_fib_arcs_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_arcs_update( + handle: *mut FibArcs, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraFibArcsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraFibArcsOutput { + arc_382: out_val.arc_382, + arc_500: out_val.arc_500, + arc_618: out_val.arc_618, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fib_arcs_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_arcs_reset(handle: *mut FibArcs) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fib_arcs_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fib_arcs_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_arcs_free(handle: *mut FibArcs) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FibChannel` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fib_channel_free`. +#[no_mangle] +pub extern "C" fn wickra_fib_channel_new() -> *mut FibChannel { + Box::into_raw(Box::new(FibChannel::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_fib_channel_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_channel_update( + handle: *mut FibChannel, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraFibChannelOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraFibChannelOutput { + base: out_val.base, + level_618: out_val.level_618, + level_1000: out_val.level_1000, + level_1618: out_val.level_1618, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fib_channel_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_channel_reset(handle: *mut FibChannel) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fib_channel_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fib_channel_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_channel_free(handle: *mut FibChannel) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FibConfluence` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fib_confluence_free`. +#[no_mangle] +pub extern "C" fn wickra_fib_confluence_new() -> *mut FibConfluence { + Box::into_raw(Box::new(FibConfluence::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_fib_confluence_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_confluence_update( + handle: *mut FibConfluence, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraFibConfluenceOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraFibConfluenceOutput { + price: out_val.price, + strength: out_val.strength, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fib_confluence_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_confluence_reset(handle: *mut FibConfluence) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fib_confluence_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fib_confluence_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_confluence_free(handle: *mut FibConfluence) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FibExtension` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fib_extension_free`. +#[no_mangle] +pub extern "C" fn wickra_fib_extension_new() -> *mut FibExtension { + Box::into_raw(Box::new(FibExtension::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_fib_extension_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_extension_update( + handle: *mut FibExtension, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraFibExtensionOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraFibExtensionOutput { + level_1272: out_val.level_1272, + level_1414: out_val.level_1414, + level_1618: out_val.level_1618, + level_2000: out_val.level_2000, + level_2618: out_val.level_2618, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fib_extension_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_extension_reset(handle: *mut FibExtension) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fib_extension_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fib_extension_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_extension_free(handle: *mut FibExtension) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FibFan` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fib_fan_free`. +#[no_mangle] +pub extern "C" fn wickra_fib_fan_new() -> *mut FibFan { + Box::into_raw(Box::new(FibFan::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_fib_fan_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_fan_update( + handle: *mut FibFan, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraFibFanOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraFibFanOutput { + fan_382: out_val.fan_382, + fan_500: out_val.fan_500, + fan_618: out_val.fan_618, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fib_fan_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_fan_reset(handle: *mut FibFan) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fib_fan_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fib_fan_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_fan_free(handle: *mut FibFan) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FibProjection` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fib_projection_free`. +#[no_mangle] +pub extern "C" fn wickra_fib_projection_new() -> *mut FibProjection { + Box::into_raw(Box::new(FibProjection::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_fib_projection_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_projection_update( + handle: *mut FibProjection, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraFibProjectionOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraFibProjectionOutput { + level_618: out_val.level_618, + level_1000: out_val.level_1000, + level_1618: out_val.level_1618, + level_2618: out_val.level_2618, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fib_projection_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_projection_reset(handle: *mut FibProjection) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fib_projection_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fib_projection_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_projection_free(handle: *mut FibProjection) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FibRetracement` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fib_retracement_free`. +#[no_mangle] +pub extern "C" fn wickra_fib_retracement_new() -> *mut FibRetracement { + Box::into_raw(Box::new(FibRetracement::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_fib_retracement_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_retracement_update( + handle: *mut FibRetracement, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraFibRetracementOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraFibRetracementOutput { + level_0: out_val.level_0, + level_236: out_val.level_236, + level_382: out_val.level_382, + level_500: out_val.level_500, + level_618: out_val.level_618, + level_786: out_val.level_786, + level_1000: out_val.level_1000, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fib_retracement_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_retracement_reset(handle: *mut FibRetracement) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fib_retracement_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fib_retracement_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_retracement_free(handle: *mut FibRetracement) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FibTimeZones` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fib_time_zones_free`. +#[no_mangle] +pub extern "C" fn wickra_fib_time_zones_new() -> *mut FibTimeZones { + Box::into_raw(Box::new(FibTimeZones::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_fib_time_zones_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_time_zones_update( + handle: *mut FibTimeZones, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraFibTimeZonesOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraFibTimeZonesOutput { + on_zone: out_val.on_zone, + bars_to_next: out_val.bars_to_next, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fib_time_zones_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_time_zones_reset(handle: *mut FibTimeZones) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fib_time_zones_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fib_time_zones_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fib_time_zones_free(handle: *mut FibTimeZones) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FibonacciPivots` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fibonacci_pivots_free`. +#[no_mangle] +pub extern "C" fn wickra_fibonacci_pivots_new() -> *mut FibonacciPivots { + Box::into_raw(Box::new(FibonacciPivots::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_fibonacci_pivots_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fibonacci_pivots_update( + handle: *mut FibonacciPivots, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraFibonacciPivotsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraFibonacciPivotsOutput { + pp: out_val.pp, + r1: out_val.r1, + r2: out_val.r2, + r3: out_val.r3, + s1: out_val.s1, + s2: out_val.s2, + s3: out_val.s3, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fibonacci_pivots_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fibonacci_pivots_reset(handle: *mut FibonacciPivots) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fibonacci_pivots_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fibonacci_pivots_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fibonacci_pivots_free(handle: *mut FibonacciPivots) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `FractalChaosBands` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_fractal_chaos_bands_free`. +#[no_mangle] +pub extern "C" fn wickra_fractal_chaos_bands_new(k: usize) -> *mut FractalChaosBands { + match FractalChaosBands::new(k) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_fractal_chaos_bands_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fractal_chaos_bands_update( + handle: *mut FractalChaosBands, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraFractalChaosBandsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraFractalChaosBandsOutput { + upper: out_val.upper, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_fractal_chaos_bands_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fractal_chaos_bands_reset(handle: *mut FractalChaosBands) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_fractal_chaos_bands_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_fractal_chaos_bands_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_fractal_chaos_bands_free(handle: *mut FractalChaosBands) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `GatorOscillator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_gator_oscillator_free`. +#[no_mangle] +pub extern "C" fn wickra_gator_oscillator_new( + jaw_period: usize, + teeth_period: usize, + lips_period: usize, +) -> *mut GatorOscillator { + match GatorOscillator::new(jaw_period, teeth_period, lips_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_gator_oscillator_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gator_oscillator_update( + handle: *mut GatorOscillator, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraGatorOscillatorOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraGatorOscillatorOutput { + upper: out_val.upper, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_gator_oscillator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gator_oscillator_reset(handle: *mut GatorOscillator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_gator_oscillator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_gator_oscillator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_gator_oscillator_free(handle: *mut GatorOscillator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `GoldenPocket` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_golden_pocket_free`. +#[no_mangle] +pub extern "C" fn wickra_golden_pocket_new() -> *mut GoldenPocket { + Box::into_raw(Box::new(GoldenPocket::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_golden_pocket_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_golden_pocket_update( + handle: *mut GoldenPocket, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraGoldenPocketOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraGoldenPocketOutput { + low: out_val.low, + mid: out_val.mid, + high: out_val.high, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_golden_pocket_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_golden_pocket_reset(handle: *mut GoldenPocket) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_golden_pocket_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_golden_pocket_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_golden_pocket_free(handle: *mut GoldenPocket) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HeikinAshi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_heikin_ashi_free`. +#[no_mangle] +pub extern "C" fn wickra_heikin_ashi_new() -> *mut HeikinAshi { + Box::into_raw(Box::new(HeikinAshi::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_heikin_ashi_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_heikin_ashi_update( + handle: *mut HeikinAshi, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraHeikinAshiOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraHeikinAshiOutput { + open: out_val.open, + high: out_val.high, + low: out_val.low, + close: out_val.close, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_heikin_ashi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_heikin_ashi_reset(handle: *mut HeikinAshi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_heikin_ashi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_heikin_ashi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_heikin_ashi_free(handle: *mut HeikinAshi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HighLowVolumeNodes` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_high_low_volume_nodes_free`. +#[no_mangle] +pub extern "C" fn wickra_high_low_volume_nodes_new( + period: usize, + bins: usize, +) -> *mut HighLowVolumeNodes { + match HighLowVolumeNodes::new(period, bins) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_high_low_volume_nodes_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_low_volume_nodes_update( + handle: *mut HighLowVolumeNodes, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraHighLowVolumeNodesOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraHighLowVolumeNodesOutput { + hvn: out_val.hvn, + lvn: out_val.lvn, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_high_low_volume_nodes_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_low_volume_nodes_reset(handle: *mut HighLowVolumeNodes) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_high_low_volume_nodes_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_high_low_volume_nodes_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_high_low_volume_nodes_free(handle: *mut HighLowVolumeNodes) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HtPhasor` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ht_phasor_free`. +#[no_mangle] +pub extern "C" fn wickra_ht_phasor_new() -> *mut HtPhasor { + Box::into_raw(Box::new(HtPhasor::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_ht_phasor_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ht_phasor_update( + handle: *mut HtPhasor, + value: f64, + out: *mut WickraHtPhasorOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraHtPhasorOutput { + inphase: out_val.inphase, + quadrature: out_val.quadrature, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ht_phasor_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ht_phasor_reset(handle: *mut HtPhasor) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ht_phasor_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ht_phasor_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ht_phasor_free(handle: *mut HtPhasor) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `HurstChannel` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_hurst_channel_free`. +#[no_mangle] +pub extern "C" fn wickra_hurst_channel_new(period: usize, multiplier: f64) -> *mut HurstChannel { + match HurstChannel::new(period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_hurst_channel_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hurst_channel_update( + handle: *mut HurstChannel, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraHurstChannelOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraHurstChannelOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_hurst_channel_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hurst_channel_reset(handle: *mut HurstChannel) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_hurst_channel_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_hurst_channel_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_hurst_channel_free(handle: *mut HurstChannel) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Ichimoku` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ichimoku_free`. +#[no_mangle] +pub extern "C" fn wickra_ichimoku_new( + tenkan_period: usize, + kijun_period: usize, + senkou_b_period: usize, + displacement: usize, +) -> *mut Ichimoku { + match Ichimoku::new(tenkan_period, kijun_period, senkou_b_period, displacement) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_ichimoku_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ichimoku_update( + handle: *mut Ichimoku, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraIchimokuOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraIchimokuOutput { + tenkan: out_val.tenkan.unwrap_or(f64::NAN), + kijun: out_val.kijun.unwrap_or(f64::NAN), + senkou_a: out_val.senkou_a.unwrap_or(f64::NAN), + senkou_b: out_val.senkou_b.unwrap_or(f64::NAN), + chikou: out_val.chikou.unwrap_or(f64::NAN), + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ichimoku_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ichimoku_reset(handle: *mut Ichimoku) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ichimoku_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ichimoku_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ichimoku_free(handle: *mut Ichimoku) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `InitialBalance` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_initial_balance_free`. +#[no_mangle] +pub extern "C" fn wickra_initial_balance_new(period: usize) -> *mut InitialBalance { + match InitialBalance::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_initial_balance_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_initial_balance_update( + handle: *mut InitialBalance, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraInitialBalanceOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraInitialBalanceOutput { + high: out_val.high, + low: out_val.low, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_initial_balance_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_initial_balance_reset(handle: *mut InitialBalance) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_initial_balance_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_initial_balance_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_initial_balance_free(handle: *mut InitialBalance) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `KalmanHedgeRatio` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kalman_hedge_ratio_free`. +#[no_mangle] +pub extern "C" fn wickra_kalman_hedge_ratio_new( + delta: f64, + observation_var: f64, +) -> *mut KalmanHedgeRatio { + match KalmanHedgeRatio::new(delta, observation_var) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_kalman_hedge_ratio_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kalman_hedge_ratio_update( + handle: *mut KalmanHedgeRatio, + x: f64, + y: f64, + out: *mut WickraKalmanHedgeRatioOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update((x, y)) { + Some(out_val) => { + *out = WickraKalmanHedgeRatioOutput { + hedge_ratio: out_val.hedge_ratio, + intercept: out_val.intercept, + spread: out_val.spread, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kalman_hedge_ratio_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kalman_hedge_ratio_reset(handle: *mut KalmanHedgeRatio) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kalman_hedge_ratio_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kalman_hedge_ratio_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kalman_hedge_ratio_free(handle: *mut KalmanHedgeRatio) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `KaseDevStop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kase_dev_stop_free`. +#[no_mangle] +pub extern "C" fn wickra_kase_dev_stop_new(period: usize, dev: f64) -> *mut KaseDevStop { + match KaseDevStop::new(period, dev) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_kase_dev_stop_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kase_dev_stop_update( + handle: *mut KaseDevStop, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraKaseDevStopOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraKaseDevStopOutput { + value: out_val.value, + direction: out_val.direction, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kase_dev_stop_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kase_dev_stop_reset(handle: *mut KaseDevStop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kase_dev_stop_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kase_dev_stop_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kase_dev_stop_free(handle: *mut KaseDevStop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `KasePermissionStochastic` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kase_permission_stochastic_free`. +#[no_mangle] +pub extern "C" fn wickra_kase_permission_stochastic_new( + length: usize, + smooth: usize, +) -> *mut KasePermissionStochastic { + match KasePermissionStochastic::new(length, smooth) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_kase_permission_stochastic_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kase_permission_stochastic_update( + handle: *mut KasePermissionStochastic, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraKasePermissionStochasticOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraKasePermissionStochasticOutput { + fast: out_val.fast, + slow: out_val.slow, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kase_permission_stochastic_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kase_permission_stochastic_reset( + handle: *mut KasePermissionStochastic, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kase_permission_stochastic_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kase_permission_stochastic_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kase_permission_stochastic_free( + handle: *mut KasePermissionStochastic, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Keltner` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_keltner_free`. +#[no_mangle] +pub extern "C" fn wickra_keltner_new( + ema_period: usize, + atr_period: usize, + multiplier: f64, +) -> *mut Keltner { + match Keltner::new(ema_period, atr_period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_keltner_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_keltner_update( + handle: *mut Keltner, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraKeltnerOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraKeltnerOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_keltner_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_keltner_reset(handle: *mut Keltner) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_keltner_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_keltner_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_keltner_free(handle: *mut Keltner) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Kst` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kst_free`. +#[no_mangle] +pub extern "C" fn wickra_kst_new( + roc1: usize, + roc2: usize, + roc3: usize, + roc4: usize, + sma1: usize, + sma2: usize, + sma3: usize, + sma4: usize, + signal: usize, +) -> *mut Kst { + match Kst::new(roc1, roc2, roc3, roc4, sma1, sma2, sma3, sma4, signal) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_kst_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kst_update( + handle: *mut Kst, + value: f64, + out: *mut WickraKstOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraKstOutput { + kst: out_val.kst, + signal: out_val.signal, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kst_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kst_reset(handle: *mut Kst) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kst_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kst_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kst_free(handle: *mut Kst) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LeadLagCrossCorrelation` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_lead_lag_cross_correlation_free`. +#[no_mangle] +pub extern "C" fn wickra_lead_lag_cross_correlation_new( + window: usize, + max_lag: usize, +) -> *mut LeadLagCrossCorrelation { + match LeadLagCrossCorrelation::new(window, max_lag) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_lead_lag_cross_correlation_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lead_lag_cross_correlation_update( + handle: *mut LeadLagCrossCorrelation, + x: f64, + y: f64, + out: *mut WickraLeadLagCrossCorrelationOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update((x, y)) { + Some(out_val) => { + *out = WickraLeadLagCrossCorrelationOutput { + lag: out_val.lag, + correlation: out_val.correlation, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_lead_lag_cross_correlation_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lead_lag_cross_correlation_reset( + handle: *mut LeadLagCrossCorrelation, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_lead_lag_cross_correlation_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_lead_lag_cross_correlation_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lead_lag_cross_correlation_free( + handle: *mut LeadLagCrossCorrelation, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LinRegChannel` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_lin_reg_channel_free`. +#[no_mangle] +pub extern "C" fn wickra_lin_reg_channel_new(period: usize, multiplier: f64) -> *mut LinRegChannel { + match LinRegChannel::new(period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_lin_reg_channel_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_channel_update( + handle: *mut LinRegChannel, + value: f64, + out: *mut WickraLinRegChannelOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraLinRegChannelOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_lin_reg_channel_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_channel_reset(handle: *mut LinRegChannel) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_lin_reg_channel_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_lin_reg_channel_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_lin_reg_channel_free(handle: *mut LinRegChannel) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `LiquidationFeatures` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_liquidation_features_free`. +#[no_mangle] +pub extern "C" fn wickra_liquidation_features_new() -> *mut LiquidationFeatures { + Box::into_raw(Box::new(LiquidationFeatures::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_liquidation_features_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_liquidation_features_update( + handle: *mut LiquidationFeatures, + funding_rate: f64, + mark_price: f64, + index_price: f64, + futures_price: f64, + open_interest: f64, + long_size: f64, + short_size: f64, + taker_buy_volume: f64, + taker_sell_volume: f64, + long_liquidation: f64, + short_liquidation: f64, + timestamp: i64, + out: *mut WickraLiquidationFeaturesOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = DerivativesTick::new( + funding_rate, + mark_price, + index_price, + futures_price, + open_interest, + long_size, + short_size, + taker_buy_volume, + taker_sell_volume, + long_liquidation, + short_liquidation, + timestamp, + ) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraLiquidationFeaturesOutput { + long: out_val.long, + short: out_val.short, + net: out_val.net, + total: out_val.total, + imbalance: out_val.imbalance, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_liquidation_features_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_liquidation_features_reset(handle: *mut LiquidationFeatures) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_liquidation_features_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_liquidation_features_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_liquidation_features_free(handle: *mut LiquidationFeatures) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MaEnvelope` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ma_envelope_free`. +#[no_mangle] +pub extern "C" fn wickra_ma_envelope_new(period: usize, percent: f64) -> *mut MaEnvelope { + match MaEnvelope::new(period, percent) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_ma_envelope_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ma_envelope_update( + handle: *mut MaEnvelope, + value: f64, + out: *mut WickraMaEnvelopeOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraMaEnvelopeOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ma_envelope_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ma_envelope_reset(handle: *mut MaEnvelope) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ma_envelope_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ma_envelope_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ma_envelope_free(handle: *mut MaEnvelope) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MacdIndicator` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_macd_indicator_free`. +#[no_mangle] +pub extern "C" fn wickra_macd_indicator_new( + fast: usize, + slow: usize, + signal: usize, +) -> *mut MacdIndicator { + match MacdIndicator::new(fast, slow, signal) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_macd_indicator_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_indicator_update( + handle: *mut MacdIndicator, + value: f64, + out: *mut WickraMacdOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraMacdOutput { + macd: out_val.macd, + signal: out_val.signal, + histogram: out_val.histogram, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_macd_indicator_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_indicator_reset(handle: *mut MacdIndicator) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_macd_indicator_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_macd_indicator_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_indicator_free(handle: *mut MacdIndicator) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MacdFix` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_macd_fix_free`. +#[no_mangle] +pub extern "C" fn wickra_macd_fix_new(signal: usize) -> *mut MacdFix { + match MacdFix::new(signal) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_macd_fix_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_fix_update( + handle: *mut MacdFix, + value: f64, + out: *mut WickraMacdOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraMacdOutput { + macd: out_val.macd, + signal: out_val.signal, + histogram: out_val.histogram, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_macd_fix_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_fix_reset(handle: *mut MacdFix) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_macd_fix_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_macd_fix_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_fix_free(handle: *mut MacdFix) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Mama` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_mama_free`. +#[no_mangle] +pub extern "C" fn wickra_mama_new(fast_limit: f64, slow_limit: f64) -> *mut Mama { + match Mama::new(fast_limit, slow_limit) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_mama_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mama_update( + handle: *mut Mama, + value: f64, + out: *mut WickraMamaOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraMamaOutput { + mama: out_val.mama, + fama: out_val.fama, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_mama_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mama_reset(handle: *mut Mama) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_mama_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_mama_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_mama_free(handle: *mut Mama) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MedianChannel` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_median_channel_free`. +#[no_mangle] +pub extern "C" fn wickra_median_channel_new(period: usize, multiplier: f64) -> *mut MedianChannel { + match MedianChannel::new(period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_median_channel_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_channel_update( + handle: *mut MedianChannel, + value: f64, + out: *mut WickraMedianChannelOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraMedianChannelOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_median_channel_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_channel_reset(handle: *mut MedianChannel) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_median_channel_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_median_channel_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_median_channel_free(handle: *mut MedianChannel) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ModifiedMaStop` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_modified_ma_stop_free`. +#[no_mangle] +pub extern "C" fn wickra_modified_ma_stop_new(period: usize) -> *mut ModifiedMaStop { + match ModifiedMaStop::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_modified_ma_stop_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_modified_ma_stop_update( + handle: *mut ModifiedMaStop, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraModifiedMaStopOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraModifiedMaStopOutput { + value: out_val.value, + direction: out_val.direction, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_modified_ma_stop_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_modified_ma_stop_reset(handle: *mut ModifiedMaStop) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_modified_ma_stop_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_modified_ma_stop_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_modified_ma_stop_free(handle: *mut ModifiedMaStop) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `MurreyMathLines` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_murrey_math_lines_free`. +#[no_mangle] +pub extern "C" fn wickra_murrey_math_lines_new(period: usize) -> *mut MurreyMathLines { + match MurreyMathLines::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_murrey_math_lines_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_murrey_math_lines_update( + handle: *mut MurreyMathLines, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraMurreyMathLinesOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraMurreyMathLinesOutput { + mm8_8: out_val.mm8_8, + mm7_8: out_val.mm7_8, + mm6_8: out_val.mm6_8, + mm5_8: out_val.mm5_8, + mm4_8: out_val.mm4_8, + mm3_8: out_val.mm3_8, + mm2_8: out_val.mm2_8, + mm1_8: out_val.mm1_8, + mm0_8: out_val.mm0_8, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_murrey_math_lines_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_murrey_math_lines_reset(handle: *mut MurreyMathLines) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_murrey_math_lines_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_murrey_math_lines_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_murrey_math_lines_free(handle: *mut MurreyMathLines) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Nrtr` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_nrtr_free`. +#[no_mangle] +pub extern "C" fn wickra_nrtr_new(pct: f64) -> *mut Nrtr { + match Nrtr::new(pct) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_nrtr_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_nrtr_update( + handle: *mut Nrtr, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraNrtrOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraNrtrOutput { + value: out_val.value, + direction: out_val.direction, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_nrtr_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_nrtr_reset(handle: *mut Nrtr) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_nrtr_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_nrtr_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_nrtr_free(handle: *mut Nrtr) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OpeningRange` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_opening_range_free`. +#[no_mangle] +pub extern "C" fn wickra_opening_range_new(period: usize) -> *mut OpeningRange { + match OpeningRange::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_opening_range_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_opening_range_update( + handle: *mut OpeningRange, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraOpeningRangeOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraOpeningRangeOutput { + high: out_val.high, + low: out_val.low, + breakout_distance: out_val.breakout_distance, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_opening_range_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_opening_range_reset(handle: *mut OpeningRange) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_opening_range_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_opening_range_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_opening_range_free(handle: *mut OpeningRange) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `OvernightIntradayReturn` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_overnight_intraday_return_free`. +#[no_mangle] +pub extern "C" fn wickra_overnight_intraday_return_new( + utc_offset_minutes: i32, +) -> *mut OvernightIntradayReturn { + Box::into_raw(Box::new(OvernightIntradayReturn::new(utc_offset_minutes))) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_overnight_intraday_return_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_overnight_intraday_return_update( + handle: *mut OvernightIntradayReturn, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraOvernightIntradayReturnOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraOvernightIntradayReturnOutput { + overnight: out_val.overnight, + intraday: out_val.intraday, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_overnight_intraday_return_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_overnight_intraday_return_reset( + handle: *mut OvernightIntradayReturn, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_overnight_intraday_return_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_overnight_intraday_return_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_overnight_intraday_return_free( + handle: *mut OvernightIntradayReturn, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ProjectionBands` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_projection_bands_free`. +#[no_mangle] +pub extern "C" fn wickra_projection_bands_new(period: usize) -> *mut ProjectionBands { + match ProjectionBands::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_projection_bands_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_projection_bands_update( + handle: *mut ProjectionBands, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraProjectionBandsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraProjectionBandsOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_projection_bands_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_projection_bands_reset(handle: *mut ProjectionBands) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_projection_bands_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_projection_bands_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_projection_bands_free(handle: *mut ProjectionBands) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Qqe` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_qqe_free`. +#[no_mangle] +pub extern "C" fn wickra_qqe_new(rsi_period: usize, smoothing: usize, factor: f64) -> *mut Qqe { + match Qqe::new(rsi_period, smoothing, factor) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_qqe_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_qqe_update( + handle: *mut Qqe, + value: f64, + out: *mut WickraQqeOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraQqeOutput { + rsi_ma: out_val.rsi_ma, + trailing_line: out_val.trailing_line, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_qqe_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_qqe_reset(handle: *mut Qqe) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_qqe_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_qqe_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_qqe_free(handle: *mut Qqe) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `QuartileBands` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_quartile_bands_free`. +#[no_mangle] +pub extern "C" fn wickra_quartile_bands_new(period: usize) -> *mut QuartileBands { + match QuartileBands::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_quartile_bands_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_quartile_bands_update( + handle: *mut QuartileBands, + value: f64, + out: *mut WickraQuartileBandsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraQuartileBandsOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_quartile_bands_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_quartile_bands_reset(handle: *mut QuartileBands) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_quartile_bands_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_quartile_bands_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_quartile_bands_free(handle: *mut QuartileBands) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RelativeStrengthAB` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_relative_strength_ab_free`. +#[no_mangle] +pub extern "C" fn wickra_relative_strength_ab_new( + ma_period: usize, + rsi_period: usize, +) -> *mut RelativeStrengthAB { + match RelativeStrengthAB::new(ma_period, rsi_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_relative_strength_ab_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_relative_strength_ab_update( + handle: *mut RelativeStrengthAB, + x: f64, + y: f64, + out: *mut WickraRelativeStrengthOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update((x, y)) { + Some(out_val) => { + *out = WickraRelativeStrengthOutput { + ratio: out_val.ratio, + ratio_ma: out_val.ratio_ma, + ratio_rsi: out_val.ratio_rsi, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_relative_strength_ab_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_relative_strength_ab_reset(handle: *mut RelativeStrengthAB) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_relative_strength_ab_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_relative_strength_ab_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_relative_strength_ab_free(handle: *mut RelativeStrengthAB) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Rwi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_rwi_free`. +#[no_mangle] +pub extern "C" fn wickra_rwi_new(period: usize) -> *mut Rwi { + match Rwi::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_rwi_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rwi_update( + handle: *mut Rwi, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraRwiOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraRwiOutput { + high: out_val.high, + low: out_val.low, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_rwi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rwi_reset(handle: *mut Rwi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_rwi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_rwi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_rwi_free(handle: *mut Rwi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SessionHighLow` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_session_high_low_free`. +#[no_mangle] +pub extern "C" fn wickra_session_high_low_new(utc_offset_minutes: i32) -> *mut SessionHighLow { + Box::into_raw(Box::new(SessionHighLow::new(utc_offset_minutes))) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_session_high_low_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_session_high_low_update( + handle: *mut SessionHighLow, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraSessionHighLowOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraSessionHighLowOutput { + high: out_val.high, + low: out_val.low, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_session_high_low_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_session_high_low_reset(handle: *mut SessionHighLow) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_session_high_low_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_session_high_low_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_session_high_low_free(handle: *mut SessionHighLow) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SessionRange` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_session_range_free`. +#[no_mangle] +pub extern "C" fn wickra_session_range_new(utc_offset_minutes: i32) -> *mut SessionRange { + Box::into_raw(Box::new(SessionRange::new(utc_offset_minutes))) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_session_range_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_session_range_update( + handle: *mut SessionRange, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraSessionRangeOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraSessionRangeOutput { + asia: out_val.asia, + eu: out_val.eu, + us: out_val.us, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_session_range_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_session_range_reset(handle: *mut SessionRange) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_session_range_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_session_range_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_session_range_free(handle: *mut SessionRange) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SmoothedHeikinAshi` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_smoothed_heikin_ashi_free`. +#[no_mangle] +pub extern "C" fn wickra_smoothed_heikin_ashi_new(period: usize) -> *mut SmoothedHeikinAshi { + match SmoothedHeikinAshi::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_smoothed_heikin_ashi_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_smoothed_heikin_ashi_update( + handle: *mut SmoothedHeikinAshi, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraSmoothedHeikinAshiOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraSmoothedHeikinAshiOutput { + open: out_val.open, + high: out_val.high, + low: out_val.low, + close: out_val.close, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_smoothed_heikin_ashi_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_smoothed_heikin_ashi_reset(handle: *mut SmoothedHeikinAshi) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_smoothed_heikin_ashi_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_smoothed_heikin_ashi_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_smoothed_heikin_ashi_free(handle: *mut SmoothedHeikinAshi) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SpreadBollingerBands` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_spread_bollinger_bands_free`. +#[no_mangle] +pub extern "C" fn wickra_spread_bollinger_bands_new( + period: usize, + num_std: f64, +) -> *mut SpreadBollingerBands { + match SpreadBollingerBands::new(period, num_std) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_spread_bollinger_bands_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spread_bollinger_bands_update( + handle: *mut SpreadBollingerBands, + x: f64, + y: f64, + out: *mut WickraSpreadBollingerBandsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update((x, y)) { + Some(out_val) => { + *out = WickraSpreadBollingerBandsOutput { + middle: out_val.middle, + upper: out_val.upper, + lower: out_val.lower, + percent_b: out_val.percent_b, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_spread_bollinger_bands_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spread_bollinger_bands_reset(handle: *mut SpreadBollingerBands) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_spread_bollinger_bands_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_spread_bollinger_bands_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_spread_bollinger_bands_free(handle: *mut SpreadBollingerBands) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `StandardErrorBands` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_standard_error_bands_free`. +#[no_mangle] +pub extern "C" fn wickra_standard_error_bands_new( + period: usize, + multiplier: f64, +) -> *mut StandardErrorBands { + match StandardErrorBands::new(period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_standard_error_bands_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_standard_error_bands_update( + handle: *mut StandardErrorBands, + value: f64, + out: *mut WickraStandardErrorBandsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraStandardErrorBandsOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_standard_error_bands_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_standard_error_bands_reset(handle: *mut StandardErrorBands) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_standard_error_bands_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_standard_error_bands_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_standard_error_bands_free(handle: *mut StandardErrorBands) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `StarcBands` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_starc_bands_free`. +#[no_mangle] +pub extern "C" fn wickra_starc_bands_new( + sma_period: usize, + atr_period: usize, + multiplier: f64, +) -> *mut StarcBands { + match StarcBands::new(sma_period, atr_period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_starc_bands_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_starc_bands_update( + handle: *mut StarcBands, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraStarcBandsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraStarcBandsOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_starc_bands_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_starc_bands_reset(handle: *mut StarcBands) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_starc_bands_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_starc_bands_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_starc_bands_free(handle: *mut StarcBands) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Stochastic` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_stochastic_free`. +#[no_mangle] +pub extern "C" fn wickra_stochastic_new(k_period: usize, d_period: usize) -> *mut Stochastic { + match Stochastic::new(k_period, d_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_stochastic_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stochastic_update( + handle: *mut Stochastic, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraStochasticOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraStochasticOutput { + k: out_val.k, + d: out_val.d, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_stochastic_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stochastic_reset(handle: *mut Stochastic) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_stochastic_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_stochastic_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_stochastic_free(handle: *mut Stochastic) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `SuperTrend` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_super_trend_free`. +#[no_mangle] +pub extern "C" fn wickra_super_trend_new(atr_period: usize, multiplier: f64) -> *mut SuperTrend { + match SuperTrend::new(atr_period, multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_super_trend_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_super_trend_update( + handle: *mut SuperTrend, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraSuperTrendOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraSuperTrendOutput { + value: out_val.value, + direction: out_val.direction, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_super_trend_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_super_trend_reset(handle: *mut SuperTrend) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_super_trend_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_super_trend_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_super_trend_free(handle: *mut SuperTrend) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdLines` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_lines_free`. +#[no_mangle] +pub extern "C" fn wickra_td_lines_new(lookback: usize, target: usize) -> *mut TdLines { + match TdLines::new(lookback, target) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_td_lines_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_lines_update( + handle: *mut TdLines, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraTdLinesOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraTdLinesOutput { + resistance: out_val.resistance, + support: out_val.support, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_lines_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_lines_reset(handle: *mut TdLines) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_lines_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_lines_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_lines_free(handle: *mut TdLines) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdMovingAverage` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_moving_average_free`. +#[no_mangle] +pub extern "C" fn wickra_td_moving_average_new( + period_st1: usize, + period_st2: usize, +) -> *mut TdMovingAverage { + match TdMovingAverage::new(period_st1, period_st2) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_td_moving_average_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_moving_average_update( + handle: *mut TdMovingAverage, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraTdMovingAverageOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraTdMovingAverageOutput { + st1: out_val.st1, + st2: out_val.st2, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_moving_average_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_moving_average_reset(handle: *mut TdMovingAverage) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_moving_average_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_moving_average_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_moving_average_free(handle: *mut TdMovingAverage) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdRangeProjection` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_range_projection_free`. +#[no_mangle] +pub extern "C" fn wickra_td_range_projection_new() -> *mut TdRangeProjection { + Box::into_raw(Box::new(TdRangeProjection::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_td_range_projection_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_range_projection_update( + handle: *mut TdRangeProjection, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraTdRangeProjectionOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraTdRangeProjectionOutput { + high: out_val.high, + low: out_val.low, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_range_projection_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_range_projection_reset(handle: *mut TdRangeProjection) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_range_projection_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_range_projection_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_range_projection_free(handle: *mut TdRangeProjection) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdRiskLevel` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_risk_level_free`. +#[no_mangle] +pub extern "C" fn wickra_td_risk_level_new(lookback: usize, target: usize) -> *mut TdRiskLevel { + match TdRiskLevel::new(lookback, target) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_td_risk_level_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_risk_level_update( + handle: *mut TdRiskLevel, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraTdRiskLevelOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraTdRiskLevelOutput { + buy_risk: out_val.buy_risk, + sell_risk: out_val.sell_risk, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_risk_level_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_risk_level_reset(handle: *mut TdRiskLevel) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_risk_level_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_risk_level_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_risk_level_free(handle: *mut TdRiskLevel) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TdSequential` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_td_sequential_free`. +#[no_mangle] +pub extern "C" fn wickra_td_sequential_new( + setup_lookback: usize, + setup_target: usize, + countdown_lookback: usize, + countdown_target: usize, +) -> *mut TdSequential { + match TdSequential::new( + setup_lookback, + setup_target, + countdown_lookback, + countdown_target, + ) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_td_sequential_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_sequential_update( + handle: *mut TdSequential, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraTdSequentialOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraTdSequentialOutput { + setup: out_val.setup, + countdown: out_val.countdown, + direction: out_val.direction, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_td_sequential_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_sequential_reset(handle: *mut TdSequential) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_td_sequential_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_td_sequential_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_td_sequential_free(handle: *mut TdSequential) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TtmSqueeze` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_ttm_squeeze_free`. +#[no_mangle] +pub extern "C" fn wickra_ttm_squeeze_new( + period: usize, + bb_mult: f64, + kc_mult: f64, +) -> *mut TtmSqueeze { + match TtmSqueeze::new(period, bb_mult, kc_mult) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_ttm_squeeze_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ttm_squeeze_update( + handle: *mut TtmSqueeze, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraTtmSqueezeOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraTtmSqueezeOutput { + squeeze: out_val.squeeze, + momentum: out_val.momentum, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_ttm_squeeze_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ttm_squeeze_reset(handle: *mut TtmSqueeze) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_ttm_squeeze_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_ttm_squeeze_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_ttm_squeeze_free(handle: *mut TtmSqueeze) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ValueArea` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_value_area_free`. +#[no_mangle] +pub extern "C" fn wickra_value_area_new( + period: usize, + bin_count: usize, + value_area_pct: f64, +) -> *mut ValueArea { + match ValueArea::new(period, bin_count, value_area_pct) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_value_area_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_value_area_update( + handle: *mut ValueArea, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraValueAreaOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraValueAreaOutput { + poc: out_val.poc, + vah: out_val.vah, + val: out_val.val, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_value_area_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_value_area_reset(handle: *mut ValueArea) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_value_area_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_value_area_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_value_area_free(handle: *mut ValueArea) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VolatilityCone` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volatility_cone_free`. +#[no_mangle] +pub extern "C" fn wickra_volatility_cone_new( + window: usize, + lookback: usize, +) -> *mut VolatilityCone { + match VolatilityCone::new(window, lookback) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_volatility_cone_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volatility_cone_update( + handle: *mut VolatilityCone, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraVolatilityConeOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraVolatilityConeOutput { + current: out_val.current, + min: out_val.min, + median: out_val.median, + max: out_val.max, + percentile: out_val.percentile, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volatility_cone_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volatility_cone_reset(handle: *mut VolatilityCone) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volatility_cone_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volatility_cone_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volatility_cone_free(handle: *mut VolatilityCone) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VolumeWeightedMacd` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volume_weighted_macd_free`. +#[no_mangle] +pub extern "C" fn wickra_volume_weighted_macd_new( + fast: usize, + slow: usize, + signal: usize, +) -> *mut VolumeWeightedMacd { + match VolumeWeightedMacd::new(fast, slow, signal) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_volume_weighted_macd_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_weighted_macd_update( + handle: *mut VolumeWeightedMacd, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraVolumeWeightedMacdOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraVolumeWeightedMacdOutput { + macd: out_val.macd, + signal: out_val.signal, + histogram: out_val.histogram, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volume_weighted_macd_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_weighted_macd_reset(handle: *mut VolumeWeightedMacd) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volume_weighted_macd_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volume_weighted_macd_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_weighted_macd_free(handle: *mut VolumeWeightedMacd) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VolumeWeightedSr` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volume_weighted_sr_free`. +#[no_mangle] +pub extern "C" fn wickra_volume_weighted_sr_new(period: usize) -> *mut VolumeWeightedSr { + match VolumeWeightedSr::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_volume_weighted_sr_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_weighted_sr_update( + handle: *mut VolumeWeightedSr, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraVolumeWeightedSrOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraVolumeWeightedSrOutput { + support: out_val.support, + resistance: out_val.resistance, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volume_weighted_sr_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_weighted_sr_reset(handle: *mut VolumeWeightedSr) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volume_weighted_sr_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volume_weighted_sr_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_weighted_sr_free(handle: *mut VolumeWeightedSr) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `Vortex` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_vortex_free`. +#[no_mangle] +pub extern "C" fn wickra_vortex_new(period: usize) -> *mut Vortex { + match Vortex::new(period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_vortex_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vortex_update( + handle: *mut Vortex, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraVortexOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraVortexOutput { + plus: out_val.plus, + minus: out_val.minus, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_vortex_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vortex_reset(handle: *mut Vortex) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_vortex_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_vortex_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vortex_free(handle: *mut Vortex) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VwapStdDevBands` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_vwap_std_dev_bands_free`. +#[no_mangle] +pub extern "C" fn wickra_vwap_std_dev_bands_new(multiplier: f64) -> *mut VwapStdDevBands { + match VwapStdDevBands::new(multiplier) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_vwap_std_dev_bands_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vwap_std_dev_bands_update( + handle: *mut VwapStdDevBands, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraVwapStdDevBandsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraVwapStdDevBandsOutput { + upper: out_val.upper, + middle: out_val.middle, + lower: out_val.lower, + stddev: out_val.stddev, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_vwap_std_dev_bands_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vwap_std_dev_bands_reset(handle: *mut VwapStdDevBands) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_vwap_std_dev_bands_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_vwap_std_dev_bands_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_vwap_std_dev_bands_free(handle: *mut VwapStdDevBands) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `WaveTrend` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_wave_trend_free`. +#[no_mangle] +pub extern "C" fn wickra_wave_trend_new( + channel_period: usize, + average_period: usize, + signal_period: usize, +) -> *mut WaveTrend { + match WaveTrend::new(channel_period, average_period, signal_period) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_wave_trend_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wave_trend_update( + handle: *mut WaveTrend, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraWaveTrendOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraWaveTrendOutput { + wt1: out_val.wt1, + wt2: out_val.wt2, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_wave_trend_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wave_trend_reset(handle: *mut WaveTrend) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_wave_trend_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_wave_trend_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_wave_trend_free(handle: *mut WaveTrend) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `WilliamsFractals` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_williams_fractals_free`. +#[no_mangle] +pub extern "C" fn wickra_williams_fractals_new() -> *mut WilliamsFractals { + Box::into_raw(Box::new(WilliamsFractals::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_williams_fractals_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_williams_fractals_update( + handle: *mut WilliamsFractals, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraWilliamsFractalsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraWilliamsFractalsOutput { + up: out_val.up.unwrap_or(f64::NAN), + down: out_val.down.unwrap_or(f64::NAN), + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_williams_fractals_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_williams_fractals_reset(handle: *mut WilliamsFractals) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_williams_fractals_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_williams_fractals_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_williams_fractals_free(handle: *mut WilliamsFractals) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `WoodiePivots` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_woodie_pivots_free`. +#[no_mangle] +pub extern "C" fn wickra_woodie_pivots_new() -> *mut WoodiePivots { + Box::into_raw(Box::new(WoodiePivots::new())) +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_woodie_pivots_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_woodie_pivots_update( + handle: *mut WoodiePivots, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraWoodiePivotsOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraWoodiePivotsOutput { + pp: out_val.pp, + r1: out_val.r1, + r2: out_val.r2, + s1: out_val.s1, + s2: out_val.s2, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_woodie_pivots_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_woodie_pivots_reset(handle: *mut WoodiePivots) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_woodie_pivots_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_woodie_pivots_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_woodie_pivots_free(handle: *mut WoodiePivots) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ZeroLagMacd` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_zero_lag_macd_free`. +#[no_mangle] +pub extern "C" fn wickra_zero_lag_macd_new( + fast: usize, + slow: usize, + signal: usize, +) -> *mut ZeroLagMacd { + match ZeroLagMacd::new(fast, slow, signal) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_zero_lag_macd_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_zero_lag_macd_update( + handle: *mut ZeroLagMacd, + value: f64, + out: *mut WickraZeroLagMacdOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraZeroLagMacdOutput { + macd: out_val.macd, + signal: out_val.signal, + histogram: out_val.histogram, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_zero_lag_macd_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_zero_lag_macd_reset(handle: *mut ZeroLagMacd) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_zero_lag_macd_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_zero_lag_macd_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_zero_lag_macd_free(handle: *mut ZeroLagMacd) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ZigZag` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_zig_zag_free`. +#[no_mangle] +pub extern "C" fn wickra_zig_zag_new(threshold: f64) -> *mut ZigZag { + match ZigZag::new(threshold) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input; on `true` the freshly computed multi-field output is written to +/// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. +/// +/// # Safety +/// `handle` (from `wickra_zig_zag_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_zig_zag_update( + handle: *mut ZigZag, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraZigZagOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return false; + }; + match ind.update(input) { + Some(out_val) => { + *out = WickraZigZagOutput { + swing: out_val.swing, + direction: out_val.direction, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_zig_zag_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_zig_zag_reset(handle: *mut ZigZag) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_zig_zag_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_zig_zag_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_zig_zag_free(handle: *mut ZigZag) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +// ===== Vec-output indicators (profiles; caller buffer + length return) ===== + +/// Scalar fields of `TpoProfileOutput` (its `Vec` payload is delivered separately). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraTpoProfileOutputScalars { + pub price_low: f64, + pub price_high: f64, +} + +/// Scalar fields of `VolumeProfileOutput` (its `Vec` payload is delivered separately). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraVolumeProfileOutputScalars { + pub price_low: f64, + pub price_high: f64, +} + +/// Create a `DayOfWeekProfile` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_day_of_week_profile_free`. +#[no_mangle] +pub extern "C" fn wickra_day_of_week_profile_new(utc_offset_minutes: i32) -> *mut DayOfWeekProfile { + Box::into_raw(Box::new(DayOfWeekProfile::new(utc_offset_minutes))) +} + +/// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise +/// returns the length of the `bins` payload and writes up to `cap` of its values +/// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written +/// to `*scalars` when non-`NULL`. +/// +/// # Safety +/// `handle` (from `wickra_day_of_week_profile_new`, not freed), `scalars` and `values` must be valid +/// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_day_of_week_profile_update( + handle: *mut DayOfWeekProfile, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + values: *mut f64, + cap: usize, +) -> isize { + let Some(ind) = handle.as_mut() else { + return -1; + }; + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return -1; + }; + match ind.update(input) { + Some(out_val) => { + if !values.is_null() { + let slots = slice::from_raw_parts_mut(values, cap); + for (slot, &v) in slots.iter_mut().zip(&out_val.bins) { + *slot = v; + } + } + isize::try_from(out_val.bins.len()).unwrap_or(isize::MAX) + } + None => -1, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_day_of_week_profile_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_day_of_week_profile_reset(handle: *mut DayOfWeekProfile) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_day_of_week_profile_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_day_of_week_profile_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_day_of_week_profile_free(handle: *mut DayOfWeekProfile) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `IntradayVolatilityProfile` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_intraday_volatility_profile_free`. +#[no_mangle] +pub extern "C" fn wickra_intraday_volatility_profile_new( + buckets: usize, + utc_offset_minutes: i32, +) -> *mut IntradayVolatilityProfile { + match IntradayVolatilityProfile::new(buckets, utc_offset_minutes) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise +/// returns the length of the `bins` payload and writes up to `cap` of its values +/// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written +/// to `*scalars` when non-`NULL`. +/// +/// # Safety +/// `handle` (from `wickra_intraday_volatility_profile_new`, not freed), `scalars` and `values` must be valid +/// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_intraday_volatility_profile_update( + handle: *mut IntradayVolatilityProfile, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + values: *mut f64, + cap: usize, +) -> isize { + let Some(ind) = handle.as_mut() else { + return -1; + }; + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return -1; + }; + match ind.update(input) { + Some(out_val) => { + if !values.is_null() { + let slots = slice::from_raw_parts_mut(values, cap); + for (slot, &v) in slots.iter_mut().zip(&out_val.bins) { + *slot = v; + } + } + isize::try_from(out_val.bins.len()).unwrap_or(isize::MAX) + } + None => -1, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_intraday_volatility_profile_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_intraday_volatility_profile_reset( + handle: *mut IntradayVolatilityProfile, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_intraday_volatility_profile_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_intraday_volatility_profile_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_intraday_volatility_profile_free( + handle: *mut IntradayVolatilityProfile, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TimeOfDayReturnProfile` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_time_of_day_return_profile_free`. +#[no_mangle] +pub extern "C" fn wickra_time_of_day_return_profile_new( + buckets: usize, + utc_offset_minutes: i32, +) -> *mut TimeOfDayReturnProfile { + match TimeOfDayReturnProfile::new(buckets, utc_offset_minutes) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise +/// returns the length of the `bins` payload and writes up to `cap` of its values +/// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written +/// to `*scalars` when non-`NULL`. +/// +/// # Safety +/// `handle` (from `wickra_time_of_day_return_profile_new`, not freed), `scalars` and `values` must be valid +/// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_time_of_day_return_profile_update( + handle: *mut TimeOfDayReturnProfile, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + values: *mut f64, + cap: usize, +) -> isize { + let Some(ind) = handle.as_mut() else { + return -1; + }; + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return -1; + }; + match ind.update(input) { + Some(out_val) => { + if !values.is_null() { + let slots = slice::from_raw_parts_mut(values, cap); + for (slot, &v) in slots.iter_mut().zip(&out_val.bins) { + *slot = v; + } + } + isize::try_from(out_val.bins.len()).unwrap_or(isize::MAX) + } + None => -1, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_time_of_day_return_profile_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_time_of_day_return_profile_reset( + handle: *mut TimeOfDayReturnProfile, +) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_time_of_day_return_profile_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_time_of_day_return_profile_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_time_of_day_return_profile_free( + handle: *mut TimeOfDayReturnProfile, +) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TpoProfile` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tpo_profile_free`. +#[no_mangle] +pub extern "C" fn wickra_tpo_profile_new(period: usize, bin_count: usize) -> *mut TpoProfile { + match TpoProfile::new(period, bin_count) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise +/// returns the length of the `counts` payload and writes up to `cap` of its values +/// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written +/// to `*scalars` when non-`NULL`. +/// +/// # Safety +/// `handle` (from `wickra_tpo_profile_new`, not freed), `scalars` and `values` must be valid +/// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_tpo_profile_update( + handle: *mut TpoProfile, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + scalars: *mut WickraTpoProfileOutputScalars, + values: *mut f64, + cap: usize, +) -> isize { + let Some(ind) = handle.as_mut() else { + return -1; + }; + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return -1; + }; + match ind.update(input) { + Some(out_val) => { + if !scalars.is_null() { + *scalars = WickraTpoProfileOutputScalars { + price_low: out_val.price_low, + price_high: out_val.price_high, + }; + } + if !values.is_null() { + let slots = slice::from_raw_parts_mut(values, cap); + for (slot, &v) in slots.iter_mut().zip(&out_val.counts) { + *slot = v; + } + } + isize::try_from(out_val.counts.len()).unwrap_or(isize::MAX) + } + None => -1, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tpo_profile_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tpo_profile_reset(handle: *mut TpoProfile) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tpo_profile_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tpo_profile_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tpo_profile_free(handle: *mut TpoProfile) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VolumeByTimeProfile` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volume_by_time_profile_free`. +#[no_mangle] +pub extern "C" fn wickra_volume_by_time_profile_new( + buckets: usize, + utc_offset_minutes: i32, +) -> *mut VolumeByTimeProfile { + match VolumeByTimeProfile::new(buckets, utc_offset_minutes) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise +/// returns the length of the `bins` payload and writes up to `cap` of its values +/// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written +/// to `*scalars` when non-`NULL`. +/// +/// # Safety +/// `handle` (from `wickra_volume_by_time_profile_new`, not freed), `scalars` and `values` must be valid +/// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_by_time_profile_update( + handle: *mut VolumeByTimeProfile, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + values: *mut f64, + cap: usize, +) -> isize { + let Some(ind) = handle.as_mut() else { + return -1; + }; + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return -1; + }; + match ind.update(input) { + Some(out_val) => { + if !values.is_null() { + let slots = slice::from_raw_parts_mut(values, cap); + for (slot, &v) in slots.iter_mut().zip(&out_val.bins) { + *slot = v; + } + } + isize::try_from(out_val.bins.len()).unwrap_or(isize::MAX) + } + None => -1, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volume_by_time_profile_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_by_time_profile_reset(handle: *mut VolumeByTimeProfile) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volume_by_time_profile_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volume_by_time_profile_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_by_time_profile_free(handle: *mut VolumeByTimeProfile) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VolumeProfile` indicator. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volume_profile_free`. +#[no_mangle] +pub extern "C" fn wickra_volume_profile_new(period: usize, bin_count: usize) -> *mut VolumeProfile { + match VolumeProfile::new(period, bin_count) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise +/// returns the length of the `bins` payload and writes up to `cap` of its values +/// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written +/// to `*scalars` when non-`NULL`. +/// +/// # Safety +/// `handle` (from `wickra_volume_profile_new`, not freed), `scalars` and `values` must be valid +/// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_profile_update( + handle: *mut VolumeProfile, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + scalars: *mut WickraVolumeProfileOutputScalars, + values: *mut f64, + cap: usize, +) -> isize { + let Some(ind) = handle.as_mut() else { + return -1; + }; + let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { + return -1; + }; + match ind.update(input) { + Some(out_val) => { + if !scalars.is_null() { + *scalars = WickraVolumeProfileOutputScalars { + price_low: out_val.price_low, + price_high: out_val.price_high, + }; + } + if !values.is_null() { + let slots = slice::from_raw_parts_mut(values, cap); + for (slot, &v) in slots.iter_mut().zip(&out_val.bins) { + *slot = v; + } + } + isize::try_from(out_val.bins.len()).unwrap_or(isize::MAX) + } + None => -1, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volume_profile_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_profile_reset(handle: *mut VolumeProfile) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volume_profile_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volume_profile_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_profile_free(handle: *mut VolumeProfile) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +// ===== Alt-chart bar builders (BarBuilder -> bars via caller buffer + count) ===== + +/// C-ABI view of one `DollarBar` (alt-chart bar element). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraDollarBar { + pub open: f64, + pub high: f64, + pub low: f64, + pub close: f64, + pub volume: f64, + pub dollar: f64, +} + +/// C-ABI view of one `ImbalanceBar` (alt-chart bar element). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraImbalanceBar { + pub open: f64, + pub high: f64, + pub low: f64, + pub close: f64, + pub imbalance: f64, + pub direction: i8, +} + +/// C-ABI view of one `KagiBar` (alt-chart bar element). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraKagiBar { + pub start: f64, + pub end: f64, + pub direction: i8, +} + +/// C-ABI view of one `PnfColumn` (alt-chart bar element). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraPnfColumn { + pub direction: i8, + pub high: f64, + pub low: f64, +} + +/// C-ABI view of one `RangeBar` (alt-chart bar element). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraRangeBar { + pub open: f64, + pub close: f64, + pub direction: i8, +} + +/// C-ABI view of one `RenkoBrick` (alt-chart bar element). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraRenkoBrick { + pub open: f64, + pub close: f64, + pub direction: i8, +} + +/// C-ABI view of one `RunBar` (alt-chart bar element). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraRunBar { + pub open: f64, + pub high: f64, + pub low: f64, + pub close: f64, + pub length: usize, + pub direction: i8, +} + +/// C-ABI view of one `LineBreakBar` (alt-chart bar element). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraLineBreakBar { + pub open: f64, + pub close: f64, + pub direction: i8, +} + +/// C-ABI view of one `TickBar` (alt-chart bar element). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraTickBar { + pub open: f64, + pub high: f64, + pub low: f64, + pub close: f64, + pub volume: f64, +} + +/// C-ABI view of one `VolumeBar` (alt-chart bar element). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraVolumeBar { + pub open: f64, + pub high: f64, + pub low: f64, + pub close: f64, + pub volume: f64, +} + +/// Create a `DollarBars` bar builder. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_dollar_bars_free`. +#[no_mangle] +pub extern "C" fn wickra_dollar_bars_new(dollar_per_bar: f64) -> *mut DollarBars { + match DollarBars::new(dollar_per_bar) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the +/// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, +/// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; +/// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. +/// +/// # Safety +/// `handle` (from `wickra_dollar_bars_new`, not freed) and `out` must be valid or `NULL`; when +/// non-`NULL`, `out` must cover `cap` `WickraDollarBar` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_dollar_bars_update( + handle: *mut DollarBars, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraDollarBar, + cap: usize, +) -> usize { + let Some(builder) = handle.as_mut() else { + return 0; + }; + let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { + return 0; + }; + let bars = builder.update(candle); + if !out.is_null() { + let slots = slice::from_raw_parts_mut(out, cap); + for (slot, bar) in slots.iter_mut().zip(&bars) { + *slot = WickraDollarBar { + open: bar.open, + high: bar.high, + low: bar.low, + close: bar.close, + volume: bar.volume, + dollar: bar.dollar, + }; + } + } + bars.len() +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_dollar_bars_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dollar_bars_reset(handle: *mut DollarBars) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_dollar_bars_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_dollar_bars_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_dollar_bars_free(handle: *mut DollarBars) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ImbalanceBars` bar builder. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_imbalance_bars_free`. +#[no_mangle] +pub extern "C" fn wickra_imbalance_bars_new(threshold: f64) -> *mut ImbalanceBars { + match ImbalanceBars::new(threshold) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the +/// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, +/// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; +/// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. +/// +/// # Safety +/// `handle` (from `wickra_imbalance_bars_new`, not freed) and `out` must be valid or `NULL`; when +/// non-`NULL`, `out` must cover `cap` `WickraImbalanceBar` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_imbalance_bars_update( + handle: *mut ImbalanceBars, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraImbalanceBar, + cap: usize, +) -> usize { + let Some(builder) = handle.as_mut() else { + return 0; + }; + let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { + return 0; + }; + let bars = builder.update(candle); + if !out.is_null() { + let slots = slice::from_raw_parts_mut(out, cap); + for (slot, bar) in slots.iter_mut().zip(&bars) { + *slot = WickraImbalanceBar { + open: bar.open, + high: bar.high, + low: bar.low, + close: bar.close, + imbalance: bar.imbalance, + direction: bar.direction, + }; + } + } + bars.len() +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_imbalance_bars_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_imbalance_bars_reset(handle: *mut ImbalanceBars) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_imbalance_bars_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_imbalance_bars_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_imbalance_bars_free(handle: *mut ImbalanceBars) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `KagiBars` bar builder. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_kagi_bars_free`. +#[no_mangle] +pub extern "C" fn wickra_kagi_bars_new(reversal: f64) -> *mut KagiBars { + match KagiBars::new(reversal) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the +/// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, +/// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; +/// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. +/// +/// # Safety +/// `handle` (from `wickra_kagi_bars_new`, not freed) and `out` must be valid or `NULL`; when +/// non-`NULL`, `out` must cover `cap` `WickraKagiBar` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_kagi_bars_update( + handle: *mut KagiBars, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraKagiBar, + cap: usize, +) -> usize { + let Some(builder) = handle.as_mut() else { + return 0; + }; + let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { + return 0; + }; + let bars = builder.update(candle); + if !out.is_null() { + let slots = slice::from_raw_parts_mut(out, cap); + for (slot, bar) in slots.iter_mut().zip(&bars) { + *slot = WickraKagiBar { + start: bar.start, + end: bar.end, + direction: bar.direction, + }; + } + } + bars.len() +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_kagi_bars_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kagi_bars_reset(handle: *mut KagiBars) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_kagi_bars_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_kagi_bars_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_kagi_bars_free(handle: *mut KagiBars) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `PointAndFigureBars` bar builder. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_point_and_figure_bars_free`. +#[no_mangle] +pub extern "C" fn wickra_point_and_figure_bars_new( + box_size: f64, + reversal: usize, +) -> *mut PointAndFigureBars { + match PointAndFigureBars::new(box_size, reversal) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the +/// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, +/// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; +/// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. +/// +/// # Safety +/// `handle` (from `wickra_point_and_figure_bars_new`, not freed) and `out` must be valid or `NULL`; when +/// non-`NULL`, `out` must cover `cap` `WickraPnfColumn` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_point_and_figure_bars_update( + handle: *mut PointAndFigureBars, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraPnfColumn, + cap: usize, +) -> usize { + let Some(builder) = handle.as_mut() else { + return 0; + }; + let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { + return 0; + }; + let bars = builder.update(candle); + if !out.is_null() { + let slots = slice::from_raw_parts_mut(out, cap); + for (slot, bar) in slots.iter_mut().zip(&bars) { + *slot = WickraPnfColumn { + direction: bar.direction, + high: bar.high, + low: bar.low, + }; + } + } + bars.len() +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_point_and_figure_bars_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_point_and_figure_bars_reset(handle: *mut PointAndFigureBars) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_point_and_figure_bars_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_point_and_figure_bars_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_point_and_figure_bars_free(handle: *mut PointAndFigureBars) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RangeBars` bar builder. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_range_bars_free`. +#[no_mangle] +pub extern "C" fn wickra_range_bars_new(range: f64) -> *mut RangeBars { + match RangeBars::new(range) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the +/// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, +/// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; +/// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. +/// +/// # Safety +/// `handle` (from `wickra_range_bars_new`, not freed) and `out` must be valid or `NULL`; when +/// non-`NULL`, `out` must cover `cap` `WickraRangeBar` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_range_bars_update( + handle: *mut RangeBars, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraRangeBar, + cap: usize, +) -> usize { + let Some(builder) = handle.as_mut() else { + return 0; + }; + let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { + return 0; + }; + let bars = builder.update(candle); + if !out.is_null() { + let slots = slice::from_raw_parts_mut(out, cap); + for (slot, bar) in slots.iter_mut().zip(&bars) { + *slot = WickraRangeBar { + open: bar.open, + close: bar.close, + direction: bar.direction, + }; + } + } + bars.len() +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_range_bars_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_range_bars_reset(handle: *mut RangeBars) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_range_bars_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_range_bars_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_range_bars_free(handle: *mut RangeBars) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RenkoBars` bar builder. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_renko_bars_free`. +#[no_mangle] +pub extern "C" fn wickra_renko_bars_new(box_size: f64) -> *mut RenkoBars { + match RenkoBars::new(box_size) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the +/// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, +/// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; +/// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. +/// +/// # Safety +/// `handle` (from `wickra_renko_bars_new`, not freed) and `out` must be valid or `NULL`; when +/// non-`NULL`, `out` must cover `cap` `WickraRenkoBrick` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_renko_bars_update( + handle: *mut RenkoBars, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraRenkoBrick, + cap: usize, +) -> usize { + let Some(builder) = handle.as_mut() else { + return 0; + }; + let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { + return 0; + }; + let bars = builder.update(candle); + if !out.is_null() { + let slots = slice::from_raw_parts_mut(out, cap); + for (slot, bar) in slots.iter_mut().zip(&bars) { + *slot = WickraRenkoBrick { + open: bar.open, + close: bar.close, + direction: bar.direction, + }; + } + } + bars.len() +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_renko_bars_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_renko_bars_reset(handle: *mut RenkoBars) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_renko_bars_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_renko_bars_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_renko_bars_free(handle: *mut RenkoBars) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `RunBars` bar builder. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_run_bars_free`. +#[no_mangle] +pub extern "C" fn wickra_run_bars_new(run_length: usize) -> *mut RunBars { + match RunBars::new(run_length) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the +/// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, +/// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; +/// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. +/// +/// # Safety +/// `handle` (from `wickra_run_bars_new`, not freed) and `out` must be valid or `NULL`; when +/// non-`NULL`, `out` must cover `cap` `WickraRunBar` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_run_bars_update( + handle: *mut RunBars, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraRunBar, + cap: usize, +) -> usize { + let Some(builder) = handle.as_mut() else { + return 0; + }; + let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { + return 0; + }; + let bars = builder.update(candle); + if !out.is_null() { + let slots = slice::from_raw_parts_mut(out, cap); + for (slot, bar) in slots.iter_mut().zip(&bars) { + *slot = WickraRunBar { + open: bar.open, + high: bar.high, + low: bar.low, + close: bar.close, + length: bar.length, + direction: bar.direction, + }; + } + } + bars.len() +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_run_bars_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_run_bars_reset(handle: *mut RunBars) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_run_bars_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_run_bars_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_run_bars_free(handle: *mut RunBars) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `ThreeLineBreakBars` bar builder. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_three_line_break_bars_free`. +#[no_mangle] +pub extern "C" fn wickra_three_line_break_bars_new(lines: usize) -> *mut ThreeLineBreakBars { + match ThreeLineBreakBars::new(lines) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the +/// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, +/// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; +/// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. +/// +/// # Safety +/// `handle` (from `wickra_three_line_break_bars_new`, not freed) and `out` must be valid or `NULL`; when +/// non-`NULL`, `out` must cover `cap` `WickraLineBreakBar` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_line_break_bars_update( + handle: *mut ThreeLineBreakBars, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraLineBreakBar, + cap: usize, +) -> usize { + let Some(builder) = handle.as_mut() else { + return 0; + }; + let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { + return 0; + }; + let bars = builder.update(candle); + if !out.is_null() { + let slots = slice::from_raw_parts_mut(out, cap); + for (slot, bar) in slots.iter_mut().zip(&bars) { + *slot = WickraLineBreakBar { + open: bar.open, + close: bar.close, + direction: bar.direction, + }; + } + } + bars.len() +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_three_line_break_bars_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_line_break_bars_reset(handle: *mut ThreeLineBreakBars) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_three_line_break_bars_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_three_line_break_bars_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_three_line_break_bars_free(handle: *mut ThreeLineBreakBars) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `TickBars` bar builder. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_tick_bars_free`. +#[no_mangle] +pub extern "C" fn wickra_tick_bars_new(ticks: usize) -> *mut TickBars { + match TickBars::new(ticks) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the +/// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, +/// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; +/// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. +/// +/// # Safety +/// `handle` (from `wickra_tick_bars_new`, not freed) and `out` must be valid or `NULL`; when +/// non-`NULL`, `out` must cover `cap` `WickraTickBar` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_tick_bars_update( + handle: *mut TickBars, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraTickBar, + cap: usize, +) -> usize { + let Some(builder) = handle.as_mut() else { + return 0; + }; + let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { + return 0; + }; + let bars = builder.update(candle); + if !out.is_null() { + let slots = slice::from_raw_parts_mut(out, cap); + for (slot, bar) in slots.iter_mut().zip(&bars) { + *slot = WickraTickBar { + open: bar.open, + high: bar.high, + low: bar.low, + close: bar.close, + volume: bar.volume, + }; + } + } + bars.len() +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_tick_bars_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tick_bars_reset(handle: *mut TickBars) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_tick_bars_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_tick_bars_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_tick_bars_free(handle: *mut TickBars) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// Create a `VolumeBars` bar builder. +/// +/// Returns `NULL` on invalid parameters; release with `wickra_volume_bars_free`. +#[no_mangle] +pub extern "C" fn wickra_volume_bars_new(volume_per_bar: f64) -> *mut VolumeBars { + match VolumeBars::new(volume_per_bar) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the +/// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, +/// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; +/// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. +/// +/// # Safety +/// `handle` (from `wickra_volume_bars_new`, not freed) and `out` must be valid or `NULL`; when +/// non-`NULL`, `out` must cover `cap` `WickraVolumeBar` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_bars_update( + handle: *mut VolumeBars, + open: f64, + high: f64, + low: f64, + close: f64, + volume: f64, + timestamp: i64, + out: *mut WickraVolumeBar, + cap: usize, +) -> usize { + let Some(builder) = handle.as_mut() else { + return 0; + }; + let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { + return 0; + }; + let bars = builder.update(candle); + if !out.is_null() { + let slots = slice::from_raw_parts_mut(out, cap); + for (slot, bar) in slots.iter_mut().zip(&bars) { + *slot = WickraVolumeBar { + open: bar.open, + high: bar.high, + low: bar.low, + close: bar.close, + volume: bar.volume, + }; + } + } + bars.len() +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_volume_bars_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_bars_reset(handle: *mut VolumeBars) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_volume_bars_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_volume_bars_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_volume_bars_free(handle: *mut VolumeBars) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +// ===== Bespoke wirings (MaType-enum ctor, Vec output) ===== + +/// Moving-average selector for `wickra_macd_ext_new`: 0=SMA, 1=EMA, 2=WMA, 3=DEMA, +/// 4=TEMA, 5=TRIMA. Any other value is rejected. +fn ma_type_from_u8(value: u8) -> Option { + match value { + 0 => Some(MaType::Sma), + 1 => Some(MaType::Ema), + 2 => Some(MaType::Wma), + 3 => Some(MaType::Dema), + 4 => Some(MaType::Tema), + 5 => Some(MaType::Trima), + _ => None, + } +} + +/// Create a `MacdExt`. `fast_type` / `slow_type` / `signal_type` pick the moving +/// average per `ma_type_from_u8`. Returns `NULL` on invalid parameters or an unknown +/// MA-type code; release with `wickra_macd_ext_free`. +#[no_mangle] +pub extern "C" fn wickra_macd_ext_new( + fast: usize, + fast_type: u8, + slow: usize, + slow_type: u8, + signal: usize, + signal_type: u8, +) -> *mut MacdExt { + let (Some(ft), Some(st), Some(gt)) = ( + ma_type_from_u8(fast_type), + ma_type_from_u8(slow_type), + ma_type_from_u8(signal_type), + ) else { + return ptr::null_mut(); + }; + match MacdExt::new(fast, ft, slow, st, signal, gt) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one value; on `true` the MACD/signal/histogram triple is written to `*out`. +/// Returns `false` during warmup, on a `NULL` handle / `out`. +/// +/// # Safety +/// `handle` (from `wickra_macd_ext_new`, not freed) and `out` must be valid or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_ext_update( + handle: *mut MacdExt, + value: f64, + out: *mut WickraMacdOutput, +) -> bool { + let Some(ind) = handle.as_mut() else { + return false; + }; + if out.is_null() { + return false; + } + match ind.update(value) { + Some(out_val) => { + *out = WickraMacdOutput { + macd: out_val.macd, + signal: out_val.signal, + histogram: out_val.histogram, + }; + true + } + None => false, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_macd_ext_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_ext_reset(handle: *mut MacdExt) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_macd_ext_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_macd_ext_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_macd_ext_free(handle: *mut MacdExt) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +/// C-ABI view of one `FootprintLevel` (price bucket with bid/ask volume). +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct WickraFootprintLevel { + pub price: f64, + pub bid_vol: f64, + pub ask_vol: f64, +} + +/// Create a `Footprint` order-flow aggregator. Returns `NULL` on invalid parameters; +/// release with `wickra_footprint_free`. +#[no_mangle] +pub extern "C" fn wickra_footprint_new(tick_size: f64) -> *mut Footprint { + match Footprint::new(tick_size) { + Ok(ind) => Box::into_raw(Box::new(ind)), + Err(_) => ptr::null_mut(), + } +} + +/// Feed one trade. Returns `-1` during warmup / on a `NULL` handle / invalid trade, +/// otherwise the number of price levels in the completed footprint; up to `cap` levels +/// are written to `out` (enlarge `cap` if the return exceeds it). +/// +/// # Safety +/// `handle` (from `wickra_footprint_new`, not freed) and `out` must be valid or `NULL`; +/// when non-`NULL`, `out` must cover `cap` `WickraFootprintLevel` elements. +#[no_mangle] +pub unsafe extern "C" fn wickra_footprint_update( + handle: *mut Footprint, + price: f64, + size: f64, + is_buy: bool, + timestamp: i64, + out: *mut WickraFootprintLevel, + cap: usize, +) -> isize { + let Some(ind) = handle.as_mut() else { + return -1; + }; + let side = if is_buy { Side::Buy } else { Side::Sell }; + let Ok(trade) = Trade::new(price, size, side, timestamp) else { + return -1; + }; + match ind.update(trade) { + Some(out_val) => { + if !out.is_null() { + let slots = slice::from_raw_parts_mut(out, cap); + for (slot, level) in slots.iter_mut().zip(&out_val.levels) { + *slot = WickraFootprintLevel { + price: level.price, + bid_vol: level.bid_vol, + ask_vol: level.ask_vol, + }; + } + } + isize::try_from(out_val.levels.len()).unwrap_or(isize::MAX) + } + None => -1, + } +} + +/// Reset all internal state. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must be valid (from `wickra_footprint_new`, not freed), or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_footprint_reset(handle: *mut Footprint) { + if let Some(ind) = handle.as_mut() { + ind.reset(); + } +} + +/// Destroy a handle created by `wickra_footprint_new`. No-op if `handle` is `NULL`. +/// +/// # Safety +/// `handle` must have been returned by `wickra_footprint_new` and not previously freed, or `NULL`. +#[no_mangle] +pub unsafe extern "C" fn wickra_footprint_free(handle: *mut Footprint) { + if !handle.is_null() { + drop(Box::from_raw(handle)); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn new_rejects_zero_period() { + assert!(wickra_sma_new(0).is_null()); + } + + #[test] + fn scalar_streaming_batch_and_lifecycle() { + let handle = wickra_sma_new(3); + assert!(!handle.is_null()); + unsafe { + assert!(wickra_sma_update(handle, 1.0).is_nan()); + assert!(wickra_sma_update(handle, 2.0).is_nan()); + assert!((wickra_sma_update(handle, 3.0) - 2.0).abs() < 1e-9); + + wickra_sma_reset(handle); + let input = [1.0_f64, 2.0, 3.0, 4.0, 5.0]; + let mut out = [0.0_f64; 5]; + wickra_sma_batch(handle, input.as_ptr(), out.as_mut_ptr(), 5); + assert!(out[0].is_nan()); + assert!((out[2] - 2.0).abs() < 1e-9); + assert!((out[4] - 4.0).abs() < 1e-9); + + wickra_sma_free(handle); + } + } + + #[test] + fn null_handle_is_a_defined_noop() { + unsafe { + assert!(wickra_sma_update(ptr::null_mut(), 1.0).is_nan()); + wickra_sma_reset(ptr::null_mut()); + wickra_sma_free(ptr::null_mut()); + } + } +} diff --git a/examples/README.md b/examples/README.md index e17d8b2e..59d208e7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -21,6 +21,19 @@ The Rust examples live in the `wickra-examples` workspace member crate. | `strategy_macd_adx.rs` | Hourly BTCUSDT trend-follower: MACD crossover entries gated by ADX(14) > 20. | `cargo run --release -p wickra-examples --bin strategy_macd_adx` | | `strategy_bollinger_squeeze.rs` | Daily BTCUSDT Bollinger-squeeze breakout with ATR(14) trailing stop. | `cargo run --release -p wickra-examples --bin strategy_bollinger_squeeze` | +## C / C++ — `examples/c/` + +Build the library first (`cargo build -p wickra-c --release`), then build and run +the examples via CMake: +`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`. + +| Example | What it does | CMake target | +| --- | --- | --- | +| `smoke.c` | Links the generated header + library and asserts SMA streaming / batch values across the boundary. | `smoke` | +| `streaming.c` | Feed a tick stream through an EMA, printing each value (NaN during warmup). | `streaming` | +| `smoke.cpp` | C++ RAII via `wickra::Handle` from [`wickra.hpp`](../bindings/c/include/wickra.hpp): construct, move, auto-free. | `cpp_smoke` | + ## Python — `examples/python/` | Example | What it does | Run | diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt new file mode 100644 index 00000000..ea17980d --- /dev/null +++ b/examples/c/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.15) +project(wickra_c_examples C CXX) + +# Directory holding the compiled Wickra C library (cargo output), e.g. +# /target/release. Override with -DWICKRA_LIB_DIR=/path/to/target/release. +if(NOT DEFINED WICKRA_LIB_DIR) + set(WICKRA_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../target/release") +endif() +set(WICKRA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../bindings/c/include") + +# Pick the right link target per platform/toolchain. +# - MSVC links the generated import library (wickra.dll.lib). +# - MinGW/gcc on Windows links the DLL directly. +# - Unix links the shared object / dylib. +if(WIN32) + set(WICKRA_RUNTIME "${WICKRA_LIB_DIR}/wickra.dll") + if(MSVC) + set(WICKRA_LINK_LIB "${WICKRA_LIB_DIR}/wickra.dll.lib") + else() + set(WICKRA_LINK_LIB "${WICKRA_LIB_DIR}/wickra.dll") + endif() +elseif(APPLE) + set(WICKRA_LINK_LIB "${WICKRA_LIB_DIR}/libwickra.dylib") +else() + set(WICKRA_LINK_LIB "${WICKRA_LIB_DIR}/libwickra.so") +endif() + +enable_testing() + +# Build one example, link it to the Wickra library, run it as a ctest. On Windows +# the DLL is copied next to the executable so the loader finds it at run time. +function(add_wickra_example name source) + add_executable(${name} ${source}) + target_include_directories(${name} PRIVATE "${WICKRA_INCLUDE_DIR}") + target_link_libraries(${name} PRIVATE "${WICKRA_LINK_LIB}") + if(UNIX AND NOT APPLE) + target_link_libraries(${name} PRIVATE m) + endif() + if(WIN32) + add_custom_command(TARGET ${name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${WICKRA_RUNTIME}" "$") + endif() + add_test(NAME ${name} COMMAND ${name}) + if(NOT WIN32) + set_tests_properties(${name} PROPERTIES + ENVIRONMENT "LD_LIBRARY_PATH=${WICKRA_LIB_DIR};DYLD_LIBRARY_PATH=${WICKRA_LIB_DIR}") + endif() +endfunction() + +add_wickra_example(smoke smoke.c) # links the boundary, asserts values +add_wickra_example(streaming streaming.c) # runnable streaming demo +add_wickra_example(cpp_smoke smoke.cpp) # C++ RAII wrapper (wickra.hpp) diff --git a/examples/c/README.md b/examples/c/README.md new file mode 100644 index 00000000..631f141a --- /dev/null +++ b/examples/c/README.md @@ -0,0 +1,68 @@ +# Wickra — C / C++ examples + +The Wickra C ABI is a single shared/static library plus a generated header +([`bindings/c/include/wickra.h`](../../bindings/c/include/wickra.h)). Any +C-capable language links against the same artifact; these examples show the +plain-C path. + +## Build the library + +From the workspace root: + +```sh +cargo build -p wickra-c --release +``` + +This produces, in `target/release/`: + +| Platform | Shared library | Link target | +|----------|----------------|-------------| +| Linux | `libwickra.so` | `-lwickra` | +| macOS | `libwickra.dylib` | `-lwickra` | +| Windows (MSVC) | `wickra.dll` | `wickra.dll.lib` (import lib) | + +A static library (`libwickra.a` / `wickra.lib`) is emitted alongside. + +## Build and run the smoke example + +### With CMake (portable, used by CI) + +```sh +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 +``` + +### Directly with a compiler + +```sh +# Linux / macOS +cc examples/c/smoke.c -I bindings/c/include -L target/release -lwickra -lm -o smoke +LD_LIBRARY_PATH=target/release ./smoke # macOS: DYLD_LIBRARY_PATH + +# Windows (MinGW gcc, linking the DLL directly) +gcc examples/c/smoke.c -I bindings/c/include target/release/wickra.dll -lm -o smoke.exe +``` + +Expected output: + +``` +OK: wickra C ABI smoke passed (SMA streaming + batch + reset + NULL-safety + free) +``` + +## Usage shape + +Every indicator follows the same five-function pattern over an opaque handle: + +```c +#include "wickra.h" + +struct Sma *sma = wickra_sma_new(14); /* NULL on invalid params */ +double v = wickra_sma_update(sma, 42.0); /* NaN during warmup */ +wickra_sma_reset(sma); /* back to fresh state */ +wickra_sma_free(sma); /* exactly once per _new */ +``` + +There is no RAII across the C boundary: every `wickra__new` must be paired +with exactly one `wickra__free`. All functions are NULL-safe (a NULL handle +yields `NaN` / a no-op, never a crash). diff --git a/examples/c/smoke.c b/examples/c/smoke.c new file mode 100644 index 00000000..46615d21 --- /dev/null +++ b/examples/c/smoke.c @@ -0,0 +1,64 @@ +/* Smoke test for the Wickra C ABI. + * + * This is the one test the Rust unit tests structurally cannot do: it links a + * foreign C consumer against the generated `wickra.h` + the compiled library and + * exercises the real FFI boundary (symbol export, header correctness, opaque + * handle, pointer ownership, `_free`). If this passes, every C-capable language + * (C, C++, Go, C#, Java, R) can link the same way. + * + * Build (from the workspace root, after `cargo build -p wickra-c --release`): + * cc examples/c/smoke.c -I bindings/c/include target/release/ -lm -o smoke + */ + +#include "wickra.h" +#include +#include + +static int near(double a, double b) { return fabs(a - b) < 1e-9; } + +int main(void) { + struct Sma *sma = wickra_sma_new(3); + if (sma == NULL) { + printf("FAIL: wickra_sma_new returned NULL\n"); + return 1; + } + + /* SMA(3): first two outputs are warmup (NaN), then the trailing mean. */ + double in[5] = {1.0, 2.0, 3.0, 4.0, 5.0}; + double r0 = wickra_sma_update(sma, in[0]); /* NaN (1/3) */ + double r1 = wickra_sma_update(sma, in[1]); /* NaN (2/3) */ + double r2 = wickra_sma_update(sma, in[2]); /* 2.0 (1+2+3)/3 */ + double r3 = wickra_sma_update(sma, in[3]); /* 3.0 (2+3+4)/3 */ + + if (!isnan(r0) || !isnan(r1)) { + printf("FAIL: warmup not NaN (%f %f)\n", r0, r1); + return 1; + } + if (!near(r2, 2.0) || !near(r3, 3.0)) { + printf("FAIL: streaming values (%f %f), expected (2.0 3.0)\n", r2, r3); + return 1; + } + + /* Batch over a reset instance must reproduce the streaming result. */ + wickra_sma_reset(sma); + double out[5]; + wickra_sma_batch(sma, in, out, 5); + if (!isnan(out[0]) || !isnan(out[1]) || + !near(out[2], 2.0) || !near(out[3], 3.0) || !near(out[4], 4.0)) { + printf("FAIL: batch mismatch (%f %f %f %f %f)\n", + out[0], out[1], out[2], out[3], out[4]); + return 1; + } + + /* NULL handle is a defined no-op / NaN, never a crash. */ + if (!isnan(wickra_sma_update(NULL, 1.0))) { + printf("FAIL: NULL update did not return NaN\n"); + return 1; + } + wickra_sma_reset(NULL); + wickra_sma_free(NULL); + + wickra_sma_free(sma); + printf("OK: wickra C ABI smoke passed (SMA streaming + batch + reset + NULL-safety + free)\n"); + return 0; +} diff --git a/examples/c/smoke.cpp b/examples/c/smoke.cpp new file mode 100644 index 00000000..f2cb5be4 --- /dev/null +++ b/examples/c/smoke.cpp @@ -0,0 +1,36 @@ +// C++ smoke test for the Wickra C ABI via the optional RAII wrapper (`wickra.hpp`). +// +// Validates that the header compiles as C++ and that `wickra::Handle` constructs, +// moves, and frees correctly across the boundary. + +#include "wickra.hpp" + +#include +#include +#include + +int main() { + wickra::Handle sma(wickra_sma_new(3)); + if (!sma) { + std::puts("FAIL: wickra_sma_new returned null"); + return 1; + } + + (void)wickra_sma_update(sma.get(), 1.0); + (void)wickra_sma_update(sma.get(), 2.0); + double value = wickra_sma_update(sma.get(), 3.0); + if (std::fabs(value - 2.0) > 1e-9) { + std::printf("FAIL: SMA(3) value %.6f, expected 2.0\n", value); + return 1; + } + + // Move transfers ownership; the moved-from handle must not double-free. + wickra::Handle moved(std::move(sma)); + if (static_cast(sma) || !static_cast(moved)) { + std::puts("FAIL: move semantics"); + return 1; + } + + std::puts("OK: wickra C++ RAII smoke passed (Handle construct + move + auto-free)"); + return 0; +} diff --git a/examples/c/streaming.c b/examples/c/streaming.c new file mode 100644 index 00000000..fae45e1b --- /dev/null +++ b/examples/c/streaming.c @@ -0,0 +1,36 @@ +/* Streaming usage example for the Wickra C ABI. + * + * The same five-function shape (new / update / batch / reset / free) drives every + * scalar indicator. Here an EMA consumes a live tick stream one value at a time; + * `update` is O(1) per tick and returns NaN until the indicator has warmed up. + * + * Build (after `cargo build -p wickra-c --release`): + * cc examples/c/streaming.c -I bindings/c/include -L target/release -lwickra -lm -o streaming + */ + +#include "wickra.h" +#include + +int main(void) { + struct Ema *ema = wickra_ema_new(5); + if (ema == NULL) { + fprintf(stderr, "failed to create EMA\n"); + return 1; + } + + const double prices[] = {10.0, 10.5, 11.0, 10.8, 11.2, 11.5, 11.3, 11.8}; + const size_t n = sizeof(prices) / sizeof(prices[0]); + + printf("EMA(5) streaming:\n"); + for (size_t i = 0; i < n; ++i) { + double value = wickra_ema_update(ema, prices[i]); + if (value != value) { /* NaN during warmup */ + printf(" tick %zu price %.2f -> (warming up)\n", i, prices[i]); + } else { + printf(" tick %zu price %.2f -> %.4f\n", i, prices[i], value); + } + } + + wickra_ema_free(ema); + return 0; +}