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

206 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`:
```rust
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
```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
```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
```javascript
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
- [Indicator: Adx](../trend-directional/Indicator-Adx.md) — alternative trend-strength
measure with explicit `+DI` / `DI` direction.
- [Indicator: Stochastic](../momentum-oscillators/Indicator-Stochastic.md) — also range-window
based, but reports close position rather than extremum age.
- [Warmup Periods](../../Warmup-Periods.md) — the `period + 1` family.