adaq-talib 0.2.1

Pure-Rust, zero-FFI, no-dependency reimplementation of TA-Lib 0.7.1 technical-analysis indicators.
Documentation

adaq-talib

AdaQ-TAlib — a pure-Rust, zero-FFI, dependency-free reimplementation of the TA-Lib 0.7.1 technical-analysis indicators.

纯 Rust、Zero-FFI、零依赖的 TA-Lib 0.7.1 技术指标复刻库。

中文文档: README.zh-CN.md


Contents


Features

  • Zero-deviation: every function reproduces the numerical output of TA-Lib 0.7.1 (within the float tolerance in ADR 0005). The authoritative reference is the golden vectors generated from TA-Lib C 0.7.1 (ADR 0003).
  • Zero-FFI / No-Dependencies: the published crate calls no C ABI and its Cargo.toml [dependencies] is empty — every algorithm is hand-written in Rust.
  • Idiomatic Rust API (Model B): slice inputs (&[f64]), Result<_, TaError> outputs, multi-output functions return structs; the leading unstable period is filled with f64::NAN and the result is equal-length (ADR 0001 / ADR 0007).
  • Performance-oriented: optimizations at the level of memory layout, loop branching, and array arithmetic. The cycle indicators (MAMA / HT_TRENDLINE) are ported line-for-line from the official C sources for bit-close fidelity.
  • Verify without system deps: cargo test compares against in-repo golden vectors, so running the tests needs no Python or TA-Lib C library.

Implemented Functions

adaq-talib now implements the full TA-Lib 0.7.1 public surface — all 161 functions across 10 categories — with zero deviation (verified 1:1 against golden vectors, see Verification & Benchmarks). The tables below list every public function, its TA-Lib counterpart, default parameters, and return shape.

Category Module Count TA-Lib group
Overlap Studies overlap 18 Overlap Studies
Momentum Indicators momentum 31 Momentum Indicators
Volatility Indicators volatility 3 Volatility Indicators
Volume Indicators volume 3 Volume Indicators
Price Transform price_transform 5 Price Transform
Statistic Functions stat 9 Statistic Functions
Cycle (Hilbert Transform) cycle 7* Cycle Indicators (5) + Overlap (2)†
Math Operators math_ops 11 Math Operators
Math Transform math_trans 15 Math Transform
Pattern Recognition pattern 61 Pattern Recognition
Total 161 161

* The cycle module holds 7 functions; TA-Lib classifies 5 as Cycle Indicators (HT_DCPERIOD/HT_DCPHASE/HT_PHASOR/HT_SINE/HT_TRENDMODE) and 2 as Overlap Studies (MAMA/HT_TRENDLINE). † Grouping follows TA-Lib's authoritative info['group'].

Convention: every function ships both an explicit-parameter version and a _default convenience version that uses TA-Lib's default parameters (see src/core/defaults.rs).

Overlap Studies — adaq_talib::overlap

Function TA-Lib Defaults Returns
sma / sma_default TA_SMA period = 30 Vec<f64>
ema / ema_default TA_EMA period = 30 Vec<f64>
wma / wma_default TA_WMA period = 30 Vec<f64>
dema / dema_default TA_DEMA period = 30 Vec<f64>
tema / tema_default TA_TEMA period = 30 Vec<f64>
midpoint / midpoint_default TA_MIDPOINT period = 30 Vec<f64>
midprice / midprice_default TA_MIDPRICE period = 30 Vec<f64>
bbands / bbands_default TA_BBANDS period = 20, nb_dev = 2.0/2.0, SMA middle Bbands { upper, middle, lower }
trima / trima_default TA_TRIMA period = 30 Vec<f64>
t3 / t3_default TA_T3 period = 5, vfactor = 0.7 Vec<f64>
ma / ma_default TA_MA period = 30, MaType::Sma Vec<f64> (dispatched by MaType)
mavp / mavp_default TA_MAVP min = 2 / max = 30, SMA Vec<f64> (variable period)
kama / kama_default TA_KAMA period = 30 Vec<f64>
sar / sar_default TA_SAR accel = 0.02, max = 0.2 Vec<f64>
sarext / sarext_default TA_SAREXT long/short accel 0.02/0.02/0.2 Vec<f64> (short side negative)
accbands / accbands_default TA_ACCBANDS period = 20 AccBands { upper, middle, lower }
MaType TA_MAType Sma/Ema/Wma/Dema/Tema/Trima/Kama/Mama enum (for ma/bbands/mavp)

Momentum Indicators — adaq_talib::momentum

