Aroon Oscillator

Parameters: length = 14

Overview

The Aroon Oscillator simplifies trend analysis by combining Aroon Up and Aroon Down into a single line that oscillates between -100 and +100, calculated as Aroon Up minus Aroon Down. Positive values emerge when recent highs occur more frequently than recent lows, signaling upward momentum with strength proportional to the distance from zero. Conversely, negative readings indicate bearish conditions where new lows dominate the lookback window. The oscillator reaches extreme values near +100 when price consistently makes new highs while avoiding new lows, confirming powerful uptrends worth riding. Zero line crosses provide clear trend change signals without the ambiguity of watching two separate lines converge. Traders use the Aroon Oscillator to filter trades by only taking long positions above zero and shorts below, while avoiding sideways markets when the oscillator fluctuates near zero. The time calculation makes it particularly effective for catching trends early, as it reacts to the frequency of new extremes rather than waiting for price distance confirmations.

Implementation Examples

Get started with the Aroon Oscillator in just a few lines:

use vector_ta::indicators::aroonosc::{aroon_osc, AroonOscInput, AroonOscParams};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

// High/low slices must be the same length
let high = vec![
    101.2, 102.4, 103.8, 104.6, 105.3, 106.1, 107.0, 106.2,
    105.5, 106.8, 107.9, 108.4, 109.1, 108.6, 107.8, 108.9
];
let low = vec![
    99.7, 100.5, 101.4, 102.1, 103.0, 104.2, 104.9, 104.0,
    103.2, 104.6, 105.7, 106.3, 107.1, 106.4, 105.6, 106.8
];

let params = AroonOscParams { length: Some(14) };
let input = AroonOscInput::from_slices_hl(&high, &low, params);
let output = aroon_osc(&input)?;

// Access the oscillator values
for (idx, value) in output.values.iter().enumerate() {
    println!("{idx}: {value}");
}

// Using Candles with defaults (length = 14)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = AroonOscInput::with_default_candles(&candles);
let output = aroon_osc(&input)?;

API Reference

Input Methods
// From parallel high/low slices
AroonOscInput::from_slices_hl(&[f64], &[f64], AroonOscParams) -> AroonOscInput

// From Candles (uses high/low columns internally)
AroonOscInput::from_candles(&Candles, AroonOscParams) -> AroonOscInput

// Convenience with default params (length = 14)
AroonOscInput::with_default_candles(&Candles) -> AroonOscInput
Parameters Structure
pub struct AroonOscParams {
    pub length: Option<usize>, // Default: 14
}
Output Structure
pub struct AroonOscOutput {
    pub values: Vec<f64>, // Oscillator values in [-100, 100]
}
Validation, Warmup & NaNs
  • length > 0; otherwise AroonOscError::InvalidPeriod.
  • Inputs must be the same length; otherwise AroonOscError::MismatchSliceLength.
  • Requires at least length + 1 valid points after the first finite high/low; otherwise AroonOscError::NotEnoughValidData.
  • Warmup: indices [0 .. first + length) are NaN. First finite output occurs at first + length.
  • Range guarantee: oscillator values are clamped to [-100, 100].
  • Streaming: AroonOscStream::update returns None until the window fills, then Some(value) thereafter.
Error Handling
use vector_ta::indicators::aroonosc::{aroon_osc, AroonOscError};

match aroon_osc(&input) {
    Ok(output) => process(output.values),
    Err(AroonOscError::EmptyInputData) =>
        eprintln!("input data is empty"),
    Err(AroonOscError::AllValuesNaN) =>
        eprintln!("all input values are NaN"),
    Err(AroonOscError::InvalidPeriod { period, data_len }) =>
        eprintln!("invalid length: {period} (data_len={data_len})"),
    Err(AroonOscError::MismatchSliceLength { high_len, low_len }) =>
        eprintln!("high/low slices differ: {high_len} vs {low_len}"),
    Err(AroonOscError::NotEnoughValidData { needed, valid }) =>
        eprintln!("need {needed} valid points, got {valid}"),
    Err(AroonOscError::OutputLengthMismatch { expected, got }) =>
        eprintln!("output length mismatch: expected {expected}, got {got}"),
    Err(AroonOscError::InvalidKernelForBatch(k)) =>
        eprintln!("invalid kernel for batch: {k:?}"),
    Err(e) => eprintln!("Aroon Oscillator failed: {e}"),
}
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 aroonosc_cuda_batch_dev, aroonosc_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)

