278b6afaa4
The 33 Markdown files under docs/wiki/ were never tracked. Commit them
into the repository so the documentation is versioned alongside the
code: 8 top-level pages plus 25 per-indicator deep dives under
indicators/{momentum,trend,volatility,volume}/.
The pages are kept in-repo (not pushed to a flat GitHub Wiki), so the
relative indicators/<family>/... links in Home.md resolve correctly
when rendered on GitHub.
143 lines
4.6 KiB
Markdown
143 lines
4.6 KiB
Markdown
# Quickstart: Rust
|
|
|
|
A five-minute tour of the Wickra Rust crate. By the end you will have run a
|
|
batch SMA, fed an RSI tick by tick, and composed two indicators with `Chain`.
|
|
|
|
## Install
|
|
|
|
```bash
|
|
cargo add wickra
|
|
```
|
|
|
|
The default features pull in `parallel` (rayon-based `batch_parallel`); turn
|
|
them off with `cargo add wickra --no-default-features` if you want a leaner
|
|
build. The `wickra` crate is a thin façade that re-exports everything from
|
|
`wickra-core`; you can also depend on `wickra-core` directly if you want to
|
|
skip the façade.
|
|
|
|
The published crate is at version `0.1.4` on
|
|
[crates.io](https://crates.io/crates/wickra).
|
|
|
|
## The `Indicator` trait in 30 seconds
|
|
|
|
Every indicator implements the same trait:
|
|
|
|
```rust
|
|
pub trait Indicator {
|
|
type Input;
|
|
type Output;
|
|
fn update(&mut self, input: Self::Input) -> Option<Self::Output>;
|
|
fn reset(&mut self);
|
|
fn warmup_period(&self) -> usize;
|
|
fn is_ready(&self) -> bool;
|
|
fn name(&self) -> &'static str;
|
|
}
|
|
```
|
|
|
|
`update` is O(1) in the input length. The companion trait `BatchExt` is a
|
|
blanket extension that adds a `batch(&[Self::Input])` method to every
|
|
indicator — its default implementation is literally a loop over `update`, so
|
|
batch and streaming results are bit-for-bit identical.
|
|
|
|
## Batch and streaming side by side
|
|
|
|
```rust
|
|
use wickra::{BatchExt, Indicator, Rsi, Sma};
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// 1. Batch: SMA(3) over five prices.
|
|
let mut sma = Sma::new(3)?;
|
|
let out: Vec<Option<f64>> = sma.batch(&[1.0, 2.0, 3.0, 4.0, 5.0]);
|
|
println!("{:?}", out);
|
|
// -> [None, None, Some(2.0), Some(3.0), Some(4.0)]
|
|
|
|
// 2. Streaming: feed Wilder's textbook example into RSI(14).
|
|
let mut rsi = Rsi::new(14)?;
|
|
let prices = [
|
|
44.34, 44.09, 44.15, 43.61, 44.33, 44.83, 45.10, 45.42, 45.84, 46.08,
|
|
45.89, 46.03, 45.61, 46.28, 46.28, 46.00, 46.03, 46.41,
|
|
];
|
|
for (tick, price) in prices.iter().enumerate() {
|
|
if let Some(v) = rsi.update(*price) {
|
|
println!("tick {:2} close={:.2} rsi={:.4}", tick + 1, price, v);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
The streaming loop prints:
|
|
|
|
```
|
|
tick 15 close=46.28 rsi=70.4641
|
|
tick 16 close=46.00 rsi=66.2496
|
|
tick 17 close=46.03 rsi=66.4809
|
|
tick 18 close=46.41 rsi=69.3469
|
|
```
|
|
|
|
The first value lands on tick 15 because `Rsi::new(14)?.warmup_period() == 15`
|
|
(14 diffs to seed Wilder's smoothing, so the 15th input emits the first RSI).
|
|
The `70.4641` value matches the textbook value pinned by the unit test
|
|
`classic_wilder_textbook_values` in `crates/wickra-core/src/indicators/rsi.rs`.
|
|
|
|
## Composing indicators with `Chain`
|
|
|
|
`Chain<A, B>` wires the output of `A` straight into the input of `B`, provided
|
|
both stages agree on `f64` as the bridging type. The chain itself is an
|
|
`Indicator`, so you can stack three stages with `.then(c)`, or four with
|
|
`.then(c).then(d)`.
|
|
|
|
```rust
|
|
use wickra::{Chain, Ema, Indicator, Rsi};
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// RSI(7) computed on the output of EMA(14).
|
|
let mut chain = Chain::new(Ema::new(14)?, Rsi::new(7)?);
|
|
for i in 1..=22 {
|
|
if let Some(v) = chain.update(f64::from(i)) {
|
|
println!("chain emitted at input #{i}: {v}");
|
|
}
|
|
}
|
|
println!("chain.warmup_period() = {}", chain.warmup_period());
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
Output:
|
|
|
|
```
|
|
chain emitted at input #21: 100
|
|
chain emitted at input #22: 100
|
|
chain.warmup_period() = 22
|
|
```
|
|
|
|
`Ema::new(14)` needs 14 inputs to seed and `Rsi::new(7)` needs 8 more once
|
|
the EMA starts flowing, so the chain emits its first value at input 21. The
|
|
`warmup_period()` reported by `Chain` is a conservative `first + second` sum
|
|
(here `14 + 8 = 22`); see [Indicator Chaining](Indicator-Chaining.md) for the
|
|
exact contract.
|
|
|
|
## A deeper example
|
|
|
|
`crates/wickra/examples/backtest.rs` shipped with the workspace computes a
|
|
panel of indicators (RSI, EMA, Bollinger, MACD, ATR, ADX, OBV) over an OHLCV
|
|
CSV by way of `wickra-data`:
|
|
|
|
```bash
|
|
cargo run --release --example backtest -- path/to/ohlcv.csv
|
|
```
|
|
|
|
For live-data work, `wickra-data` ships a streaming CSV reader, a
|
|
tick-to-candle aggregator, a candle resampler, and a Binance kline WebSocket
|
|
adapter under the `live-binance` feature. `crates/wickra-data/examples/live_binance.rs`
|
|
is the canonical example for the latter.
|
|
|
|
## See also
|
|
|
|
- [Quickstart: Python](Quickstart-Python.md) — same engine, NumPy-flavoured.
|
|
- [Streaming vs Batch](Streaming-vs-Batch.md) — the `batch == repeated update`
|
|
contract and the benchmark numbers it buys you.
|
|
- [Indicator Chaining](Indicator-Chaining.md) — three-stage chains and the
|
|
stacked-warmup rule.
|
|
- Source: <https://github.com/kingchenc/wickra>
|