quantedge-ta
A streaming technical analysis library for Rust. Correct, tested, documented.
Features
Type-safe convergence
Indicators return Option<Self::Output>. No value until there's enough data.
No silent NaN, no garbage early values. The type system enforces correctness.
For indicators with infinite memory (EMA), convergence enforcement is
configurable: opt in to suppress values until the seed's influence has decayed
below 1%.
Bring your own data
Indicators accept any type implementing the Ohlcv trait. No forced conversion
to a library-specific struct. Implement five required methods on your existing
type and you're done. Volume has a default implementation for data sources that
don't provide it.
O(1) incremental updates
Indicators maintain running state and update in constant time per tick. No re-scanning the window.
Live repainting
Indicators track bar boundaries using open_time. A kline with a new
open_time advances the window; same open_time replaces the current value.
Useful for trading terminals and real-time systems that need indicator values
on forming bars.
Typed outputs
Each indicator defines its own output type via an associated type on the
Indicator trait. SMA, EMA, and RSI return f64. Bollinger Bands returns
BbValue { upper, middle, lower }. No downcasting, no enums, full type
safety.
Usage
use ;
use NonZero;
let mut sma = new;
for kline in stream
Bollinger Bands returns a struct:
use ;
use NonZero;
let config = builder
.length
.build;
let mut bb = new;
for kline in stream
Custom standard deviation multiplier:
use ;
use NonZero;
let config = builder
.length
.std_dev
.build;
Live data with repainting:
// Open kline arrives (open_time = 1000)
sma.compute; // computes with current bar
// Same bar, new trade (open_time = 1000, updated close)
sma.compute; // replaces current bar value
// Next bar (open_time = 2000)
sma.compute; // advances the window
The caller controls bar boundaries. The library handles the rest.
Indicator Trait
Each indicator defines its output type. No downcasting needed:
// Sma: Output = f64
// Ema: Output = f64
// Rsi: Output = f64
// Bb: Output = BbValue { upper: f64, middle: f64, lower: f64 }
Ohlcv Trait
Implement the Ohlcv trait on your own data type:
use ;
Convergence
SMA and BB converge as soon as the window fills (length bars). EMA and RSI
use exponential smoothing with infinite memory; the SMA seed influences all
subsequent values. RSI output begins at bar length + 1. For EMA, EmaConfig
provides methods to control convergence:
enforce_convergence()-- whentrue,compute()returnsNoneuntil the seed's contribution decays below 1%.required_bars_to_converge()-- returns the number of bars needed.
use EmaConfig;
use NonZero;
let config = builder
.length
.enforce_convergence // None until ~63 bars
.build;
config.required_bars_to_converge; // 63 = 3 * (20 + 1)
Use required_bars_to_converge() to determine how much history to fetch before
going live.
Price Sources
Each indicator is configured with a PriceSource that determines which value
to extract from the Ohlcv input:
| Source | Formula |
|---|---|
| Close | close |
| Open | open |
| High | high |
| Low | low |
| HL2 | (high + low) / 2 |
| HLC3 | (high + low + close) / 3 |
| OHLC4 | (open + high + low + close) / 4 |
| HLCC4 | (high + low + close + close) / 4 |
| TrueRange | max(high - low, |high - prev_close|, |low - prev_close|) |
Indicators
| Indicator | Output | Description |
|---|---|---|
| SMA | f64 |
Simple Moving Average |
| EMA | f64 |
Exponential Moving Average |
| RSI | f64 |
Relative Strength Index (Wilder's smoothing) |
| BB | BbValue |
Bollinger Bands (upper, mid, lower) |
Planned
MACD, ATR, CHOP, and more.
Benchmarks
Measured with Criterion.rs on 744 BTC/USDT 1-hour bars from Binance.
Stream measures end-to-end throughput including window fill.
Tick isolates steady-state per-bar cost on a fully converged indicator.
Repaint measures single-tick repaint cost (same open_time, perturbed close)
on a converged indicator.
Repaint Stream measures end-to-end throughput with 3 ticks per bar
(open → mid → final), 2232 total observations.
Hardware: Apple M3 Max (16 cores), 48 GB RAM, macOS 26.3, rustc 1.93.1,
--release profile.
Stream — process 744 bars from cold start
| Indicator | Period | Time (median) | Throughput |
|---|---|---|---|
| SMA | 20 | 2.95 µs | 252 Melem/s |
| SMA | 200 | 2.82 µs | 264 Melem/s |
| EMA | 20 | 3.27 µs | 228 Melem/s |
| EMA | 200 | 3.33 µs | 224 Melem/s |
| BB | 20 | 3.70 µs | 201 Melem/s |
| BB | 200 | 3.57 µs | 208 Melem/s |
| RSI | 14 | 4.08 µs | 182 Melem/s |
| RSI | 140 | 4.01 µs | 186 Melem/s |
Tick — single compute() on a converged indicator
| Indicator | Period | Time (median) |
|---|---|---|
| SMA | 20 | 12.0 ns |
| SMA | 200 | 16.2 ns |
| EMA | 20 | 7.97 ns |
| EMA | 200 | 8.30 ns |
| BB | 20 | 17.0 ns |
| BB | 200 | 20.4 ns |
| RSI | 14 | 6.98 ns |
| RSI | 140 | 6.61 ns |
Repaint — single compute() repaint on a converged indicator
| Indicator | Period | Time (median) |
|---|---|---|
| SMA | 20 | 11.3 ns |
| SMA | 200 | 15.9 ns |
| EMA | 20 | 7.71 ns |
| EMA | 200 | 8.36 ns |
| BB | 20 | 16.7 ns |
| BB | 200 | 19.5 ns |
| RSI | 14 | 7.58 ns |
| RSI | 140 | 7.71 ns |
Repaint Stream — process 744 bars × 3 ticks from cold start
| Indicator | Period | Time (median) | Throughput |
|---|---|---|---|
| SMA | 20 | 8.35 µs | 267 Melem/s |
| SMA | 200 | 8.29 µs | 269 Melem/s |
| EMA | 20 | 8.06 µs | 277 Melem/s |
| EMA | 200 | 8.38 µs | 266 Melem/s |
| BB | 20 | 10.8 µs | 207 Melem/s |
| BB | 200 | 10.6 µs | 210 Melem/s |
| RSI | 14 | 6.73 µs | 332 Melem/s |
| RSI | 140 | 6.95 µs | 321 Melem/s |
Run locally:
Minimum Supported Rust Version
1.93
Licence
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributing
Contributions welcome. Please open an issue before submitting large changes.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 licence, shall be dual-licensed as above, without any additional terms or conditions.