Adaptive Momentum Oscillator

Parameters: 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.
  • length and smoothing_length must both be at least 1.
  • The indicator requires at least length + smoothing_length valid points after the first finite bar, otherwise it returns AdaptiveMomentumOscillatorError::NotEnoughValidData.
  • Single-run outputs are filled with NaN until 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

Comparison:
View:
Placeholder data (no recorded benchmarks for this indicator)

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

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)

Related Indicators