quantedge-ta 0.1.0

A streaming technical analysis library for Rust
Documentation
  • Coverage
  • 100%
    46 out of 46 items documented8 out of 8 items with examples
  • Size
  • Source code size: 121.61 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 6.67 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 14s Average build duration of successful builds.
  • all releases: 14s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • dluksza/quantedge-ta
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • dluksza

quantedge-ta

CI codecov License

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 and EMA return f64. Bollinger Bands returns BbValue { upper, middle, lower }. No downcasting, no enums, full type safety.

Usage

use quantedge_ta::{Sma, SmaConfig, Indicator};
use std::num::NonZero;

let mut sma = Sma::new(SmaConfig::close(NonZero::new(20).unwrap()));

for kline in stream {
    if let Some(value) = sma.compute(&kline) {
        println!("SMA(20): {value}");
    }
    // None = not enough data yet
}

Bollinger Bands returns a struct:

use quantedge_ta::{
    Bb, BbConfig, Indicator, IndicatorConfig, IndicatorConfigBuilder,
};
use std::num::NonZero;

let config = BbConfig::builder()
    .length(NonZero::new(20).unwrap())
    .build();
let mut bb = Bb::new(config);

for kline in stream {
    if let Some(value) = bb.compute(&kline) {
        println!("BB upper: {}, middle: {}, lower: {}",
            value.upper(), value.middle(), value.lower());
    }
}

Custom standard deviation multiplier:

use quantedge_ta::{BbConfig, StdDev, IndicatorConfig, IndicatorConfigBuilder};
use std::num::NonZero;

let config = BbConfig::builder()
    .length(NonZero::new(20).unwrap())
    .std_dev(StdDev::new(1.5))
    .build();

Live data with repainting:

// Open kline arrives (open_time = 1000)
sma.compute(&open_kline);    // computes with current bar

// Same bar, new trade (open_time = 1000, updated close)
sma.compute(&updated_kline); // replaces current bar value

// Next bar (open_time = 2000)
sma.compute(&next_kline);    // advances the window

The caller controls bar boundaries. The library handles the rest.

Indicator Trait

Each indicator defines its output type. No downcasting needed:

trait Indicator: Sized + Clone + Display + Debug {
    type Config: IndicatorConfig;
    type Output: Send + Sync + Display + Debug;

    fn new(config: Self::Config) -> Self;
    fn compute(&mut self, kline: &impl Ohlcv) -> Option<Self::Output>;
    fn value(&self) -> Option<Self::Output>;
}

// Sma: Output = f64
// Ema: Output = f64
// Bb:  Output = BbValue { upper: f64, middle: f64, lower: f64 }

Ohlcv Trait

Implement the Ohlcv trait on your own data type:

use quantedge_ta::{Ohlcv, Price, Timestamp};

struct MyKline {
    open: f64,
    high: f64,
    low: f64,
    close: f64,
    open_time: u64,
}

impl Ohlcv for MyKline {
    fn open(&self) -> Price { self.open }
    fn high(&self) -> Price { self.high }
    fn low(&self) -> Price { self.low }
    fn close(&self) -> Price { self.close }
    fn open_time(&self) -> Timestamp { self.open_time }
    // fn volume(&self) -> f64 { 0.0 }  -- default, override if needed
}

Convergence

SMA and BB converge as soon as the window fills (length bars). EMA has infinite memory; the SMA seed influences all subsequent values. EmaConfig provides methods to control this:

  • enforce_convergence() -- when true, compute() returns None until the seed's contribution decays below 1%.
  • required_bars_to_converge() -- returns the number of bars needed.
use quantedge_ta::{EmaConfig, IndicatorConfig, IndicatorConfigBuilder};
use std::num::NonZero;

let config = EmaConfig::builder()
    .length(NonZero::new(20).unwrap())
    .enforce_convergence(true) // 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

v0.1

Indicator Output Description
SMA f64 Simple Moving Average
EMA f64 Exponential Moving Average
BB BbValue Bollinger Bands (upper, mid, lower)

Planned

RSI, 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.

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 3.05 µs 243 Melem/s
SMA 200 2.89 µs 257 Melem/s
EMA 20 3.26 µs 228 Melem/s
EMA 200 3.24 µs 229 Melem/s
BB 20 5.47 µs 136 Melem/s
BB 200 4.19 µs 177 Melem/s

Tick — single compute() on a converged indicator

Indicator Period Time (median)
SMA 20 21.8 ns
SMA 200 29.5 ns
EMA 20 19.2 ns
EMA 200 18.4 ns
BB 20 23.7 ns
BB 200 32.7 ns

Run locally:

cargo bench              # all benchmarks
cargo bench -- stream    # stream only
cargo bench -- tick      # single-tick only

Minimum Supported Rust Version

1.93

Licence

Licensed under either of:

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.