Function TA-Lib Defaults Returns
mom / mom_default TA_MOM period = 10 Vec<f64>
roc / roc_default TA_ROC period = 10 Vec<f64>
rocp / rocp_default TA_ROCP period = 10 Vec<f64>
rocr / rocr_default TA_ROCR period = 10 Vec<f64>
rocr100 / rocr100_default TA_ROCR100 period = 10 Vec<f64>
rsi / rsi_default TA_RSI period = 14 Vec<f64>
macd / macd_default TA_MACD fast = 12, slow = 26, signal = 9 Macd { macd, signal, hist }
macd_fix / macd_fix_default TA_MACDFIX fast = 12, slow = 26, signal = 9 Macd
macd_ext / macd_ext_default TA_MACDEXT fast = 12, slow = 26, signal = 9 Macd (all-EMA default)
apo / apo_default TA_APO fast = 12, slow = 26 Vec<f64>
ppo / ppo_default TA_PPO fast = 12, slow = 26 Vec<f64>
cmo / cmo_default TA_CMO period = 14 Vec<f64>
imi / imi_default TA_IMI period = 14 Vec<f64> (open/close)
cci / cci_default TA_CCI period = 20 Vec<f64>
mfi / mfi_default TA_MFI period = 14 Vec<f64> (needs volume)
willr / willr_default TA_WILLR period = 14 Vec<f64>
bop TA_BOP — Vec<f64> (lookback 0)
ultosc / ultosc_default TA_ULTOSC 7 / 14 / 28 Vec<f64>
plus_dm / plus_dm_default TA_PLUS_DM period = 14 Vec<f64>
minus_dm / minus_dm_default TA_MINUS_DM period = 14 Vec<f64>
plus_di / plus_di_default TA_PLUS_DI period = 14 Vec<f64>
minus_di / minus_di_default TA_MINUS_DI period = 14 Vec<f64>
adx / adx_default TA_ADX period = 14 Vec<f64>
adxr / adxr_default TA_ADXR period = 14 Vec<f64>
dx / dx_default TA_DX period = 14 Vec<f64> (OHLC)
aroon / aroon_default TA_AROON period = 14 Aroon { up, down }
aroon_osc / aroon_osc_default TA_AROONOSC period = 14 Vec<f64>
stoch / stoch_default TA_STOCH fastK = 5, slowK = 3, slowD = 3 Stoch { slow_k, slow_d }
stoch_f / stoch_f_default TA_STOCHF fastK = 5, fastD = 3 StochF { fast_k, fast_d }
stoch_rsi / stoch_rsi_default TA_STOCHRSI rsi = 14, period = 14 Vec<f64>
trix / trix_default TA_TRIX period = 30 Vec<f64>

Volatility Indicators — adaq_talib::volatility

Function TA-Lib Defaults Returns
trange TA_TRANGE — Vec<f64> (lookback 0)
atr / atr_default TA_ATR period = 14 Vec<f64>
natr / natr_default TA_NATR period = 14 Vec<f64>

Volume Indicators — adaq_talib::volume

Function TA-Lib Defaults Returns
ad TA_AD — Vec<f64> (cumulative, lookback 0)
adosc / adosc_default TA_ADOSC fast = 3, slow = 10 Vec<f64>
obv TA_OBV — Vec<f64> (cumulative, lookback 0)

Price Transform — adaq_talib::price_transform

Function TA-Lib Defaults Returns
avgdev / avgdev_default TA_AVGDEV period = 14 Vec<f64>
avgprice TA_AVGPRICE — Vec<f64> ((H+L+C+O)/4)
medprice TA_MEDPRICE — Vec<f64> ((H+L)/2)
typprice TA_TYPPRICE — Vec<f64> ((H+L+C)/3)
wclprice TA_WCLPRICE — Vec<f64> ((H+L+2C)/4)

Statistic Functions — adaq_talib::stat

Function TA-Lib Defaults Returns
stddev / stddev_default TA_STDDEV period = 5, nb_dev = 1.0 Vec<f64>
var / var_default TA_VAR period = 5, nb_dev (ignored) Vec<f64>
linear_reg / linear_reg_default TA_LINEARREG period = 14 Vec<f64>
linear_reg_angle / linear_reg_angle_default TA_LINEARREG_ANGLE period = 14 Vec<f64> (angle, degrees)
linear_reg_intercept / linear_reg_intercept_default TA_LINEARREG_INTERCEPT period = 14 Vec<f64>
linear_reg_slope / linear_reg_slope_default TA_LINEARREG_SLOPE period = 14 Vec<f64>
tsf / tsf_default TA_TSF period = 14 Vec<f64>
beta / beta_default TA_BETA period = 5 Vec<f64>
correl / correl_default TA_CORREL period = 5 Vec<f64>

Cycle (Hilbert Transform) — adaq_talib::cycle

