Files
wickra/README.md
T
kingchenc 1295b63e1f Wire release pipeline to crates.io, PyPI, and npm
On every v* tag push the release workflow now publishes the project to
all three public registries in parallel:

- crates.io: wickra-core then wickra-data then wickra, with a 45-second
  sleep between each so the registry index can refresh before the next
  publish step asserts the previous version is available.

- PyPI: maturin-action builds wheels for Linux x86_64 + aarch64, macOS
  x86_64 + aarch64, and Windows x64, plus an sdist; all artefacts upload
  to PyPI via MATURIN_PYPI_TOKEN.

- npm: napi-rs builds a native binary per platform (linux-x64-gnu,
  darwin-x64, darwin-arm64, win32-x64-msvc), publishes each as its own
  platform package, then publishes the main `wickra` meta-package
  that resolves to the right platform at install time.

- WASM: wasm-pack builds the bundler-target package and publishes it as
  `wickra-wasm` on npm, with the auto-generated package.json enriched
  with the author/repo/license metadata.

Package metadata was unified so all targets point at kingchenc/wickra
on GitHub; the Node binding is now plain `wickra` (the @wickra/ scope
was unnecessary because the bare name was free). README install table
updated to match.
2026-05-21 20:35:18 +02:00

223 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Wickra
**Streaming-first technical indicators. Install with `pip install wickra` — no system dependencies.**
Wickra is a multi-language technical-analysis library with a Rust core and
bindings for Python, Node.js, and WebAssembly. Every indicator is a state
machine that updates in O(1) per new data point, so live trading bots and
historical backtests share the exact same implementation.
```python
import numpy as np
import wickra as ta
# Batch: classic TA-Lib-style usage
prices = np.linspace(100, 200, 1000)
rsi = ta.RSI(14)
values = rsi.batch(prices) # numpy array, NaN during warmup
# Streaming: same indicator, fed tick by tick
rsi = ta.RSI(14)
for price in live_feed:
value = rsi.update(price) # O(1) — no recomputation over history
if value is not None and value > 70:
print("overbought")
```
## Why Wickra exists
The Python TA ecosystem has plenty of libraries — TA-Lib, pandas-ta, finta,
talipp, tulipy — and every one of them shares the same blind spot:
| Library | Install pain | Streaming | Multi-language | Active |
|--------------------|-----------------|-----------|----------------|--------|
| TA-Lib (Python) | yes (C deps) | no | no | barely |
| pandas-ta | clean | no | no | slow |
| finta | clean | no | no | stale |
| ta-lib-python | yes (C deps) | no | no | barely |
| talipp | clean | yes | no | yes |
| Tulip Indicators | yes (C deps) | no | partial | stale |
| ooples (C#) | clean | no | C# only | yes |
| **Wickra** | **clean** | **yes** | **Python+Node+WASM+Rust** | **yes** |
Wickra is the only library that combines all of: clean install, streaming,
multi-language reach, and active maintenance.
## Benchmark: how much faster is "streaming-first"?
Reproduced on this machine with `python -m benchmarks.compare_libraries`.
Lower µs/op = faster. Wickra wins every batch category outright, and the
streaming gap widens linearly with how much history a batch-only library has
to recompute on every tick.
### Batch — single full pass over a 5 000-bar series
Reading the table: each cell shows that library's runtime, plus how many times
slower it is than Wickra in parentheses. **★** marks the winner per row.
| Indicator | Wickra | finta | talipp |
|---------------------|---------------------|------------------------|------------------------------|
| SMA(20) | **26.0 µs ★** | 295.3 µs (11.4× slower) | 1 812.8 µs (69.7× slower) |
| EMA(20) | **16.8 µs ★** | 205.5 µs (12.2× slower) | 2 534.4 µs (150.9× slower) |
| RSI(14) | **31.2 µs ★** | 714.1 µs (22.9× slower) | 3 751.7 µs (120.2× slower) |
| MACD(12, 26, 9) | **30.8 µs ★** | 359.5 µs (11.7× slower) | 11 642.2 µs (378.0× slower) |
| Bollinger(20, 2.0) | **26.7 µs ★** | 690.6 µs (25.9× slower) | 27 482.4 µs (1 030.1× slower) |
| ATR(14) | **40.6 µs ★** | 1 120.3 µs (27.6× slower) | 3 760.2 µs (92.7× slower) |
### Streaming — per-tick latency after seeding with 2 000 historical bars
A batch-only library has to re-run its full indicator over the entire history on
every new tick; Wickra updates state in O(1).
| Indicator | Wickra (per tick) | talipp (per tick) |
|-----------|---------------------|---------------------------|
| RSI(14) | **0.07 µs ★** | 1.16 µs (17.5× slower) |
> TA-Lib and pandas-ta are not included here because both fail to install
> cleanly on Windows without C build tooling — which is precisely the install
> pain Wickra was built to remove. The benchmark script auto-detects every
> peer library it can find and runs them on the same inputs as Wickra; install
> them in your environment to see those rows light up too.
Run the suite yourself:
```bash
pip install -e bindings/python[bench]
python -m benchmarks.compare_libraries
```
## Indicators in 0.1.0
25 streaming-first indicators across four families. Every one passes the
`batch == streaming` equivalence test, reference-value tests, and reset
semantics tests.
| Family | Indicators |
|-------------|-----------|
| Trend | SMA, EMA, WMA, DEMA, TEMA, HMA, KAMA |
| Momentum | RSI (Wilder), MACD, Stochastic, CCI, ROC, Williams %R, ADX (+DI/-DI), MFI, TRIX, Awesome Oscillator, Aroon |
| Volatility | Bollinger Bands, ATR, Keltner Channels, Donchian Channels, Parabolic SAR |
| Volume | OBV, VWAP (cumulative + rolling) |
Adding a new indicator means implementing one trait in Rust; all four bindings
inherit it automatically.
## Languages
| Binding | Install | Example |
|-------------------|-----------------------------------------------|---------|
| Python (PyO3) | `pip install wickra` | `examples/python/backtest.py` |
| Node.js (napi-rs) | `npm install wickra` | `bindings/node/__tests__/smoke.test.js` |
| Browser / WASM | `npm install wickra-wasm` | `bindings/wasm/examples/index.html` |
| Rust | `cargo add wickra` | `examples/rust/backtest.rs` |
The wickra-core crate is `unsafe`-forbidden, so every binding inherits a
memory-safe implementation.
## Rust API
```rust
use wickra::{Indicator, BatchExt, Chain, Ema, Rsi, Sma};
// Streaming or batch — same trait, same code.
let mut sma = Sma::new(14)?;
let out: Vec<Option<f64>> = sma.batch(&[1.0, 2.0, 3.0, 4.0, 5.0]);
let mut rsi = Rsi::new(14)?;
for price in live_feed {
if let Some(v) = rsi.update(price) {
println!("RSI = {v}");
}
}
// Compose indicators: RSI(7) on top of EMA(14).
let mut chain = Chain::new(Ema::new(14)?, Rsi::new(7)?);
chain.update(price);
```
## Live data sources
`wickra-data` (separate crate, opt-in) ships:
- A streaming OHLCV **CSV reader**.
- A **tick-to-candle aggregator** with arbitrary timeframes.
- A **candle resampler** for multi-timeframe analysis (1m → 5m → 1h on the fly).
- A **Binance Spot WebSocket** kline adapter (feature `live-binance`).
```rust
use wickra::{Indicator, Rsi};
use wickra_data::live::binance::{BinanceKlineStream, Interval};
let mut stream = BinanceKlineStream::connect(&["BTCUSDT".into()], Interval::OneMinute).await?;
let mut rsi = Rsi::new(14)?;
while let Some(event) = stream.next_event().await? {
if event.is_closed {
if let Some(v) = rsi.update(event.candle.close) {
println!("RSI = {v:.2}");
}
}
}
```
A Python live-trading example using the public `websockets` package lives at
`examples/python/live_trading.py`.
## Project layout
```
wickra/
├── crates/
│ ├── wickra-core/ core engine + all 25 indicators
│ ├── wickra/ top-level facade crate (publishes on crates.io)
│ └── wickra-data/ CSV reader, tick aggregator, live exchange feeds
├── bindings/
│ ├── python/ PyO3 + maturin (publishes on PyPI)
│ ├── node/ napi-rs (publishes on npm)
│ └── wasm/ wasm-bindgen (browsers, bundlers, Node)
├── examples/
│ ├── python/ backtest, live trading, parallel assets, multi-tf
│ └── rust/ backtest, live Binance
├── benches/ cargo bench targets
└── .github/workflows/ CI and release pipelines
```
## Building everything from source
```bash
# Rust core + tests
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo bench -p wickra
# Python binding (requires Rust toolchain + maturin)
cd bindings/python
maturin develop --release
pytest
# WASM binding (requires wasm-pack + wasm32-unknown-unknown target)
wasm-pack build bindings/wasm --target web --release --features panic-hook
# Node binding (requires @napi-rs/cli)
cd bindings/node && npm install && npm run build && npm test
```
## Test counts
- `wickra-core`: 171 unit tests + 2 doctests, including textbook-value tests
for Wilder RSI, Bollinger Bands, MACD, ATR, and Stochastic.
- `wickra-data`: 11 unit tests + 1 doctest, covers CSV decoding, the tick
aggregator, the resampler, and the Binance payload parser.
- `bindings/python`: 56 pytest tests covering smoke checks, streaming==batch
equivalence, reference values, lifecycle, and dict/tuple candle inputs.
- `bindings/node`: 7 Node test-runner cases via `node --test`.
## License
Licensed under the **PolyForm Noncommercial License 1.0.0**. See [LICENSE](LICENSE).
In plain English: use it, fork it, modify it, redistribute it, file issues, send
pull requests — all welcome. Personal projects, research, education, non-profits,
government, hobby trading bots: all fine. The one thing that's not allowed is
commercial sale of the software or of services built around it. If you want to
use Wickra commercially, get in touch about a license.