rsta
Rust Statistical Technical Analysis — a focused, well-tested toolkit for trend, momentum, volume, and volatility indicators, plus a streaming signals layer and a single-asset backtesting engine.
At a glance
use ;
use Sma;
use ;
use ;
let candles: = /* … */ vec!;
let bt = new;
let mut strat = Crossover ;
let result = bt.run;
println!;
A runnable version of this on 12 years of real Kraken BTC/USD daily data
ships as cargo run --release --example sma_crossover_backtest.
What's in the box
26 indicators
| Family | Indicators |
|---|---|
| Trend | Sma, Ema, Wma, Dema, Tema, Hma, Macd (+MacdResult), Adx (+AdxResult), Sar, Ichimoku (+IchimokuResult), pivot_classic/pivot_fibonacci/pivot_camarilla (+PivotResult) |
| Momentum | Rsi, StochasticOscillator (+StochasticResult), WilliamsR, Cci |
| Volatility | Atr, BollingerBands (+BollingerBandsResult), KeltnerChannels (+KeltnerChannelsResult), Std, Donchian (+DonchianResult) |
| Volume | Obv, Vroc, Adl, Cmf, Mfi, Vwap |
| Transforms | heikin_ashi(&[Candle]) -> Vec<Candle> |
Every indicator implements the Indicator<T, O> trait with both
calculate(&[T]) (batch) and next(T) (streaming) — the two paths
produce identical values bar-for-bar. Close-priced indicators also
accept &[Candle] directly; multi-output indicators emit a typed
struct so consumers don't need to remember column orders.
Signals layer
signals::{Signal, SignalEvent} turns indicator outputs into discrete
trading events:
CrossUp/CrossDown— two-series crossovers (fast MA vs slow MA, …)ThresholdAbove/ThresholdBelow— value crossing a fixed level (RSI breaching 70 / 30, …)Breakout— value escaping a(value, upper, lower)channel (drive fromDonchian)Divergence— bullish/bearish divergences between price and an oscillatorSignalExt::and/or/notcombinators for composing signals
Backtesting engine
backtest::Backtester runs a Strategy against a &[Candle] slice
with configurable proportional fees and slippage. Single asset, single
position; long, short, or flat. Tracks cash, position MTM, full trade
log, per-bar equity curve. Reports total return, max drawdown,
annualised Sharpe, win rate, and profit factor.
CSV import/export (opt-in via the csv feature)
= { = "0.1", = ["csv"] }
csv::CsvFormatter loads OHLCV from a CSV, runs an arbitrary set of
registered indicators, and writes an enriched CSV with one column per
indicator.
Installation
[]
= "0.1"
Optional: enable the CSV pipeline.
[]
= { = "0.1", = ["csv"] }
MSRV is 1.82 (std::iter::repeat_n). The crate compiles cleanly
under stable, beta, and nightly on Linux, macOS, and Windows.
Verifying correctness
The crate ships golden CSVs generated by pandas-ta against 12 years
of real Kraken XBTUSD daily OHLCV (tests/data/btc_usd_daily.csv,
~4.5k bars from 2013-10-06 to 2026-04-21). The integration tests in
tests/golden_indicators.rs compare rsta's outputs against this
reference at tight tolerance (1e-6 for SMA/EMA/ATR/MACD, 1e-2 for
RSI past warmup, where Wilder seed conventions diverge). To regenerate:
This is the layer that catches subtle bugs cross-implementation —
during 0.0.3 it surfaced a real internal inconsistency in Ema that
synthetic tests had missed.
Examples
# End-to-end: indicators → signals → backtest → metrics on real BTC daily
# Streaming indicator usage with RSI threshold signals
# Enrich a CSV with indicator columns (csv feature required)
Benchmarks
Microbenchmarks via criterion:
On a 2024 M-class laptop (release, single thread):
| Indicator | 100k bars | elem/s |
|---|---|---|
Sma(20) |
~144 µs | ~690 M |
Ema(20) |
~155 µs | ~640 M |
Rsi(14) |
~620 µs | ~160 M |
BollingerBands(20) |
~3.0 ms | ~33 M |
Run cargo bench locally for your hardware's numbers.
Comparison
| rsta | ta-rs |
pandas-ta |
|
|---|---|---|---|
| Language | Rust | Rust | Python |
| Indicators | 26 | ~25 | 130+ |
Streaming API (next() per bar) |
yes | yes | no |
| Signals layer (Cross / Threshold / Breakout / Divergence + combinators) | yes | no | partial |
| Backtest engine | yes (single-asset) | no | no |
| Golden tests vs pandas-ta on real data | yes | no | n/a |
| Generic over numeric type | not yet (#26) | no | n/a |
Stability
This is the first 0.1.x release. The API is usable and well-tested,
but reserves the right to evolve before 1.0:
- Indicator constructors and their result types are unlikely to move.
- The
Indicator/Signaltraits may grow optional methods (with defaults) but are expected to stay source-compatible. - Backtester semantics (single-asset, exec-at-close) are intentionally scoped — multi-asset and tick-precision will be opt-in additions.
Open issues track known direction, including generic numeric support.
Contributing
PRs welcome. New indicators should:
- Live in their own file under
src/indicators/<family>/<name>.rs. - Implement
Indicator<T, O>for whichever input type makes sense (Indicator<f64, f64>for close-priced,Indicator<Candle, _>for OHLCV-based). - Provide a
reset_state()inherent method that the traitreset()delegates to. - Override
name()andperiod()on the trait when applicable. - Include unit tests covering construction validation, warmup, and batch-vs-streaming parity.
- If practical, add a pandas-ta golden in
scripts/gen_golden.pyand a comparison test intests/golden_indicators.rs.
cargo fmt, cargo clippy --all-features --all-targets -- -D warnings,
and cargo test --all-features all need to be green before review.
License
MIT. See LICENSE.