Files
wickra/docs/wiki/indicators/volatility-bands/Indicator-ChaikinVolatility.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

4.3 KiB
Raw Blame History

ChaikinVolatility

Chaikin Volatility — the rate of change of a smoothed high-low spread; is the trading range widening or narrowing?

Quick reference

Field Value
Family Volatility & Bands
Input type Candle (uses high, low)
Output type f64
Output range unbounded around zero (percent)
Default parameters ema_period = 10, roc_period = 10 (Python)
Warmup period ema_period + roc_period
Interpretation Positive = ranges expanding, negative = ranges contracting.

Formula

spread_t   = high_t  low_t
smoothed_t = EMA(spread, ema_period)_t
ChaikinVol = 100 · (smoothed_t  smoothed_{troc_period}) / smoothed_{troc_period}

Marc Chaikin's volatility measure tracks not the level of the trading range but how fast it is widening or narrowing. The bar's high-low spread is EMA-smoothed, then run through a rate-of-change: a rising value means ranges are expanding (often near a market top, as fear spikes), a falling value means they are contracting (a quiet, complacent market). The classic configuration smooths the spread with a 10-period EMA and takes its 10-period rate of change.

Parameters

  • ema_period — the EMA that smooths the high-low spread (10).
  • roc_period — the rate-of-change lookback over the smoothed spread (10).

ChaikinVolatility::classic() returns the (10, 10) configuration.

Inputs / Outputs

From crates/wickra-core/src/indicators/chaikin_volatility.rs:

impl Indicator for ChaikinVolatility {
    type Input = Candle;
    type Output = f64;
    // update(&mut self, input: Candle) -> Option<f64>
}

ChaikinVolatility is a candle-input indicator that reads high and low. Python's streaming update accepts a 6-tuple or a dict; the batch helper takes high, low numpy arrays. Node and WASM expose update(high, low) and the matching batch.

Warmup

ChaikinVolatility::classic().warmup_period() == 20. The EMA emits at candle ema_period; the rate-of-change then needs roc_period more smoothed values.

Edge cases

  • Constant range. A constant high-low spread smooths to a constant EMA, whose rate of change is 0.
  • Expanding range. A monotonically widening range reads positive.
  • Reset. cv.reset() clears the inner EMA and ROC.

Examples

Rust

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

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut cv = ChaikinVolatility::new(10, 10)?;
    // A constant 2-wide range -> constant EMA -> zero rate of change.
    let candles: Vec<Candle> = (0..40)
        .map(|i| {
            let base = 100.0 + f64::from(i);
            Candle::new(base, base + 1.0, base - 1.0, base, 1.0, i).unwrap()
        })
        .collect();
    println!("{:?}", cv.batch(&candles).last().unwrap());
    Ok(())
}

Output:

Some(0.0)

Python

import numpy as np
import wickra as ta

cv = ta.ChaikinVolatility(10, 10)
n = 40
base = np.arange(n, dtype=float) + 100.0
print(cv.batch(base + 1.0, base - 1.0)[-1])

Output:

0.0

Node

const ta = require('wickra');
const cv = new ta.ChaikinVolatility(10, 10);
const base = Array.from({ length: 40 }, (_, i) => 100 + i);
const out = cv.batch(base.map((b) => b + 1), base.map((b) => b - 1));
console.log(out[out.length - 1]);

Output:

0

Interpretation

A rising Chaikin Volatility warns that ranges are expanding fast — Chaikin associated sharp rises with market tops, where panic widens bars. A low or falling reading is the calm, range-contracting market that often precedes a move. It complements Atr: ATR gives the level of volatility, Chaikin Volatility gives its momentum.

Common pitfalls

  • Reading it as a volatility level. It is a rate of change — zero means steady ranges, not zero volatility.
  • Feeding it scalar prices. It needs the high/low bar.

References

Marc Chaikin's Chaikin Volatility; the EMA-of-spread rate-of-change definition here is the standard one.

See also