Stacked on #315 (the native Binance REST fetcher). Retarget to `main` once #315 merges.
Migrates the runnable examples off third-party data-I/O packages onto Wickra's
native data layer (`CandleReader`, `Resampler`, `BinanceFeed`, `fetch_*klines`).
## Third-party packages removed (the zero-dep selling point)
- **Node**: `ws` (live feed → BinanceFeed) — dropped from package.json + lockfile
- **Go**: `github.com/coder/websocket` — dropped from go.mod / go.sum (`go mod tidy`)
- **Java**: `jackson-databind` (live feed + REST fetch) — dropped from pom.xml
- **R**: `jsonlite` + `websocket` + `later` — dropped from the README notes
Each language's CSV loading now goes through `CandleReader`, manual resampling
through `Resampler`, the live feed through `BinanceFeed`, and (Java/R) the REST
download through the native fetcher.
## Verification
Ran the offline examples per language against the bundled data — backtest and
multi_timeframe produce identical output across Python / Node / Go / Java / R
(e.g. ATR(14) last 345.1010; 1h→5m resamples to 240 bars, →15m to 80 bars).
C# / C / WASM (stdlib-only, no third-party deps to remove) follow in this branch.
Note: the streaming `strategy_*` examples have pre-existing candle-indicator
runtime bugs (CI only syntax-smokes them); the CSV migration preserves their
shape and leaves those bugs for a separate fix.
The examples stream a live Binance feed into the indicators and print signals;
they place no orders, so 'live_trading' overstated them and was inconsistent
with the C/Go/R examples already named live_binance. Rename the Python/Node/WASM
files to live_binance.* and update every reference, run command, header, and the
project-tree listings. Accurate use-case wording ('suitable for live trading
bots') and the risk disclaimers are left unchanged.
Wires real indicators into complete signal -> fill -> PnL -> equity
loops over the checked-in BTCUSDT datasets, with per-trade Sharpe and
max-drawdown reported on stdout. Closes the gap where existing
examples showed only the mechanics of calling `update`/`batch` but not
how Wickra plugs into a trading-system shape.
Three strategies, each in Rust + Python (six files total):
- strategy_rsi_mean_reversion — RSI(14) thresholds (30/70) on 1h
BTCUSDT. Binary position, 0.1% per-trade fee.
- strategy_macd_adx — MACD crossover entries gated by ADX(14) > 20 on
1h BTCUSDT. Trend-follower demo of multi-indicator gating.
- strategy_bollinger_squeeze — Bollinger-bandwidth 180-day-low squeeze
+ upper-band breakout entry, ATR(14) * 2 stop. On 1d BTCUSDT for
interpretable lookback.
Each file is self-contained — print_summary is inlined per script so
the example stays a single-file read. Every script prints a
NOT-financial-advice notice next to its results.
examples/README.md updated to list the new bins/scripts.
Python had no sibling for the Rust `fetch_btcusdt` data-generator —
adding it closes the "fetch (data-gen)" cell for Python and lets users
without a Rust toolchain regenerate the bundled BTCUSDT datasets.
* examples/python/fetch_btcusdt.py — uses only the standard library
(urllib.request + json + csv); same pagination strategy as the Rust
version (paginate backwards via `endTime`, drop the in-progress
bucket, sort and trim to the configured target). Applies the same
OHLC validity check the Rust `Candle::new` constructor enforces
(finite fields, high >= low/open/close, low <= open/close,
volume >= 0) so a malformed kline is skipped rather than written.
* Number formatting matches Rust's `f64` Display: shortest round-trip,
no trailing `.0` for whole-number floats. Verified by running the
script and `git diff`-ing against the checked-in dataset: every row
older than the run is byte-identical to Rust's output; the diff only
shows the most recent ~24 hours where Binance has produced fresh
candles since the original snapshot.
examples/README.md gains the new row.
Python and Rust both lacked a standalone "streaming indicators" example
that mirrors examples/node/streaming.js — the quickstart docs cover the
pattern, but a runnable file makes the parity visible across all four
languages.
* examples/python/streaming.py — argparse-driven synthetic streaming demo
feeding SMA(20) / EMA(20) / RSI(14) / MACD(12,26,9), tagging BUY?/SELL?
candidates when RSI extremes and MACD-histogram direction agree.
* examples/rust/src/bin/streaming.rs — same demo as a wickra-examples
binary, reusing the seeded LCG so its first 40 rows are bit-identical
to the Python (and Node) sibling — a strong cross-language consistency
signal verified by running both side by side.
* examples/README.md gains a `streaming` row in the Rust and Python tables.
backtest.py and multi_timeframe.py read OHLCV CSVs with csv.DictReader
and crashed opaquely on malformed input: a missing column raised a bare
KeyError, a non-numeric cell surfaced NumPy's column-less ValueError,
and an empty or all-NaN series hit IndexError deep in summarize.
Validate the header against the required columns up front, catch
non-numeric cells and report the offending row/column, reject a
header-only file distinctly from a headerless one, and guard resample
and summarize against empty input. Every failure mode now raises a
ValueError naming the file, row and column.
Two issues in the live_trading example:
- The status line rendered indicator values with `... if snap.rsi
else "--"`. A genuine reading of 0.0 is falsy, so an RSI / MACD
histogram / Bollinger value of exactly zero was misreported as "--".
Switch to explicit `is not None` checks.
- --symbol and --interval were interpolated straight into the stream
name and WebSocket URL with no checks. Add validate_args: the symbol
must be strictly alphanumeric and the interval must be one Binance
recognises. main() validates before connecting and exits with a clear
error and code 2 otherwise; the strict values keep the URL well-formed
without escaping.
The combined Binance stream interleaves the kline payloads with
subscription acks, heartbeats and error objects. The example pulled
k = payload.get("k", {}) and immediately did float(k.get("c")) — for
any non-kline frame k is {}, k.get("c") is None, and float(None) raises
TypeError, crashing the script the moment it connects.
Skip frames without a kline payload (no "k" object, or no "c" close
field) with a debug log line, matching the C2 fix on the Rust adapter.
A multi-language technical analysis library: 25 indicators across trend,
momentum, volatility, and volume families, every one a state machine with
O(1) per-tick updates. Batch evaluation is provided by a blanket extension
trait over the streaming primitive, so live trading bots and historical
backtests run the same code path.
What ships in this initial drop:
crates/wickra-core - 25 indicators, Indicator/BatchExt/Chain traits,
OHLCV types with validation; 171 unit tests,
property tests, Wilder/Bollinger textbook tests.
crates/wickra - top-level facade + criterion benches for every
indicator at 1K/10K/100K series sizes.
crates/wickra-data - streaming CSV reader, tick-to-candle aggregator,
multi-timeframe resampler, Binance Spot kline
WebSocket adapter behind feature live-binance;
11 unit + 1 doctest.
bindings/python - PyO3 + maturin, NumPy I/O, type stubs (.pyi),
56 pytest tests including streaming==batch
equivalence, Wilder reference values, lifecycle.
bindings/node - napi-rs native module, TypeScript .d.ts
auto-generated, 7 node --test cases.
bindings/wasm - wasm-bindgen ES module for browser/bundler/Node;
interactive HTML demo at examples/index.html.
examples/ - Python and Rust scripts: backtest, live trading,
parallel multi-asset, multi-timeframe, Binance.
benchmarks/ - cross-library comparison against TA-Lib,
pandas-ta, finta, talipp; Wickra wins every
category by 11-1030x (batch) and 17x+ streaming.
.github/workflows/ - CI matrix (Rust + Python + Node + WASM on
Linux/macOS/Windows), release pipeline for
PyPI wheels and npm.
Indicators (25):
Trend SMA EMA WMA DEMA TEMA HMA KAMA
Momentum RSI MACD Stochastic CCI ROC WilliamsR ADX MFI TRIX
AwesomeOscillator Aroon
Volatility BollingerBands ATR Keltner Donchian PSAR
Volume OBV VWAP (cumulative + rolling)
cargo clippy --workspace --all-targets -D warnings is clean. License: Apache-2.0.