Function TA-Lib Defaults Returns
mama / mama_default TA_MAMA fast = 0.5, slow = 0.05 Mama { mama, fama }
ht_trendline / ht_trendline_default TA_HT_TRENDLINE — Vec<f64> (lookback 63)
ht_dcperiod / ht_dcperiod_default TA_HT_DCPERIOD — Vec<f64> (dominant cycle period)
ht_dcphase / ht_dcphase_default TA_HT_DCPHASE — Vec<f64> (dominant cycle phase)
ht_phasor / ht_phasor_default TA_HT_PHASOR — Phasor { in_phase, quadrature }
ht_sine / ht_sine_default TA_HT_SINE — HtSine { sine, lead_sine }
ht_trendmode / ht_trendmode_default TA_HT_TRENDMODE — Vec<f64> (0/1 trend mode)

Math Operators — adaq_talib::math_ops

Element-wise / array operators over one or two equal-length series. All return Vec<f64> (equal-length; lookback 0). Binary operators take (&[f64], &[f64]); maxindex/minindex/ minmax/minmaxindex reduce a windowed series.

Function TA-Lib Signature Returns
add / add_default TA_ADD (a, b) Vec<f64>
sub / sub_default TA_SUB (a, b) Vec<f64>
mult / mult_default TA_MULT (a, b) Vec<f64>
div / div_default TA_DIV (a, b) Vec<f64>
sum / sum_default TA_SUM (a, period) Vec<f64>
min / min_default TA_MIN (a, period) Vec<f64>
max / max_default TA_MAX (a, period) Vec<f64>
min_index / min_index_default TA_MININDEX (a, period) Vec<f64> (index of min)
max_index / max_index_default TA_MAXINDEX (a, period) Vec<f64> (index of max)
minmax / minmax_default TA_MINMAX (a, period) MinMax { min, max }
minmax_index / minmax_index_default TA_MINMAXINDEX (a, period) MinMaxIndex { min_idx, max_idx }

Math Transform — adaq_talib::math_trans

Element-wise transcendental / rounding transforms over a single series. All return Vec<f64> (equal-length; lookback 0).

Function TA-Lib Returns
acos / acos_default TA_ACOS Vec<f64>
asin / asin_default TA_ASIN Vec<f64>
atan / atan_default TA_ATAN Vec<f64>
ceil / ceil_default TA_CEIL Vec<f64>
cos / cos_default TA_COS Vec<f64>
cosh / cosh_default TA_COSH Vec<f64>
exp / exp_default TA_EXP Vec<f64>
floor / floor_default TA_FLOOR Vec<f64>
ln / ln_default TA_LN Vec<f64>
log10 / log10_default TA_LOG10 Vec<f64>
sin / sin_default TA_SIN Vec<f64>
sinh / sinh_default TA_SINH Vec<f64>
sqrt / sqrt_default TA_SQRT Vec<f64>
tan / tan_default TA_TAN Vec<f64>
tanh / tanh_default TA_TANH Vec<f64>

Pattern Recognition — adaq_talib::pattern

