Aroon Indicator (Aroon)
length = 14 Overview
Aroon measures trend strength by calculating how recently price reached its highest high and lowest low within a specified lookback period, converting time observations into momentum signals. Tushar Chande developed this system to identify emerging trends before traditional indicators by focusing on the recency of extremes rather than their magnitude. Aroon Up calculates as ((period - bars since highest high) / period) × 100, while Aroon Down uses the same formula for the lowest low, producing two lines that oscillate between 0 and 100. When Aroon Up stays above 70, it signals consistent new highs and strong upward momentum; similarly, Aroon Down above 70 indicates persistent new lows and bearish pressure. Crossovers between the lines mark potential trend changes, with Aroon Up crossing above Aroon Down suggesting bullish shifts. The indicator excels during trending markets where one line dominates above 70 while the other lingers below 30, but both lines dropping below 50 warns of consolidation phases where breakout strategies may fail.
Implementation Examples
Compute Aroon Up and Down from highs/lows slices or candle data:
use vectorta::indicators::aroon::{aroon, AroonInput, AroonParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using high/low slices
let high = vec![101.2, 102.5, 101.8, 103.6, 104.0];
let low = vec![99.8, 100.5, 100.2, 101.7, 102.9];
let params = AroonParams { length: Some(14) };
let input = AroonInput::from_slices_hl(&high, &low, params);
let output = aroon(&input)?;
for (up, down) in output.aroon_up.iter().zip(output.aroon_down.iter()) {
println!("up={up:.2}, down={down:.2}");
}
// Using Candles with default parameters (length=14; uses high/low columns)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = AroonInput::with_default_candles(&candles);
let output = aroon(&input)?; API Reference
Input Methods ▼
// From high/low slices
AroonInput::from_slices_hl(&high, &low, AroonParams) -> AroonInput
// From Candles with explicit parameters
AroonInput::from_candles(&candles, AroonParams) -> AroonInput
// Convenience helper (length = 14, uses high/low columns)
AroonInput::with_default_candles(&candles) -> AroonInput Parameters Structure ▼
pub struct AroonParams {
pub length: Option<usize>, // Default: 14
} Output Structure ▼
pub struct AroonOutput {
pub aroon_up: Vec<f64>, // Time since highest high, scaled to 0-100
pub aroon_down: Vec<f64>, // Time since lowest low, scaled to 0-100
} Validation, Warmup & NaNs ▼
length > 0andlength <= data_len; otherwiseAroonError::InvalidLength.- High/low slices must have equal length; else
AroonError::MismatchSliceLength. - There must be at least
lengthvalid pairs after the first finite pair; elseAroonError::NotEnoughValidData. - If no finite high/low pair exists, returns
AroonError::AllValuesNaN. - Warmup: indices before
first_valid_index + lengthare set toNaN. - Any window containing a
NaNyieldsNaNat that index; streaming returnsNonein that case.
Error Handling ▼
use vectorta::indicators::aroon::{aroon, AroonError};
match aroon(&input) {
Ok(output) => consume(output),
Err(AroonError::AllValuesNaN) => {
eprintln!("All values are NaN");
}
Err(AroonError::EmptyInputData) => {
eprintln!("Input data slice is empty");
}
Err(AroonError::MismatchSliceLength { high_len, low_len }) => {
eprintln!("High/low length mismatch: {high_len} vs {low_len}");
}
Err(AroonError::InvalidLength { length, data_len }) => {
eprintln!("Invalid length {length} for data_len {data_len}");
}
Err(AroonError::NotEnoughValidData { needed, valid }) => {
eprintln!("Need {needed} non-NaN bars, only {valid} provided");
}
Err(e) => eprintln!("Aroon failed: {e}"),
} Python Bindings
Basic Usage ▼
Compute Aroon from NumPy arrays and receive both legs:
import numpy as np
from vectorta import aroon
high = np.array([101.2, 102.5, 101.8, 103.6, 104.0], dtype=np.float64)
low = np.array([99.8, 100.5, 100.2, 101.7, 102.9], dtype=np.float64)
# Returns a tuple (up, down)
up, down = aroon(high, low, length=14)
print("Aroon Up:", up)
print("Aroon Down:", down)
# Optional: kernel selection ('auto', 'avx2', 'avx512' when available)
up, down = aroon(high, low, length=20, kernel="avx2") Streaming Real-time Updates ▼
Maintain a rolling window and react as soon as new values stabilize:
from vectorta import AroonStream
stream = AroonStream(length=14)
for candle in realtime_feed:
result = stream.update(candle.high, candle.low)
if result is None:
continue # warmup period or NaN in window
up, down = result
if up > 70 and down < 30:
on_trend_confirmation()
elif down > 70 and up < 30:
on_downtrend_confirmation() Batch Parameter Optimization ▼
Scan many lookback lengths and inspect each result efficiently:
import numpy as np
from vectorta import aroon_batch
high = np.asarray(high_series, dtype=np.float64)
low = np.asarray(low_series, dtype=np.float64)
result = aroon_batch(
high,
low,
length_range=(10, 40, 5), # start, end, step
kernel="auto"
)
# result['up'] and result['down'] are (num_lengths, len(series)) arrays
lengths = result["lengths"]
for idx, length in enumerate(lengths):
up_row = result["up"][idx]
down_row = result["down"][idx]
evaluate(length, up_row, down_row) JavaScript/WASM Bindings
Basic Usage ▼
Calculate Aroon in JavaScript/TypeScript:
import { aroon_js } from 'vectorta-wasm';
const high = new Float64Array([101.2, 102.5, 103.6, 104.0]);
const low = new Float64Array([99.8, 100.5, 101.7, 102.9]);
const { up, down } = aroon_js(high, low, 14);
console.log('Aroon Up:', up);
console.log('Aroon Down:', down);
// Provide helper typings for clarity
interface AroonResult {
up: Float64Array;
down: Float64Array;
}
const result = aroon_js(high, low, 20) as AroonResult; Memory-Efficient Operations ▼
Work directly inside WASM memory to avoid extra copies for large datasets (output stores [up, down] back-to-back):
import { aroon_alloc, aroon_free, aroon_into, memory } from 'vectorta-wasm';
const high = new Float64Array([...]);
const low = new Float64Array([...]);
const length = high.length;
// Allocate three buffers; aroon_alloc(len) reserves 2*len f64 slots
const highPtr = aroon_alloc(length);
const lowPtr = aroon_alloc(length);
const outPtr = aroon_alloc(length); // output stores [up, down] of length len each
// Copy inputs into WASM memory
new Float64Array(memory.buffer, highPtr, length).set(high);
new Float64Array(memory.buffer, lowPtr, length).set(low);
// Compute into preallocated memory: args (high_ptr, low_ptr, out_ptr, len, length)
aroon_into(highPtr, lowPtr, outPtr, length, 14);
// Read results: up in [0..len), down in [len..2*len)
const up = new Float64Array(memory.buffer, outPtr, length).slice();
const down = new Float64Array(
memory.buffer,
outPtr + length * Float64Array.BYTES_PER_ELEMENT,
length
).slice();
console.log('Up leg', up);
console.log('Down leg', down);
// Free buffers
aroon_free(highPtr, length);
aroon_free(lowPtr, length);
aroon_free(outPtr, length); Batch Processing ▼
Evaluate many configurations and track the resulting parameter metadata:
import { aroon_batch_js, aroon_batch_metadata_js } from 'vectorta-wasm';
const high = new Float64Array([...]);
const low = new Float64Array([...]);
const metadata = aroon_batch_metadata_js(10, 40, 5); // [10, 15, 20, ...]
const { up, down, rows, cols, combos } = aroon_batch_js(
high,
low,
10,
40,
5
);
// up/down are flattened: reshape if needed
const numCombos = combos.length;
const upRows = [];
for (let i = 0; i < numCombos; i++) {
const start = i * cols;
const end = start + cols;
upRows.push(Array.from(up.slice(start, end)));
}
console.log('Length sweep:', metadata); Performance Analysis
Across sizes, Rust CPU runs about 1.06× slower than Tulip C in this benchmark.
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 Oscillator
Technical analysis indicator
Chande Momentum Oscillator
Technical analysis indicator
Directional Indicator (+DI/-DI)
Technical analysis indicator