Slope Adaptive Moving Average (SAMA)
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. Alsolength + 1 ≤ data.len()orSamaError::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:
NaNinputs at indexiproduceNaNati. 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
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-02-28