E13: add the WASM quickstart and the data-layer wiki page

The wiki had quickstarts for Python, Rust, and Node but none for the
WebAssembly binding, and the wickra-data crate (CSV reader, tick
aggregator, resampler, Binance feed) was not documented anywhere.

- Quickstart-WASM.md: install via npm, building with wasm-pack, and
  streaming/batch/multi-output usage in a browser or bundler.
- Data-Layer.md: the wickra-data crate — CandleReader, TickAggregator
  (including the opt-in gap fill), Resampler/resample_all, and the
  feature-gated Binance live feed.
- Home.md links both from the wiki contents list.
This commit is contained in:
kingchenc
2026-05-22 16:36:29 +02:00
parent 9d822d26aa
commit 5917d4928f
3 changed files with 299 additions and 0 deletions
+160
View File
@@ -0,0 +1,160 @@
# Data Layer (`wickra-data`)
`wickra-data` is a separate crate that feeds candles into Wickra's indicators.
It is not part of `wickra-core` — depend on it explicitly:
```toml
[dependencies]
wickra = "0.1"
wickra-data = "0.1"
```
It provides four pieces:
- a streaming OHLCV **CSV reader**,
- a **tick-to-candle aggregator**,
- a **candle resampler** for multi-timeframe analysis,
- an optional **Binance Spot WebSocket** kline feed (feature `live-binance`).
## CSV reader
`CandleReader` streams OHLCV rows out of a CSV file into validated `Candle`
values.
```rust
use wickra_data::csv::CandleReader;
let mut reader = CandleReader::open("ohlcv.csv")?;
let candles = reader.read_all()?; // Vec<Candle>
// Or stream row by row without buffering the whole file:
let mut reader = CandleReader::open("ohlcv.csv")?;
for candle in reader.candles() {
let candle = candle?;
// feed `candle` into an indicator...
}
```
The reader is defensive about real-world files:
- The first line **must** be a header naming the columns
`timestamp,open,high,low,close,volume`. A missing column, or a file with no
header at all, is rejected with a clear `Error::Malformed` instead of
silently consuming the first data row.
- A leading UTF-8 byte-order mark (Excel exports one) is stripped.
- Whitespace around values is trimmed.
- Each row is validated through `Candle::new`, so an inconsistent OHLC row
(e.g. `high < low`) surfaces as an error.
## Tick aggregator
`TickAggregator` rolls a stream of trade `Tick`s up into `Candle`s of an
arbitrary timeframe. The timeframe's bucket size is in the same unit as the
tick timestamps (milliseconds for Binance, seconds for daily bars, …).
```rust
use wickra_data::aggregator::{TickAggregator, Timeframe};
use wickra_core::Tick;
let mut agg = TickAggregator::new(Timeframe::one_minute_ms());
for tick in trade_feed {
// push returns every candle that closed because of this tick —
// empty while the bar grows, one candle when a bar boundary is crossed.
for closed in agg.push(tick)? {
// feed `closed` into an indicator...
}
}
// Capture the final, still-open bar at the end of the stream.
if let Some(last) = agg.flush()? {
// ...
}
```
Out-of-order ticks — across or within a bucket — are rejected with
`Error::Malformed` rather than silently corrupting a bar.
### Gap filling
By default a tick that jumps across one or more empty buckets simply opens
the next non-empty bar, leaving a time hole in the output. Enable
`with_gap_fill` to emit a flat placeholder candle
(`open == high == low == close`, `volume == 0`) for every skipped bucket, so
downstream indicators see an unbroken, evenly spaced series:
```rust
let mut agg = TickAggregator::new(Timeframe::one_minute_ms()).with_gap_fill(true);
```
## Resampler
`Resampler` rolls an existing candle stream up to a coarser timeframe — for
example 1-minute bars into 5-minute bars, without touching the original tick
stream.
```rust
use wickra_data::aggregator::Timeframe;
use wickra_data::resample::{resample_all, Resampler};
// One-shot over an iterator:
let five_min = resample_all(Timeframe::millis(5 * 60_000), one_min_candles)?;
// Or incrementally:
let mut r = Resampler::new(Timeframe::millis(60 * 60_000)); // 1-hour bars
for candle in one_min_candles {
if let Some(closed) = r.push(candle?)? {
// a coarser bar just closed
}
}
let last = r.flush()?;
```
The output timeframe's bucket must be a multiple of the input timeframe's
bucket — picking sensible aggregations (1m → 5m → 1h) is the caller's
responsibility. A candle that arrives in a bucket earlier than the open bar
is rejected as out of order.
## Binance live feed
With the `live-binance` feature enabled, `BinanceKlineStream` connects to the
Binance Spot WebSocket and yields closed klines as candles.
```toml
wickra-data = { version = "0.1", features = ["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}");
}
}
}
```
The stream is resilient: it reconnects with exponential backoff after a
dropped connection, skips non-kline frames (subscription acks, heartbeats),
applies a read timeout and message-size limits, and tracks a closed flag so a
deliberately closed stream is not reused.
A runnable example lives at `crates/wickra-data/examples/live_binance.rs`:
```bash
cargo run -p wickra-data --example live_binance --features live-binance
```
## See also
- [Quickstart: Rust](Quickstart-Rust.md) — the core indicator API.
- [Indicators Overview](Indicators-Overview.md) — every indicator and its
parameters.
- Source: <https://github.com/kingchenc/wickra>
+6
View File
@@ -45,6 +45,12 @@ Release notes and tagged builds:
- [Quickstart: Node](Quickstart-Node.md) — `npm install wickra`, basic
`SMA` and `MACD` calls, and the current Windows install caveat
(`wickra-win32-x64-msvc@0.1.4` is held by the npm spam filter).
- [Quickstart: WASM](Quickstart-WASM.md) — `npm install wickra-wasm`,
building with `wasm-pack`, and running indicators client-side in a
browser or bundler.
- [Data Layer](Data-Layer.md) — the `wickra-data` crate: the CSV reader,
the tick-to-candle aggregator, the multi-timeframe resampler, and the
Binance live feed.
- [Streaming vs Batch](Streaming-vs-Batch.md) — the conceptual difference
between Wickra's O(1) `update` and the recompute-everything loops in
batch-only libraries, with the benchmark numbers from the project README.
+133
View File
@@ -0,0 +1,133 @@
# Quickstart: WebAssembly
A five-minute tour of the Wickra WebAssembly binding. The same Rust core that
powers the Python, Node, and Rust APIs is compiled to WebAssembly with
[wasm-bindgen](https://rustwasm.github.io/wasm-bindgen/), so indicators run
entirely client-side — in a browser tab, a bundler build, or Node — with no
server round-trips.
## Install
The published package is `wickra-wasm` on npm:
```bash
npm install wickra-wasm
```
The npm package is built for the bundler target, so it works directly with
Webpack, Vite, Rollup, and similar toolchains.
## Build from source
To build the binding yourself you need [`wasm-pack`](https://rustwasm.github.io/wasm-pack/)
and the `wasm32-unknown-unknown` target:
```bash
rustup target add wasm32-unknown-unknown
cargo install wasm-pack
# Browser ES-module build (import directly from a <script type="module">):
wasm-pack build bindings/wasm --target web --release --features panic-hook
# Bundler build (Webpack / Vite / Rollup):
wasm-pack build bindings/wasm --target bundler --release --features panic-hook
```
`wasm-pack` writes the generated module, the `.wasm` binary, and TypeScript
definitions into `bindings/wasm/pkg/`. The `panic-hook` feature routes Rust
panics to `console.error` for readable stack traces during development.
## A first run (browser)
With a `--target web` build, import the generated module directly:
```html
<script type="module">
import init, { version, SMA } from "./pkg/wickra_wasm.js";
await init(); // load and instantiate the .wasm binary
console.log("wickra-wasm", version());
const sma = new SMA(3);
console.log(sma.batch([2, 4, 6, 8, 10]));
// -> Float64Array [ NaN, NaN, 4, 6, 8 ]
</script>
```
`init()` must be awaited once before any indicator is constructed — it fetches
and instantiates the WebAssembly binary. After that the API mirrors the other
bindings.
## Streaming
```javascript
import init, { RSI } from "wickra-wasm";
await init();
const rsi = new RSI(14);
for (const price of liveFeed) {
const value = rsi.update(price); // number, or null/undefined during warmup
if (value != null && value > 70) {
console.log("overbought");
}
}
```
Every indicator is an O(1)-per-update state machine: `update` advances the
indicator by exactly one input, so a browser charting app pays no cost for
recomputing history on each tick.
## Multi-output indicators
`MACD` and `BollingerBands` return a structured object from `update`:
```javascript
import init, { MACD } from "wickra-wasm";
await init();
const macd = new MACD(12, 26, 9);
let last = null;
for (let i = 0; i < 40; i++) {
last = macd.update(100 + i * 0.5); // null during warmup, else { macd, signal, histogram }
}
console.log(last);
```
`batch` returns a flat `Float64Array`; multi-output indicators interleave
their fields per row (`[macd0, signal0, hist0, macd1, ...]`). The exact
layout is documented in the generated `pkg/wickra_wasm.d.ts`.
## Errors
Unlike the Node binding (whose constructors clamp pathological values), the
WASM binding's constructors throw a JavaScript error for invalid parameters:
```javascript
try {
new MACD(0, 0, 0);
} catch (e) {
console.error("invalid MACD parameters:", e);
}
```
## A complete example
`bindings/wasm/examples/index.html` is a self-contained browser demo: it
streams a synthetic price series through six indicators and draws a live
chart on a `<canvas>`. Open it after a `--target web` build:
```bash
wasm-pack build bindings/wasm --target web --release --features panic-hook
# then serve the bindings/wasm directory and open examples/index.html
```
## See also
- [Quickstart: Node](Quickstart-Node.md) — the native (non-WASM) Node binding.
- [Streaming vs Batch](Streaming-vs-Batch.md) — why `update` is the primary
entry point.
- [Indicators Overview](Indicators-Overview.md) — every indicator and its
parameters.
- Source: <https://github.com/kingchenc/wickra>