New Adaptive Moving Average (NAMA)

Parameters: 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 > 0 and period ≤ data_len; else NamaError::InvalidPeriod { period, data_len }.
  • There must be at least period valid points after the first finite value; else NamaError::NotEnoughValidData { needed, valid }.
  • Empty input: NamaError::EmptyInputData. All-NaN input: NamaError::AllValuesNaN.
  • Warmup: indices [.. first + period − 1] are NaN. First finite output uses y = α · x.
  • Slice input TR: TRfirst = 0, then TRj = |xj − xj-1|. Candles TR uses max of H − L, |H − Cprev|, |L − Cprev|.
  • Streaming: returns None until 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

Comparison:
View:
Loading chart...

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.

Related Indicators