Disparity Index

Parameters: 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, and smoothing_period must each be greater than zero.
  • smoothing_type must be either "ema" or "sma".
  • The longest contiguous finite run must be at least ema_period + lookback_period + smoothing_period - 2 bars.
  • Warmup lasts for ema_period + lookback_period + smoothing_period - 3 bars, which is also what the streaming API reports through get_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

Comparison:
View:
Placeholder data (no recorded benchmarks for this indicator)

Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)

Related Indicators