Vortex Indicator (VI)
period = 14 Overview
The Vortex Indicator captures directional movement by measuring the relationship between current price extremes and prior period highs and lows, revealing the strength of upward versus downward vortex flows. The indicator produces two lines: VI+ quantifies positive vortex movement by comparing each high to the previous low, while VI- measures negative movement by comparing each low to the previous high. Both lines are normalized by the rolling sum of True Range to create ratio values that remain comparable across different price levels and volatility regimes. Crossovers between VI+ and VI- signal potential trend changes, with VI+ crossing above VI- indicating emerging upward momentum and the reverse suggesting downward pressure. The indicator proves particularly effective at identifying the start of new trends, as vortex movements often accelerate before price itself makes decisive directional moves. Traders also watch for divergences where vortex strength weakens while price continues trending, often foreshadowing reversals. Default period is 14 bars, providing balanced sensitivity to both short term movements and underlying directional flow.
Implementation Examples
Compute VI from slices or candles:
use vectorta::indicators::vi::{vi, ViInput, ViParams, ViOutput};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// From high/low/close slices
let high = vec![/* ... */];
let low = vec![/* ... */];
let close = vec![/* ... */];
let params = ViParams { period: Some(14) }; // default 14
let input = ViInput::from_slices(&high, &low, &close, params);
let ViOutput { plus, minus } = vi(&input)?;
// From Candles with default params (period=14)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = ViInput::with_default_candles(&candles);
let ViOutput { plus, minus } = vi(&input)?;
API Reference
Input Methods ▼
// From H/L/C slices
ViInput::from_slices(&[f64], &[f64], &[f64], ViParams) -> ViInput
// From candles (uses sources "high", "low", "close")
ViInput::from_candles(&Candles, ViParams) -> ViInput
// With defaults (period=14)
ViInput::with_default_candles(&Candles) -> ViInput Parameters Structure ▼
pub struct ViParams {
pub period: Option<usize>, // Default: 14
} Output Structure ▼
pub struct ViOutput {
pub plus: Vec<f64>, // VI+
pub minus: Vec<f64>, // VI-
} Validation, Warmup & NaNs ▼
period > 0andperiod ≤ len; otherwiseViError::InvalidPeriod.- Inputs must be non-empty and equal length; otherwise
ViError::EmptyData. - First valid index is the earliest where H/L/C are all finite; if none,
ViError::AllValuesNaN. - If
len - first_valid < period, returnsViError::NotEnoughValidData. - Warmup: elements before
first_valid + period - 1areNaN. First defined outputs occur at that index. - Streaming returns
Noneuntil the window fills. Streaming update requires previous bar values (Ht-1/Lt-1/Ct-1).
Error Handling ▼
use vectorta::indicators::vi::{vi, ViError};
match vi(&input) {
Ok(out) => {
process(out.plus);
process(out.minus);
}
Err(ViError::EmptyData) => eprintln!("no data"),
Err(ViError::InvalidPeriod { period, data_len }) => {
eprintln!("invalid period: {} (len={})", period, data_len)
}
Err(ViError::NotEnoughValidData { needed, valid }) => {
eprintln!("need {} valid bars, have {}", needed, valid)
}
Err(ViError::AllValuesNaN) => eprintln!("all values NaN"),
}
Python Bindings
Basic Usage ▼
import numpy as np
from vectorta import vi
high = np.array([/* ... */], dtype=np.float64)
low = np.array([/* ... */], dtype=np.float64)
close = np.array([/* ... */], dtype=np.float64)
res = vi(high, low, close, period=14, kernel="auto")
plus = res["plus"]
minus = res["minus"]
Streaming ▼
from vectorta import ViStream
stream = ViStream(period=14)
vi_points = []
for h, l, c in zip(high, low, close):
out = stream.update(h, l, c) # returns (vi_plus, vi_minus) or None until warmed up
if out is not None:
vi_points.append(out)
Batch Processing ▼
import numpy as np
from vectorta import vi_batch
# Sweep period from 7 to 28 by 7
out = vi_batch(high, low, close, period_range=(7, 28, 7), kernel="auto")
# Shapes: plus/minus are (rows, cols)
plus_matrix = out["plus"]
minus_matrix = out["minus"]
periods = out["periods"]
# Select the row for period=14
row = list(periods).index(14)
vi_plus_14 = plus_matrix[row]
vi_minus_14 = minus_matrix[row]
CUDA Acceleration ▼
CUDA support for VI is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.
# Coming soon: CUDA-accelerated VI computations
# Pattern: vi_cuda_batch(...) and vi_cuda_many_series_one_param(...)
# matching other indicators in this library. JavaScript/WASM Bindings
Basic Usage ▼
Calculate VI in JS/TS:
import { vi_js } from 'vectorta-wasm';
const high = new Float64Array([/* ... */]);
const low = new Float64Array([/* ... */]);
const close = new Float64Array([/* ... */]);
const { plus, minus } = vi_js(high, low, close, 14);
console.log('VI+:', plus);
console.log('VI-:', minus); Memory-Efficient Operations ▼
Use zero-copy style with manual memory management:
import { vi_alloc, vi_free, vi_into, memory } from 'vectorta-wasm';
const n = high.length;
// Allocate a single buffer for both outputs and split it
const basePtr = vi_alloc(n);
const plusPtr = basePtr; // first n slots
const minusPtr = basePtr + n * 8; // second n slots (Float64 = 8 bytes)
// Input arrays can be passed directly when using vi_into
vi_into(highPtr, lowPtr, closePtr, plusPtr, minusPtr, n, 14);
const viPlus = new Float64Array(memory.buffer, plusPtr, n).slice();
const viMinus = new Float64Array(memory.buffer, minusPtr, n).slice();
vi_free(basePtr, n); Batch Processing ▼
Sweep period values and reshape results:
import { vi_batch_js } from 'vectorta-wasm';
const cfg = { period_range: [7, 28, 7] };
const result = vi_batch_js(high, low, close, cfg);
const { plus, minus, periods, rows, cols } = result;
// plus/minus are flat arrays (rows * cols)
const plusMatrix: number[][] = [];
for (let r = 0; r < rows; r++) {
const start = r * cols;
plusMatrix.push(plus.slice(start, start + cols));
}
Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
Related Indicators
Average Directional Index
Technical analysis indicator
Average Directional Movement Index Rating
Technical analysis indicator
Alligator
Technical analysis indicator
Aroon
Technical analysis indicator
Aroon Oscillator
Technical analysis indicator
Chande Momentum Oscillator
Technical analysis indicator