Normalized Volume True Range

Parameters: true_range_style = body | outlier_range = 5 | atr_length = 14 | volume_length = 14

Overview

Normalized Volume True Range measures how unusual the current bar is by comparing its volume and true range with rolling baselines. The true-range component can be based on candle body size, full high-low span, or close-to-close delta, and the volume side is normalized against a rolling average and positive deviation estimate.

The output includes normalized volume, normalized true range, a combined baseline, and the underlying ATR and average-volume series that support the normalization. It accepts candles or aligned OHLCV slices, and the stream version returns five values for each update while exposing a full reset path.

Defaults: `true_range_style = "body"`, `outlier_range = 5.0`, `atr_length = 14`, and `volume_length = 14`.

Implementation Examples

Run the indicator on candles or on aligned OHLCV slices.

use vector_ta::indicators::normalized_volume_true_range::{
    normalized_volume_true_range,
    NormalizedVolumeTrueRangeInput,
    NormalizedVolumeTrueRangeParams,
    NormalizedVolumeTrueRangeStyle,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let output = normalized_volume_true_range(&NormalizedVolumeTrueRangeInput::from_slices(
    &open,
    &high,
    &low,
    &close,
    &volume,
    NormalizedVolumeTrueRangeParams {
        true_range_style: Some(NormalizedVolumeTrueRangeStyle::Body),
        outlier_range: Some(5.0),
        atr_length: Some(14),
        volume_length: Some(14),
    },
))?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = normalized_volume_true_range(
    &NormalizedVolumeTrueRangeInput::with_default_candles(&candles),
)?;

println!("normalized volume = {:?}", output.normalized_volume.last());
println!("baseline = {:?}", candle_output.baseline.last());

API Reference

Input Methods
NormalizedVolumeTrueRangeInput::from_candles(&Candles, NormalizedVolumeTrueRangeParams)
    -> NormalizedVolumeTrueRangeInput

NormalizedVolumeTrueRangeInput::from_slices(
    &[f64], &[f64], &[f64], &[f64], &[f64],
    NormalizedVolumeTrueRangeParams,
) -> NormalizedVolumeTrueRangeInput

NormalizedVolumeTrueRangeInput::with_default_candles(&Candles)
    -> NormalizedVolumeTrueRangeInput
Parameters Structure
pub enum NormalizedVolumeTrueRangeStyle {
    Body,
    Hl,
    Delta,
}

pub struct NormalizedVolumeTrueRangeParams {
    pub true_range_style: Option<NormalizedVolumeTrueRangeStyle>, // default Body
    pub outlier_range: Option<f64>,                               // default 5.0
    pub atr_length: Option<usize>,                               // default 14
    pub volume_length: Option<usize>,                            // default 14
}
Output Structure
pub struct NormalizedVolumeTrueRangeOutput {
    pub normalized_volume: Vec<f64>,
    pub normalized_true_range: Vec<f64>,
    pub baseline: Vec<f64>,
    pub atr: Vec<f64>,
    pub average_volume: Vec<f64>,
}
Validation, Warmup & Data Rules
  • Open, high, low, close, and volume inputs must have matching lengths and must not be empty.
  • At least one full OHLCV row must be finite; otherwise the indicator returns AllValuesNaN.
  • outlier_range must be finite and at least 0.5.
  • atr_length and volume_length must each be at least 2.
  • The accepted styles are body, hl, and delta.
  • Batch mode validates the sweep tuples for ATR and volume lengths and rejects unsupported kernels.
  • The streaming implementation reuses the same validated parameters and exposes reset() for a clean restart.
Builder, Streaming & Batch APIs
NormalizedVolumeTrueRangeBuilder::new()
    .true_range_style(NormalizedVolumeTrueRangeStyle)
    .outlier_range(f64)
    .atr_length(usize)
    .volume_length(usize)
    .kernel(Kernel)
    .apply(&Candles)
    .apply_slices(&open, &high, &low, &close, &volume)
    .into_stream()

NormalizedVolumeTrueRangeStream::try_new(params)
stream.update(open, high, low, close, volume)
    -> Option<(f64, f64, f64, f64, f64)>
stream.reset()

NormalizedVolumeTrueRangeBatchBuilder::new()
    .outlier_range_range(start, end, step)
    .atr_length_range(start, end, step)
    .volume_length_range(start, end, step)
    .true_range_style(NormalizedVolumeTrueRangeStyle)
    .kernel(Kernel)
    .apply_slices(&open, &high, &low, &close, &volume)
    .apply(&Candles)

Python Bindings

Python exposes a scalar OHLCV function, a stream class, and a batch helper. The scalar call returns five NumPy arrays, the stream yields five scalars or None, and the batch helper returns reshaped matrices along with the swept outlier, ATR, volume, and style metadata.

from vector_ta import (
    normalized_volume_true_range,
    normalized_volume_true_range_batch,
    NormalizedVolumeTrueRangeStream,
)

normalized_volume, normalized_true_range, baseline, atr, average_volume = (
    normalized_volume_true_range(
        open,
        high,
        low,
        close,
        volume,
        true_range_style="body",
        outlier_range=5.0,
        atr_length=14,
        volume_length=14,
    )
)

stream = NormalizedVolumeTrueRangeStream("body", 5.0, 14, 14)
point = stream.update(open[-1], high[-1], low[-1], close[-1], volume[-1])

batch = normalized_volume_true_range_batch(
    open,
    high,
    low,
    close,
    volume,
    outlier_range_range=(4.0, 6.0, 1.0),
    atr_length_range=(10, 20, 5),
    volume_length_range=(10, 20, 5),
    true_range_style="body",
)

print(batch.keys())
# dict_keys(['normalized_volume', 'normalized_true_range', 'baseline', 'atr',
#            'average_volume', 'outlier_ranges', 'atr_lengths',
#            'volume_lengths', 'true_range_styles', 'rows', 'cols'])

JavaScript/WASM Bindings

The WASM layer includes high-level scalar and batch entry points, low-level allocation helpers, and into-buffer exports for reusing caller memory. The high-level calls return plain JS objects, and the low-level functions write five output arrays into caller-managed memory.

import init, {
  normalized_volume_true_range_js,
  normalized_volume_true_range_batch,
  normalized_volume_true_range_alloc,
  normalized_volume_true_range_free,
  normalized_volume_true_range_into,
  normalized_volume_true_range_batch_into,
} from "vector-ta-wasm";

await init();

const out = normalized_volume_true_range_js(
  open,
  high,
  low,
  close,
  volume,
  "body",
  5.0,
  14,
  14,
);

const batch = normalized_volume_true_range_batch(open, high, low, close, volume, {
  true_range_style: "body",
  outlier_range_range: [4.0, 6.0, 1.0],
  atr_length_range: [10, 20, 5],
  volume_length_range: [10, 20, 5],
});

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