Accumulation/Distribution Line (A/D)
Overview
The Accumulation/Distribution Line tracks cumulative money flow by multiplying volume with a close location value that measures where price closed within its daily range. When price closes near the high, most of the volume counts as accumulation; conversely, closes near the low register as distribution. Each period's money flow value adds to a running total, creating a line that rises when buyers dominate and falls during distribution phases. Divergences between A/D and price reveal whether volume confirms the current trend or signals an impending reversal. Traders watch for the A/D line making higher highs while price stalls, indicating accumulation before a breakout, or lower lows during price advances, warning of distribution before a decline.
Implementation Examples
Get started with A/D in just a few lines:
use vector_ta::indicators::ad::{ad, AdInput, AdParams};
use vector_ta::utilities::data_loader::Candles;
// Using with OHLCV slices
let high = vec![100.0, 102.0, 101.5, 103.0, 105.0];
let low = vec![98.0, 99.0, 100.0, 101.0, 103.0];
let close = vec![99.5, 101.5, 100.5, 102.5, 104.5];
let volume = vec![10000.0, 12000.0, 11000.0, 13000.0, 15000.0];
let input = AdInput::from_slices(&high, &low, &close, &volume, AdParams::default());
let result = ad(&input)?;
// Using with Candles data structure
// Quick and simple with default parameters
let input = AdInput::with_default_candles(&candles);
let result = ad(&input)?;
// Access the A/D values
for (i, value) in result.values.iter().enumerate() {
println!("A/D at {}: {}", i, value);
} API Reference
Input Methods ▼
// From OHLCV slices
AdInput::from_slices(&[f64], &[f64], &[f64], &[f64], AdParams) -> AdInput
// From candles structure
AdInput::from_candles(&Candles, AdParams) -> AdInput
// From candles with default params (no parameters for A/D)
AdInput::with_default_candles(&Candles) -> AdInput Parameters Structure ▼
pub struct AdParams {} // No adjustable parameters for A/D Output Structure ▼
pub struct AdOutput {
pub values: Vec<f64>, // Cumulative A/D values
} Error Handling ▼
use vector_ta::indicators::ad::AdError;
match ad(&input) {
Ok(output) => process_results(output.values),
Err(AdError::CandleFieldError(msg)) =>
println!("Candle field error: {}", msg),
Err(AdError::DataLengthMismatch { high_len, low_len, close_len, volume_len }) =>
println!("Data arrays must have equal length: H:{} L:{} C:{} V:{}",
high_len, low_len, close_len, volume_len),
Err(AdError::EmptyInputData) =>
println!("Input arrays are empty"),
Err(e) => println!("A/D error: {}", e)
} Python Bindings
Basic Usage ▼
Calculate A/D using NumPy arrays:
import numpy as np
from vector_ta import ad
# Prepare OHLCV data as NumPy arrays
high = np.array([100.0, 102.0, 101.5, 103.0, 105.0])
low = np.array([98.0, 99.0, 100.0, 101.0, 103.0])
close = np.array([99.5, 101.5, 100.5, 102.5, 104.5])
volume = np.array([10000.0, 12000.0, 11000.0, 13000.0, 15000.0])
# Calculate A/D (no parameters needed)
result = ad(high, low, close, volume)
# Specify kernel for performance optimization
result = ad(high, low, close, volume, kernel="avx2")
# Result is a NumPy array matching input length
print(f"A/D values: {result}")
# Detect divergences
price_trend = close[-1] > close[0]
ad_trend = result[-1] > result[0]
if price_trend != ad_trend:
print("Divergence detected!") Streaming Real-time Updates ▼
Process real-time OHLCV updates efficiently:
from vector_ta import AdStream
# Initialize streaming A/D calculator
stream = AdStream()
# Process real-time OHLCV updates
for tick in market_data_feed:
ad_value = stream.update(
tick.high,
tick.low,
tick.close,
tick.volume
)
print(f"Current A/D: {ad_value}")
# Track changes for trading signals
if hasattr(stream, 'previous_ad'):
change = ad_value - stream.previous_ad
if abs(change) > threshold:
if change > 0:
print(f"Accumulation detected: +{change}")
else:
print(f"Distribution detected: {change}")
stream.previous_ad = ad_value Batch Processing Multiple Securities ▼
Process multiple securities efficiently in parallel:
import numpy as np
from vector_ta import ad_batch
# Prepare OHLCV data for multiple securities
# Each list contains arrays for different securities
highs = [high_stock1, high_stock2, high_stock3] # List of NumPy arrays
lows = [low_stock1, low_stock2, low_stock3]
closes = [close_stock1, close_stock2, close_stock3]
volumes = [volume_stock1, volume_stock2, volume_stock3]
# Calculate A/D for all securities in parallel
results = ad_batch(
highs,
lows,
closes,
volumes,
kernel="auto" # Auto-select best kernel
)
# Access results
print(f"Values shape: {results['values'].shape}") # (num_securities, time_steps)
print(f"Rows (securities): {results['rows']}")
print(f"Cols (time steps): {results['cols']}")
# Analyze each security
for i in range(results['rows']):
ad_values = results['values'][i]
# Find strongest accumulation
max_ad = np.max(ad_values)
max_idx = np.argmax(ad_values)
print(f"Security {i}: Peak accumulation of {max_ad} at index {max_idx}")
# Calculate rate of change
if len(ad_values) > 1:
daily_change = np.diff(ad_values)
avg_change = np.mean(daily_change)
print(f"Security {i}: Average daily A/D change: {avg_change}") CUDA Acceleration ▼
CUDA helpers are available when the Python package is built with CUDA support. Inputs must be float32; outputs are device arrays (DLPack / __cuda_array_interface__ compatible).
import numpy as np
from vector_ta import ad_cuda_dev, ad_cuda_many_series_one_param_dev
# One series (float32)
high_f32 = np.asarray(load_high(), dtype=np.float32)
low_f32 = np.asarray(load_low(), dtype=np.float32)
close_f32 = np.asarray(load_close(), dtype=np.float32)
volume_f32 = np.asarray(load_volume(), dtype=np.float32)
dev = ad_cuda_dev(
high_f32=high_f32,
low_f32=low_f32,
close_f32=close_f32,
volume_f32=volume_f32,
device_id=0,
)
# Many series (time-major)
high_tm_f32 = np.asarray(load_high_time_major_matrix(), dtype=np.float32)
rows, cols = high_tm_f32.shape
high_tm_f32 = high_tm_f32.ravel()
low_tm_f32 = np.asarray(load_low_time_major_matrix(), dtype=np.float32)
low_tm_f32 = low_tm_f32.ravel()
close_tm_f32 = np.asarray(load_close_time_major_matrix(), dtype=np.float32)
close_tm_f32 = close_tm_f32.ravel()
volume_tm_f32 = np.asarray(load_volume_time_major_matrix(), dtype=np.float32)
volume_tm_f32 = volume_tm_f32.ravel()
dev_tm = ad_cuda_many_series_one_param_dev(
high_tm_f32=high_tm_f32,
low_tm_f32=low_tm_f32,
close_tm_f32=close_tm_f32,
volume_tm_f32=volume_tm_f32,
cols=cols,
rows=rows,
device_id=0,
) JavaScript/WASM Bindings
Basic Usage ▼
Calculate A/D in JavaScript/TypeScript:
import { ad_js } from 'vectorta-wasm';
// OHLCV data as Float64Array or regular array
const high = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0]);
const low = new Float64Array([98.0, 99.0, 100.0, 101.0, 103.0]);
const close = new Float64Array([99.5, 101.5, 100.5, 102.5, 104.5]);
const volume = new Float64Array([10000, 12000, 11000, 13000, 15000]);
// Calculate A/D (no parameters needed)
const result = ad_js(high, low, close, volume);
// Result is a Float64Array
console.log('A/D values:', result);
// TypeScript type definitions
interface AdResult {
values: Float64Array;
}
// Use with async/await for better error handling
async function calculateAD(
high: Float64Array,
low: Float64Array,
close: Float64Array,
volume: Float64Array
): Promise<Float64Array> {
try {
return ad_js(high, low, close, volume);
} catch (error) {
console.error('A/D calculation failed:', error);
throw error;
}
} Memory-Efficient Operations ▼
Use zero-copy operations for better performance with large datasets:
import { ad_alloc, ad_free, ad_into, memory } from 'vectorta-wasm';
// Prepare your OHLCV data
const high = new Float64Array([/* your data */]);
const low = new Float64Array([/* your data */]);
const close = new Float64Array([/* your data */]);
const volume = new Float64Array([/* your data */]);
const length = high.length;
// Allocate WASM memory for output
const outputPtr = ad_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);
const volumeArray = new Float64Array(memory.buffer, /* offset */, length);
highArray.set(high);
lowArray.set(low);
closeArray.set(close);
volumeArray.set(volume);
// Calculate A/D directly into allocated memory (zero-copy)
ad_into(
highArray.byteOffset, // High pointer
lowArray.byteOffset, // Low pointer
closeArray.byteOffset, // Close pointer
volumeArray.byteOffset, // Volume pointer
outputPtr, // Output pointer
length // Data length
);
// Read results from WASM memory
const result = new Float64Array(memory.buffer, outputPtr, length);
const adValues = Array.from(result); // Convert to regular array if needed
// Important: Free allocated memory when done
ad_free(outputPtr, length);
console.log('A/D values:', adValues); Batch Processing ▼
Process multiple securities efficiently:
import { ad_batch_js, ad_batch_metadata_js } from 'vectorta-wasm';
// Prepare OHLCV data for multiple securities
// Flatten arrays: [security1_data, security2_data, ...]
const numSecurities = 3;
const timeSteps = 100;
const highs_flat = new Float64Array(numSecurities * timeSteps);
const lows_flat = new Float64Array(numSecurities * timeSteps);
const closes_flat = new Float64Array(numSecurities * timeSteps);
const volumes_flat = new Float64Array(numSecurities * timeSteps);
// Fill with your data...
for (let i = 0; i < numSecurities; i++) {
const offset = i * timeSteps;
highs_flat.set(securities[i].high, offset);
lows_flat.set(securities[i].low, offset);
closes_flat.set(securities[i].close, offset);
volumes_flat.set(securities[i].volume, offset);
}
// Get metadata about batch dimensions
const metadata = ad_batch_metadata_js(numSecurities, timeSteps);
console.log('Processing ' + metadata[0] + ' securities with ' + metadata[1] + ' time steps');
// Calculate A/D for all securities
const results = ad_batch_js(
highs_flat,
lows_flat,
closes_flat,
volumes_flat,
numSecurities
);
// Results is a flat array: [security1_values..., security2_values..., ...]
// Reshape for easier access
const resultMatrix = [];
for (let i = 0; i < numSecurities; i++) {
const start = i * timeSteps;
const end = start + timeSteps;
resultMatrix.push(results.slice(start, end));
}
// Analyze each security
resultMatrix.forEach((adValues, idx) => {
const lastAD = adValues[adValues.length - 1];
const firstAD = adValues[0];
const totalChange = lastAD - firstAD;
console.log('Security ' + idx + ': Total A/D change: ' + totalChange);
if (totalChange > 0) {
console.log('Security ' + idx + ': Net accumulation');
} else {
console.log('Security ' + idx + ': Net distribution');
}
}); CUDA Bindings (Rust)
use vector_ta::cuda::CudaAd;
let cuda = CudaAd::new(0)?;
let high: [f32] = /* ... */;
let low: [f32] = /* ... */;
let close: [f32] = /* ... */;
let volume: [f32] = /* ... */;
let out = cuda.ad_series_dev(&high, &low, &close, &volume)?;
let _ = out; Performance Analysis
Across sizes, Rust CPU runs about 2.31× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-08
Related Indicators
Accumulation/Distribution Oscillator
Technical analysis indicator
Average Directional Index
Technical analysis indicator
Average Directional Movement Index Rating
Technical analysis indicator
Balance of Power
Technical analysis indicator
Chaikin Flow Oscillator
Technical analysis indicator
Elder Force Index
Technical analysis indicator