# KAMA > Kaufman's Adaptive Moving Average — picks its own smoothing constant > on every bar from a fast/slow EMA pair, weighted by an efficiency > ratio that measures how trending the recent price action has been. ## Quick reference | Field | Value | |-------|-------| | Family | Moving Averages | | Input type | `f64` (single close) | | Output type | `f64` | | Output range | unbounded; tracks the input price scale | | Default parameters | Python: `(er_period=10, fast=2, slow=30)`; Rust: `Kama::classic()` returns the same triple | | Warmup period (`warmup_period()`) | `er_period + 1` — see below; the *first* emission lands at this index, but on a fresh KAMA that emission equals the seed (the input itself) | | Interpretation | Fast in trending markets, slow in choppy markets — by construction. | ## Formula For each new input `price_t` (with `n = er_period`): ``` direction_t = | price_t - price_{t-n} | volatility_t = Σ_{i=1}^{n} | price_{t-i+1} - price_{t-i} | ER_t = direction_t / volatility_t // 0 = pure chop, 1 = pure trend; 0 if volatility = 0 fast_sc = 2 / (fast + 1) // fast EMA smoothing constant slow_sc = 2 / (slow + 1) // slow EMA smoothing constant SC_t = (ER_t * (fast_sc - slow_sc) + slow_sc) ^ 2 KAMA_t = KAMA_{t-1} + SC_t * (price_t - KAMA_{t-1}) ``` The squared `SC_t` is Kaufman's choice (he found that squaring widens the dynamic range between "act like a fast EMA" and "act like a slow EMA"). On the very first emission `KAMA_{t-1}` is seeded with the oldest price in the window (`window.front()`), which is the convention in the source. ## Parameters | Name | Type | Default (Python `KAMA(...)`) | Valid range | Description | |-------------|---------|-------------------------------|-------------|-------------| | `er_period` | `usize` | `10` | `>= 1` | Lookback for the efficiency ratio. Larger → smoother ER, slower adaptation. | | `fast` | `usize` | `2` | `>= 1`, strictly `< slow` | Fast EMA period; sets the lower bound on responsiveness. | | `slow` | `usize` | `30` | `>= 1`, strictly `> fast` | Slow EMA period; sets the upper bound on smoothness. | Any of `er_period`, `fast`, `slow` being `0` errors with `Error::PeriodZero`; `fast >= slow` errors with `Error::InvalidPeriod`. The Python defaults come from `#[pyo3(signature = (er_period=10, fast=2, slow=30))]` in `bindings/python/src/lib.rs`; the Rust convenience constructor `Kama::classic()` returns the same triple. ## Inputs / Outputs From `crates/wickra-core/src/indicators/kama.rs`: ```rust impl Indicator for Kama { type Input = f64; type Output = f64; // update(&mut self, input: f64) -> Option } ``` Python returns `float | None` (streaming) / `numpy.ndarray` (batch, `NaN` for warmup). Node returns `number | null` (streaming) / `Array` with `NaN` (batch). `warmup_period()` is exposed in Rust and Python but **not** on the Node `KAMA` class (consult `bindings/node/index.d.ts` for the surface). ## Warmup `Kama::new(er_period, fast, slow).warmup_period() == er_period + 1`. The "off-by-one" is because the efficiency ratio compares `price_t` to `price_{t-er_period}` and sums `er_period` consecutive absolute diffs; that requires `er_period + 1` prices in the window. For `Kama::classic()` (`er_period = 10`) the first emission lands on input 11, matching the table in [Warmup Periods](../../Warmup-Periods.md). The implementation uses a `VecDeque` of capacity `er_period + 1`. Once full, every subsequent `update` pops the front and pushes the new input — `update` is O(`er_period`) in principle (the volatility sum is re-computed) but O(1) in `period`/`fast`/`slow` since the EMA-style recursion has no window. Note: on the *first* emission, `prev = window.front()` (the oldest price), and the output is `prev + SC · (input − prev)`. On a perfectly trending series (`ER ≈ 1`, `SC ≈ fast_sc² ≈ 0.444`) this means the first KAMA value is materially below the latest price; on `[1, 2, …, 20]` for instance, KAMA's first emission at input 11 is `5.444…`, not `11`. See the example output below. ## Edge cases - **Constant series.** Feeding `[100.0; n]` produces `Some(100.0)`: both `direction` and `volatility` are zero, the source branches `if volatility == 0.0 { 0.0 } else { ... }` so `ER = 0`, `SC = slow_sc² ≈ 0.00416`, and `100 + 0.00416 · (100 − 100) = 100`. The unit test `constant_series_yields_constant_kama` pins this with `Kama::classic()`. - **NaN / infinity inputs.** The first line of `update` is `if !input.is_finite() { return self.state; }`. Non-finite inputs are silently dropped; the window is not advanced, the previously emitted value is preserved. - **Reset.** `kama.reset()` clears both the window and the smoothed state. The next `update` starts a fresh `er_period + 1` warmup countdown. ## Examples ### Rust ```rust use wickra::{BatchExt, Indicator, Kama}; fn main() -> Result<(), Box> { let mut kama = Kama::classic(); // (10, 2, 30) let prices: Vec = (1..=20).map(f64::from).collect(); let out: Vec> = kama.batch(&prices); println!("warmup_period = {}", kama.warmup_period()); println!("{:?}", out); Ok(()) } ``` Output: ``` warmup_period = 11 [None, None, None, None, None, None, None, None, None, None, Some(5.444444444444443), Some(8.358024691358022), Some(10.421124828532234), Some(12.011736015851241), Some(13.339853342139579), Some(14.522140745633099), Some(15.62341152535172), Some(16.679673069639843), Some(17.710929483133246), Some(18.728294157296247)] ``` `Kama::classic().periods()` returns `(10, 0.6666666666666666, 0.06451612903225806)` — the second and third numbers are `fast_sc = 2/3` and `slow_sc = 2/31`, not the integer `fast`/`slow` periods themselves. On the linear ramp `1, 2, …, 20` the efficiency ratio is `1.0` (every step moves direction the same as volatility), so `SC = fast_sc² ≈ 0.4444`. The first emission `5.444…` is `1 + 0.4444 · (11 − 1)` and each subsequent value follows the same recursion. ### Python ```python import numpy as np import wickra as ta kama = ta.KAMA() # defaults: er_period=10, fast=2, slow=30 out = kama.batch(np.arange(1.0, 21.0)) print("warmup_period =", kama.warmup_period()) print(out) ``` Output: ``` warmup_period = 11 [ nan nan nan nan nan nan nan nan nan nan 5.44444444 8.35802469 10.42112483 12.01173602 13.33985334 14.52214075 15.62341153 16.67967307 17.71092948 18.72829416] ``` ### Node ```javascript const ta = require('wickra'); const kama = new ta.KAMA(10, 2, 30); // no default constructor; pass the triple const prices = Array.from({ length: 20 }, (_, i) => i + 1); console.log(kama.batch(prices)); ``` Output: ``` [ NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 5.444444444444443, 8.358024691358022, 10.421124828532234, 12.011736015851241, 13.339853342139579, 14.522140745633099, 15.62341152535172, 16.679673069639843, 17.710929483133246, 18.728294157296247 ] ``` (The Node `KAMA` class does not expose `warmupPeriod()`; use the Rust or Python binding if you need that getter from your application.) ## Interpretation KAMA's defining property is that it **changes its own behaviour with the market**. In a clean trend the efficiency ratio approaches `1`, `SC` approaches `fast_sc²`, and KAMA behaves like a fast EMA — it tracks price closely. In a choppy sideways market the efficiency ratio collapses toward `0`, `SC` approaches `slow_sc²`, and KAMA effectively freezes — its line goes nearly flat regardless of how violently price oscillates around it. This is by design: Kaufman's argument is that you should not chase noise. The two usable signals are slope (positive = uptrend; flat = ranging; negative = downtrend) and price-vs-KAMA crossover. Because KAMA can sit nearly flat for long stretches in a range, "price crossed KAMA" generates fewer false signals than the same test against an EMA of similar period. Prefer `Kama` over a static EMA/SMA when the market regime varies materially (trending → ranging → trending). Prefer a fixed `Ema` / `Hma` when you want a predictable smoothing profile that is independent of price action. ## Common pitfalls - **Assuming `warmup_period()` is when the line is "good".** The first emission lands at input 11 (for the default `er_period = 10`), but the seed `KAMA_{t-1} = window.front()` is the *oldest* price in the window, so the very first emitted value is biased toward the 10-bars-ago price. On a strong trend this means the first 3–5 emissions are noticeably below (or above, depending on direction) the current price. If that matters, drop the first `er_period` post-warmup emissions, not just the warmup itself. - **Tuning `fast` and `slow` independently of `er_period`.** Kaufman's derivation assumes `slow >> fast` so that the per-bar SC has room to move. Picking, say, `(10, 5, 6)` gives `fast_sc ≈ 0.333` and `slow_sc ≈ 0.286`, so SC barely changes regardless of the efficiency ratio — KAMA degenerates into "an EMA somewhere around period 6". Keep `slow` at least `5×` `fast` if you want the adaptive behaviour to actually matter. ## References Perry J. Kaufman, *Smarter Trading*, McGraw-Hill, 1995 (book-length introduction); reprinted in Kaufman's *Trading Systems and Methods* across multiple editions, where the squared-SC choice is justified empirically. ## See also - [Indicator-Ema.md](../moving-averages/Indicator-Ema.md) — the two endpoints (`fast` and `slow`) KAMA interpolates between. - [Indicator-Hma.md](../moving-averages/Indicator-Hma.md) — the other "smart" trend filter in Wickra. - [Indicators-Overview.md](../../Indicators-Overview.md) — the full taxonomy.