Adaptive Momentum Oscillator
length = 14 | smoothing_length = 9 Overview
Adaptive Momentum Oscillator is built to answer a narrower question than a standard momentum study: how strong is the most dominant move inside the recent lookback, and is that move still sustaining? In the VectorTA implementation, each new value is compared against the prior length bars and the largest absolute delta becomes the raw momentum candidate. That raw series is then smoothed with a linear regression stream to produce amo, while a second adaptive averaging stage produces ama as a companion line.
In practice, traders can treat amo as the faster adaptive momentum read and use ama as a slower confirmation line. Crosses between the two can help separate temporary surges from sustained directional pressure, while the sign and expansion of amo can be used to judge whether momentum is accelerating or fading. Because the raw engine only becomes reliable after enough historical bars have accumulated, early values should be considered warmup rather than decision-ready output.
Defaults: Adaptive Momentum Oscillator uses `length = 14` and `smoothing_length = 9`.
Implementation Examples
Compute Adaptive Momentum Oscillator from a slice or directly from candle data.
use vector_ta::indicators::adaptive_momentum_oscillator::{
adaptive_momentum_oscillator,
AdaptiveMomentumOscillatorInput,
AdaptiveMomentumOscillatorParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let close = vec![100.0, 99.6, 100.9, 102.1, 101.8, 103.5, 104.2];
let params = AdaptiveMomentumOscillatorParams {
length: Some(14),
smoothing_length: Some(9),
};
let input = AdaptiveMomentumOscillatorInput::from_slice(&close, params);
let result = adaptive_momentum_oscillator(&input)?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_input = AdaptiveMomentumOscillatorInput::with_default_candles(&candles);
let candle_result = adaptive_momentum_oscillator(&candle_input)?;
println!("Latest AMO: {:?}", result.amo.last());
println!("Latest AMA: {:?}", result.ama.last());
println!("Candle output length: {}", candle_result.amo.len()); API Reference
Input Methods ▼
// From candles with an explicit source field
AdaptiveMomentumOscillatorInput::from_candles(&Candles, "close", AdaptiveMomentumOscillatorParams) -> AdaptiveMomentumOscillatorInput
// From a raw numeric slice
AdaptiveMomentumOscillatorInput::from_slice(&[f64], AdaptiveMomentumOscillatorParams) -> AdaptiveMomentumOscillatorInput
// From candles using the default close source and default params
AdaptiveMomentumOscillatorInput::with_default_candles(&Candles) -> AdaptiveMomentumOscillatorInput Parameters Structure ▼
pub struct AdaptiveMomentumOscillatorParams {
pub length: Option<usize>, // default: 14
pub smoothing_length: Option<usize>, // default: 9
} Output Structure ▼
pub struct AdaptiveMomentumOscillatorOutput {
pub amo: Vec<f64>, // smoothed adaptive momentum oscillator
pub ama: Vec<f64>, // adaptive average companion line
} Validation, Warmup & NaNs ▼
- Empty input returns
AdaptiveMomentumOscillatorError::EmptyInputData. - An all-NaN series returns
AdaptiveMomentumOscillatorError::AllValuesNaN. lengthandsmoothing_lengthmust both be at least1.- The indicator requires at least
length + smoothing_lengthvalid points after the first finite bar, otherwise it returnsAdaptiveMomentumOscillatorError::NotEnoughValidData. - Single-run outputs are filled with
NaNuntil the adaptive raw stage and smoothing stage have enough history to produce finite values. - Streaming state can be reset explicitly with
reset(). - Batch mode rejects non-batch kernels via
AdaptiveMomentumOscillatorError::InvalidKernelForBatch.
Error Handling ▼
use vector_ta::indicators::adaptive_momentum_oscillator::AdaptiveMomentumOscillatorError;
match adaptive_momentum_oscillator(&input) {
Ok(output) => {
println!("latest AMO = {:?}", output.amo.last());
}
Err(AdaptiveMomentumOscillatorError::EmptyInputData) =>
eprintln!("Adaptive Momentum Oscillator needs at least one input value."),
Err(AdaptiveMomentumOscillatorError::AllValuesNaN) =>
eprintln!("Adaptive Momentum Oscillator cannot initialize from an all-NaN series."),
Err(AdaptiveMomentumOscillatorError::InvalidLength { length }) =>
eprintln!("length must be >= 1, got {length}."),
Err(AdaptiveMomentumOscillatorError::InvalidSmoothingLength { smoothing_length }) =>
eprintln!("smoothing_length must be >= 1, got {smoothing_length}."),
Err(AdaptiveMomentumOscillatorError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {needed} valid points, only {valid} available."),
Err(AdaptiveMomentumOscillatorError::OutputLengthMismatch { expected, got }) =>
eprintln!("Output buffers must be {expected} long, got {got}."),
Err(AdaptiveMomentumOscillatorError::InvalidLengthRange { start, end, step }) =>
eprintln!("Invalid length range: ({start}, {end}, {step})"),
Err(AdaptiveMomentumOscillatorError::InvalidSmoothingLengthRange { start, end, step }) =>
eprintln!("Invalid smoothing length range: ({start}, {end}, {step})"),
Err(AdaptiveMomentumOscillatorError::InvalidKernelForBatch(kernel)) =>
eprintln!("Kernel {:?} cannot be used for batch mode.", kernel),
} Python Bindings
Basic Usage ▼
The Python helper returns two NumPy arrays: `amo` and `ama`.
import numpy as np
from vector_ta import adaptive_momentum_oscillator
close = np.array([100.0, 99.6, 100.9, 102.1, 101.8, 103.5, 104.2], dtype=np.float64)
amo, ama = adaptive_momentum_oscillator(
close,
length=14,
smoothing_length=9,
kernel="auto",
)
print(amo[-5:])
print(ama[-5:]) Streaming Real-time Updates ▼
The Python stream object mirrors the Rust stream and exposes `update()` plus `reset()`.
from vector_ta import AdaptiveMomentumOscillatorStream
stream = AdaptiveMomentumOscillatorStream(length=14, smoothing_length=9)
for close in live_close_feed:
value = stream.update(close)
if value is not None:
amo, ama = value
print(amo, ama)
stream.reset() Batch Processing ▼
Batch mode returns a dictionary with flattened outputs and the `length`/`smoothing_length` grid used for each row.
import numpy as np
from vector_ta import adaptive_momentum_oscillator_batch
close = np.asarray(load_close_series(), dtype=np.float64)
result = adaptive_momentum_oscillator_batch(
close,
length_range=(10, 18, 4),
smoothing_length_range=(5, 9, 2),
kernel="auto",
)
print(result["rows"], result["cols"])
print(result["lengths"])
print(result["smoothing_lengths"])
print(result["amo"].shape, result["ama"].shape) JavaScript/WASM Bindings
Basic Usage ▼
The main WASM helper returns a serialized object with `amo` and `ama` arrays.
import { adaptive_momentum_oscillator_js } from 'vectorta-wasm';
const close = new Float64Array([100.0, 99.6, 100.9, 102.1, 101.8, 103.5, 104.2]);
const result = adaptive_momentum_oscillator_js(close, 14, 9) as {
amo: number[];
ama: number[];
};
console.log(result.amo.slice(-5));
console.log(result.ama.slice(-5)); Memory-Efficient Operations ▼
Use the allocation helpers and `adaptive_momentum_oscillator_into` when you want explicit control over the output buffers.
import {
adaptive_momentum_oscillator_alloc,
adaptive_momentum_oscillator_free,
adaptive_momentum_oscillator_into,
memory,
} from 'vectorta-wasm';
const close = new Float64Array([/* close values */]);
const len = close.length;
const inPtr = adaptive_momentum_oscillator_alloc(len);
const amoPtr = adaptive_momentum_oscillator_alloc(len);
const amaPtr = adaptive_momentum_oscillator_alloc(len);
new Float64Array(memory.buffer, inPtr, len).set(close);
adaptive_momentum_oscillator_into(inPtr, amoPtr, amaPtr, len, 14, 9);
const amo = new Float64Array(memory.buffer, amoPtr, len).slice();
const ama = new Float64Array(memory.buffer, amaPtr, len).slice();
adaptive_momentum_oscillator_free(inPtr, len);
adaptive_momentum_oscillator_free(amoPtr, len);
adaptive_momentum_oscillator_free(amaPtr, len); Batch Processing ▼
The unified batch helper accepts `length_range` and `smoothing_length_range` and returns flattened outputs with combo metadata.
import { adaptive_momentum_oscillator_batch } from 'vectorta-wasm';
const close = new Float64Array([/* historical closes */]);
const batch = adaptive_momentum_oscillator_batch(close, {
length_range: [10, 18, 4],
smoothing_length_range: [5, 9, 2],
}) as {
amo: number[];
ama: number[];
combos: Array<{ length?: number; smoothing_length?: number }>;
rows: number;
cols: number;
};
console.log(batch.rows, batch.cols);
console.log(batch.combos[0]);
console.log(batch.amo.slice(0, batch.cols)); 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)
Related Indicators
Acceleration Oscillator
Technical analysis indicator
Accumulation/Distribution
Technical analysis indicator
Awesome Oscillator
Technical analysis indicator
Absolute Price Oscillator
Technical analysis indicator
Commodity Channel Index
Technical analysis indicator
CCI Cycle
Technical analysis indicator