New Adaptive Moving Average (NAMA)
period = 30 Overview
The New Adaptive Moving Average intelligently adjusts its responsiveness by measuring the efficiency of price movement through the ratio of net price displacement to total volatility effort over a lookback window. NAMA calculates this efficiency by dividing the absolute price range by the sum of True Range values, creating an adaptive coefficient that accelerates the average during directional moves and slows it during choppy consolidation. When price moves efficiently in one direction with minimal volatility, the indicator stays close to price action, but during periods of high effort with little progress, it increases smoothing to filter out noise. This adaptive behavior makes NAMA particularly effective at riding trends while avoiding whipsaws in ranging markets, as it automatically adjusts to changing market conditions without manual parameter changes. Traders value this indicator for its ability to distinguish between meaningful price movements and random fluctuations, providing cleaner signals than fixed period moving averages.
Implementation Examples
Get started with NAMA in Rust:
use vectorta::indicators::moving_averages::nama::{nama, NamaInput, NamaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with a price slice
let prices = vec![100.0, 101.2, 100.5, 102.3, 103.0, 101.8];
let params = NamaParams { period: Some(30) }; // Default is 30
let input = NamaInput::from_slice(&prices, params);
let result = nama(&input)?;
// Using with Candles (default: source = "close", period = 30)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = NamaInput::with_default_candles(&candles);
let result = nama(&input)?;
// Access results
for v in result.values { println!("NAMA: {}", v); } API Reference
Input Methods ▼
// From price slice
NamaInput::from_slice(&[f64], NamaParams) -> NamaInput
// From candles with custom source
NamaInput::from_candles(&Candles, &str, NamaParams) -> NamaInput
// From candles with default params (source="close", period=30)
NamaInput::with_default_candles(&Candles) -> NamaInput Parameters Structure ▼
pub struct NamaParams {
pub period: Option<usize>, // Default: 30
} Output Structure ▼
pub struct NamaOutput {
pub values: Vec<f64>, // Adaptive moving average values
} Validation, Warmup & NaNs ▼
period > 0andperiod ≤ data_len; elseNamaError::InvalidPeriod { period, data_len }.- There must be at least
periodvalid points after the first finite value; elseNamaError::NotEnoughValidData { needed, valid }. - Empty input:
NamaError::EmptyInputData. All-NaN input:NamaError::AllValuesNaN. - Warmup: indices
[.. first + period − 1]areNaN. First finite output usesy = α · x. - Slice input TR:
TRfirst = 0, thenTRj = |xj − xj-1|. Candles TR uses max ofH − L,|H − Cprev|,|L − Cprev|. - Streaming: returns
Noneuntil the window is filled; afterwards yields values per update.
Error Handling ▼
use vectorta::indicators::moving_averages::nama::{nama, NamaError};
match nama(&input) {
Ok(out) => process(out.values),
Err(NamaError::EmptyInputData) => eprintln!("NAMA: input is empty"),
Err(NamaError::AllValuesNaN) => eprintln!("NAMA: all values are NaN"),
Err(NamaError::InvalidPeriod { period, data_len }) =>
eprintln!("NAMA: invalid period {} for data_len {}", period, data_len),
Err(NamaError::NotEnoughValidData { needed, valid }) =>
eprintln!("NAMA: need {} valid points, only {}", needed, valid),
} Python Bindings
Basic Usage ▼
import numpy as np
from vectorta import nama # Python binding
prices = np.array([100.0, 101.2, 100.5, 102.3, 103.0, 101.8], dtype=np.float64)
out = nama(prices, period=30, kernel=None) # kernel: None/'auto'/'avx2'/...
print(out.shape) # (len(prices),) Streaming ▼
from vectorta import NamaStream
stream = NamaStream(period=30)
# Single-series updates
for px in prices_stream:
y = stream.update(px)
if y is not None:
handle(y)
# OHLC updates (prev_close is optional for the first bar)
for bar in ohlc_stream: # bar = (src, high, low, prev_close)
y = stream.update_ohlc(bar.src, bar.high, bar.low, bar.prev_close)
if y is not None:
handle(y) Batch Processing ▼
import numpy as np
from vectorta import nama_batch
prices = np.asarray(prices, dtype=np.float64)
res = nama_batch(prices, period_range=(10, 40, 5), kernel=None)
# Results
values = res['values'] # shape: (num_periods, len(prices))
periods = res['periods'] # list of window lengths tested
print(values.shape, periods) CUDA Acceleration ▼
CUDA APIs are available for NAMA when built with CUDA support.
import numpy as np
from vectorta import nama_cuda_batch_dev, nama_cuda_many_series_one_param_dev
# 1) One series, many parameters (time × 1), returns device array wrapper
prices_f32 = prices.astype(np.float32)
out_dev = nama_cuda_batch_dev(prices_f32, period_range=(10, 40, 5), device_id=0)
# out_dev exposes a device array; copy methods depend on vectorTA CUDA helpers
# 2) Many series (time-major), one parameter
tm = np.random.rand(2000, 64).astype(np.float32) # [time, series]
out_dev2 = nama_cuda_many_series_one_param_dev(tm, period=30, device_id=0) JavaScript/WASM Bindings
Basic Usage ▼
Compute NAMA in JS/TS:
import { nama_js } from 'vectorta-wasm';
const prices = new Float64Array([100.0, 101.2, 100.5, 102.3, 103.0, 101.8]);
const period = 30;
const values = nama_js(prices, period);
console.log('NAMA values:', values); Memory-Efficient Operations ▼
Use zero-copy helpers with pre-allocated WASM memory:
import { nama_alloc, nama_free, nama_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* your data */]);
const len = prices.length;
// Allocate WASM memory
const inPtr = nama_alloc(len);
const outPtr = nama_alloc(len);
// Copy input data
new Float64Array(memory.buffer, inPtr, len).set(prices);
// Compute NAMA directly into WASM memory
nama_into(inPtr, outPtr, len, 30);
// Read back results (copy out)
const out = new Float64Array(memory.buffer, outPtr, len).slice();
// Free memory
nama_free(inPtr, len);
nama_free(outPtr, len); Batch Processing ▼
Test multiple periods and retrieve a matrix of results:
import { nama_batch } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
const cfg = { period_range: [10, 40, 5] };
const js = nama_batch(prices, cfg);
// js has shape metadata and flattened values
const rows = js.rows, cols = js.cols;
const combos = js.combos; // array of { period: number | null }
const flat = js.values; // length = rows * cols
// Rehydrate into row-major 2D (one row per period)
const mat = [] as Float64Array[];
for (let r = 0; r < rows; r++) {
const start = r * cols;
mat.push(flat.slice(start, start + cols));
}
console.log('Periods tested:', combos.map(c => c.period ?? 30)); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
CUDA note
In our benchmark workload, the Rust CPU implementation is faster than CUDA for this indicator. Prefer the Rust/CPU path unless your workload differs.