All 61 candlestick patterns (TA-Lib Pattern Recognition group). Each takes (&[f64] open, &[f64] high, &[f64] low, &[f64] close) and returns an equal-length Vec<f64> of integer signals: +100 bullish / 0 neutral / −100 bearish; the leading lookback positions are 0.0 (consistent with TA-Lib's integer-output convention, ADR 0007). Only the default candle settings are implemented (ADR 0009).

Function TA-Lib Function TA-Lib
cdl_2crows CDL2CROWS cdl_identical3crows CDLIDENTICAL3CROWS
cdl_3blackcrows CDL3BLACKCROWS cdl_inneck CDLINNECK
cdl_3inside CDL3INSIDE cdl_invertedhammer CDLINVERTEDHAMMER
cdl_3linestrike CDL3LINESTRIKE cdl_kicking CDLKICKING
cdl_3outside CDL3OUTSIDE cdl_kickingbylength CDLKICKINGBYLENGTH
cdl_3starsinsouth CDL3STARSINSOUTH cdl_ladderbottom CDLLADDERBOTTOM
cdl_3whitesoldiers CDL3WHITESOLDIERS cdl_longleggeddoji CDLLONGLEGGEDDOJI
cdl_abandonedbaby CDLABANDONEDBABY cdl_longline CDLLONGLINE
cdl_advanceblock CDLADVANCEBLOCK cdl_marubozu CDLMARUBOZU
cdl_belthold CDLBELTHOLD cdl_matchinglow CDLMATCHINGLOW
cdl_breakaway CDLBREAKAWAY cdl_mathold CDLMATHOLD
cdl_closingmarubozu CDLCLOSINGMARUBOZU cdl_morningdojistar CDLMORNINGDOJISTAR
cdl_concealbabyswall CDLCONCEALBABYSWALL cdl_morningstar CDLMORNINGSTAR
cdl_counterattack CDLCOUNTERATTACK cdl_onneck CDLONNECK
cdl_darkcloudcover CDLDARKCLOUDCOVER cdl_piercing CDLPIERCING
cdl_doji CDLDOJI cdl_rickshawman CDLRICKSHAWMAN
cdl_dojistar CDLDOJISTAR cdl_risefall3methods CDLRISEFALL3METHODS
cdl_dragonflydoji CDLDRAGONFLYDOJI cdl_separatinglines CDLSEPARATINGLINES
cdl_engulfing CDLENGULFING cdl_shootingstar CDLSHOOTINGSTAR
cdl_eveningdojistar CDLEVENINGDOJISTAR cdl_shortline CDLSHORTLINE
cdl_eveningstar CDLEVENINGSTAR cdl_spinningtop CDLSPINNINGTOP
cdl_gapsidesidewhite CDLGAPSIDESIDEWHITE cdl_stalledpattern CDLSTALLEDPATTERN
cdl_gravestonedoji CDLGRAVESTONEDOJI cdl_sticksandwich CDLSTICKSANDWICH
cdl_hammer CDLHAMMER cdl_takuri CDLTAKURI
cdl_hangingman CDLHANGINGMAN cdl_tasukigap CDLTASUKIGAP
cdl_harami CDLHARAMI cdl_thrusting CDLTHRUSTING
cdl_haramicross CDLHARAMICROSS cdl_tristar CDLTRISTAR
cdl_highwave CDLHIGHWAVE cdl_unique3river CDLUNIQUE3RIVER
cdl_hikkake CDLHIKKAKE cdl_upsidegap2crows CDLUPSIDEGAP2CROWS
cdl_hikkakemod CDLHIKKAKEMOD cdl_xsidegap3methods CDLXSIDEGAP3METHODS
cdl_homingpigeon CDLHOMINGPIGEON

Error Type

  • TaError (adaq_talib::TaError) — the public error enum, a semantic mapping of TA-Lib's TA_RetCode: BadParam / OutOfRange / LibNotInitialized / OutOfMemory / InternalError (see ADR 0006).

Quick Start

use adaq_talib::overlap::sma;

let prices = [1.0, 2.0, 3.0, 4.0, 5.0];
let out = sma(&prices, 3).unwrap();
// `out` has the same length as `prices`; the first 2 positions are NaN, the rest are window means.
assert!(out[0].is_nan());
assert!((out[2] - 2.0).abs() < 1e-9);

Multi-output examples (Bollinger Bands, MACD):

use adaq_talib::overlap::bbands_default;
use adaq_talib::momentum::macd_default;

let close = vec![10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0,
                 20.0, 19.0, 18.0, 17.0, 16.0, 15.0, 14.0, 13.0, 12.0, 11.0];

// Bollinger Bands: three equal-length bands, leading unstable period is NaN.
let b = bbands_default(&close).unwrap();
let _ = (b.upper, b.middle, b.lower);

// MACD: macd / signal / hist, aligned to the same leading NaN.
let m = macd_default(&close).unwrap();
let _ = (m.macd, m.signal, m.hist);

OHLC input example (ATR, needs high/low/close):

use adaq_talib::volatility::atr_default;

// A 40-bar sample so the first valid value is observable (leading period-1 = 13 are NaN).
let high:  Vec<f64> = (0..40).map(|i| 10.0 + i as f64 * 0.1 + 0.5).collect();
let low:   Vec<f64> = (0..40).map(|i| 9.0  + i as f64 * 0.1 - 0.5).collect();
let close: Vec<f64> = (0..40).map(|i| 9.5  + i as f64 * 0.1).collect();

let out = atr_default(&high, &low, &close).unwrap();
// The first `period - 1` = 13 positions are NaN; the first valid value is the
// mean of the first 14 True Ranges (Wilder-smoothing seed).
assert!(out[0].is_nan() && out[12].is_nan());
assert!(!out[13].is_nan()); // first valid at index period-1 = 13

Installation

Requirements

  • Rust toolchain ≥ 1.85 (this crate uses edition = "2024", see rust-version in Cargo.toml). Install via rustup.rs.
  • No external crates: the published crate's [dependencies] is empty.
  • No TA-Lib C library or Python required to build or test (golden vectors are in-repo).

From crates.io

cargo add adaq-talib

Or add manually to Cargo.toml:

[dependencies]
adaq-talib = "0.1"

Build from source (after cloning):

git clone https://github.com/tonywxx/adaq-talib
cd adaq-talib
cargo build --release

Usage

1. Calling conventions

  • Single input series: e.g. sma(&prices, period), rsi(&close, period).
  • OHLC multi-input: e.g. atr(&high, &low, &close, period), cci(&high, &low, &close, period), bop(&open, &high, &low, &close).
  • With volume: e.g. mfi(&high, &low, &close, &volume, period), obv(&close, &volume).
  • Multi-output: returned as a dedicated struct, e.g. Macd { macd, signal, hist }, Bbands { upper, middle, lower }, Aroon { up, down }, Mama { mama, fama }.

2. Default-parameter helpers

Every function has a _default variant that applies TA-Lib's original default parameters:

use adaq_talib::momentum::rsi_default;
use adaq_talib::volatility::atr_default;

let rsi = rsi_default(&close).unwrap();   // default period = 14
let atr = atr_default(&high, &low, &close).unwrap(); // default period = 14

3. Error handling

All functions return Result<_, TaError>. The common error is TaError::BadParam (e.g. period == 0, mismatched input lengths, or mama's fast/slow_limit outside [0.01, 0.99]).

use adaq_talib::overlap::sma;
use adaq_talib::TaError;

match sma(&[1.0, 2.0], 0) {
    Ok(out) => println!("{out:?}"),
    Err(TaError::BadParam(msg)) => eprintln!("bad param: {msg}"),
    Err(e) => eprintln!("other error: {e}"),
}

4. Unstable period & NaN

Consistent with TA-Lib, the warm-up (unstable) period is filled with f64::NAN, and the return is equal-length with the input. Common lookbacks:

  • SMA/EMA/WMA/TRIMA etc.: period - 1
  • DEMA: 2*(period-1); TEMA: 3*(period-1); T3: 6*(period-1); TRIX: 3*period-2
  • RSI/CMO/CCI etc.: period (Wilder-style)
  • ADX: 2*period-1; ADXR: 3*period-2
  • MAMA: 32; HT_TRENDLINE: 63
  • Lookback 0 (no leading NaN): trange, ad, obv, price transforms, bop

Skip the unstable period with f64::is_nan() before consuming the valid segment.

5. Reuse outside the library

src/utils.rs and src/core/ are doc(hidden) internal implementation details (alignment, range checks, and shared rolling primitives) and are not part of the public API — do not depend on them externally.


Interactive Demo

examples/demo.rs is a command-line demo entry point that runs a built-in example by indicator name — no code required:

cargo run --example demo -- sma
cargo run --example demo -- rsi
cargo run --example demo -- macd
cargo run --example demo -- bbands
cargo run --example demo -- atr
cargo run --example demo -- adx
cargo run --example demo -- mama

All supported indicators (run directly with cargo run --example demo -- <name>, grouped by category):

Overlap:            sma ema wma dema tema midpoint midprice bbands trima t3 ma mavp kama sar sarext accbands
Momentum:           rsi cmo trix mom macd cci willr bop ultosc adx aroon stoch mfi dx imi
Volatility:         trange atr natr
Volume:             ad adosc obv
Price Transform:    avgprice medprice typprice wclprice avgdev
Statistic:          stddev var linear_reg linear_reg_angle linear_reg_intercept linear_reg_slope tsf beta correl
Cycle:              mama ht_trendline

Note: the interactive demo covers the indicators above. The remaining implemented functions (e.g. roc, rocp, apo, ppo, stoch_f, stoch_rsi, aroon_osc, the directional components, adxr, macd_fix, macd_ext, the math operators in math_ops, the math transforms in math_trans, and all 61 candlestick patterns in pattern, …) are callable directly in code — see the function overview above.

An unknown name prints the supported list and exits with code 2.


Verification & Benchmarks

Correctness (1:1 golden vectors)

  • cargo test compares against in-repo golden vectors (real TA-Lib C 0.7.1 output, in tests/fixtures/ — 222 golden-vector fixture files covering the full 161-function surface). Running the tests needs no Python or TA-Lib C library.
  • Tolerance: relative 1e-8 + absolute 1e-10 (ADR 0005).
  • Full suite: 326 tests, 0 failures; tools/reconcile.py confirms 161/161 public functions map 1:1 to TA-Lib 0.7.1 (exit 0). The complete 1:1 validation and performance report for all 161 indicators is in docs/validation-and-performance-report.md.
  • The golden-vector generator lives in tools/gen_fixtures (requires TA-Lib C + the TA-Lib Python binding; for maintainers only). Current fixture status and known gaps are in tools/README.md.
cargo test                 # unit tests + golden-vector comparison (no Python / C needed)
cargo test --doc           # doc examples only

Performance (Rust vs TA-Lib C)

All 161 / 161 indicators were benchmarked head-to-head against native TA-Lib C 0.7.1 (dual-track, ADR 0004; the C track FFI-links system TA-Lib C under --features bench-c). Environment: Apple Silicon aarch64, N = 100,000 elements per indicator; ns/elem = elapsed / ITERS / N; Rust/C = Rust_ns/elem ÷ C_ns/elem. Final numbers are the median of 5 runs to damp per-function benchmark noise (~20–40%). The focused micro-benches (cdl_bench, momentum_wilder_bench) run 9 rounds and take the median for even lower noise on small per-function effects. Status: ratio < 0.8 → Faster, 0.8–1.2 → At parity, > 1.2 → Slower.

Headline: 85 faster, 60 at parity, 16 slower than native C; geomean Rust/C = 0.786× — adaq-talib is now ~1.27× faster than C on average (down from 1.50× slower prior to the 0.1.3 optimization pass). 145 of 161 indicators are at-or-faster than C (Rust/C ≤ 1.2); only 16 remain slower, all isolated cases.

Optional parallel feature: the parallel feature (default-off) applies overlap-seed parallel chunking to the 5 A-class window functions (midpoint, minmax, minmax_index, willr, stoch_f), lifting them out of the Slower bucket. Under --features parallel the totals become 88 faster / 63 at parity / 10 slower (geomean Rust/C = 0.734×, ≈1.36× faster than C); the default (serial) build stays at 85/60/16 (0.786×). For the other 156 functions the feature is a no-op. See the Optimizations table (P3-2b) and docs/validation-and-performance-report.md §3.5.

TA-Lib Group Indicators Faster (<0.8) At parity (0.8–1.2) Slower (>1.2) Geomean Rust/C
Cycle Indicators 5 2 2 1 0.980×
Math Operators 11 7 2 2 0.805×
Math Transform 15 4 11 0 0.858×
Momentum Indicators 31 8 20 3 0.852×
Overlap Studies 18 6 10 2 0.842×
Pattern Recognition 61 43 13 5 0.677×
Price Transform 5 5 0 0 0.599×
Statistic Functions 9 7 1 1 0.548×
Volatility Indicators 3 2 0 1 0.841×
Volume Indicators 3 1 1 1 0.994×
Total 161 85 60 16 0.786×

adaq-talib is now faster than C on average in all 10 groups (every group geomean Rust/C < 1) — Cycle, Math Operators, Math Transform, Momentum, Overlap Studies, Pattern Recognition, Price Transform, Statistic Functions, Volatility and Volume; no group is slower on average. The headline improvement is Pattern Recognition, which flipped from the slowest group (geomean 2.98× slower) to among the fastest (0.677×) after the 0.1.3 inline-accumulator rollout across all 61 candlestick functions. The 16 remaining slower functions are isolated, genuine single-thread recurrence / dual-extreme floors: midpoint, minmax, minmax_index, mfi, willr, stoch_f, correl, adosc, trange, ht_phasor, ht_trendline, plus the Pattern candle-decision branches cdl_separatinglines/cdl_harami/cdl_longline/cdl_shortline. (cdl_engulfing also benches slower at ~2×, but that ratio is a C-side benchmark artifact — C's ~1 ns/elem timing is implausibly low for its 2-candle cache loop while Rust's ~2 ns/elem is normal; it is not a genuine Rust gap. See docs/validation-and-performance-report.md §5.) The EMA-family gap (EMA/KAMA/APO/PPO/T3/TRIX/ULTOSC/ADX/ADXR/DX) was closed by the P3-6 FMA-contraction pass (see the optimizations table). The full per-indicator table — all 161, with Rust/C ratio, status and a live TA-Lib parity checksum — is in docs/validation-and-performance-report.md. Caveats (e.g. stoch_rsi exposes only the fastk line, so its bench checksum differs — a bench artifact, not a correctness gap) are detailed there.

Performance optimizations applied

All optimizations below are zero-deviation — verified 1:1 against TA-Lib 0.7.1 golden vectors (see benches/BASELINE.md for the full per-indicator write-up). ns/elem measured on Apple Silicon aarch64, N = 1_000_000, PERIOD = 20, ITERS = 20 (spot measurement, ±5% jitter).

Phase Function(s) Technique Result (Rust/C or ns/elem) Δ vs prior
P3-1 (0.1.3) Pattern Recognition (all 61 CDL) inline CandleAvg running-sum / trailing-sum accumulators (tools/opt_pattern.py, parity-preserving) geomean 2.98× → 0.677×; 43 faster / 13 parity / 5 slower biggest single driver
P3-2 (0.1.3) min / max / min_index / max_index ring-buffer MonoQueue (masked index, no bounds checks) replacing VecDeque per-extreme 3.447 → 2.347 ns/elem; min 1.17→0.76, max 1.57→0.99, min_index 1.14→0.77, max_index 1.54→1.01 ~32% faster per extreme
P3-3 (0.1.3) ht_dcperiod cycle-IIR fast path skips the unused compute_dc_phase sin/cos window 3.589× → 1.191× (now at parity) —
P3-4 (0.1.3) ht_dcphase / ht_sine / ht_trendmode sin/cos angle-addition recurrence (sin(θ+w), cos(θ+w)) 1.216→0.786 / 0.840→0.687 / 1.432→1.122 —
P3-5 (0.1.3) mfi single-pass sliding-window fusion (two ring-buffer running sums) 2.563× → 1.406× (still slower — per-bar divisions dominate) ~1.8× closer to C
P3-6 (0.1.3) ema / kama / apo / ppo / t3 / adosc (recurrence sites) explicit .mul_add() FMA at every recurrence (GCC -ffp-contract=fast parity) ema 1.488→0.977, kama 1.484→1.069, apo 1.529→1.085, ppo 1.425→1.077, t3 1.325→0.999 (all At parity); transitive trix/ultosc Faster, adx/adxr/dx At parity closed the EMA-family gap
P3-2b (0.1.3) midpoint / minmax / minmax_index / willr / stoch_f overlap-seed parallel chunking (std::thread::scope + available_parallelism, No-Deps, default-off parallel feature) midpoint 1.620→0.901, minmax 1.523→0.844, minmax_index 1.434→0.915 (all At parity); willr 1.455→0.748, stoch_f 1.228→0.579 (Faster); totals 85/60/16 → 88/63/10, geomean 0.786×→0.734× lifts the 5 A-class seedable floors out of Slower
P2-1 dema / tema / t3 single-pass nested-EMA fusion core (core::nested_ema_with_output) 3.63 / 3.46 / 3.76 ns/elem ~2× / ~3× / ~6× vs naive
P2-2 midpoint / midprice monotonic-queue core::rolling_extreme O(n) 6.88 / 7.30 ~3× / ~3×
P2-3 wma O(n) sliding recurrence (W[i] = W[i-1] + period·x[i] − sw[i-1]) 2.11 ~4.7×
P2-4 bbands (SMA middle) single-pass rolling_mean_var fusion 3.02 ~1.5–1.6×
P2-5 linear_reg family / correl O(n) sliding sum / cross-product 2.33 / 4.81 ~20× asymptotic
P2-5 willr / stoch / stoch_f shared monotonic extreme queue O(n) 7.90 / 10.99 ~20× asymptotic
P1② minmax reuse single-pass core::rolling_minmax (consolidation; perf-neutral) 6.76 ≈ (accuracy-only)
P1③ max_index / min_index / minmax_index single-pass core::rolling_extreme_index O(n) 3.43 / 3.31 / 6.79 ~1.9× (index)
Wilder 接缝 (0.1.7) rsi / cmo / plus_di / minus_di / dx / adx / adxr route inline Wilder recurrences onto core::ema::wilder_step / wilder_step_sum (precomputed k = 1/period replaces hot-loop /p divisions; mean vs sum forms kept distinct) rsi 5.40→3.13, cmo 6.06→3.28, plus_di 7.02→4.04, minus_di 7.14→4.06, dx 6.74→4.89, adx 6.95→5.87, adxr 6.87→6.05 ns/elem −12% ~ −46% faster (momentum_wilder_bench, N=100k, median-of-9)
Accuracy 加固 (0.1.9) macd_fix / stoch_rsi / max_index / min_index / minmax_index / cdl_shootingstar correctness fixes to match C TA-Lib 0.7.1: MACDFIX fixed 0.15/0.075 factors, stoch_rsi fastD alignment, index-family leading NaN, cdl_shootingstar lookback+1 (perf-neutral, no hot-path change) — 0 regression (Wilder-family Δ within ±1.4%; stoch_rsi fastD copy ≈ 0.18%) — accuracy-only, not a perf optimization

† midpoint / midprice (P2-2) and max_index / min_index / minmax_index (P1③) now run on the same ring-buffer MonoQueue introduced in 0.1.3 (P3-2).

Every optimization keeps the full cargo test suite green (326/326) and each refactored function still reproduces its TA-Lib 0.7.1 golden vector within tolerance (ADR 0005). The full QA write-up (methodology, residual gaps, Python-binding reference numbers) lives in docs/perf-verify-report.md.

Benchmarks (how to run)

# 1) Rust side (default, dependency-free): std::time timing, harness = false
cargo bench --bench sma_bench

# 2) Native C comparison (optional feature): FFI-links the system TA-Lib C library
cargo bench --bench sma_bench --features bench-c

# 3) All 161 indicators vs native C (auto-generated suite):
cargo bench --bench all161_bench
cargo bench --bench all161_bench --features bench-c   # with the C reference track
cargo bench --bench all161_bench --features bench-c,parallel   # parallel overlap-seed pass
cargo bench --bench momentum_wilder_bench   # Wilder-family micro-bench (rsi/cmo/±di/dx/adx/adxr), median-of-9
cargo bench --bench cdl_bench               # candle-pattern micro-bench, median-of-9

The second form needs the TA-Lib C library installed (brew install ta-lib / build from source); build.rs links it only under bench-c, so the build is unaffected otherwise. Reports must clearly distinguish the two tracks.


Known Issues & Deprecations

Known issues

  • 16 indicators remain slower than native TA-Lib C — all genuine single-thread recurrence / dual-extreme floors, not correctness gaps (in the default serial build). The optional parallel feature lifts the 5 A-class functions (midpoint/minmax/minmax_index/willr/stoch_f) to parity/faster via overlap-seed parallel chunking, so the effective slower count drops to 10 (geomean Rust/C = 0.734×, ≈1.36× faster than C); the 16 figure is what the default serial build reports. The clearest structural case is MIDPOINT (~1.62×), a data-dependent monotonic structure costing ~2× C's single MINMAX scan; minmax/minmax_index (~1.52×/1.43×) carry the same dual-deque cost. The strict-recurrence ht_phasor/ht_trendline (~1.24×/1.27×), sliding-window mfi/willr/stoch_f/adosc/correl (~1.23–1.55×), trange (1.22×), and the Pattern candle-decision branches cdl_separatinglines/cdl_harami/cdl_longline/ cdl_shortline (~1.30–1.75×). cdl_engulfing (~2.00×) also appears slower, but that is a C-side benchmark artifact (C's timing is anomalously low for its 2-candle cache), not a genuine Rust gap (full list in §4 of the performance report). The previously-slow EMA family (ema/kama/apo/ppo/t3/trix/ultosc/adx/ adxr/dx) was closed to At parity / Faster by the P3-6 FMA-contraction pass. The planned P3 SIMD pass is a documented NO-GO for the recurrence floors (ADR 0010) — single-thread micro-optimization is exhausted; the >2× path is parallelization, gated per NEXT-ACTIONS-perf.md. This is a known, accepted trade-off, not a defect.
  • No native-C wiring for linear_reg / correl / willr / stoch — their Rust-side numbers are the canonical reference. A C comparison would require unsafe plus the system TA-Lib C library, which goes against the zero-FFI design; their Rust results are authoritative.
  • Pattern recognition uses TA-Lib's default candle settings only (ADR 0009); no configuration API is exposed. There is no functional coverage gap against TA-Lib 0.7.1 — all 61 candlestick patterns are implemented.
  • aroon / aroon_osc output order — adaq-talib follows the canonical TA-Lib C 0.7.1 outAroonUp / outAroonDown order (the authoritative golden vectors). If you cross-check against the talib Python wheel (0.7.1), note that build historically swaps these two outputs; see ADR 0003.

Dependencies

  • No runtime dependencies. The published crate's [dependencies] is and remains empty. All recent work adds only dev benchmarks (benches/), a release workflow, and internal core primitives — no new external crates are introduced.

Deprecated features

  • None. This release introduces no deprecations and removes no published capability (ADR 0002).

Documentation

  • Design decisions (ADR 0001–0009): docs/adr/
  • Changelog: CHANGELOG.md
  • Unified API conventions: docs/api-conventions.md
  • 0.1.0 function scope baseline: docs/0.1.0-scope.md
  • Glossary: CONTEXT.md
  • Every public function carries a bilingual (Chinese/English) doc-comment (formula source, parameters, return value, leading NaN, runnable example). Browse with cargo doc --open.

License

Apache-2.0 (see LICENSE).


Roadmap

Milestone-based release (ADR 0002). This release ships the complete TA-Lib 0.7.1 public surface — all 161 functions across 10 categories — with no deletion of published capabilities.

  • ✅ 0.1.9 (current): 161 / 161 functions, faster than C on average — Overlap Studies (18), Momentum (31), Volatility (3), Volume (3), Price Transform (5), Statistic (9), Cycle / Hilbert Transform (7), Math Operators (11), Math Transform (15), and Pattern Recognition (61 candlestick patterns). Every function is verified 1:1 against TA-Lib 0.7.1 golden vectors (cargo test → 326/326 green, reconcile.py → 161/161) across 222 golden-vector fixtures, and a comprehensive all-161 benchmark + validation suite (docs/validation-and-performance-report.md) confirms full coverage and that adaq-talib is now ~1.27× faster than C on average (geomean Rust/C = 0.786×; 85 faster / 60 at parity / 16 slower) after the 0.1.3 optimization pass; under the optional parallel feature this becomes 88 / 63 / 10 (0.734×). 0.1.4, 0.1.5, 0.1.6, 0.1.7, 0.1.8 and 0.1.9 were internal architecture-refactor / correctness-hardening releases — core modularization, the zero-cost indicator! scaffold, candle-pattern readability / boilerplate consolidation, the Wilder recurrence seam, and the 0.1.9 TA-Lib 0.7.1 accuracy hardening (MACDFIX fixed factors, stoch_rsi fastD alignment, index-family leading NaN, cdl_shootingstar lookback fix) consolidation, and changelog extraction — adding no new public functions or API changes (see Changelog).
  • 🔜 Future work (post-1.0): per ADR 0009 only the default candle settings are implemented and no configuration API is exposed; optional bench-c wiring for the newly optimized indicators (LINREG/CORREL/WILLR/STOCH), and documentation/CI polish. No functional coverage gap remains against TA-Lib 0.7.1.

Once those land, adaq-talib reaches full coverage equivalent to TA-Lib 0.7.1.


Changelog

CHANGELOG.md