Slope Adaptive Moving Average (SAMA)

Parameters: length = 200 | maj_length = 14 | min_length = 6

Overview

The Slope Adaptive Moving Average dynamically adjusts its responsiveness by measuring where current price falls within a recent high to low range. SAMA computes the highest and lowest prices over an extended lookback period, then determines price position as a ratio within that range to derive an adaptive smoothing factor. When price trades near range extremes, the indicator blends toward a faster minor exponential moving average to capture trend acceleration, while prices in the middle of the range shift toward a slower major EMA for stability. This position based adaptation allows SAMA to respond quickly during breakouts and trending moves while remaining smooth during consolidation phases. The calculation normalizes the position ratio and interpolates between two EMA alpha values, producing a single adaptive moving average that naturally adjusts to market structure. Traders use SAMA for dynamic support and resistance that adapts to volatility, with the indicator staying close to price during trends and widening distance during ranges.

Implementation Examples

Get started with SAMA using slices or candles:

use vector_ta::indicators::moving_averages::sama::{sama, SamaInput, SamaParams};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

// From a price slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = SamaParams { length: Some(200), maj_length: Some(14), min_length: Some(6) };
let input = SamaInput::from_slice(&prices, params);
let out = sama(&input)?;

// From candles with defaults (source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = SamaInput::with_default_candles(&candles);
let out = sama(&input)?;

for v in out.values { println!("SAMA: {}", v); }

API Reference

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

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

// From candles with default params (close prices; 200/14/6)
SamaInput::with_default_candles(&Candles) -> SamaInput
Parameters Structure
pub struct SamaParams {
    pub length: Option<usize>,     // Default: 200
    pub maj_length: Option<usize>, // Default: 14
    pub min_length: Option<usize>, // Default: 6
}
Output Structure
pub struct SamaOutput {
    pub values: Vec<f64>, // Adaptive MA values
}
Validation, Warmup & NaNs
  • length > 0, maj_length > 0, min_length > 0. Also length + 1 ≤ data.len() or SamaError::InvalidPeriod.
  • Requires at least one valid sample after the first finite value; otherwise SamaError::NotEnoughValidData.
  • Indices before the first finite input are NaN. First finite output is seeded to the first finite price (Pine‑compatible warmup).
  • Batch mode: NaN inputs at index i produce NaN at i. Streaming: update(NaN) leaves state unchanged and returns the current value if initialized.
Error Handling
use vector_ta::indicators::moving_averages::sama::{sama, SamaError};

match sama(&input) {
    Ok(output) => process(output.values),
    Err(SamaError::EmptyInputData) => eprintln!("empty input"),
    Err(SamaError::AllValuesNaN) => eprintln!("all values are NaN"),
    Err(SamaError::InvalidPeriod { period, data_len }) => {
        eprintln!("invalid period: {} for data_len {}", period, data_len)
    }
    Err(SamaError::NotEnoughValidData { needed, valid }) => {
        eprintln!("need {} data points, only {} valid", needed, valid)
    }
}

Python Bindings

Basic Usage

Calculate SAMA using NumPy arrays (length, maj_length, min_length are required):

import numpy as np
from vector_ta import sama

prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5])

# Specify parameters explicitly; choose kernel optionally ("auto", "avx2", ...)
values = sama(prices, length=200, maj_length=14, min_length=6, kernel="auto")
print(values)
Streaming Real-time Updates
from vector_ta import SamaStream

stream = SamaStream(length=200, maj_length=14, min_length=6)
for price in feed:
    value = stream.update(price)
    if value is not None:
        print("SAMA:", value)
Batch Parameter Optimization
import numpy as np
from vector_ta import sama_batch

prices = np.array([...])

results = sama_batch(
    prices,
    length_range=(100, 300, 50),
    maj_length_range=(10, 20, 2),
    min_length_range=(4, 12, 2),
    kernel="auto",
)

# Results is a dict-like object with 'values', 'combos', 'rows', 'cols'
print(results["rows"], results["cols"])
CUDA Acceleration

CUDA helpers are available when the Python package is built with CUDA support. Inputs must be float32; outputs are device arrays (DLPack / __cuda_array_interface__ compatible).

import numpy as np
from vector_ta import sama_cuda_batch_dev, sama_cuda_many_series_one_param_dev

# One series (float32)
data_f32 = np.asarray(load_data(), dtype=np.float32)

dev = sama_cuda_batch_dev(
    data_f32=data_f32,
    length_range=(5, 30, 5),
    maj_length_range=(5, 30, 5),
    min_length_range=(5, 30, 5),
    device_id=0,
)

# Many series (time-major)
data_tm_f32 = np.asarray(load_data_time_major_matrix(), dtype=np.float32)

dev_tm = sama_cuda_many_series_one_param_dev(
    data_tm_f32=data_tm_f32,
    length=14,
    maj_length=14,
    min_length=14,
    device_id=0,
)

JavaScript/WASM Bindings

Basic Usage

Calculate SAMA in JavaScript/TypeScript:

import { sama_js } from 'vectorta-wasm';

const prices = new Float64Array([100, 102, 101.5, 103, 105, 104.5]);
const values = sama_js(prices, 200, 14, 6);
console.log('SAMA:', values);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { sama_alloc, sama_free, sama_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* your data */]);
const len = prices.length;
const inPtr = sama_alloc(len);
const outPtr = sama_alloc(len);

new Float64Array(memory.buffer, inPtr, len).set(prices);
sama_into(inPtr, outPtr, len, 200, 14, 6);

const out = new Float64Array(memory.buffer, outPtr, len).slice();
sama_free(inPtr, len);
sama_free(outPtr, len);
Batch Processing

Test multiple combinations using unified batch API:

import { sama_batch } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);
const config = {
  length_range: [100, 300, 50],
  maj_length_range: [10, 20, 2],
  min_length_range: [4, 12, 2],
};

const result = sama_batch(prices, config);
// result = { values: Float64Array(flat), combos: SamaParams[], rows, cols }
console.log(result.rows, result.cols);

CUDA Bindings (Rust)

use vector_ta::cuda::CudaSama;
use vector_ta::indicators::moving_averages::sama::SamaBatchRange;

let cuda = CudaSama::new(0)?;

let data_f32: [f32] = /* ... */;
let sweep = SamaBatchRange::default();

let out = cuda.sama_batch_dev(&data_f32, &sweep)?;
let _ = out;

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators