Simple Moving Average (SMA)

Parameters: period = 9

Overview

The Simple Moving Average computes the arithmetic mean of the most recent period values to produce a smoothed line that reveals underlying trend direction. SMA treats all values within the window equally, summing them and dividing by the period length to calculate each point. As new data arrives, the oldest value drops from the window while the newest enters, creating a rolling average that follows price with consistent lag. This equal weighting makes SMA the most straightforward and interpretable moving average, though it responds slowly to sudden price changes since extreme values carry the same weight as moderate ones. Traders use SMA extensively for identifying support and resistance levels, with longer periods like 50 and 200 days marking major trend lines that institutions watch closely. The indicator works well for confirming trend direction through price crosses and generating signals when faster SMAs cross slower ones, providing clear and objective entry and exit points that form the foundation of many technical trading strategies.

Implementation Examples

Compute SMA from a slice or candles:

use vectorta::indicators::sma::{sma, SmaInput, SmaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with a price slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = SmaParams { period: Some(9) };
let input = SmaInput::from_slice(&prices, params);
let result = sma(&input)?;

// Using with Candles (default: source="close", period=9)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = SmaInput::with_default_candles(&candles);
let result = sma(&input)?;

// Access values
for value in result.values {
    println!("SMA: {}", value);
}

API Reference

Input Methods
// From price slice
SmaInput::from_slice(&[f64], SmaParams) -> SmaInput

// From candles with custom source
SmaInput::from_candles(&Candles, &str, SmaParams) -> SmaInput

// From candles with default params (source="close", period=9)
SmaInput::with_default_candles(&Candles) -> SmaInput
Parameters Structure
pub struct SmaParams {
    pub period: Option<usize>, // Default: 9
}
Output Structure
pub struct SmaOutput {
    pub values: Vec<f64>, // SMA values
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ data.len(); otherwise SmaError::InvalidPeriod.
  • If all inputs are NaN: SmaError::AllValuesNaN.
  • First finite index = first; require at least period valid points after first, else SmaError::NotEnoughValidData.
  • Warmup: indices [0 .. first+period-2] are NaN; first valid output at first+period-1.
  • Zero‑copy API sma_into_slice checks output length; mismatch yields SmaError::OutputLenMismatch.
Error Handling
use vectorta::indicators::sma::{sma, SmaError};

match sma(&input) {
    Ok(output) => process_results(output.values),
    Err(SmaError::EmptyData) => eprintln!("empty data"),
    Err(SmaError::InvalidPeriod { period, data_len }) =>
        eprintln!("invalid period: {} (data_len={})", period, data_len),
    Err(SmaError::NotEnoughValidData { needed, valid }) =>
        eprintln!("not enough valid data: needed={}, valid={}", needed, valid),
    Err(SmaError::AllValuesNaN) => eprintln!("all inputs are NaN"),
    Err(SmaError::OutputLenMismatch { expected, got }) =>
        eprintln!("output length mismatch: expected={}, got={}", expected, got),
}

Python Bindings

Basic Usage

Calculate SMA using NumPy arrays:

import numpy as np
from vectorta import sma, sma_batch, SmaStream

prices = np.asarray([100.0, 102.0, 101.5, 103.0, 105.0, 104.5], dtype=float)

# Basic
values = sma(prices, period=9, kernel="auto")
print(values)

# Batch sweep (start, end, step)
out = sma_batch(prices, period_range=(5, 30, 5), kernel="auto")
print(out["values"].shape)  # (num_periods, len(prices))
print(out["periods"])
Streaming Real-time Updates

O(1) update complexity with internal state:

from vectorta import SmaStream

stream = SmaStream(period=9)
for price in price_feed:
    y = stream.update(float(price))
    if y is not None:
        print(y)
CUDA Acceleration

Available when built with CUDA support:

import numpy as np
from vectorta import sma_cuda_batch_dev, sma_cuda_many_series_one_param_dev

# Device batch: single series, many periods (f32 input for GPU efficiency)
prices_f32 = np.asarray([/* your data */], dtype=np.float32)
dev_buf, meta = sma_cuda_batch_dev(prices_f32, period_range=(5, 30, 5), device_id=0)
print(meta["periods"])  # periods used

# Many series (time-major [T, N]), one parameter set
data_tm = np.asarray([/* T x N */], dtype=np.float32)
dev_buf2 = sma_cuda_many_series_one_param_dev(data_tm, period=20, device_id=0)

JavaScript/WASM Bindings

Basic Usage

Calculate SMA in JavaScript/TypeScript:

import { sma_js } from 'vectorta-wasm';

const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);
const result = sma_js(prices, 9); // period=9
console.log('SMA values:', result);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { sma_alloc, sma_free, sma_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* your data */]);
const n = prices.length;

const inPtr = sma_alloc(n);
const outPtr = sma_alloc(n);

new Float64Array(memory.buffer, inPtr, n).set(prices);

// in_ptr, out_ptr, len, period
sma_into(inPtr, outPtr, n, 9);

const out = new Float64Array(memory.buffer, outPtr, n).slice();
console.log('SMA:', out);

sma_free(inPtr, n);
sma_free(outPtr, n);
Batch Processing

Test multiple periods efficiently:

import { sma_batch_js, sma_batch_metadata_js } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);

const start = 5, end = 30, step = 5; // 5,10,15,20,25,30
const periods = sma_batch_metadata_js(start, end, step);

const flat = sma_batch_js(prices, start, end, step);
const numCombos = periods.length;

const rows: number[][] = [];
for (let i = 0; i < numCombos; i++) {
  const off = i * prices.length;
  rows.push(Array.from(flat.slice(off, off + prices.length)));
}

Performance Analysis

Comparison:
View:

Across sizes, Rust CPU runs about 2.04× faster than Tulip C in this benchmark.

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05

Related Indicators