Files
wickra/docs/wiki/indicators/trend-directional/Indicator-Aroon.md
T
kingchenc d2f99efd78 F13c: restructure the indicator catalogue into eight families
The original taxonomy was four classical families plus a statistics group,
with the F1-F12 expansion slotted in as sub-categories. This regroups the
whole 71-indicator catalogue into eight top-level families, each with at
least five members:

  Moving Averages (12), Momentum Oscillators (13), Trend & Directional (9),
  Price Oscillators (5), Volatility & Bands (12), Trailing Stops (5),
  Volume (9), Price Statistics (7).

- Wiki: docs/wiki/indicators/ reorganised into eight family folders; all 71
  indicator pages moved with `git mv`. Every internal cross-link is
  normalised to `../<family>/Indicator-X.md`, each page's `Family` field is
  set to its new family, and two pre-existing `../Indicator-Chaining.md`
  links (should have been `../../`) are corrected. A link check confirms
  every relative wiki link resolves.
- Indicators-Overview.md fully rewritten around the eight families;
  Home.md indicator reference and the README family table follow suit.
- Warmup-Periods.md gains the eight F13 indicators; CHANGELOG records the
  46-indicator expansion (25 -> 71) and the eight-family taxonomy.
- Tests: Node indicators.test.js and Python test_new_indicators.py cover
  all eight new indicators (Node 91/91, Python 117/117 green).

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 508 core tests,
25 data tests and 74 doctests green.
2026-05-22 21:21:56 +02:00

6.3 KiB
Raw Blame History

Aroon

Tushar Chande's Aroon indicator — tracks the bars-since-highest-high and bars-since-lowest-low inside a period + 1-bar window, reported as percentages.

Quick reference

Field Value
Family Trend & Directional
Input type Candle
Output type AroonOutput { up, down }
Output range up, down ∈ [0, 100]
Default parameters period = 14 (Python)
Warmup period period + 1 (15 for period = 14)
Interpretation up > 70 && down < 30 strong uptrend (mirror for downtrend); crossovers as turn signals

Formula

Scan the rolling period + 1-bar window for the position of the highest high and the position of the lowest low (with 0 = oldest, period = newest):

hh_idx_t  =  argmax_{i in 0..period}  high_{t-period+i}
ll_idx_t  =  argmin_{i in 0..period}  low_{t-period+i}

up_t      =  100 · hh_idx_t / period
down_t    =  100 · ll_idx_t / period

When the highest high lands on the most-recent bar (hh_idx == period), up == 100; when it lands on the oldest bar in the window, up == 0. The same holds for down.

In Wickra's implementation the scan uses >= / <=, so ties go to the latest matching bar — which is why a perfectly flat window produces up = down = 100 rather than 0 (the latest bar is always tied with the oldest).

Parameters

Name Type Default (Python) Valid range Description
period usize 14 >= 1 Lookback length. The internal window holds period + 1 candles.

Aroon::new(0) returns Error::PeriodZero.

Inputs / Outputs

From impl Indicator for Aroon:

type Input  = Candle;
type Output = AroonOutput;
fn update(&mut self, candle: Candle) -> Option<AroonOutput>;

AroonOutput:

Field Description
up 100 · bars_since_oldest_HH / period, in [0, 100]. High = recent new high.
down 100 · bars_since_oldest_LL / period, in [0, 100]. High = recent new low.

Python's Aroon.batch(high, low) returns a (n, 2) float64 array with columns [up, down]; warmup rows are [NaN, NaN]. Streaming update(candle) returns a (up, down) tuple or None.

Node's Aroon.batch(high, low) returns a flat number[] of length n * 2, interleaved [up_0, down_0, up_1, down_1, …]. Only batch is exposed on the Node binding.

Warmup

warmup_period() returns period + 1. Aroon scans period + 1 bars to find "bars since highest high" (which ranges over 0..period), so the indicator is not ready until exactly period + 1 candles have arrived. This is the same off-by-one as RSI and ROC, but for a window-position reason rather than a diff reason.

Edge cases

  • Pure uptrend. Every new candle is a new high — hh_idx is always the latest position, up == 100. The lowest low is the oldest candle in the window, down == 0. Tests pure_uptrend_aroon_up_100 pin this.
  • Constant input. Every candle's high is equal to every other candle's high. The >= tiebreak in the scan means the most-recent candle always wins both the HH and LL positions — both up and down end up at 100. (Be careful: this is not a neutral reading; it is an artefact of the tiebreak rule.)
  • Reset. reset() clears the candle buffer; the next period + 1 updates return None.

Examples

Rust

use wickra::{Aroon, BatchExt, Candle, Indicator};

let candles: Vec<Candle> = (1..=15)
    .map(|i| Candle::new(i as f64, i as f64 + 1.0, i as f64 - 1.0, i as f64, 1.0, 0).unwrap())
    .collect();
let mut aroon = Aroon::new(14)?;
let out = aroon.batch(&candles);
let v = out[14].unwrap();
println!("uptrend row 14  up={} down={}", v.up, v.down);
# Ok::<(), wickra::Error>(())

Verified output:

uptrend row 14  up=100 down=0

Python

import numpy as np
import wickra as ta

i = np.arange(1, 16, dtype=float)
high = i + 1.0
low  = i - 1.0
aroon = ta.Aroon(14)
out = aroon.batch(high, low)
print('warmup:', aroon.warmup_period())
print('shape :', out.shape)
print('row 14:', out[14])

Verified output:

warmup: 15
shape : (15, 2)
row 14: [100.   0.]

Node

const wickra = require('wickra');

const high = [], low = [];
for (let i = 1; i <= 15; i++) {
  high.push(i + 1);
  low.push(i - 1);
}
const a = new wickra.Aroon(14);
const out = a.batch(high, low);
console.log('len   :', out.length);
console.log('row 14:', { up: out[14 * 2], down: out[14 * 2 + 1] });

Verified output:

len   : 30
row 14: { up: 100, down: 0 }

Interpretation

  • Strong trend bands. up > 70 with down < 30 indicates a strong uptrend (new highs are recent, new lows are old); mirror for a downtrend.
  • Crossover. up crossing above down is a bullish trend-shift signal; the mirror is bearish. Crossovers near 50/50 are weak (the window has no clear leader); crossovers from 0/100 extremes are strong.
  • Consolidation. Both lines wandering near 50 means neither recent highs nor recent lows are dominating — typical of a range-bound market.

Common pitfalls

  • Constant input gives up == down == 100, not 0 or 50. The >= / <= tiebreak in the scan rewards the most-recent candle. Treat constant or near-constant windows as a degenerate case; a reading of (100, 100) is not a strong trend in both directions — it is "no information".
  • period + 1 warmup, not period. Same off-by-one trap as RSI: the indicator looks at a window of size period + 1 so that bars_since_high can range from 0 to period. Indexing your output array as if it were ready at the period-th input gives you one NaN / None row at the start you didn't expect.

References

  • Tushar Chande, "A New Tool for Technical Traders: The Aroon Indicator", Technical Analysis of Stocks & Commodities, September 1995 — the original publication.

See also