dev = aroonosc_cuda_batch_dev(
    high_f32=high_f32,
    low_f32=low_f32,
    length_range=(5, 30, 5),
    device_id=0,
)

# Many series (time-major)
high_tm_f32 = np.asarray(load_high_time_major_matrix(), dtype=np.float32)
low_tm_f32 = np.asarray(load_low_time_major_matrix(), dtype=np.float32)

dev_tm = aroonosc_cuda_many_series_one_param_dev(
    high_tm_f32=high_tm_f32,
    low_tm_f32=low_tm_f32,
    length=14,
    device_id=0,
)

Python Bindings

Quick Start

Compute Aroon Oscillator from NumPy arrays, stream updates, or sweep lengths in batch:

import numpy as np
from vector_ta import aroonosc, aroonosc_batch, AroonOscStream

high = np.asarray([...], dtype=np.float64)
low = np.asarray([...], dtype=np.float64)

# One-shot
values = aroonosc(high, low, length=14, kernel=None)

# Streaming
stream = AroonOscStream(length=14)
for h, l in zip(high, low):
    v = stream.update(float(h), float(l))
    if v is not None:
        handle(v)

# Batch sweep
res = aroonosc_batch(high, low, length_range=(5, 30, 5), kernel=None)
vals = res['values']

JavaScript/WASM Bindings

Basic Usage

Compute the oscillator from browser or Node.js environments:

import { aroonosc_js } from 'vectorta-wasm';

const high = new Float64Array([/* recent highs */]);
const low = new Float64Array([/* matching lows */]);

// length defaults to 14 if omitted
const values = aroonosc_js(high, low, 20);
console.log('Aroon Oscillator values:', values);
Memory-Efficient Operations

Use the zero-copy helpers when integrating with WebAssembly memory directly:

import { aroonosc_alloc, aroonosc_free, aroonosc_into, memory } from 'vectorta-wasm';

const high = new Float64Array([/* highs */]);
const low = new Float64Array([/* lows */]);
const length = high.length;

const highPtr = aroonosc_alloc(length);
const lowPtr = aroonosc_alloc(length);
const outPtr = aroonosc_alloc(length);

const highView = new Float64Array(memory.buffer, highPtr, length);
const lowView = new Float64Array(memory.buffer, lowPtr, length);
highView.set(high);
lowView.set(low);

aroonosc_into(
  highPtr,  // input high pointer
  lowPtr,   // input low pointer
  outPtr,   // output pointer
  length,   // number of samples
  14        // length parameter
);

const result = new Float64Array(memory.buffer, outPtr, length);
console.log(result);

// Free allocations when finished
aroonosc_free(highPtr, length);
aroonosc_free(lowPtr, length);
aroonosc_free(outPtr, length);
Batch Processing

Evaluate multiple length windows without leaving WASM:

import {
  aroonosc_batch_js,
  aroonosc_batch_metadata_js,
  aroonosc_batch as aroonosc_batch_unified
} from 'vectorta-wasm';

const high = new Float64Array([/* highs */]);
const low = new Float64Array([/* lows */]);

// Legacy flat-array batch helpers
const metadata = aroonosc_batch_metadata_js(10, 40, 5); // [10, 15, 20, 25, 30, 35, 40]
const flatResults = aroonosc_batch_js(high, low, 10, 40, 5);

// Structured API returning values + metadata together
const config = { length_range: [10, 40, 5] };
const { values, combos, rows, cols } = aroonosc_batch_unified(high, low, config);

console.log('rows x cols:', rows, cols);
console.log('first combo length:', combos[0].length);

CUDA Bindings (Rust)

use vector_ta::cuda::CudaAroonOsc;
use vector_ta::indicators::aroonosc::AroonOscBatchRange;

let cuda = CudaAroonOsc::new(0)?;

let high_f32: [f32] = /* ... */;
let low_f32: [f32] = /* ... */;
let sweep = AroonOscBatchRange::default();

let out = cuda.aroonosc_batch_dev(&high_f32, &low_f32, &sweep)?;
let _ = out;

Performance Analysis

Comparison:
View:

Across sizes, Rust CPU runs about 1.08× slower than Tulip C in this benchmark.

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-02-28

Related Indicators