N-Order EMA
period = 9 | order = 1 (1–64) | ema_style = ema | iir_style = impulse_matched Overview
N-Order EMA implements a family of recursive moving averages built from an nth-order IIR core. The same engine can emit a standard EMA, a double EMA, a triple EMA, or a Hull-like EMA variant by composing one or more filtered stages. The coefficient generator is also configurable, so the response can be shaped with all-pole, impulse-matched, matched-z, or bilinear coefficients instead of a single fixed recursion.
For candle input, the default source is close. Slice input accepts a single data series. The output is one moving-average series with a style-dependent warmup prefix filled with NaN, and the streaming API resets when it encounters a non-finite input value.
Defaults: period = 9.0, order = 1, ema_style = "ema", iir_style = "impulse_matched", candle source = "close".
Implementation Examples
Compute the indicator from a single series or candle data:
use vector_ta::indicators::moving_averages::n_order_ema::{
n_order_ema, NOrderEmaInput, NOrderEmaParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let data = vec![100.0, 101.0, 102.5, 101.75, 103.0, 104.2, 105.1];
let input = NOrderEmaInput::from_slice(
&data,
NOrderEmaParams {
period: Some(9.0),
order: Some(2),
ema_style: Some("tema".to_string()),
iir_style: Some("matched_z".to_string()),
},
);
let out = n_order_ema(&input)?;
println!("{:?}", out.values);
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let default_input = NOrderEmaInput::with_default_candles(&candles); // source = close
let default_out = n_order_ema(&default_input)?; API Reference
Input Methods v
NOrderEmaInput::from_slice(&[f64], NOrderEmaParams) -> NOrderEmaInput
NOrderEmaInput::from_candles(&Candles, "source", NOrderEmaParams) -> NOrderEmaInput
NOrderEmaInput::with_default_candles(&Candles) -> NOrderEmaInput // source = "close"
n_order_ema(&NOrderEmaInput) -> Result<NOrderEmaOutput, NOrderEmaError>
n_order_ema_into(&NOrderEmaInput, &mut [f64]) -> Result<(), NOrderEmaError> Parameters and Styles v
pub struct NOrderEmaParams {
pub period: Option<f64>,
pub order: Option<usize>,
pub ema_style: Option<String>,
pub iir_style: Option<String>,
}
// Defaults
period = 9.0
order = 1
ema_style = "ema"
iir_style = "impulse_matched"
// Accepted ema_style values
"ema" | "dema" | "hema" | "tema"
// Accepted iir_style values
"all_pole"
"impulse_matched"
"matched_z"
"bilinear" Output and Builders v
pub struct NOrderEmaOutput {
pub values: Vec<f64>,
}
NOrderEmaBuilder::new()
.period(f64)
.order(usize)
.ema_style(NOrderEmaStyle)
.iir_style(NOrderEmaIirStyle)
.kernel(Kernel)
.apply(&Candles)
.apply_slice(&[f64])
.into_stream()
NOrderEmaBatchBuilder::new()
.period_range((start, end, step))
.order_range((start, end, step))
.ema_style(NOrderEmaStyle)
.iir_style(NOrderEmaIirStyle)
.kernel(Kernel)
.apply_slice(&[f64])
pub struct NOrderEmaBatchOutput {
pub values: Vec<f64>,
pub combos: Vec<NOrderEmaParams>,
pub rows: usize,
pub cols: usize,
} Validation, Warmup & NaNs v
periodmust be finite, at least1.0, andceil(period) <= data.len()for one-shot calls.ordermust be in1..=64.- Input must be non-empty and contain at least one finite value.
- The implementation requires one contiguous valid run long enough for the chosen style and order; non-finite samples break the run.
- Warmup is style dependent: EMA and DEMA use
order * (ceil(period) - 1); TEMA uses three times that base lookback; HEMA uses the max of the half-period and base lookbacks plus the sqrt-period lookback. - One-shot output prefixes the warmup region with
NaN. Streaming returnsNoneuntil warmup completes, and also returnsNoneafter any non-finite input because the stream resets. - Batch output is row-major. Each row corresponds to one
(period, order)combo, and each row gets its own warmupNaNprefix. - Batch kernel validation rejects non-batch kernels with
NOrderEmaError::InvalidKernelForBatch.
Error Handling v
use vector_ta::indicators::moving_averages::n_order_ema::NOrderEmaError;
match NOrderEmaBuilder::new().period(20.0).order(2).apply_slice(&data) {
Ok(out) => println!("{:?}", out.values),
Err(NOrderEmaError::InvalidPeriod { period, data_len }) =>
eprintln!("invalid period {} for len {}", period, data_len),
Err(NOrderEmaError::InvalidOrder { order }) =>
eprintln!("invalid order {}", order),
Err(NOrderEmaError::InvalidEmaStyle { value }) =>
eprintln!("bad ema_style {}", value),
Err(NOrderEmaError::InvalidIirStyle { value }) =>
eprintln!("bad iir_style {}", value),
Err(NOrderEmaError::NotEnoughValidData { needed, valid }) =>
eprintln!("need {} contiguous valid values, got {}", needed, valid),
Err(e) => eprintln!("n_order_ema error: {}", e),
} Python Bindings
Basic Usage v
The Python function returns a single NumPy array of values:
import numpy as np
from vector_ta import n_order_ema
data = np.asarray(close_prices, dtype=np.float64)
values = n_order_ema(
data,
period=21.0,
order=2,
ema_style="tema",
iir_style="matched_z",
)
fast_values = n_order_ema(
data,
period=9.0,
order=1,
ema_style="ema",
iir_style="impulse_matched",
kernel="avx2",
) Streaming Real-time Updates v
The Python stream returns a float and emits NaN until warmup completes:
import numpy as np
from vector_ta import NOrderEmaStream
stream = NOrderEmaStream(
period=34.0,
order=3,
ema_style="hema",
iir_style="bilinear",
)
for value in live_prices:
ema_value = stream.update(float(value))
if not np.isnan(ema_value):
print("ready:", ema_value) Batch Processing v
Batch mode returns a dict with the reshaped values matrix and the expanded parameter vectors:
import numpy as np
from vector_ta import n_order_ema_batch
data = np.asarray(close_prices, dtype=np.float64)
result = n_order_ema_batch(
data,
period_range=(9.0, 21.0, 4.0),
order_range=(1, 3, 1),
ema_style="dema",
iir_style="all_pole",
)
values = result["values"] # shape: (rows, cols)
periods = result["periods"] # one entry per row
orders = result["orders"] # one entry per row
rows = result["rows"]
cols = result["cols"] JavaScript/WASM Bindings
Basic Usage v
The high-level WASM helper returns a Float64Array with the same length as the input series:
import { n_order_ema_js } from 'vectorta-wasm';
const data = new Float64Array(closePrices);
const values = n_order_ema_js(
data,
21.0,
2,
'tema',
'impulse_matched',
);
console.log(values.length);
console.log(values[0]); // NaN during warmup Memory-Efficient Operations v
Use the low-level allocation helpers when you want direct writes into WASM memory:
import { memory, n_order_ema_alloc, n_order_ema_free, n_order_ema_into } from 'vectorta-wasm';
const data = new Float64Array(closePrices);
const len = data.length;
const inPtr = n_order_ema_alloc(len);
const outPtr = n_order_ema_alloc(len);
new Float64Array(memory.buffer, inPtr, len).set(data);
n_order_ema_into(inPtr, outPtr, len, 34.0, 3, 'hema', 'matched_z');
const values = new Float64Array(memory.buffer, outPtr, len).slice();
n_order_ema_free(inPtr, len);
n_order_ema_free(outPtr, len); Batch Processing v
The batch API accepts a config object and returns values, combos, rows, and cols:
import { n_order_ema_batch_js } from 'vectorta-wasm';
const data = new Float64Array(closePrices);
const result = n_order_ema_batch_js(data, {
period_range: [9.0, 21.0, 4.0],
order_range: [1, 3, 1],
ema_style: 'dema',
iir_style: 'bilinear',
});
const { values, combos, rows, cols } = result as {
values: number[];
combos: Array<{
period?: number;
order?: number;
ema_style?: string;
iir_style?: string;
}>;
rows: number;
cols: number;
};
// Low-level batch writes are also available through n_order_ema_batch_into. CUDA Bindings (Rust)
Additional details for the CUDA bindings can be found inside the VectorTA repository.
Performance Analysis
Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)