rsta 0.1.0

Technical analysis indicators, streaming signals, and a single-asset backtesting engine for Rust
Documentation

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.

CI Codecov Crates.io Docs License: MIT

At a glance

use rsta::backtest::{Action, BacktestConfig, Backtester, Context, Quantity, Strategy};
use rsta::indicators::trend::Sma;
use rsta::indicators::{Candle, Indicator};
use rsta::signals::{CrossDown, CrossUp, Signal, SignalEvent};

struct Crossover { fast: Sma, slow: Sma, up: CrossUp, down: CrossDown }

impl Strategy for Crossover {
    fn on_candle(&mut self, c: &Candle, _ctx: &Context) -> Action {
        let f = <Sma as Indicator<f64, f64>>::next(&mut self.fast, c.close).unwrap();
        let s = <Sma as Indicator<f64, f64>>::next(&mut self.slow, c.close).unwrap();
        let (Some(f), Some(s)) = (f, s) else { return Action::Hold };
        match (self.up.next((f, s)), self.down.next((f, s))) {
            (Some(SignalEvent::Long), _)  => Action::EnterLong(Quantity::AllCash),
            (_, Some(SignalEvent::Short)) => Action::Exit,
            _ => Action::Hold,
        }
    }
}

let candles: Vec<Candle> = /**/ vec![];
let bt = Backtester::new(BacktestConfig::default());
let mut strat = Crossover {
    fast: Sma::new(20).unwrap(),
    slow: Sma::new(50).unwrap(),
    up: CrossUp::new(),
    down: CrossDown::new(),
};
let result = bt.run(&candles, &mut strat);
println!("return = {:.2}%", result.metrics.total_return * 100.0);

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 from Donchian)
  • Divergence — bullish/bearish divergences between price and an oscillator
  • SignalExt::and / or / not combinators 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)

rsta = { version = "0.1", features = ["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

[dependencies]
rsta = "0.1"

Optional: enable the CSV pipeline.

[dependencies]
rsta = { version = "0.1", features = ["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:

pip install pandas pandas-ta
python scripts/gen_golden.py

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
cargo run --release --example sma_crossover_backtest

# Streaming indicator usage with RSI threshold signals
cargo run --release --example realtime_streaming

# Enrich a CSV with indicator columns (csv feature required)
cargo run --release --features csv --example csv_to_indicators -- input.csv output.csv

Benchmarks

Microbenchmarks via criterion:

cargo bench --bench indicators        # 11 indicators × 100k synthetic bars
cargo bench --bench backtest          # SMA crossover on BTC daily + 1M-bar synthetic

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 / Signal traits 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:

  1. Live in their own file under src/indicators/<family>/<name>.rs.
  2. Implement Indicator<T, O> for whichever input type makes sense (Indicator<f64, f64> for close-priced, Indicator<Candle, _> for OHLCV-based).
  3. Provide a reset_state() inherent method that the trait reset() delegates to.
  4. Override name() and period() on the trait when applicable.
  5. Include unit tests covering construction validation, warmup, and batch-vs-streaming parity.
  6. If practical, add a pandas-ta golden in scripts/gen_golden.py and a comparison test in tests/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.