diff --git a/.gitattributes b/.gitattributes
index 1ae4ab5d..1c53277a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -6,3 +6,7 @@
# check (regenerate + `git diff`) never trips on a CRLF normalization.
bindings/c/include/wickra.h text eol=lf
+# C# sources (including the generated binding) are pinned to LF so the committed
+# files stay stable regardless of the committer's autocrlf setting.
+*.cs text eol=lf
+
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 06ec110f..d0df5ebf 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -30,9 +30,9 @@ assignees: ""
## Environment
- Wickra version:
-- Language / binding:
+- Language / binding:
- OS and architecture:
-- Rust / Python / Node version (If relevant):
+- Rust / Python / Node / .NET version (If relevant):
## Additional context
diff --git a/.github/ISSUE_TEMPLATE/bug_report_detailed.md b/.github/ISSUE_TEMPLATE/bug_report_detailed.md
index ce44fbc1..c0b60a0c 100644
--- a/.github/ISSUE_TEMPLATE/bug_report_detailed.md
+++ b/.github/ISSUE_TEMPLATE/bug_report_detailed.md
@@ -26,7 +26,7 @@ assignees: []
| Binding version | `e.g. python 0.4.2 / node 0.4.2` |
| OS / arch | `e.g. Windows 11 x86_64, Linux glibc` |
| Rust toolchain | `rustc --version` (If building from source) |
-| Python / Node version | `python --version` / `node --version` |
+| Python / Node / .NET version | `python --version` / `node --version` / `dotnet --version` |
## Minimal reproducer
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 987eeab6..8b48ce2d 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -25,6 +25,8 @@ assignees: ""
- [ ] Should be exposed in the Python binding
- [ ] Should be exposed in the Node binding
- [ ] Should be exposed in the WASM binding
+- [ ] Should be exposed in the C ABI
+- [ ] Should be exposed in the C# / .NET binding
## Additional context
diff --git a/.github/ISSUE_TEMPLATE/performance_regression.md b/.github/ISSUE_TEMPLATE/performance_regression.md
index 971d3761..eb971488 100644
--- a/.github/ISSUE_TEMPLATE/performance_regression.md
+++ b/.github/ISSUE_TEMPLATE/performance_regression.md
@@ -13,7 +13,7 @@ assignees: []
## Affected code path
- Indicator / API: `e.g. EMA.update`
-- Binding: `Rust / Python / Node / Wasm / C ABI`
+- Binding: `Rust / Python / Node / Wasm / C ABI / C# (.NET)`
- Hot loop or one-shot call?
## Versions compared
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
index d5e5cedb..2942b179 100644
--- a/.github/ISSUE_TEMPLATE/question.md
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -33,4 +33,4 @@ import wickra as ta
## Environment (Only if relevant)
- Wickra version: `e.g. 0.4.2`
-- Binding: `Rust / Python / Node / Wasm / C ABI`
+- Binding: `Rust / Python / Node / Wasm / C ABI / C# (.NET)`
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index ec52060c..f49a3d2a 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -22,7 +22,7 @@
- [ ] `cargo clippy --workspace --all-targets -- -D warnings` is clean.
- [ ] `cargo test --workspace` passes.
- [ ] New behaviour has tests; bug fixes have a regression test.
-- [ ] Public API changes are mirrored in the Python / Node / WASM bindings and the C ABI is regenerated
+- [ ] Public API changes are mirrored in the Python / Node / WASM bindings, and the C ABI + C# binding are regenerated
and their type stubs (If applicable).
- [ ] The relevant page on the [documentation site](https://docs.wickra.org)
and the `README.md` are updated (If applicable). Docs edits go to a
diff --git a/.github/PULL_REQUEST_TEMPLATE/detailed.md b/.github/PULL_REQUEST_TEMPLATE/detailed.md
index 7726a977..cdebdebb 100644
--- a/.github/PULL_REQUEST_TEMPLATE/detailed.md
+++ b/.github/PULL_REQUEST_TEMPLATE/detailed.md
@@ -23,6 +23,8 @@ Please fill in the sections below. Delete any that don't apply.
- [ ] Python binding (`bindings/python`)
- [ ] Node.js binding (`bindings/node`)
- [ ] WebAssembly binding (`bindings/wasm`)
+- [ ] C ABI (`bindings/c`)
+- [ ] C# / .NET binding (`bindings/csharp`)
- [ ] Examples / docs
## Linked issues
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4f68bfdb..1d66588b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -677,6 +677,56 @@ jobs:
cmake --build examples/c/build --config Release
ctest --test-dir examples/c/build -C Release --output-on-failure
+ csharp:
+ name: C# 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
+
+ # The binding links against the C ABI hub at runtime; build it first so the
+ # DllImportResolver finds target/release/wickra.{dll,so,dylib}. .NET 8 SDK is
+ # preinstalled on the GitHub runners, so no setup-dotnet step is needed.
+ - name: Build the C ABI library
+ run: cargo build -p wickra-c --release
+
+ - name: .NET info
+ run: dotnet --info
+
+ - name: Test the C# binding
+ run: dotnet test bindings/csharp/Wickra.Tests/Wickra.Tests.csproj -c Release
+
+ - name: Build the C# examples
+ shell: bash
+ run: |
+ for d in streaming backtest multi_timeframe parallel_assets \
+ strategy_rsi_mean_reversion strategy_macd_adx strategy_bollinger_squeeze \
+ fetch_btcusdt live_binance; do
+ dotnet build "examples/csharp/$d" -c Release
+ done
+
+ # Run only the offline examples (fetch_btcusdt / live_binance need network).
+ - name: Run the offline C# examples
+ shell: bash
+ run: |
+ for d in streaming backtest multi_timeframe parallel_assets \
+ strategy_rsi_mean_reversion strategy_macd_adx strategy_bollinger_squeeze; do
+ dotnet run --project "examples/csharp/$d" -c Release --no-build
+ done
+
# 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 60c0c2a7..ab111a89 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -624,6 +624,88 @@ jobs:
path: wickra-c-${{ matrix.target }}.tar.gz
if-no-files-found: error
+ # Pack and publish the .NET binding to NuGet. Independent of the GitHub-release
+ # job so a C# hiccup never blocks the C/C++ asset release. Authentication uses
+ # NuGet Trusted Publishing (OIDC) — no long-lived API key. The 'wickra-release'
+ # trusted-publishing policy on nuget.org (owner KingchenC, repo wickra-lib/wickra,
+ # workflow release.yml) exchanges the GitHub OIDC token for a short-lived key.
+ csharp-publish:
+ name: Publish to NuGet
+ needs: c-abi-build
+ runs-on: ubuntu-latest
+ environment: release
+ permissions:
+ contents: read
+ id-token: write # request the GitHub OIDC token for trusted publishing
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+
+ - name: Download the C ABI native libraries
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ pattern: c-abi-*
+ path: c-abi-artifacts
+
+ - name: Stage native libraries into runtimes//native
+ shell: bash
+ run: |
+ set -e
+ declare -A RID=(
+ [x86_64-unknown-linux-gnu]=linux-x64
+ [aarch64-unknown-linux-gnu]=linux-arm64
+ [x86_64-apple-darwin]=osx-x64
+ [aarch64-apple-darwin]=osx-arm64
+ [x86_64-pc-windows-msvc]=win-x64
+ [aarch64-pc-windows-msvc]=win-arm64
+ )
+ base=bindings/csharp/Wickra/runtimes
+ for target in "${!RID[@]}"; do
+ archive=$(find c-abi-artifacts -name "wickra-c-$target.tar.gz" | head -1)
+ if [ -z "$archive" ]; then
+ echo "::error::missing native artifact for $target"; exit 1
+ fi
+ tmp=$(mktemp -d); tar -xzf "$archive" -C "$tmp"
+ dest="$base/${RID[$target]}/native"; mkdir -p "$dest"
+ for f in libwickra.so libwickra.dylib wickra.dll; do
+ src=$(find "$tmp" -name "$f" | head -1)
+ [ -n "$src" ] && cp "$src" "$dest/"
+ done
+ echo "staged ${RID[$target]}:"; ls -l "$dest"
+ done
+
+ - name: Pack
+ shell: bash
+ run: |
+ version="${GITHUB_REF_NAME#v}"
+ dotnet pack bindings/csharp/Wickra/Wickra.csproj -c Release -p:Version="$version" -o nupkg
+
+ # Exchange the GitHub OIDC token for a short-lived (~1h) NuGet API key.
+ # 'user' is the nuget.org profile name (the package owner), not an email.
+ - name: NuGet login (OIDC -> temporary API key)
+ uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1.2.0
+ id: nuget_login
+ with:
+ user: KingchenC
+
+ # Pass the temporary key through the environment (not string-interpolated
+ # into the script) so it cannot be parsed as shell — avoids template injection.
+ - name: Push to NuGet
+ shell: bash
+ env:
+ NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }}
+ run: |
+ dotnet nuget push "nupkg/*.nupkg" --api-key "$NUGET_API_KEY" \
+ --source https://api.nuget.org/v3/index.json --skip-duplicate
+
+ - name: Upload the NuGet package as a build artifact
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: nuget-package
+ path: nupkg/*.nupkg
+ if-no-files-found: error
+
github-release:
name: Attach assets to the draft GitHub Release
needs: [cargo-publish, python-publish, node-publish, wasm-publish, c-abi-build]
diff --git a/.github/workflows/sync-about.yml b/.github/workflows/sync-about.yml
index 7b507ad7..09632959 100644
--- a/.github/workflows/sync-about.yml
+++ b/.github/workflows/sync-about.yml
@@ -149,7 +149,7 @@ jobs:
# actually live (Cloudflare Pages, P8.1); merging this PR is therefore
# gated on the domain resolving, otherwise the About link would 404.
homepage="https://docs.wickra.org"
- desc="Streaming-first technical indicators with a Rust core and Python, Node.js, WebAssembly, and C ABI bindings. ${n} indicators, O(1) per-tick updates, no system dependencies. Drop-in TA-Lib replacement."
+ desc="Streaming-first technical indicators with a Rust core and Python, Node.js, WebAssembly, C ABI, and .NET bindings. ${n} indicators, O(1) per-tick updates, no system dependencies. Drop-in TA-Lib replacement."
# Enforce the homepage unconditionally — it is a constant, so this both
# corrects the stale kingchenc URL and self-heals any future drift.
# Same Administration-write permission as --description (no extra scope).
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index 12e3d173..424f8fb9 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -27,21 +27,26 @@ or replace lives behind a separate crate boundary.
│ no I/O, no deps │ │ optional features │
└──────────────────────┘ └────────────────────┘
▲
- │ (every binding wraps the same core)
- │
- ┌───────────┬────────────┴┬──────────────┬──────────────────────┐
- │ │ │ │ │
-┌──▼─────┐ ┌───▼────┐ ┌──────▼──────┐ ┌─────▼──────────────┐
-│ Python │ │ Node │ │ WASM │ │ C ABI (cbindgen) │
-│ (PyO3) │ │(napi-rs)│ │(wasm-bindgen)│ │ cdylib + header │
-└────────┘ └────────┘ └─────────────┘ └────────────────────┘
+ │ every binding wraps the same core
+ ┌────────────┼────────────┬────────────────┐
+ │ │ │ │
+┌──▼───────┐ ┌──▼───────┐ ┌──▼───────────┐ ┌──▼──────────────────┐
+│ Python │ │ Node │ │ WASM │ │ C ABI (cbindgen) │
+│ (PyO3) │ │ (napi-rs)│ │(wasm-bindgen)│ │ cdylib + header │
+└──────────┘ └──────────┘ └──────────────┘ └─────────┬───────────┘
+ │ linked by
+ ┌──────────▼──────────┐
+ │ C · C++ · C# · Go │
+ │ · Java · R │
+ └─────────────────────┘
```
Python, Node and WASM are *native* Rust bindings (PyO3 / napi-rs /
wasm-bindgen). The C ABI is the *hub* every other C-capable language links
against: it builds to a `cdylib`/`staticlib` plus a generated `wickra.h`, and
-C / C++ / Go / C# / Java / R consume that one artifact rather than each
-re-wrapping the core.
+downstream languages link that one artifact rather than each re-wrapping the
+core. C and C++ link it directly; the **C# / .NET** binding (`bindings/csharp`,
+on NuGet) is generated from `wickra.h`, with Go / Java / R planned the same way.
| Crate | Path | What it owns | Public deps |
|---|---|---|---|
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2f333cea..cbeb563c 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# / .NET binding (`bindings/csharp`)** — the first language stecker on the
+ C ABI hub. Exposes all 514 indicators as idiomatic `IDisposable` classes via
+ `[LibraryImport]` source-generated P/Invoke, generated from `wickra.h`. Ships
+ on NuGet as `Wickra` with prebuilt native libraries for six target triples
+ (win/linux/osx × x64/arm64), plus a full example suite mirroring the C examples.
## [0.7.5] - 2026-06-09
### Added
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d8de3de2..d9de81cc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -22,6 +22,7 @@ licensed as above, without any additional terms or conditions.
| `bindings/node` | napi-rs bindings (`wickra` on npm). |
| `bindings/wasm` | wasm-bindgen bindings (`wickra-wasm` on npm). |
| `bindings/c` | C ABI — `cdylib` + `staticlib` + generated `include/wickra.h`. The hub for C / C++ and any C-capable language. |
+| `bindings/csharp` | .NET binding over the C ABI (`Wickra` on NuGet) — `[LibraryImport]` P/Invoke generated from `wickra.h`. |
| `examples/` | Runnable examples. |
| `docs/` | Pointer to the documentation site (docs.wickra.org); the docs live in the `wickra-lib/wickra-docs` repo. |
@@ -105,7 +106,8 @@ installed. Dependabot also keeps the `.github/requirements` pins current.
- **Bindings.** A change to a public indicator API must be mirrored across the
Python, Node, and WASM bindings, including their type stubs / `.d.ts`. The C ABI
(`bindings/c`) is generated from the core, so regenerate it from the core and
- commit `src/lib.rs` + `include/wickra.h`.
+ commit `src/lib.rs` + `include/wickra.h`. The C# binding (`bindings/csharp`) is
+ generated from `wickra.h`, so regenerate and commit its `Generated/*.g.cs` too.
- **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/README.md b/README.md
index c0fe8563..d374d332 100644
--- a/README.md
+++ b/README.md
@@ -2,24 +2,25 @@
-[](https://github.com/wickra-lib/wickra/actions/workflows/ci.yml)
-[](https://github.com/wickra-lib/wickra/actions/workflows/codeql.yml)
-[](https://codecov.io/gh/wickra-lib/wickra)
-[](https://github.com/wickra-lib/wickra/releases/latest)
-[](https://crates.io/crates/wickra)
-[](https://pypi.org/project/wickra/)
-[](https://www.npmjs.com/package/wickra)
-[](#license)
-[](https://scorecard.dev/viewer/?uri=github.com/wickra-lib/wickra)
-[](https://www.bestpractices.dev/projects/13094)
-[](https://github.com/wickra-lib/wickra/attestations)
-[](https://docs.wickra.org)
+[](https://github.com/wickra-lib/wickra/actions/workflows/ci.yml)
+[](https://github.com/wickra-lib/wickra/actions/workflows/codeql.yml)
+[](https://codecov.io/gh/wickra-lib/wickra)
+[](https://github.com/wickra-lib/wickra/releases/latest)
+[](https://crates.io/crates/wickra)
+[](https://pypi.org/project/wickra/)
+[](https://www.npmjs.com/package/wickra)
+[](https://www.nuget.org/packages/Wickra)
+[](#license)
+[](https://scorecard.dev/viewer/?uri=github.com/wickra-lib/wickra)
+[](https://www.bestpractices.dev/projects/13094)
+[](https://github.com/wickra-lib/wickra/attestations)
+[](https://docs.wickra.org)
**Streaming-first technical indicators. Install with `pip install wickra` — no system dependencies.**
Wickra is a multi-language technical-analysis library with a Rust core and
-native bindings for Python, Node.js and WebAssembly, plus a C ABI that any
-C-capable language (C, C++, and beyond) links against. Every indicator is a
+native bindings for Python, Node.js and WebAssembly, plus a C ABI that C, C++,
+C# / .NET and any other C-capable language links against. Every indicator is a
state machine that updates in O(1) per new data point, so live trading bots and
historical backtests share the exact same implementation.
@@ -47,7 +48,9 @@ Full documentation lives at **[docs.wickra.org](https://docs.wickra.org)**:
- **Quickstarts** — [Rust](https://docs.wickra.org/Quickstart-Rust),
[Python](https://docs.wickra.org/Quickstart-Python),
[Node](https://docs.wickra.org/Quickstart-Node),
- [WASM](https://docs.wickra.org/Quickstart-WASM).
+ [WASM](https://docs.wickra.org/Quickstart-WASM),
+ [C](https://docs.wickra.org/Quickstart-C),
+ [C#](https://docs.wickra.org/Quickstart-CSharp).
- **Indicators** — a per-indicator deep dive (formula, parameters, warmup) for
every one of the 514 indicators; start at the
[indicators overview](https://docs.wickra.org/Indicators-Overview).
@@ -73,7 +76,7 @@ times to get there.
metrics — every single one updating in **O(1) per tick**. TA-Lib ships ~150 and
none of them stream.
- **One Rust core, five first-class targets.** Native **Python · Node.js ·
- WebAssembly · Rust** plus a **C ABI** for C / C++ and any C-capable language —
+ WebAssembly · Rust** plus a **C ABI** for C / C++, C# / .NET and any other C-capable language —
identical math, identical results, zero per-language reimplementation and zero
GIL bottleneck.
- **Correct by construction, not by hope.** Every `update` validates its input,
@@ -97,7 +100,8 @@ 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 · C** | **514** | **yes** |
+| **★ Wickra**| **clean** | **yes, O(1)** | **Rust · Python · Node · WASM** | **514** | **yes** |
+| | | | **C · C#** | | |
| kand | clean | yes | Python · WASM · Rust | ~60 | yes |
| ta-rs | clean | yes | Rust only | ~30 | stale |
| yata | clean | partial | Rust only | ~35 | yes |
@@ -168,8 +172,9 @@ 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 five bindings
-inherit it automatically (the C ABI is generated from the core).
+Adding a new indicator means implementing one trait in Rust; every binding
+inherits it automatically (the C ABI — and the C# binding generated from it —
+regenerate from the core).
## Languages
@@ -180,6 +185,7 @@ inherit it automatically (the C ABI is generated from the core).
| Browser / WASM | `npm install wickra-wasm` | `examples/wasm/index.html` |
| Rust | `cargo add wickra` | `examples/rust/src/bin/backtest.rs` |
| C / C++ (C ABI) | header + library, see [`bindings/c`](bindings/c) | `examples/c/streaming.c` |
+| C# / .NET (C ABI) | `dotnet add package Wickra`, see [`bindings/csharp`](bindings/csharp) | `examples/csharp/streaming` |
Each binding ships several runnable examples (streaming, backtest, live feed);
[`examples/README.md`](examples/README.md) is the full cross-language index.
@@ -249,14 +255,16 @@ wickra/
│ ├── python/ PyO3 + maturin (publishes on PyPI)
│ ├── node/ napi-rs (publishes on npm)
│ ├── wasm/ wasm-bindgen (browsers, bundlers, Node)
-│ └── c/ C ABI (cdylib + staticlib) + generated include/wickra.h
+│ ├── c/ C ABI (cdylib + staticlib) + generated include/wickra.h
+│ └── csharp/ .NET binding over the C ABI (publishes on NuGet)
├── 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`
-│ └── c/ C smoke + streaming, C++ RAII wrapper
+│ ├── c/ C smoke + streaming, C++ RAII wrapper
+│ └── csharp/ streaming, backtest, strategies (load `Wickra`)
└── .github/workflows/ CI and release pipelines
```
@@ -289,6 +297,9 @@ cd bindings/node && npm install && npm run build && npm test
cargo build -p wickra-c --release
cmake -S examples/c -B examples/c/build -DWICKRA_LIB_DIR="$PWD/target/release"
cmake --build examples/c/build && ctest --test-dir examples/c/build --output-on-failure
+
+# C# / .NET binding (requires the .NET 8 SDK; links the C ABI above)
+dotnet test bindings/csharp/Wickra.Tests/Wickra.Tests.csproj
```
## Testing
diff --git a/ROADMAP.md b/ROADMAP.md
index ebcb911e..ed2ae445 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -21,8 +21,9 @@ minor releases; breaking changes are called out in the changelog.
versioning stability for a 1.0 release.
- **Performance.** Keep per-tick updates O(1) and maintain the benchmark suite;
investigate further allocation and cache improvements.
-- **Bindings parity.** Keep the Python, Node.js and WebAssembly bindings in
- lockstep with the Rust core, including type stubs and platform coverage.
+- **Bindings parity.** Keep the Python, Node.js and WebAssembly bindings — plus
+ the C ABI and the C# / .NET binding generated from it — in lockstep with the
+ Rust core, including type stubs and platform coverage.
- **Documentation.** Maintain a deep-dive page per indicator on
, plus quickstarts and cookbook material.
- **Project health.** Maintain test coverage, static and dynamic analysis,
diff --git a/SUPPORT.md b/SUPPORT.md
index c2de87c5..3ad9269e 100644
--- a/SUPPORT.md
+++ b/SUPPORT.md
@@ -7,8 +7,8 @@ Thanks for using Wickra! Here is where to get help, depending on what you need.
Most questions are answered in the documentation:
- **Docs site:** — quickstarts for Rust, Python,
- Node.js and WebAssembly, a per-indicator reference, warmup periods, the data
- layer, and an FAQ.
+ Node.js, WebAssembly, C and C#, a per-indicator reference, warmup periods, the
+ data layer, and an FAQ.
- **README:** — installation and a
quick overview.
- **API docs (Rust):** .
diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md
index 654fab6a..58142539 100644
--- a/THREAT_MODEL.md
+++ b/THREAT_MODEL.md
@@ -3,7 +3,8 @@
This document describes Wickra's attack surface and the threats considered,
together with their mitigations. It complements the security assurance case in
[`SECURITY.md`](SECURITY.md). Wickra is a computational technical-analysis
-library (a Rust core with Python, Node.js and WebAssembly bindings plus a C ABI),
+library (a Rust core with Python, Node.js and WebAssembly bindings plus a C ABI
+and the .NET binding built on it),
not a network service or trading system; the attack surface is correspondingly
small.
diff --git a/bindings/c/README.md b/bindings/c/README.md
index 36e62d18..83b75b4e 100644
--- a/bindings/c/README.md
+++ b/bindings/c/README.md
@@ -1,59 +1,80 @@
-# wickra-c
+# Wickra — C / 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
+[](https://github.com/wickra-lib/wickra/actions/workflows/ci.yml)
+[](https://codecov.io/gh/wickra-lib/wickra)
+[](https://github.com/wickra-lib/wickra/releases/latest)
+[](https://github.com/wickra-lib/wickra#license)
+
+**Streaming-first technical indicators for C and C++. A prebuilt shared/static
+library plus a generated `wickra.h` — no system dependencies.**
+
+Wickra is a multi-language technical-analysis library with a Rust core and
+bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C# and any
+other C-capable language. Every indicator is an O(1)
+streaming state machine, so live trading bots and historical backtests share
+the exact same implementation. This package is the **C ABI hub**: it compiles the
core to a C-compatible shared/static library plus a generated header, so any
C-capable language (C, C++, Go, C#, Java, R) links against one artifact instead
-of re-wiring every indicator natively.
+of re-wrapping every indicator natively.
-The native Python, Node, and WebAssembly bindings are unaffected — this is
-additive, for the ecosystems without first-class Rust tooling.
+## Install
-## Artifacts
+Grab the prebuilt header + library for your platform from the
+[GitHub releases](https://github.com/wickra-lib/wickra/releases) — each archive
+has `wickra.h`, the optional `wickra.hpp` C++ wrapper, and the shared/static
+library — or build from source:
-```sh
+```bash
cargo build -p wickra-c --release
+# -> target/release/libwickra.{so,dylib} or wickra.dll (+ import lib) + a staticlib
```
-- `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.
+Then compile against the header and link the library
+(`cc app.c -I include -L lib -lwickra -lm -o app`).
-## API shape
-
-Each indicator is exposed as five `extern "C"` functions over an opaque handle:
+## Quick start
```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 */
+#include "wickra.h"
+
+struct Rsi *rsi = wickra_rsi_new(14); /* NULL on invalid params */
+for (size_t i = 0; i < n; ++i) {
+ double v = wickra_rsi_update(rsi, prices[i]); /* NaN during warmup */
+ if (v == v && v > 70.0) /* v == v is the NaN check */
+ printf("overbought\n");
+}
+wickra_rsi_free(rsi); /* exactly once per _new */
```
-Conventions:
+Every indicator is an opaque handle with the same five functions —
+`_new` / `_update` / `_batch` / `_reset` / `_free`. `update` is O(1); there is no
+RAII across the C boundary, so each `_new` needs exactly one `_free`, and every
+function is NULL-safe (a NULL handle yields `NaN` or a no-op, never a crash).
+Multi-output indicators (MACD, Bollinger, ADX, …) take a pointer to a `#[repr(C)]`
+struct and return a `bool`. The optional `wickra.hpp` wraps any handle in a
+move-only `wickra::Handle` for exception-safe C++ lifetimes.
-- **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.
+## Documentation
-## Header regeneration
+The full indicator catalogue, guides, quickstarts, and API reference live in
+the main repository and documentation site:
-The header is generated and committed; CI checks it is in sync:
+- **Repository & full indicator list:**
+- **Docs** (C quickstart, cookbook, TA-Lib migration):
+- **Runnable examples:** [`examples/c/`](https://github.com/wickra-lib/wickra/tree/main/examples/c)
-```sh
-cbindgen --config bindings/c/cbindgen.toml --crate wickra-c --output bindings/c/include/wickra.h
-```
+Wickra ships native bindings for Python, Node.js, WebAssembly and Rust, plus this
+C ABI hub that any C-capable language (C, C++, Go, C#, Java, R) links against —
+all exposing the same indicators from the shared Rust core.
-## Examples
+## Disclaimer
-Runnable C examples (build via CMake or a direct compiler invocation) live in
-[`examples/c`](../../examples/c).
+Wickra is an indicator toolkit, not a trading system. The values it computes
+are deterministic transforms of the input data — they are not financial advice
+and do not predict the market. Any use in a live trading context is at your own
+risk. The library is provided **as is**, without warranty of any kind.
## License
-`MIT OR Apache-2.0`, the same as the rest of Wickra.
+Licensed under either of [Apache-2.0](https://github.com/wickra-lib/wickra/blob/main/LICENSE-APACHE)
+or [MIT](https://github.com/wickra-lib/wickra/blob/main/LICENSE-MIT) at your option.
diff --git a/bindings/csharp/.gitignore b/bindings/csharp/.gitignore
new file mode 100644
index 00000000..15fbd7d5
--- /dev/null
+++ b/bindings/csharp/.gitignore
@@ -0,0 +1,12 @@
+# .NET build output
+bin/
+obj/
+*.user
+
+# NuGet packaging output
+*.nupkg
+*.snupkg
+
+# Native libraries staged for packaging (produced by the release pipeline from
+# the wickra-c-.tar.gz assets; never committed to source).
+Wickra/runtimes/
diff --git a/bindings/csharp/README.md b/bindings/csharp/README.md
new file mode 100644
index 00000000..b9b4d37e
--- /dev/null
+++ b/bindings/csharp/README.md
@@ -0,0 +1,78 @@
+# Wickra — .NET
+
+[](https://github.com/wickra-lib/wickra/actions/workflows/ci.yml)
+[](https://codecov.io/gh/wickra-lib/wickra)
+[](https://www.nuget.org/packages/Wickra)
+[](https://github.com/wickra-lib/wickra#license)
+
+**Streaming-first technical indicators for .NET. `dotnet add package Wickra` —
+prebuilt native library, no system dependencies.**
+
+Wickra is a multi-language technical-analysis library with a Rust core and
+bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C# and any
+other C-capable language. Every indicator is an O(1)
+streaming state machine, so live trading bots and historical backtests share
+the exact same implementation. This package is the .NET binding; it consumes the
+C ABI hub through `[LibraryImport]` P/Invoke and exposes all 514 streaming-first
+indicators as idiomatic `IDisposable` classes.
+
+## Install
+
+```bash
+dotnet add package Wickra
+```
+
+The native library ships prebuilt per platform (Linux, macOS, Windows — x64 and
+arm64) under `runtimes//native/`, selected automatically. There is nothing
+to compile. Targets .NET 8 and later.
+
+## Quick start
+
+```csharp
+using Wickra;
+
+// Batch: run an indicator over a whole series (NaN at warmup positions).
+var prices = Enumerable.Range(0, 1000).Select(i => 100.0 + i * 0.1).ToArray();
+using var sma = new Sma(20);
+double[] values = sma.Batch(prices);
+
+// Streaming: the same indicator, fed tick by tick in O(1).
+using var rsi = new Rsi(14);
+foreach (var price in liveFeed)
+{
+ var value = rsi.Update(price); // NaN during warmup, no recomputation
+ if (double.IsFinite(value) && value > 70)
+ {
+ Console.WriteLine("overbought");
+ }
+}
+```
+
+`Batch(prices)` and feeding the same prices through `Update()` produce identical
+values — the equivalence is enforced by the test suite. Multi-output indicators
+(MACD, Bollinger, ADX, …) return a nullable `record struct`, `null` while warming up.
+
+## Documentation
+
+The full indicator catalogue, guides, quickstarts, and API reference live in
+the main repository and documentation site:
+
+- **Repository & full indicator list:**
+- **Docs** (quickstarts, cookbook, TA-Lib migration):
+- **Runnable examples:** [`examples/csharp/`](https://github.com/wickra-lib/wickra/tree/main/examples/csharp)
+
+Wickra ships native bindings for Python, Node.js, WebAssembly and Rust, plus a
+C ABI hub that any C-capable language (C, C++, Go, C#, Java, R) links against —
+all exposing the same indicators from the shared, `unsafe`-forbidden Rust core.
+
+## Disclaimer
+
+Wickra is an indicator toolkit, not a trading system. The values it computes
+are deterministic transforms of the input data — they are not financial advice
+and do not predict the market. Any use in a live trading context is at your own
+risk. The library is provided **as is**, without warranty of any kind.
+
+## License
+
+Licensed under either of [Apache-2.0](https://github.com/wickra-lib/wickra/blob/main/LICENSE-APACHE)
+or [MIT](https://github.com/wickra-lib/wickra/blob/main/LICENSE-MIT) at your option.
diff --git a/bindings/csharp/Wickra.Tests/ArchetypeTests.cs b/bindings/csharp/Wickra.Tests/ArchetypeTests.cs
new file mode 100644
index 00000000..18bb7eb1
--- /dev/null
+++ b/bindings/csharp/Wickra.Tests/ArchetypeTests.cs
@@ -0,0 +1,144 @@
+using Wickra;
+using Xunit;
+
+namespace Wickra.Tests;
+
+///
+/// One representative per FFI archetype, exercising every marshalling path the
+/// generator produces (scalar, candle, pairwise, multi-output, bars, profile,
+/// values-profile, array-input). Garbage marshalling surfaces as NaN, wild
+/// values, or crashes — so finite/sane assertions are the real check.
+///
+public class ArchetypeTests
+{
+ private static (double open, double high, double low, double close, double volume, long ts) Candle(int i)
+ {
+ var close = 100.0 + 10.0 * Math.Sin(i * 0.3);
+ var open = 100.0 + 10.0 * Math.Sin((i - 1) * 0.3);
+ var high = Math.Max(open, close) + 1.0;
+ var low = Math.Min(open, close) - 1.0;
+ return (open, high, low, close, 1_000.0, i * 60_000L);
+ }
+
+ [Fact]
+ public void Scalar_Ema_IsFiniteAfterWarmup()
+ {
+ using var ema = new Ema(3);
+ double last = double.NaN;
+ for (var i = 1; i <= 10; i++)
+ {
+ last = ema.Update(i);
+ }
+
+ Assert.True(double.IsFinite(last));
+ Assert.InRange(last, 1.0, 10.0);
+ }
+
+ [Fact]
+ public void Candle_Atr_IsFinitePositive()
+ {
+ using var atr = new Atr(3);
+ double last = double.NaN;
+ for (var i = 0; i < 20; i++)
+ {
+ var (o, h, l, c, v, ts) = Candle(i);
+ last = atr.Update(o, h, l, c, v, ts);
+ }
+
+ Assert.True(double.IsFinite(last));
+ Assert.True(last > 0.0);
+ }
+
+ [Fact]
+ public void Pairwise_Beta_IsFinite()
+ {
+ using var beta = new Beta(5);
+ double last = double.NaN;
+ for (var i = 0; i < 30; i++)
+ {
+ var market = 100.0 + 10.0 * Math.Sin(i * 0.5);
+ var asset = 50.0 + 6.0 * Math.Sin(i * 0.5 + 0.2);
+ last = beta.Update(market, asset);
+ }
+
+ Assert.True(double.IsFinite(last));
+ }
+
+ [Fact]
+ public void MultiOutput_Adx_ReturnsFiniteStruct()
+ {
+ using var adx = new Adx(5);
+ AdxOutput? result = null;
+ for (var i = 0; i < 60; i++)
+ {
+ var (o, h, l, c, v, ts) = Candle(i);
+ result = adx.Update(o, h, l, c, v, ts);
+ }
+
+ Assert.NotNull(result);
+ Assert.True(double.IsFinite(result!.Value.Adx));
+ Assert.True(double.IsFinite(result.Value.PlusDi));
+ Assert.True(double.IsFinite(result.Value.MinusDi));
+ }
+
+ [Fact]
+ public void Bars_DollarBars_EmitsBars()
+ {
+ using var bars = new DollarBars(5_000.0);
+ var total = 0;
+ for (var i = 0; i < 200; i++)
+ {
+ var (o, h, l, c, v, ts) = Candle(i);
+ total += bars.Update(o, h, l, c, v, ts).Length;
+ }
+
+ Assert.True(total > 0);
+ }
+
+ [Fact]
+ public void Profile_VolumeProfile_ReturnsValues()
+ {
+ using var profile = new VolumeProfile(20, 8);
+ VolumeProfileOutputScalars? result = null;
+ for (var i = 0; i < 60; i++)
+ {
+ var (o, h, l, c, v, ts) = Candle(i);
+ result = profile.Update(o, h, l, c, v, ts);
+ }
+
+ Assert.NotNull(result);
+ Assert.NotNull(result!.Value.Values);
+ Assert.True(result.Value.PriceLow <= result.Value.PriceHigh);
+ }
+
+ [Fact]
+ public void ProfileValues_DayOfWeekProfile_NoCrash()
+ {
+ using var profile = new DayOfWeekProfile(0);
+ double[]? result = null;
+ for (var i = 0; i < 60; i++)
+ {
+ var close = 100.0 + 5.0 * Math.Sin(i * 0.2);
+ // one day apart so the day-of-week buckets fill
+ result = profile.Update(close, close + 1, close - 1, close, 1_000.0, i * 86_400_000L);
+ }
+
+ if (result is not null)
+ {
+ Assert.All(result, v => Assert.True(double.IsFinite(v)));
+ }
+ }
+
+ [Fact]
+ public void ArrayInput_DepthSlope_IsFinite()
+ {
+ using var slope = new DepthSlope();
+ ReadOnlySpan bidPrice = stackalloc double[] { 99.0, 98.0, 97.0 };
+ ReadOnlySpan bidSize = stackalloc double[] { 10.0, 20.0, 30.0 };
+ ReadOnlySpan askPrice = stackalloc double[] { 101.0, 102.0, 103.0 };
+ ReadOnlySpan askSize = stackalloc double[] { 12.0, 22.0, 32.0 };
+
+ var result = slope.Update(bidPrice, bidSize, askPrice, askSize);
+ Assert.True(double.IsFinite(result));
+ }
+}
diff --git a/bindings/csharp/Wickra.Tests/SmaTests.cs b/bindings/csharp/Wickra.Tests/SmaTests.cs
new file mode 100644
index 00000000..506ccb85
--- /dev/null
+++ b/bindings/csharp/Wickra.Tests/SmaTests.cs
@@ -0,0 +1,51 @@
+using Wickra;
+using Xunit;
+
+namespace Wickra.Tests;
+
+public class SmaTests
+{
+ [Fact]
+ public void StreamingMatchesReference()
+ {
+ using var sma = new Sma(3);
+ Assert.True(double.IsNaN(sma.Update(1)));
+ Assert.True(double.IsNaN(sma.Update(2)));
+ Assert.Equal(2.0, sma.Update(3), 9);
+ Assert.Equal(3.0, sma.Update(4), 9);
+ Assert.Equal(4.0, sma.Update(5), 9);
+ }
+
+ [Fact]
+ public void BatchMatchesStreaming()
+ {
+ using var sma = new Sma(3);
+ var output = sma.Batch(new double[] { 1, 2, 3, 4, 5 });
+
+ Assert.True(double.IsNaN(output[0]));
+ Assert.True(double.IsNaN(output[1]));
+ Assert.Equal(2.0, output[2], 9);
+ Assert.Equal(3.0, output[3], 9);
+ Assert.Equal(4.0, output[4], 9);
+ }
+
+ [Fact]
+ public void ResetClearsState()
+ {
+ using var sma = new Sma(3);
+ sma.Update(1);
+ sma.Update(2);
+ sma.Update(3);
+ sma.Reset();
+ Assert.True(double.IsNaN(sma.Update(10)));
+ }
+
+ [Fact]
+ public void ZeroPeriodThrows()
+ {
+ // Zero is rejected by the native constructor (returns NULL) -> ArgumentException;
+ // a negative period is caught earlier by the wrapper guard.
+ Assert.Throws(() => new Sma(0));
+ Assert.Throws(() => new Sma(-1));
+ }
+}
diff --git a/bindings/csharp/Wickra.Tests/Wickra.Tests.csproj b/bindings/csharp/Wickra.Tests/Wickra.Tests.csproj
new file mode 100644
index 00000000..4b92c837
--- /dev/null
+++ b/bindings/csharp/Wickra.Tests/Wickra.Tests.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net8.0
+ latest
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bindings/csharp/Wickra/Generated/Indicators.g.cs b/bindings/csharp/Wickra/Generated/Indicators.g.cs
new file mode 100644
index 00000000..617dcbc0
--- /dev/null
+++ b/bindings/csharp/Wickra/Generated/Indicators.g.cs
@@ -0,0 +1,29189 @@
+//
+// Generated from bindings/c/include/wickra.h. Do not edit by hand.
+using System.Runtime.InteropServices;
+
+namespace Wickra;
+
+public readonly record struct AccelerationBandsOutput(double Upper, double Middle, double Lower);
+public readonly record struct AdxOutput(double PlusDi, double MinusDi, double Adx);
+public readonly record struct AlligatorOutput(double Jaw, double Teeth, double Lips);
+public readonly record struct AndrewsPitchforkOutput(double Median, double Upper, double Lower);
+public readonly record struct AroonOutput(double Up, double Down);
+public readonly record struct AtrBandsOutput(double Upper, double Middle, double Lower);
+public readonly record struct AtrRatchetOutput(double Value, double Direction);
+public readonly record struct AutoFibOutput(double Level0, double Level236, double Level382, double Level500, double Level618, double Level786, double Level1000);
+public readonly record struct BollingerOutput(double Upper, double Middle, double Lower, double Stddev);
+public readonly record struct BomarBandsOutput(double Upper, double Middle, double Lower);
+public readonly record struct CamarillaPivotsOutput(double Pp, double R1, double R2, double R3, double R4, double S1, double S2, double S3, double S4);
+public readonly record struct CandleVolumeOutput(double Body, double Width);
+public readonly record struct CentralPivotRangeOutput(double Pivot, double Tc, double Bc);
+public readonly record struct ChandeKrollStopOutput(double StopLong, double StopShort);
+public readonly record struct ChandelierExitOutput(double LongStop, double ShortStop);
+public readonly record struct ClassicPivotsOutput(double Pp, double R1, double R2, double R3, double S1, double S2, double S3);
+public readonly record struct CointegrationOutput(double HedgeRatio, double Spread, double AdfStat);
+public readonly record struct CompositeProfileOutput(double Poc, double Vah, double Val);
+public readonly record struct DemarkPivotsOutput(double Pp, double R1, double S1);
+public readonly record struct DollarBar(double Open, double High, double Low, double Close, double Volume, double Dollar);
+public readonly record struct DonchianOutput(double Upper, double Middle, double Lower);
+public readonly record struct DonchianStopOutput(double StopLong, double StopShort);
+public readonly record struct DoubleBollingerOutput(double UpperOuter, double UpperInner, double Middle, double LowerInner, double LowerOuter);
+public readonly record struct ElderRayOutput(double BullPower, double BearPower);
+public readonly record struct ElderSafeZoneOutput(double Value, double Direction);
+public readonly record struct EquivolumeOutput(double Height, double Width);
+public readonly record struct FibArcsOutput(double Arc382, double Arc500, double Arc618);
+public readonly record struct FibChannelOutput(double Base, double Level618, double Level1000, double Level1618);
+public readonly record struct FibConfluenceOutput(double Price, double Strength);
+public readonly record struct FibExtensionOutput(double Level1272, double Level1414, double Level1618, double Level2000, double Level2618);
+public readonly record struct FibFanOutput(double Fan382, double Fan500, double Fan618);
+public readonly record struct FibProjectionOutput(double Level618, double Level1000, double Level1618, double Level2618);
+public readonly record struct FibRetracementOutput(double Level0, double Level236, double Level382, double Level500, double Level618, double Level786, double Level1000);
+public readonly record struct FibTimeZonesOutput(double OnZone, double BarsToNext);
+public readonly record struct FibonacciPivotsOutput(double Pp, double R1, double R2, double R3, double S1, double S2, double S3);
+public readonly record struct FootprintLevel(double Price, double BidVol, double AskVol);
+public readonly record struct FractalChaosBandsOutput(double Upper, double Lower);
+public readonly record struct GatorOscillatorOutput(double Upper, double Lower);
+public readonly record struct GoldenPocketOutput(double Low, double Mid, double High);
+public readonly record struct HeikinAshiOutput(double Open, double High, double Low, double Close);
+public readonly record struct HighLowVolumeNodesOutput(double Hvn, double Lvn);
+public readonly record struct HtPhasorOutput(double Inphase, double Quadrature);
+public readonly record struct HurstChannelOutput(double Upper, double Middle, double Lower);
+public readonly record struct IchimokuOutput(double Tenkan, double Kijun, double SenkouA, double SenkouB, double Chikou);
+public readonly record struct ImbalanceBar(double Open, double High, double Low, double Close, double Imbalance, double Direction);
+public readonly record struct InitialBalanceOutput(double High, double Low);
+public readonly record struct KagiBar(double Start, double End, double Direction);
+public readonly record struct KalmanHedgeRatioOutput(double HedgeRatio, double Intercept, double Spread);
+public readonly record struct KaseDevStopOutput(double Value, double Direction);
+public readonly record struct KasePermissionStochasticOutput(double Fast, double Slow);
+public readonly record struct KeltnerOutput(double Upper, double Middle, double Lower);
+public readonly record struct KstOutput(double Kst, double Signal);
+public readonly record struct LeadLagCrossCorrelationOutput(double Lag, double Correlation);
+public readonly record struct LinRegChannelOutput(double Upper, double Middle, double Lower);
+public readonly record struct LineBreakBar(double Open, double Close, double Direction);
+public readonly record struct LiquidationFeaturesOutput(double Long, double Short, double Net, double Total, double Imbalance);
+public readonly record struct MaEnvelopeOutput(double Upper, double Middle, double Lower);
+public readonly record struct MacdOutput(double Macd, double Signal, double Histogram);
+public readonly record struct MamaOutput(double Mama, double Fama);
+public readonly record struct MedianChannelOutput(double Upper, double Middle, double Lower);
+public readonly record struct ModifiedMaStopOutput(double Value, double Direction);
+public readonly record struct MurreyMathLinesOutput(double Mm88, double Mm78, double Mm68, double Mm58, double Mm48, double Mm38, double Mm28, double Mm18, double Mm08);
+public readonly record struct NrtrOutput(double Value, double Direction);
+public readonly record struct OpeningRangeOutput(double High, double Low, double BreakoutDistance);
+public readonly record struct OvernightIntradayReturnOutput(double Overnight, double Intraday);
+public readonly record struct PnfColumn(double Direction, double High, double Low);
+public readonly record struct ProjectionBandsOutput(double Upper, double Middle, double Lower);
+public readonly record struct QqeOutput(double RsiMa, double TrailingLine);
+public readonly record struct QuartileBandsOutput(double Upper, double Middle, double Lower);
+public readonly record struct RangeBar(double Open, double Close, double Direction);
+public readonly record struct RelativeStrengthOutput(double Ratio, double RatioMa, double RatioRsi);
+public readonly record struct RenkoBrick(double Open, double Close, double Direction);
+public readonly record struct RunBar(double Open, double High, double Low, double Close, double Length, double Direction);
+public readonly record struct RwiOutput(double High, double Low);
+public readonly record struct SessionHighLowOutput(double High, double Low);
+public readonly record struct SessionRangeOutput(double Asia, double Eu, double Us);
+public readonly record struct SmoothedHeikinAshiOutput(double Open, double High, double Low, double Close);
+public readonly record struct SpreadBollingerBandsOutput(double Middle, double Upper, double Lower, double PercentB);
+public readonly record struct StandardErrorBandsOutput(double Upper, double Middle, double Lower);
+public readonly record struct StarcBandsOutput(double Upper, double Middle, double Lower);
+public readonly record struct StochasticOutput(double K, double D);
+public readonly record struct SuperTrendOutput(double Value, double Direction);
+public readonly record struct TdLinesOutput(double Resistance, double Support);
+public readonly record struct TdMovingAverageOutput(double St1, double St2);
+public readonly record struct TdRangeProjectionOutput(double High, double Low);
+public readonly record struct TdRiskLevelOutput(double BuyRisk, double SellRisk);
+public readonly record struct TdSequentialOutput(double Setup, double Countdown, double Direction);
+public readonly record struct TickBar(double Open, double High, double Low, double Close, double Volume);
+public readonly record struct TpoProfileOutputScalars(double PriceLow, double PriceHigh, double[] Values);
+public readonly record struct TtmSqueezeOutput(double Squeeze, double Momentum);
+public readonly record struct ValueAreaOutput(double Poc, double Vah, double Val);
+public readonly record struct VolatilityConeOutput(double Current, double Min, double Median, double Max, double Percentile);
+public readonly record struct VolumeBar(double Open, double High, double Low, double Close, double Volume);
+public readonly record struct VolumeProfileOutputScalars(double PriceLow, double PriceHigh, double[] Values);
+public readonly record struct VolumeWeightedMacdOutput(double Macd, double Signal, double Histogram);
+public readonly record struct VolumeWeightedSrOutput(double Support, double Resistance);
+public readonly record struct VortexOutput(double Plus, double Minus);
+public readonly record struct VwapStdDevBandsOutput(double Upper, double Middle, double Lower, double Stddev);
+public readonly record struct WaveTrendOutput(double Wt1, double Wt2);
+public readonly record struct WilliamsFractalsOutput(double Up, double Down);
+public readonly record struct WoodiePivotsOutput(double Pp, double R1, double R2, double S1, double S2);
+public readonly record struct ZeroLagMacdOutput(double Macd, double Signal, double Histogram);
+public readonly record struct ZigZagOutput(double Swing, double Direction);
+
+public sealed class AbandonedBaby : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AbandonedBaby()
+ {
+ var ptr = NativeMethods.wickra_abandoned_baby_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AbandonedBaby parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_abandoned_baby_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_abandoned_baby_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_abandoned_baby_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_abandoned_baby_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Abcd : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Abcd()
+ {
+ var ptr = NativeMethods.wickra_abcd_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Abcd parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_abcd_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_abcd_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_abcd_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_abcd_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AbsoluteBreadthIndex : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AbsoluteBreadthIndex()
+ {
+ var ptr = NativeMethods.wickra_absolute_breadth_index_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AbsoluteBreadthIndex parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_absolute_breadth_index_free);
+ }
+
+ public double Update(ReadOnlySpan change, ReadOnlySpan volume, ReadOnlySpan newHigh, ReadOnlySpan newLow, ReadOnlySpan aboveMa, ReadOnlySpan onBuySignal, long timestamp)
+ {
+ if (volume.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newHigh.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newLow.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (aboveMa.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (onBuySignal.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+
+ double result;
+ unsafe
+ {
+ fixed (double* changePtr = change)
+ fixed (double* volumePtr = volume)
+ fixed (bool* newHighPtr = newHigh)
+ fixed (bool* newLowPtr = newLow)
+ fixed (bool* aboveMaPtr = aboveMa)
+ fixed (bool* onBuySignalPtr = onBuySignal)
+ {
+ result = NativeMethods.wickra_absolute_breadth_index_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_absolute_breadth_index_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AccelerationBands : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AccelerationBands(int period, double factor)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_acceleration_bands_new((nuint)period, factor);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AccelerationBands parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_acceleration_bands_free);
+ }
+
+ public AccelerationBandsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraAccelerationBandsOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_acceleration_bands_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new AccelerationBandsOutput(native.upper, native.middle, native.lower) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_acceleration_bands_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AcceleratorOscillator : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AcceleratorOscillator(int aoFast, int aoSlow, int signalPeriod)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(aoFast);
+ ArgumentOutOfRangeException.ThrowIfNegative(aoSlow);
+ ArgumentOutOfRangeException.ThrowIfNegative(signalPeriod);
+ var ptr = NativeMethods.wickra_accelerator_oscillator_new((nuint)aoFast, (nuint)aoSlow, (nuint)signalPeriod);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AcceleratorOscillator parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_accelerator_oscillator_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_accelerator_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_accelerator_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_accelerator_oscillator_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AdOscillator : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AdOscillator()
+ {
+ var ptr = NativeMethods.wickra_ad_oscillator_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AdOscillator parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_ad_oscillator_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_ad_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_ad_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_ad_oscillator_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AdVolumeLine : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AdVolumeLine()
+ {
+ var ptr = NativeMethods.wickra_ad_volume_line_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AdVolumeLine parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_ad_volume_line_free);
+ }
+
+ public double Update(ReadOnlySpan change, ReadOnlySpan volume, ReadOnlySpan newHigh, ReadOnlySpan newLow, ReadOnlySpan aboveMa, ReadOnlySpan onBuySignal, long timestamp)
+ {
+ if (volume.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newHigh.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newLow.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (aboveMa.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (onBuySignal.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+
+ double result;
+ unsafe
+ {
+ fixed (double* changePtr = change)
+ fixed (double* volumePtr = volume)
+ fixed (bool* newHighPtr = newHigh)
+ fixed (bool* newLowPtr = newLow)
+ fixed (bool* aboveMaPtr = aboveMa)
+ fixed (bool* onBuySignalPtr = onBuySignal)
+ {
+ result = NativeMethods.wickra_ad_volume_line_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_ad_volume_line_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AdaptiveCci : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AdaptiveCci(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_adaptive_cci_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AdaptiveCci parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_adaptive_cci_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_adaptive_cci_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_adaptive_cci_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_adaptive_cci_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AdaptiveCycle : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AdaptiveCycle()
+ {
+ var ptr = NativeMethods.wickra_adaptive_cycle_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AdaptiveCycle parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_adaptive_cycle_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_adaptive_cycle_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_adaptive_cycle_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_adaptive_cycle_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AdaptiveLaguerreFilter : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AdaptiveLaguerreFilter(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_adaptive_laguerre_filter_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AdaptiveLaguerreFilter parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_adaptive_laguerre_filter_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_adaptive_laguerre_filter_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_adaptive_laguerre_filter_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_adaptive_laguerre_filter_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AdaptiveRsi : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AdaptiveRsi(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_adaptive_rsi_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AdaptiveRsi parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_adaptive_rsi_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_adaptive_rsi_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_adaptive_rsi_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_adaptive_rsi_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Adl : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Adl()
+ {
+ var ptr = NativeMethods.wickra_adl_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Adl parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_adl_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_adl_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_adl_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_adl_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AdvanceBlock : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AdvanceBlock()
+ {
+ var ptr = NativeMethods.wickra_advance_block_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AdvanceBlock parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_advance_block_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_advance_block_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_advance_block_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_advance_block_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AdvanceDecline : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AdvanceDecline()
+ {
+ var ptr = NativeMethods.wickra_advance_decline_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AdvanceDecline parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_advance_decline_free);
+ }
+
+ public double Update(ReadOnlySpan change, ReadOnlySpan volume, ReadOnlySpan newHigh, ReadOnlySpan newLow, ReadOnlySpan aboveMa, ReadOnlySpan onBuySignal, long timestamp)
+ {
+ if (volume.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newHigh.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newLow.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (aboveMa.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (onBuySignal.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+
+ double result;
+ unsafe
+ {
+ fixed (double* changePtr = change)
+ fixed (double* volumePtr = volume)
+ fixed (bool* newHighPtr = newHigh)
+ fixed (bool* newLowPtr = newLow)
+ fixed (bool* aboveMaPtr = aboveMa)
+ fixed (bool* onBuySignalPtr = onBuySignal)
+ {
+ result = NativeMethods.wickra_advance_decline_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_advance_decline_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AdvanceDeclineRatio : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AdvanceDeclineRatio()
+ {
+ var ptr = NativeMethods.wickra_advance_decline_ratio_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AdvanceDeclineRatio parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_advance_decline_ratio_free);
+ }
+
+ public double Update(ReadOnlySpan change, ReadOnlySpan volume, ReadOnlySpan newHigh, ReadOnlySpan newLow, ReadOnlySpan aboveMa, ReadOnlySpan onBuySignal, long timestamp)
+ {
+ if (volume.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newHigh.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newLow.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (aboveMa.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (onBuySignal.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+
+ double result;
+ unsafe
+ {
+ fixed (double* changePtr = change)
+ fixed (double* volumePtr = volume)
+ fixed (bool* newHighPtr = newHigh)
+ fixed (bool* newLowPtr = newLow)
+ fixed (bool* aboveMaPtr = aboveMa)
+ fixed (bool* onBuySignalPtr = onBuySignal)
+ {
+ result = NativeMethods.wickra_advance_decline_ratio_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_advance_decline_ratio_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Adx : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Adx(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_adx_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Adx parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_adx_free);
+ }
+
+ public AdxOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraAdxOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_adx_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new AdxOutput(native.plus_di, native.minus_di, native.adx) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_adx_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Adxr : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Adxr(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_adxr_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Adxr parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_adxr_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_adxr_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_adxr_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_adxr_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Alligator : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Alligator(int jawPeriod, int teethPeriod, int lipsPeriod)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(jawPeriod);
+ ArgumentOutOfRangeException.ThrowIfNegative(teethPeriod);
+ ArgumentOutOfRangeException.ThrowIfNegative(lipsPeriod);
+ var ptr = NativeMethods.wickra_alligator_new((nuint)jawPeriod, (nuint)teethPeriod, (nuint)lipsPeriod);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Alligator parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_alligator_free);
+ }
+
+ public AlligatorOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraAlligatorOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_alligator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new AlligatorOutput(native.jaw, native.teeth, native.lips) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_alligator_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Alma : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Alma(int period, double offset, double sigma)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_alma_new((nuint)period, offset, sigma);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Alma parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_alma_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_alma_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_alma_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_alma_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Alpha : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Alpha(int period, double riskFree)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_alpha_new((nuint)period, riskFree);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Alpha parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_alpha_free);
+ }
+
+ public double Update(double x, double y)
+ {
+ var result = NativeMethods.wickra_alpha_update(_handle.DangerousGetHandle(), x, y);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan x, ReadOnlySpan y)
+ {
+ var n = x.Length;
+ if (y.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* xPtr = x)
+ fixed (double* yPtr = y)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_alpha_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_alpha_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AmihudIlliquidity : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AmihudIlliquidity(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_amihud_illiquidity_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AmihudIlliquidity parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_amihud_illiquidity_free);
+ }
+
+ public double Update(double price, double size, bool isBuy, long timestamp)
+ {
+ var result = NativeMethods.wickra_amihud_illiquidity_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_amihud_illiquidity_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AnchoredRsi : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AnchoredRsi()
+ {
+ var ptr = NativeMethods.wickra_anchored_rsi_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AnchoredRsi parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_anchored_rsi_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_anchored_rsi_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_anchored_rsi_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_anchored_rsi_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AnchoredVwap : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AnchoredVwap()
+ {
+ var ptr = NativeMethods.wickra_anchored_vwap_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AnchoredVwap parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_anchored_vwap_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_anchored_vwap_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_anchored_vwap_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_anchored_vwap_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AndrewsPitchfork : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AndrewsPitchfork(int strength)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(strength);
+ var ptr = NativeMethods.wickra_andrews_pitchfork_new((nuint)strength);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AndrewsPitchfork parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_andrews_pitchfork_free);
+ }
+
+ public AndrewsPitchforkOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraAndrewsPitchforkOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_andrews_pitchfork_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new AndrewsPitchforkOutput(native.median, native.upper, native.lower) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_andrews_pitchfork_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Apo : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Apo(int fast, int slow)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(fast);
+ ArgumentOutOfRangeException.ThrowIfNegative(slow);
+ var ptr = NativeMethods.wickra_apo_new((nuint)fast, (nuint)slow);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Apo parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_apo_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_apo_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_apo_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_apo_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Aroon : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Aroon(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_aroon_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Aroon parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_aroon_free);
+ }
+
+ public AroonOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraAroonOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_aroon_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new AroonOutput(native.up, native.down) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_aroon_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AroonOscillator : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AroonOscillator(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_aroon_oscillator_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AroonOscillator parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_aroon_oscillator_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_aroon_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_aroon_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_aroon_oscillator_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Atr : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Atr(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_atr_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Atr parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_atr_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_atr_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_atr_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_atr_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AtrBands : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AtrBands(int period, double multiplier)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_atr_bands_new((nuint)period, multiplier);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AtrBands parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_atr_bands_free);
+ }
+
+ public AtrBandsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraAtrBandsOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_atr_bands_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new AtrBandsOutput(native.upper, native.middle, native.lower) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_atr_bands_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AtrRatchet : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AtrRatchet(int atrPeriod, double startMult, double increment)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(atrPeriod);
+ var ptr = NativeMethods.wickra_atr_ratchet_new((nuint)atrPeriod, startMult, increment);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AtrRatchet parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_atr_ratchet_free);
+ }
+
+ public AtrRatchetOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraAtrRatchetOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_atr_ratchet_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new AtrRatchetOutput(native.@value, native.direction) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_atr_ratchet_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AtrTrailingStop : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AtrTrailingStop(int atrPeriod, double multiplier)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(atrPeriod);
+ var ptr = NativeMethods.wickra_atr_trailing_stop_new((nuint)atrPeriod, multiplier);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AtrTrailingStop parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_atr_trailing_stop_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_atr_trailing_stop_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_atr_trailing_stop_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_atr_trailing_stop_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AutoFib : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AutoFib()
+ {
+ var ptr = NativeMethods.wickra_auto_fib_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AutoFib parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_auto_fib_free);
+ }
+
+ public AutoFibOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraAutoFibOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_auto_fib_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new AutoFibOutput(native.level_0, native.level_236, native.level_382, native.level_500, native.level_618, native.level_786, native.level_1000) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_auto_fib_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Autocorrelation : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Autocorrelation(int period, int lag)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ ArgumentOutOfRangeException.ThrowIfNegative(lag);
+ var ptr = NativeMethods.wickra_autocorrelation_new((nuint)period, (nuint)lag);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Autocorrelation parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_autocorrelation_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_autocorrelation_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_autocorrelation_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_autocorrelation_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AutocorrelationPeriodogram : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AutocorrelationPeriodogram(int minPeriod, int maxPeriod)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(minPeriod);
+ ArgumentOutOfRangeException.ThrowIfNegative(maxPeriod);
+ var ptr = NativeMethods.wickra_autocorrelation_periodogram_new((nuint)minPeriod, (nuint)maxPeriod);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AutocorrelationPeriodogram parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_autocorrelation_periodogram_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_autocorrelation_periodogram_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_autocorrelation_periodogram_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_autocorrelation_periodogram_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AverageDailyRange : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AverageDailyRange(int period, int utcOffsetMinutes)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_average_daily_range_new((nuint)period, utcOffsetMinutes);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AverageDailyRange parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_average_daily_range_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_average_daily_range_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_average_daily_range_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_average_daily_range_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AverageDrawdown : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AverageDrawdown(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_average_drawdown_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AverageDrawdown parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_average_drawdown_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_average_drawdown_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_average_drawdown_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_average_drawdown_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AvgPrice : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AvgPrice()
+ {
+ var ptr = NativeMethods.wickra_avg_price_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AvgPrice parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_avg_price_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_avg_price_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_avg_price_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_avg_price_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AwesomeOscillator : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AwesomeOscillator(int fast, int slow)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(fast);
+ ArgumentOutOfRangeException.ThrowIfNegative(slow);
+ var ptr = NativeMethods.wickra_awesome_oscillator_new((nuint)fast, (nuint)slow);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AwesomeOscillator parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_awesome_oscillator_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_awesome_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_awesome_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_awesome_oscillator_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class AwesomeOscillatorHistogram : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public AwesomeOscillatorHistogram(int fast, int slow, int smaPeriod)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(fast);
+ ArgumentOutOfRangeException.ThrowIfNegative(slow);
+ ArgumentOutOfRangeException.ThrowIfNegative(smaPeriod);
+ var ptr = NativeMethods.wickra_awesome_oscillator_histogram_new((nuint)fast, (nuint)slow, (nuint)smaPeriod);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid AwesomeOscillatorHistogram parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_awesome_oscillator_histogram_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_awesome_oscillator_histogram_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_awesome_oscillator_histogram_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_awesome_oscillator_histogram_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BalanceOfPower : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BalanceOfPower()
+ {
+ var ptr = NativeMethods.wickra_balance_of_power_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BalanceOfPower parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_balance_of_power_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_balance_of_power_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_balance_of_power_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_balance_of_power_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BandpassFilter : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BandpassFilter(int period, double bandwidth)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_bandpass_filter_new((nuint)period, bandwidth);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BandpassFilter parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_bandpass_filter_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_bandpass_filter_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_bandpass_filter_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_bandpass_filter_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Bat : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Bat()
+ {
+ var ptr = NativeMethods.wickra_bat_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Bat parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_bat_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_bat_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_bat_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_bat_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BeltHold : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BeltHold()
+ {
+ var ptr = NativeMethods.wickra_belt_hold_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BeltHold parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_belt_hold_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_belt_hold_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_belt_hold_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_belt_hold_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Beta : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Beta(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_beta_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Beta parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_beta_free);
+ }
+
+ public double Update(double x, double y)
+ {
+ var result = NativeMethods.wickra_beta_update(_handle.DangerousGetHandle(), x, y);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan x, ReadOnlySpan y)
+ {
+ var n = x.Length;
+ if (y.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* xPtr = x)
+ fixed (double* yPtr = y)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_beta_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_beta_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BetaNeutralSpread : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BetaNeutralSpread(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_beta_neutral_spread_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BetaNeutralSpread parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_beta_neutral_spread_free);
+ }
+
+ public double Update(double x, double y)
+ {
+ var result = NativeMethods.wickra_beta_neutral_spread_update(_handle.DangerousGetHandle(), x, y);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan x, ReadOnlySpan y)
+ {
+ var n = x.Length;
+ if (y.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* xPtr = x)
+ fixed (double* yPtr = y)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_beta_neutral_spread_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_beta_neutral_spread_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BetterVolume : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BetterVolume(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_better_volume_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BetterVolume parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_better_volume_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_better_volume_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_better_volume_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_better_volume_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BipowerVariation : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BipowerVariation(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_bipower_variation_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BipowerVariation parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_bipower_variation_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_bipower_variation_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_bipower_variation_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_bipower_variation_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BodySizePct : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BodySizePct()
+ {
+ var ptr = NativeMethods.wickra_body_size_pct_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BodySizePct parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_body_size_pct_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_body_size_pct_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_body_size_pct_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_body_size_pct_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BollingerBands : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BollingerBands(int period, double multiplier)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_bollinger_bands_new((nuint)period, multiplier);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BollingerBands parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_bollinger_bands_free);
+ }
+
+ public BollingerOutput? Update(double @value)
+ {
+ WickraBollingerOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_bollinger_bands_update(_handle.DangerousGetHandle(), @value, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new BollingerOutput(native.upper, native.middle, native.lower, native.stddev) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_bollinger_bands_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BollingerBandwidth : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BollingerBandwidth(int period, double multiplier)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_bollinger_bandwidth_new((nuint)period, multiplier);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BollingerBandwidth parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_bollinger_bandwidth_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_bollinger_bandwidth_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_bollinger_bandwidth_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_bollinger_bandwidth_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BomarBands : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BomarBands(int period, double coverage)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_bomar_bands_new((nuint)period, coverage);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BomarBands parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_bomar_bands_free);
+ }
+
+ public BomarBandsOutput? Update(double @value)
+ {
+ WickraBomarBandsOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_bomar_bands_update(_handle.DangerousGetHandle(), @value, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new BomarBandsOutput(native.upper, native.middle, native.lower) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_bomar_bands_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BreadthThrust : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BreadthThrust(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_breadth_thrust_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BreadthThrust parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_breadth_thrust_free);
+ }
+
+ public double Update(ReadOnlySpan change, ReadOnlySpan volume, ReadOnlySpan newHigh, ReadOnlySpan newLow, ReadOnlySpan aboveMa, ReadOnlySpan onBuySignal, long timestamp)
+ {
+ if (volume.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newHigh.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newLow.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (aboveMa.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (onBuySignal.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+
+ double result;
+ unsafe
+ {
+ fixed (double* changePtr = change)
+ fixed (double* volumePtr = volume)
+ fixed (bool* newHighPtr = newHigh)
+ fixed (bool* newLowPtr = newLow)
+ fixed (bool* aboveMaPtr = aboveMa)
+ fixed (bool* onBuySignalPtr = onBuySignal)
+ {
+ result = NativeMethods.wickra_breadth_thrust_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_breadth_thrust_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Breakaway : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Breakaway()
+ {
+ var ptr = NativeMethods.wickra_breakaway_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Breakaway parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_breakaway_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_breakaway_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_breakaway_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_breakaway_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BullishPercentIndex : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BullishPercentIndex()
+ {
+ var ptr = NativeMethods.wickra_bullish_percent_index_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BullishPercentIndex parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_bullish_percent_index_free);
+ }
+
+ public double Update(ReadOnlySpan change, ReadOnlySpan volume, ReadOnlySpan newHigh, ReadOnlySpan newLow, ReadOnlySpan aboveMa, ReadOnlySpan onBuySignal, long timestamp)
+ {
+ if (volume.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newHigh.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (newLow.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (aboveMa.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+ if (onBuySignal.Length != change.Length)
+ {
+ throw new ArgumentException("input spans in the same group must have equal length");
+ }
+
+ double result;
+ unsafe
+ {
+ fixed (double* changePtr = change)
+ fixed (double* volumePtr = volume)
+ fixed (bool* newHighPtr = newHigh)
+ fixed (bool* newLowPtr = newLow)
+ fixed (bool* aboveMaPtr = aboveMa)
+ fixed (bool* onBuySignalPtr = onBuySignal)
+ {
+ result = NativeMethods.wickra_bullish_percent_index_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_bullish_percent_index_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class BurkeRatio : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public BurkeRatio(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_burke_ratio_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid BurkeRatio parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_burke_ratio_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_burke_ratio_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_burke_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_burke_ratio_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Butterfly : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Butterfly()
+ {
+ var ptr = NativeMethods.wickra_butterfly_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Butterfly parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_butterfly_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_butterfly_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_butterfly_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_butterfly_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class CalendarSpread : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public CalendarSpread()
+ {
+ var ptr = NativeMethods.wickra_calendar_spread_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid CalendarSpread parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_calendar_spread_free);
+ }
+
+ public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
+ {
+ var result = NativeMethods.wickra_calendar_spread_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_calendar_spread_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class CalmarRatio : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public CalmarRatio(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_calmar_ratio_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid CalmarRatio parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_calmar_ratio_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_calmar_ratio_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_calmar_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_calmar_ratio_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Camarilla : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Camarilla()
+ {
+ var ptr = NativeMethods.wickra_camarilla_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Camarilla parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_camarilla_free);
+ }
+
+ public CamarillaPivotsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraCamarillaPivotsOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_camarilla_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new CamarillaPivotsOutput(native.pp, native.r1, native.r2, native.r3, native.r4, native.s1, native.s2, native.s3, native.s4) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_camarilla_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class CandleVolume : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public CandleVolume(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_candle_volume_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid CandleVolume parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_candle_volume_free);
+ }
+
+ public CandleVolumeOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraCandleVolumeOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_candle_volume_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new CandleVolumeOutput(native.body, native.width) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_candle_volume_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Cci : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Cci(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_cci_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Cci parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_cci_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_cci_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_cci_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_cci_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class CenterOfGravity : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public CenterOfGravity(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_center_of_gravity_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid CenterOfGravity parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_center_of_gravity_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_center_of_gravity_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_center_of_gravity_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_center_of_gravity_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class CentralPivotRange : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public CentralPivotRange()
+ {
+ var ptr = NativeMethods.wickra_central_pivot_range_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid CentralPivotRange parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_central_pivot_range_free);
+ }
+
+ public CentralPivotRangeOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraCentralPivotRangeOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_central_pivot_range_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new CentralPivotRangeOutput(native.pivot, native.tc, native.bc) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_central_pivot_range_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class Cfo : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public Cfo(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_cfo_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid Cfo parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_cfo_free);
+ }
+
+ public double Update(double @value)
+ {
+ var result = NativeMethods.wickra_cfo_update(_handle.DangerousGetHandle(), @value);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan input)
+ {
+ var n = input.Length;
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* inputPtr = input)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_cfo_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_cfo_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class ChaikinMoneyFlow : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public ChaikinMoneyFlow(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_chaikin_money_flow_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid ChaikinMoneyFlow parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_chaikin_money_flow_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_chaikin_money_flow_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_chaikin_money_flow_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_chaikin_money_flow_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class ChaikinOscillator : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public ChaikinOscillator(int fast, int slow)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(fast);
+ ArgumentOutOfRangeException.ThrowIfNegative(slow);
+ var ptr = NativeMethods.wickra_chaikin_oscillator_new((nuint)fast, (nuint)slow);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid ChaikinOscillator parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_chaikin_oscillator_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_chaikin_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_chaikin_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_chaikin_oscillator_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class ChaikinVolatility : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public ChaikinVolatility(int emaPeriod, int rocPeriod)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(emaPeriod);
+ ArgumentOutOfRangeException.ThrowIfNegative(rocPeriod);
+ var ptr = NativeMethods.wickra_chaikin_volatility_new((nuint)emaPeriod, (nuint)rocPeriod);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid ChaikinVolatility parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_chaikin_volatility_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_chaikin_volatility_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_chaikin_volatility_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_chaikin_volatility_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class ChandeKrollStop : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public ChandeKrollStop(int atrPeriod, double atrMultiplier, int stopPeriod)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(atrPeriod);
+ ArgumentOutOfRangeException.ThrowIfNegative(stopPeriod);
+ var ptr = NativeMethods.wickra_chande_kroll_stop_new((nuint)atrPeriod, atrMultiplier, (nuint)stopPeriod);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid ChandeKrollStop parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_chande_kroll_stop_free);
+ }
+
+ public ChandeKrollStopOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraChandeKrollStopOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_chande_kroll_stop_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new ChandeKrollStopOutput(native.stop_long, native.stop_short) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_chande_kroll_stop_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class ChandelierExit : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public ChandelierExit(int period, double multiplier)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_chandelier_exit_new((nuint)period, multiplier);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid ChandelierExit parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_chandelier_exit_free);
+ }
+
+ public ChandelierExitOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraChandelierExitOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_chandelier_exit_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new ChandelierExitOutput(native.long_stop, native.short_stop) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_chandelier_exit_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class ChoppinessIndex : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public ChoppinessIndex(int period)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(period);
+ var ptr = NativeMethods.wickra_choppiness_index_new((nuint)period);
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid ChoppinessIndex parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_choppiness_index_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_choppiness_index_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_choppiness_index_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_choppiness_index_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class ClassicPivots : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public ClassicPivots()
+ {
+ var ptr = NativeMethods.wickra_classic_pivots_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid ClassicPivots parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_classic_pivots_free);
+ }
+
+ public ClassicPivotsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ WickraClassicPivotsOutput native;
+ bool ok;
+ unsafe
+ {
+ ok = NativeMethods.wickra_classic_pivots_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
+ }
+
+ GC.KeepAlive(_handle);
+ return ok ? new ClassicPivotsOutput(native.pp, native.r1, native.r2, native.r3, native.s1, native.s2, native.s3) : null;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_classic_pivots_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class CloseVsOpen : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public CloseVsOpen()
+ {
+ var ptr = NativeMethods.wickra_close_vs_open_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid CloseVsOpen parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_close_vs_open_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_close_vs_open_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan low, ReadOnlySpan close, ReadOnlySpan volume, ReadOnlySpan timestamp)
+ {
+ var n = open.Length;
+ if (high.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (low.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (close.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (volume.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+ if (timestamp.Length != n)
+ {
+ throw new ArgumentException("all input spans must have the same length");
+ }
+
+ var output = new double[n];
+ unsafe
+ {
+ fixed (double* openPtr = open)
+ fixed (double* highPtr = high)
+ fixed (double* lowPtr = low)
+ fixed (double* closePtr = close)
+ fixed (double* volumePtr = volume)
+ fixed (long* timestampPtr = timestamp)
+ fixed (double* outputPtr = output)
+ {
+ NativeMethods.wickra_close_vs_open_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
+ }
+ }
+
+ GC.KeepAlive(_handle);
+ return output;
+ }
+
+ public void Reset()
+ {
+ NativeMethods.wickra_close_vs_open_reset(_handle.DangerousGetHandle());
+ GC.KeepAlive(_handle);
+ }
+
+ public void Dispose() => _handle.Dispose();
+}
+
+public sealed class ClosingMarubozu : IDisposable
+{
+ private readonly WickraHandle _handle;
+
+ public ClosingMarubozu()
+ {
+ var ptr = NativeMethods.wickra_closing_marubozu_new();
+ if (ptr == nint.Zero)
+ {
+ throw new ArgumentException("invalid ClosingMarubozu parameters");
+ }
+
+ _handle = new WickraHandle(ptr, NativeMethods.wickra_closing_marubozu_free);
+ }
+
+ public double Update(double open, double high, double low, double close, double volume, long timestamp)
+ {
+ var result = NativeMethods.wickra_closing_marubozu_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
+ GC.KeepAlive(_handle);
+ return result;
+ }
+
+ public double[] Batch(ReadOnlySpan open, ReadOnlySpan high, ReadOnlySpan