Chaikin Accumulation/Distribution Oscillator (ADOSC)
short_period = 3 (1–50) | long_period = 10 (2–200) Overview
The Chaikin Oscillator measures the momentum of the Accumulation/Distribution Line by calculating the difference between its 3 period and 10 period exponential moving averages. Marc Chaikin developed this oscillator to identify when the pace of accumulation or distribution accelerates, providing earlier signals than the A/D line itself. Positive values indicate that short term money flow exceeds the longer term average, suggesting increasing buying pressure. Negative readings signal distribution as recent volume weighted momentum turns bearish. The oscillator crosses above zero when accumulation accelerates and drops below when distribution takes hold. Traders use ADOSC to confirm price breakouts with volume momentum and spot divergences where price trends higher while the oscillator falls, warning of weakening participation that often precedes reversals.
Implementation Examples
Get started with ADOSC in just a few lines:
use vectorta::indicators::adosc::{adosc, AdoscInput, AdoscParams};
use vectorta::utilities::data_loader::Candles;
// Using with OHLCV data slices
let high = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let low = vec![98.0, 99.0, 100.0, 101.0, 102.0, 103.0];
let close = vec![99.0, 101.0, 100.5, 102.5, 104.0, 103.5];
let volume = vec![10000.0, 12000.0, 11000.0, 15000.0, 13000.0, 14000.0];
let params = AdoscParams {
short_period: Some(3),
long_period: Some(10),
};
let input = AdoscInput::from_slices(&high, &low, &close, &volume, params);
let result = adosc(&input)?;
// Using with Candles data structure
// Quick and simple with default parameters (3/10)
let input = AdoscInput::with_default_candles(&candles);
let result = adosc(&input)?;
// Access the ADOSC values
for value in result.values {
println!("ADOSC: {}", value);
} API Reference
Input Methods ▼
// From OHLCV slices
AdoscInput::from_slices(&[f64], &[f64], &[f64], &[f64], AdoscParams) -> AdoscInput
// From candles with custom parameters
AdoscInput::from_candles(&Candles, AdoscParams) -> AdoscInput
// From candles with default params (short=3, long=10)
AdoscInput::with_default_candles(&Candles) -> AdoscInput Parameters Structure ▼
pub struct AdoscParams {
pub short_period: Option<usize>, // Default: 3
pub long_period: Option<usize>, // Default: 10
} Output Structure ▼
pub struct AdoscOutput {
pub values: Vec<f64>, // ADOSC values (short_ema - long_ema of A/D Line)
} Error Handling ▼
use vectorta::indicators::adosc::AdoscError;
match adosc(&input) {
Ok(output) => process_results(output.values),
Err(AdoscError::AllValuesNaN) =>
println!("All input values are NaN"),
Err(AdoscError::ShortPeriodGreaterThanLong { short, long }) =>
println!("Short period {} must be < long period {}", short, long),
Err(AdoscError::InvalidPeriod { short, long, data_len }) =>
println!("Invalid periods: short={}, long={}, data length={}", short, long, data_len),
Err(AdoscError::EmptySlices { high, low, close, volume }) =>
println!("Empty or mismatched slices: h={}, l={}, c={}, v={}", high, low, close, volume),
Err(e) => println!("ADOSC error: {}", e)
} Python Bindings
Basic Usage ▼
Calculate ADOSC using NumPy arrays:
import numpy as np
from vectorta import adosc
# Prepare OHLCV data as NumPy arrays
high = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5])
low = np.array([98.0, 99.0, 100.0, 101.0, 102.0, 103.0])
close = np.array([99.0, 101.0, 100.5, 102.5, 104.0, 103.5])
volume = np.array([10000.0, 12000.0, 11000.0, 15000.0, 13000.0, 14000.0])
# Calculate ADOSC with default parameters (3/10)
result = adosc(high, low, close, volume, short_period=3, long_period=10)
# Or specify custom parameters
result = adosc(high, low, close, volume, short_period=5, long_period=15)
# Specify kernel for performance optimization
result = adosc(high, low, close, volume, short_period=3, long_period=10, kernel="avx2")
# Result is a NumPy array matching input length
print(f"ADOSC values: {result}")
# Interpret the values
for i, val in enumerate(result):
if val > 0:
print(f"Bar {i}: Accumulation momentum (value: {val:.2f})")
else:
print(f"Bar {i}: Distribution momentum (value: {val:.2f})") Streaming Real-time Updates ▼
Process real-time OHLCV updates efficiently:
from vectorta import AdoscStream
# Initialize streaming ADOSC calculator
stream = AdoscStream(short_period=3, long_period=10)
# Process real-time market data
for tick in market_data_feed:
adosc_value = stream.update(
high=tick['high'],
low=tick['low'],
close=tick['close'],
volume=tick['volume']
)
# ADOSC value is always available (no warmup period)
print(f"Current ADOSC: {adosc_value:.2f}")
# Make trading decisions based on ADOSC
if adosc_value > 0 and prev_value <= 0:
print("Bullish crossover - accumulation momentum increasing")
elif adosc_value < 0 and prev_value >= 0:
print("Bearish crossover - distribution momentum increasing")
# Check for divergences
if price_making_new_high and adosc_value < previous_high_adosc:
print("Bearish divergence detected - potential reversal")
prev_value = adosc_value Batch Parameter Optimization ▼
Test multiple parameter combinations for optimization:
import numpy as np
from vectorta import adosc_batch
# Your OHLCV data
high = np.array([...]) # Historical high prices
low = np.array([...]) # Historical low prices
close = np.array([...]) # Historical close prices
volume = np.array([...]) # Historical volumes
# Define parameter ranges to test
# (start, end, step) for each parameter
short_range = (2, 5, 1) # Test: 2, 3, 4, 5
long_range = (8, 20, 2) # Test: 8, 10, 12, 14, 16, 18, 20
# Run batch calculation
results = adosc_batch(
high, low, close, volume,
short_period_range=short_range,
long_period_range=long_range,
kernel="auto" # Auto-select best kernel
)
# Access results
print(f"Values shape: {results['values'].shape}") # (num_combinations, len(data))
print(f"Short periods tested: {results['shorts']}")
print(f"Long periods tested: {results['longs']}")
# Find best performing parameters based on zero-line crossovers
crossover_counts = []
for i in range(results['values'].shape[0]):
values = results['values'][i]
# Count zero-line crossovers
crossovers = np.sum(np.diff(np.sign(values)) != 0)
crossover_counts.append(crossovers)
best_idx = np.argmax(crossover_counts)
best_short = results['shorts'][best_idx]
best_long = results['longs'][best_idx]
print(f"Best parameters: short={best_short}, long={best_long}") CUDA Acceleration ▼
CUDA support for ADOSC is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.
# Coming soon: CUDA-accelerated ADOSC calculations
#
# from vectorta import adosc_cuda_batch, adosc_cuda_many_series_one_param
# import numpy as np
#
# # Option 1: One Series, Many Parameters (parameter optimization)
# # Test multiple parameter combinations on a single OHLCV series
# results = adosc_cuda_batch(
# high=high_prices,
# low=low_prices,
# close=close_prices,
# volume=volumes,
# short_period_range=(2, 5, 1), # Test all shorts from 2 to 5
# long_period_range=(8, 20, 1), # Test all longs from 8 to 20
# device_id=0
# )
# # Returns:
# # - results['values']: 2D array [num_combinations x len(data)]
# # - results['short_periods']: array of short periods tested
# # - results['long_periods']: array of long periods tested
#
# # Option 2: Many Series, One Parameter Set (portfolio processing)
# # Process multiple stocks/assets with the same ADOSC 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]
# portfolio_volume = np.array([...]) # Shape: [time_steps, num_assets]
#
# results = adosc_cuda_many_series_one_param(
# high_tm=portfolio_high,
# low_tm=portfolio_low,
# close_tm=portfolio_close,
# volume_tm=portfolio_volume,
# short_period=3,
# long_period=10,
# device_id=0
# )
# # Returns: 2D array [time_steps, num_assets] with ADOSC for each asset
#
# # Zero-copy variant with pre-allocated output (F32 for GPU efficiency)
# out = np.empty((time_steps, num_assets), dtype=np.float32)
# adosc_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),
# volume_tm_f32=portfolio_volume.astype(np.float32),
# short_period=3,
# long_period=10,
# out=out,
# device_id=0
# ) JavaScript/WASM Bindings
Basic Usage ▼
Calculate ADOSC in JavaScript/TypeScript:
import { adosc_js } from 'vectorta-wasm';
// OHLCV data as Float64Array or regular arrays
const high = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);
const low = new Float64Array([98.0, 99.0, 100.0, 101.0, 102.0, 103.0]);
const close = new Float64Array([99.0, 101.0, 100.5, 102.5, 104.0, 103.5]);
const volume = new Float64Array([10000, 12000, 11000, 15000, 13000, 14000]);
// Calculate ADOSC with specified parameters
const result = adosc_js(high, low, close, volume, 3, 10); // short=3, long=10
// Result is a Float64Array
console.log('ADOSC values:', result);
// TypeScript type definitions
interface AdoscResult {
values: Float64Array;
}
// Use with async/await for better error handling
async function calculateADOSC(
high: Float64Array,
low: Float64Array,
close: Float64Array,
volume: Float64Array
): Promise<Float64Array> {
try {
return adosc_js(high, low, close, volume, 3, 10);
} catch (error) {
console.error('ADOSC calculation failed:', error);
throw error;
}
}
// Interpret the results
result.forEach((value, index) => {
if (value > 0) {
console.log(`Bar ${index}: Accumulation momentum (${value.toFixed(2)})`);
} else {
console.log(`Bar ${index}: Distribution momentum (${value.toFixed(2)})`);
}
}); Memory-Efficient Operations ▼
Use zero-copy operations for better performance with large datasets:
import { adosc_alloc, adosc_free, adosc_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 = adosc_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 ADOSC directly into allocated memory (zero-copy)
adosc_into(
highArray.byteOffset, // High pointer
lowArray.byteOffset, // Low pointer
closeArray.byteOffset, // Close pointer
volumeArray.byteOffset, // Volume pointer
outputPtr, // Output pointer
length, // Data length
3, // Short period
10 // Long period
);
// Read results from WASM memory
const result = new Float64Array(memory.buffer, outputPtr, length);
const adoscValues = Array.from(result); // Convert to regular array if needed
// Important: Free allocated memory when done
adosc_free(outputPtr, length);
console.log('ADOSC values:', adoscValues); Batch Processing ▼
Test multiple parameter combinations efficiently:
import { adosc_batch_js, adosc_batch_metadata_js } from 'vectorta-wasm';
// Your OHLCV data
const high = new Float64Array([/* historical highs */]);
const low = new Float64Array([/* historical lows */]);
const close = new Float64Array([/* historical closes */]);
const volume = new Float64Array([/* historical volumes */]);
// Define parameter sweep ranges
const shortStart = 2, shortEnd = 5, shortStep = 1; // 2, 3, 4, 5
const longStart = 8, longEnd = 20, longStep = 2; // 8, 10, 12...20
// Get metadata about parameter combinations
const metadata = adosc_batch_metadata_js(
shortStart, shortEnd, shortStep,
longStart, longEnd, longStep
);
// metadata contains [short1, long1, short2, long2, ...]
const numCombos = metadata.length / 2;
// Calculate all combinations
const results = adosc_batch_js(
high, low, close, volume,
shortStart, shortEnd, shortStep,
longStart, longEnd, longStep
);
// Results is a flat array: [combo1_values..., combo2_values..., ...]
// Reshape based on your needs
const resultMatrix = [];
for (let i = 0; i < numCombos; i++) {
const start = i * high.length;
const end = start + high.length;
resultMatrix.push(results.slice(start, end));
}
// Access specific parameter combination results
const shortPeriod = metadata[0]; // First combo's short period
const longPeriod = metadata[1]; // First combo's long period
const adoscValues = resultMatrix[0]; // First combo's ADOSC values
// Find best parameters based on signal quality
let bestScore = -Infinity;
let bestParams = { short: 0, long: 0 };
for (let i = 0; i < numCombos; i++) {
const short = metadata[i * 2];
const long = metadata[i * 2 + 1];
const values = resultMatrix[i];
// Score based on zero-line crossovers
let crossovers = 0;
for (let j = 1; j < values.length; j++) {
if ((values[j] > 0) !== (values[j-1] > 0)) {
crossovers++;
}
}
if (crossovers > bestScore) {
bestScore = crossovers;
bestParams = { short, long };
}
}
console.log('Best parameters:', bestParams); Performance Analysis
Across sizes, Rust CPU runs about 1.17× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05