Exponential Moving Average (EMA)
period = 9 Overview
The Exponential Moving Average tracks price trends by applying exponentially decreasing weights to older data points, with the most recent prices receiving the highest influence. Unlike simple averages that weight all values equally, EMA uses a smoothing factor (typically 2/(period+1)) that creates a recursive calculation where each new value depends on the previous EMA and current price. When prices rise above the EMA, it signals potential upward momentum, while prices below suggest bearish pressure. Traders use EMA crossovers as entry and exit signals: faster EMAs crossing above slower ones indicate buying opportunities, while the reverse suggests selling points. The exponential weighting makes EMA particularly effective for tracking trending markets, as it responds quickly to directional moves while maintaining enough smoothing to filter out minor fluctuations.
Implementation Examples
Get started with EMA in just a few lines:
use vectorta::indicators::ema::{ema, EmaInput, EmaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with price data slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = EmaParams { period: Some(9) };
let input = EmaInput::from_slice(&prices, params);
let result = ema(&input)?;
// Using with Candles (default: source="close", period=9)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = EmaInput::with_default_candles(&candles);
let result = ema(&input)?;
// Access EMA values
for value in result.values {
println!("EMA: {}", value);
} API Reference
Input Methods ▼
// From price slice
EmaInput::from_slice(&[f64], EmaParams) -> EmaInput
// From candles with custom source
EmaInput::from_candles(&Candles, &str, EmaParams) -> EmaInput
// From candles with default params (source="close", period=9)
EmaInput::with_default_candles(&Candles) -> EmaInput Parameters Structure ▼
pub struct EmaParams {
pub period: Option<usize>, // Default: 9
} Output Structure ▼
pub struct EmaOutput {
pub values: Vec<f64>, // EMA values
} Validation, Warmup & NaNs ▼
period > 0andperiod ≤ data.len(); elseEmaError::InvalidPeriod.- There must be at least
periodvalid points after the first finite value; otherwiseEmaError::NotEnoughValidData. - Indices before the first finite input are
NaN. - Warmup: from first finite value until
first + period - 1, output is a running mean; then standard EMA withα = 2/(n+1). - Non‑finite inputs during warmup carry forward the current mean; during EMA phase they carry forward the previous EMA value.
- Streaming: returns
Noneuntilperiodvalid values have been processed; then returns the current EMA. NaNs do not update state.
Error Handling ▼
use vectorta::indicators::ema::{ema, EmaError};
match ema(&input) {
Ok(output) => process_results(output.values),
Err(EmaError::EmptyInputData) =>
println!("Input data is empty"),
Err(EmaError::AllValuesNaN) =>
println!("All input values are NaN"),
Err(EmaError::InvalidPeriod { period, data_len }) =>
println!("Invalid period {} for data length {}", period, data_len),
Err(EmaError::NotEnoughValidData { needed, valid }) =>
println!("Need {} valid points, have {}", needed, valid),
} Python Bindings
Basic EMA Calculation ▼
import numpy as np
from vectorta import ema
# Price data
prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5], dtype=float)
# Compute EMA (default kernel = auto)
values = ema(prices, period=9)
print(values) Kernel Selection ▼
# Choose execution kernel (optional)
from vectorta import ema
values = ema(prices, period=14, kernel="avx2") # or "neon" on ARM
print(values) JavaScript / WASM Bindings
Basic Usage ▼
import { ema_js } from 'vectorta-wasm';
const prices = new Float64Array([100, 102, 101.5, 103, 105, 104.5]);
const period = 9;
const values = ema_js(prices, period);
console.log(values); Memory-Efficient Operations ▼
Use zero-copy operations for large datasets:
import { ema_alloc, ema_free, ema_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* your data */]);
const length = prices.length;
// Allocate WASM memory for input and output
const inPtr = ema_alloc(length);
const outPtr = ema_alloc(length);
// Copy input data into WASM memory
new Float64Array(memory.buffer, inPtr, length).set(prices);
// Calculate EMA directly into allocated memory
// Args: in_ptr, out_ptr, len, period
ema_into(inPtr, outPtr, length, 9);
// Read results from WASM memory (slice() to copy out)
const emaValues = new Float64Array(memory.buffer, outPtr, length).slice();
// Free allocated memory when done
ema_free(inPtr, length);
ema_free(outPtr, length);
console.log('EMA values:', emaValues); Batch Processing ▼
Test multiple period values efficiently:
import { ema_batch, ema_batch_metadata_js } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
// Define parameter sweep range
const start = 5, end = 50, step = 5; // 5, 10, 15, ... 50
// Get metadata about period combinations (single axis)
const metadata = ema_batch_metadata_js(start, end, step);
const numCombos = metadata.length;
// Calculate all combinations using unified batch API
const config = { period_range: [start, end, step] };
const result = ema_batch(prices, config);
// result is an object with values/combos/rows/cols
console.log(result.rows, result.cols);
// Access a specific row (e.g., first period)
const firstRow = result.values.slice(0, prices.length); Performance Analysis
Across sizes, Rust CPU runs about 1.23× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
Related Indicators
Arnaud Legoux Moving Average
Moving average indicator
Compound Ratio Moving Average (CoRa Wave)
Moving average indicator
Centered Weighted Moving Average
Moving average indicator
Double Exponential Moving Average
Moving average indicator
Ehlers Distance Coefficient Filter
Moving average indicator
Ehlers Error-Correcting EMA (ECEMA)
Moving average indicator