Average Directional Index (ADX)
period = 14 Overview
ADX quantifies trend strength on a 0 to 100 scale by measuring the spread between positive and negative directional indicators, which track whether each bar's range extends beyond the previous bar's boundaries. J. Welles Wilder developed this system to identify when markets exhibit persistent directional movement versus choppy consolidation. The calculation first determines directional movement by comparing consecutive highs and lows, then smooths these values using Wilder's averaging method before computing their relative difference. Values above 25 signal strong trending conditions where momentum strategies excel, while readings below 20 indicate sideways movement better suited for range trading tactics. As ADX rises, trend strength increases regardless of whether price moves up or down, making it essential for adapting strategy selection to prevailing market dynamics.
Implementation Examples
Get started with ADX in just a few lines:
use vectorta::indicators::adx::{adx, AdxInput, AdxParams};
use vectorta::utilities::data_loader::Candles;
// Using with price data slices (high, low, close)
let high = vec![45.0, 46.2, 45.8, 47.0, 48.5, 47.2];
let low = vec![43.5, 44.0, 43.8, 44.5, 45.0, 44.8];
let close = vec![44.5, 45.5, 44.8, 46.2, 47.5, 46.0];
let params = AdxParams {
period: Some(14),
};
let input = AdxInput::from_slices(&high, &low, &close, params);
let result = adx(&input)?;
// Using with Candles data structure
// Quick and simple with default parameters
let input = AdxInput::with_default_candles(&candles);
let result = adx(&input)?;
// Access the ADX values
for value in result.values {
println!("ADX: {:.2}", value);
} Implementation Examples
Compute ADX from candles or H/L/C slices:
use vectorta::indicators::adx::{adx, AdxInput, AdxParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// From candle data (defaults: period=14)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = AdxInput::with_default_candles(&candles);
let output = adx(&input)?;
// From H/L/C slices with custom period
let (high, low, close) = (&candles.high, &candles.low, &candles.close);
let params = AdxParams { period: Some(14) };
let input = AdxInput::from_slices(high, low, close, params);
let output = adx(&input)?;
// ADX values (0..=100), NaN during warmup
for v in output.values { println!("ADX: {}", v); } API Reference
Input Methods ▼
// From price slices (high, low, close)
AdxInput::from_slices(&[f64], &[f64], &[f64], AdxParams) -> AdxInput
// From candles
AdxInput::from_candles(&Candles, AdxParams) -> AdxInput
// From candles with default params (period=14)
AdxInput::with_default_candles(&Candles) -> AdxInput Parameters Structure ▼
pub struct AdxParams {
pub period: Option<usize>, // Default: 14 (Wilder's recommendation)
} Output Structure ▼
pub struct AdxOutput {
pub values: Vec<f64>, // ADX values (0-100 scale)
}
// Note: ADX requires 2*period-1 bars for initial calculation
// Earlier values will be NaN Error Handling ▼
use vectorta::indicators::adx::AdxError;
match adx(&input) {
Ok(output) => process_results(output.values),
Err(AdxError::EmptyInputData) =>
println!("Input data is empty"),
Err(AdxError::InconsistentLengths) =>
println!("High, low, and close arrays must have same length"),
Err(AdxError::InvalidPeriod { period, data_len }) =>
println!("Period {} is invalid for data length {}", period, data_len),
Err(AdxError::NotEnoughValidData { needed, valid }) =>
println!("Need {} data points, only {} valid", needed, valid),
Err(AdxError::CandleFieldError { field }) =>
println!("Cannot access field: {}", field),
Err(e) => println!("ADX error: {}", e)
} Python Bindings
Basic Usage ▼
Calculate ADX using NumPy arrays:
import numpy as np
from vectorta import adx
# Prepare OHLC data as NumPy arrays
high = np.array([45.0, 46.2, 45.8, 47.0, 48.5, 47.2])
low = np.array([43.5, 44.0, 43.8, 44.5, 45.0, 44.8])
close = np.array([44.5, 45.5, 44.8, 46.2, 47.5, 46.0])
# Calculate ADX with default parameters (period=14)
result = adx(high, low, close)
# Or specify custom period
result = adx(high, low, close, period=20)
# Specify kernel for performance optimization
result = adx(high, low, close, period=14, kernel="avx2")
# Result is a NumPy array matching input length
print(f"ADX values: {result}")
# Filter for strong trends
strong_trends = result > 25
print(f"Strong trend periods: {np.where(strong_trends)[0]}") Streaming Real-time Updates ▼
Process real-time OHLC updates efficiently:
from vectorta import AdxStream
# Initialize streaming ADX calculator
stream = AdxStream(period=14)
# Process real-time OHLC updates
for bar in price_feed:
adx_value = stream.update(bar['high'], bar['low'], bar['close'])
if adx_value is not None:
# ADX value is ready (None during warmup period)
print(f"Current ADX: {adx_value:.2f}")
# Adapt strategy based on market condition
if adx_value > 25:
print("Strong trend - use trend-following strategy")
# Activate breakout or momentum strategies
elif adx_value < 20:
print("Ranging market - use mean reversion strategy")
# Activate oscillator-based strategies
else:
print("Transitional phase - reduce position size") Batch Parameter Optimization ▼
Test multiple period values for optimization:
import numpy as np
from vectorta import adx_batch
# Your OHLC data
high = np.array([...]) # Your historical high prices
low = np.array([...]) # Your historical low prices
close = np.array([...]) # Your historical close prices
# Define parameter range to test
# (start, end, step) for period parameter
period_range = (7, 28, 7) # Test: 7, 14, 21, 28
# Run batch calculation
results = adx_batch(
high, low, close,
period_range=period_range,
kernel="auto" # Auto-select best kernel
)
# Access results
print(f"Values shape: {results['values'].shape}") # (num_periods, len(prices))
print(f"Periods tested: {results['periods']}")
# Find optimal period based on trend detection
for i, period in enumerate(results['periods']):
adx_values = results['values'][i]
avg_strength = np.nanmean(adx_values)
trend_percent = np.sum(adx_values > 25) / len(adx_values) * 100
print(f"Period {period}: Avg ADX={avg_strength:.2f}, "
f"Trending {trend_percent:.1f}% of time") CUDA Acceleration ▼
CUDA support for ADX is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.
# Coming soon: CUDA-accelerated ADX calculations
#
# from vectorta import adx_cuda_batch, adx_cuda_many_series_one_param
# import numpy as np
#
# # Option 1: One Series, Many Parameters (parameter optimization)
# # Test multiple periods on a single asset
# results = adx_cuda_batch(
# high=high_data,
# low=low_data,
# close=close_data,
# period_range=(7, 50, 1), # Test all periods from 7 to 50
# device_id=0
# )
# # Returns:
# # - results['values']: 2D array [num_periods x len(prices)]
# # - results['periods']: array of periods tested
#
# # Option 2: Many Series, One Parameter (portfolio processing)
# # Process multiple assets with the same ADX parameters
# portfolio_high = np.array([...]) # Shape: [time_steps, num_assets]
# portfolio_low = np.array([...]) # Shape: [time_steps, num_assets]
# portfolio_close = np.array([...]) # Shape: [time_steps, num_assets]
#
# results = adx_cuda_many_series_one_param(
# high_tm=portfolio_high, # Time-major format [T, N]
# low_tm=portfolio_low,
# close_tm=portfolio_close,
# period=14,
# device_id=0
# )
# # Returns: 2D array [time_steps, num_assets] with ADX for each asset
#
# # Zero-copy variant with pre-allocated output (F32 for GPU efficiency)
# out = np.empty((time_steps, num_assets), dtype=np.float32)
# adx_cuda_many_series_one_param_into(
# high_tm_f32=portfolio_high.astype(np.float32),
# low_tm_f32=portfolio_low.astype(np.float32),
# close_tm_f32=portfolio_close.astype(np.float32),
# period=14,
# out=out,
# device_id=0
# ) JavaScript/WASM Bindings
Basic Usage ▼
Calculate ADX in JavaScript/TypeScript:
import { adx_js } from 'vectorta-wasm';
// OHLC data as Float64Arrays or regular arrays
const high = new Float64Array([45.0, 46.2, 45.8, 47.0, 48.5, 47.2]);
const low = new Float64Array([43.5, 44.0, 43.8, 44.5, 45.0, 44.8]);
const close = new Float64Array([44.5, 45.5, 44.8, 46.2, 47.5, 46.0]);
// Calculate ADX with specified period
const result = adx_js(high, low, close, 14); // period=14
// Result is a Float64Array
console.log('ADX values:', result);
// TypeScript type definitions
interface AdxResult {
values: Float64Array;
}
// Use with async/await for better error handling
async function calculateADX(
high: Float64Array,
low: Float64Array,
close: Float64Array
): Promise<Float64Array> {
try {
return adx_js(high, low, close, 14);
} catch (error) {
console.error('ADX calculation failed:', error);
throw error;
}
}
// Identify market conditions
result.forEach((adx, i) => {
if (adx > 25) {
console.log(`Bar ${i}: Strong trend (ADX=${adx.toFixed(2)})`);
} else if (adx < 20 && !isNaN(adx)) {
console.log(`Bar ${i}: Ranging market (ADX=${adx.toFixed(2)})`);
}
}); Memory-Efficient Operations ▼
Use zero-copy operations for better performance with large datasets:
import { adx_alloc, adx_free, adx_into, memory } from 'vectorta-wasm';
// Prepare your OHLC data
const high = new Float64Array([/* your data */]);
const low = new Float64Array([/* your data */]);
const close = new Float64Array([/* your data */]);
const length = high.length;
// Allocate WASM memory for output
const outputPtr = adx_alloc(length);
// Get input data pointers in WASM memory
const highArray = new Float64Array(memory.buffer, /* offset */, length);
const lowArray = new Float64Array(memory.buffer, /* offset */, length);
const closeArray = new Float64Array(memory.buffer, /* offset */, length);
highArray.set(high);
lowArray.set(low);
closeArray.set(close);
// Calculate ADX directly into allocated memory (zero-copy)
adx_into(
highArray.byteOffset, // High pointer
lowArray.byteOffset, // Low pointer
closeArray.byteOffset, // Close pointer
outputPtr, // Output pointer
length, // Data length
14 // Period
);
// Read results from WASM memory
const result = new Float64Array(memory.buffer, outputPtr, length);
const adxValues = Array.from(result); // Convert to regular array if needed
// Important: Free allocated memory when done
adx_free(outputPtr, length);
console.log('ADX values:', adxValues); Batch Processing ▼
Test multiple period values efficiently:
import { adx_batch_js, adx_batch_metadata_js } from 'vectorta-wasm';
// Your OHLC data
const high = new Float64Array([/* historical high prices */]);
const low = new Float64Array([/* historical low prices */]);
const close = new Float64Array([/* historical close prices */]);
// Define parameter sweep range
const periodStart = 7, periodEnd = 28, periodStep = 7; // 7, 14, 21, 28
// Get metadata about parameter combinations
const metadata = adx_batch_metadata_js(
periodStart, periodEnd, periodStep
);
// metadata contains [period1, period2, ...]
const numPeriods = metadata.length;
// Calculate all combinations
const results = adx_batch_js(
high, low, close,
periodStart, periodEnd, periodStep
);
// Results is a flat array: [period1_values..., period2_values..., ...]
// Reshape based on your needs
const resultMatrix = [];
for (let i = 0; i < numPeriods; i++) {
const start = i * high.length;
const end = start + high.length;
resultMatrix.push(results.slice(start, end));
}
// Analyze results for each period
metadata.forEach((period, i) => {
const adxValues = resultMatrix[i];
const validValues = adxValues.filter(v => !isNaN(v));
const avgADX = validValues.reduce((a, b) => a + b, 0) / validValues.length;
const strongTrendPercent = validValues.filter(v => v > 25).length / validValues.length * 100;
console.log(`Period ${period}: Avg ADX=${avgADX.toFixed(2)}, Strong trend ${strongTrendPercent.toFixed(1)}% of time`);
}); Performance Analysis
Across sizes, Rust CPU runs about 1.16× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
Related Indicators
Accumulation/Distribution
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