Disparity Index
ema_period = 14 | lookback_period = 14 | smoothing_period = 9 | smoothing_type = ema Overview
Disparity Index starts by comparing price to an EMA baseline and expressing that distance as a percentage of the EMA itself. That makes the core signal less about raw price units and more about how stretched the market has become relative to its recent trend anchor. Positive values show price trading above the EMA, while negative values show price below it.
VectorTA then takes those disparity percentages, looks back across a rolling window, and rescales the current disparity inside the local high-low range. The normalized reading is finally smoothed with either an EMA or an SMA. The result behaves more like a bounded momentum oscillator than a raw percentage-distance line, which makes it useful for spotting when price has moved toward the top or bottom of its recent disparity envelope rather than merely checking whether price is above or below an average.
Defaults: Disparity Index uses `ema_period = 14`, `lookback_period = 14`, `smoothing_period = 9`, and `smoothing_type = "ema"`.
Implementation Examples
Compute the smoothed disparity oscillator from a close series or from a candle source.
use vector_ta::indicators::disparity_index::{
disparity_index,
DisparityIndexInput,
DisparityIndexParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let close = vec![100.0, 100.7, 101.1, 100.9, 101.8, 102.4, 101.9];
let output = disparity_index(&DisparityIndexInput::from_slice(
&close,
DisparityIndexParams {
ema_period: Some(14),
lookback_period: Some(14),
smoothing_period: Some(9),
smoothing_type: Some("ema".to_string()),
},
))?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = disparity_index(
&DisparityIndexInput::with_default_candles(&candles)
)?;
println!("latest disparity index = {:?}", output.values.last());
println!("length = {}", candle_output.values.len()); API Reference
Input Methods ▼
// From candles and a named source field
DisparityIndexInput::from_candles(
&Candles,
&str,
DisparityIndexParams,
) -> DisparityIndexInput
// From a raw slice
DisparityIndexInput::from_slice(&[f64], DisparityIndexParams)
-> DisparityIndexInput
// From candles with default parameters
DisparityIndexInput::with_default_candles(&Candles)
-> DisparityIndexInput Parameters Structure ▼
pub struct DisparityIndexParams {
pub ema_period: Option<usize>, // default 14
pub lookback_period: Option<usize>, // default 14
pub smoothing_period: Option<usize>, // default 9
pub smoothing_type: Option<String>, // "ema" or "sma", default "ema"
} Output Structure ▼
pub struct DisparityIndexOutput {
pub values: Vec<f64>,
} Validation, Warmup & NaNs ▼
- The input slice must be non-empty and contain at least one finite value.
ema_period,lookback_period, andsmoothing_periodmust each be greater than zero.smoothing_typemust be either"ema"or"sma".- The longest contiguous finite run must be at least
ema_period + lookback_period + smoothing_period - 2bars. - Warmup lasts for
ema_period + lookback_period + smoothing_period - 3bars, which is also what the streaming API reports throughget_warmup_period(). - If the EMA baseline is effectively zero while price is non-zero, the raw disparity cannot be formed for that bar.
- Streaming resets its internal EMA, disparity window, and final smoothing state when it sees a non-finite input.
- Destination slices for in-place APIs must match the input length exactly, and batch mode rejects invalid ranges or non-batch kernels.
Builder, Streaming & Batch APIs ▼
// Builder
DisparityIndexBuilder::new()
.ema_period(usize)
.lookback_period(usize)
.smoothing_period(usize)
.smoothing_type(&'static str)
.kernel(Kernel)
.apply_slice(&[f64])
DisparityIndexBuilder::new()
.apply(&Candles)
DisparityIndexBuilder::new()
.into_stream()
// Stream
DisparityIndexStream::try_new(DisparityIndexParams)
DisparityIndexStream::update(f64) -> Option<f64>
DisparityIndexStream::reset()
DisparityIndexStream::get_warmup_period() -> usize
// Batch
DisparityIndexBatchBuilder::new()
.ema_period_range(start, end, step)
.lookback_period_range(start, end, step)
.smoothing_period_range(start, end, step)
.smoothing_types(iterable)
.kernel(Kernel)
.apply_slice(&[f64])
DisparityIndexBatchBuilder::new()
.apply_candles(&Candles) Error Handling ▼
pub enum DisparityIndexError {
EmptyInputData,
AllValuesNaN,
InvalidEmaPeriod { ema_period: usize },
InvalidLookbackPeriod { lookback_period: usize },
InvalidSmoothingPeriod { smoothing_period: usize },
InvalidSmoothingType { smoothing_type: String },
NotEnoughValidData { needed: usize, valid: usize },
OutputLengthMismatch { expected: usize, got: usize },
InvalidRange { start: usize, end: usize, step: usize },
InvalidKernelForBatch(Kernel),
MismatchedOutputLen { dst_len: usize, expected_len: usize },
InvalidInput { msg: String },
} Python Bindings
Python exposes an array-returning single-run function, a streaming class, and a batch function. The single-run binding returns one NumPy array of oscillator values. Batch returns the flattened values matrix reshaped to rows-by-cols, plus the tested EMA periods, lookback periods, smoothing periods, and smoothing types.
import numpy as np
from vector_ta import (
disparity_index,
disparity_index_batch,
DisparityIndexStream,
)
data = np.asarray(close_values, dtype=np.float64)
values = disparity_index(
data,
ema_period=14,
lookback_period=14,
smoothing_period=9,
smoothing_type="ema",
kernel="auto",
)
stream = DisparityIndexStream(
ema_period=14,
lookback_period=14,
smoothing_period=9,
smoothing_type="sma",
)
print(stream.update(data[-1]))
print(stream.warmup_period)
batch = disparity_index_batch(
data,
ema_period_range=(10, 20, 5),
lookback_period_range=(10, 20, 5),
smoothing_period_range=(5, 9, 4),
smoothing_types=["ema", "sma"],
kernel="auto",
)
print(batch["rows"], batch["cols"])
print(batch["ema_periods"], batch["smoothing_types"]) JavaScript/WASM Bindings
The WASM layer exposes a direct array-returning single-run wrapper, an object-returning batch wrapper, and
lower-level allocation and in-place exports. The JavaScript helpers accept the smoothing type as a string for the
high-level wrappers. The lower-level in-place exports use smoothing type codes, where 0 means
"ema" and 1 means "sma".
import init, {
disparity_index_js,
disparity_index_batch_js,
} from "/pkg/vector_ta.js";
await init();
const data = new Float64Array(closeValues);
const values = disparity_index_js(data, 14, 14, 9, "ema");
console.log(values);
const batch = disparity_index_batch_js(data, {
ema_period_range: [10, 20, 5],
lookback_period_range: [10, 20, 5],
smoothing_period_range: [5, 9, 4],
smoothing_types: ["ema", "sma"],
});
console.log(batch.values, batch.combos, batch.rows, batch.cols); CUDA Bindings (Rust)
Additional details for the CUDA bindings can be found inside the VectorTA repository.
Performance Analysis
Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)