GMMA Oscillator

Parameters: gmma_type = guppy | smooth_length = 1 | signal_length = 13 | anchor_minutes = 0 | interval_minutes =

Overview

GMMA Oscillator condenses Guppy Multiple Moving Average structure into a single spread measure. It maintains a family of fast EMAs and a family of slow EMAs, averages each side independently, then expresses the difference as a percentage of the slow-group average. Positive readings mean the fast group is leading the slow group, while negative readings show downside compression. A separate signal line tracks that spread with an EMA.

VectorTA supports the classic six-fast and six-slow Guppy set as well as a denser Super Guppy set. It also supports anchored-timeframe scaling, where the underlying EMA periods are multiplied to approximate a higher timeframe. The main output is the oscillator itself, optionally smoothed with an SMA, alongside the signal line.

Defaults: GMMA Oscillator uses `gmma_type = "guppy"`, `smooth_length = 1`, `signal_length = 13`, `anchor_minutes = 0`, and `interval_minutes = null`.

Implementation Examples

Compute the oscillator and signal line from a price slice or from candle closes.

use vector_ta::indicators::gmma_oscillator::{
    gmma_oscillator,
    GmmaOscillatorInput,
    GmmaOscillatorParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let close = vec![100.0, 100.8, 101.4, 101.1, 102.2, 103.0, 102.7];

let output = gmma_oscillator(&GmmaOscillatorInput::from_slice(
    &close,
    GmmaOscillatorParams {
        gmma_type: Some("guppy".to_string()),
        smooth_length: Some(1),
        signal_length: Some(13),
        anchor_minutes: Some(0),
        interval_minutes: None,
    },
))?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = gmma_oscillator(&GmmaOscillatorInput::with_default_candles(&candles))?;

println!("oscillator = {:?}", output.oscillator.last());
println!("signal = {:?}", candle_output.signal.last());

API Reference

Input Methods
// From candles with an explicit source
GmmaOscillatorInput::from_candles(&Candles, &str, GmmaOscillatorParams)
    -> GmmaOscillatorInput

// From a raw price slice
GmmaOscillatorInput::from_slice(&[f64], GmmaOscillatorParams)
    -> GmmaOscillatorInput

// From candles with default source and parameters
GmmaOscillatorInput::with_default_candles(&Candles)
    -> GmmaOscillatorInput
Parameters Structure
pub struct GmmaOscillatorParams {
    pub gmma_type: Option<String>,      // "guppy" or "super_guppy"
    pub smooth_length: Option<usize>,   // default 1
    pub signal_length: Option<usize>,   // default 13
    pub anchor_minutes: Option<usize>,  // default 0
    pub interval_minutes: Option<usize>,
}
Output Structure
pub struct GmmaOscillatorOutput {
    pub oscillator: Vec<f64>,
    pub signal: Vec<f64>,
}
Validation, Warmup & NaNs
  • The input series must be non-empty and contain at least one finite value.
  • gmma_type must resolve to either guppy or super_guppy.
  • smooth_length and signal_length must both be greater than 0.
  • anchor_minutes must stay within a valid daily range, and anchored operation requires either interval_minutes or candle timestamps so the multiplier can be resolved.
  • When smooth_length > 1, the oscillator output is NaN-prefixed until that SMA warmup has filled.
  • Streaming resets when it sees a non-finite value.
  • Batch mode validates the parameter axes and rejects unsupported kernels through InvalidKernelForBatch.
Builder, Streaming & Batch APIs
// Builder
GmmaOscillatorBuilder::new()
    .gmma_type(&'static str)
    .smooth_length(usize)
    .signal_length(usize)
    .anchor_minutes(usize)
    .interval_minutes(usize)
    .kernel(Kernel)
    .apply(&Candles)

GmmaOscillatorBuilder::new()
    .apply_candles(&Candles, &str)

GmmaOscillatorBuilder::new()
    .apply_slice(&[f64])

GmmaOscillatorBuilder::new()
    .into_stream()

// Stream
GmmaOscillatorStream::try_new(GmmaOscillatorParams)
GmmaOscillatorStream::update(f64) -> Option<(f64, f64)>
GmmaOscillatorStream::reset()
GmmaOscillatorStream::get_warmup_period() -> usize

// Batch
GmmaOscillatorBatchBuilder::new()
    .gmma_type(&'static str)
    .anchor_minutes(usize)
    .interval_minutes(usize)
    .smooth_length_range((usize, usize, usize))
    .signal_length_range((usize, usize, usize))
    .kernel(Kernel)
    .apply_slice(&[f64])

GmmaOscillatorBatchBuilder::new()
    .apply_candles(&Candles, &str)
Error Handling
pub enum GmmaOscillatorError {
    EmptyInputData,
    AllValuesNaN,
    InvalidGmmaType { gmma_type: String },
    InvalidSmoothLength { smooth_length: usize },
    InvalidSignalLength { signal_length: usize },
    InvalidAnchorMinutes { anchor_minutes: usize },
    InvalidIntervalMinutes { interval_minutes: usize },
    OutputLengthMismatch { expected: usize, got: usize },
    InvalidRange { start: usize, end: usize, step: usize },
    InvalidKernelForBatch(Kernel),
    MismatchedOutputLen { dst_len: usize, expected_len: usize },
    InvalidInput { msg: String },
}

Python Bindings

Python exposes a scalar price-series function, a streaming class, and a batch sweep. The scalar path returns two NumPy arrays: oscillator and signal. The stream returns one `(oscillator, signal)` tuple or `None`, while batch returns both flattened matrices plus the tested smoothing lengths, signal lengths, GMMA mode labels, and output dimensions.

import numpy as np
from vector_ta import (
    gmma_oscillator,
    gmma_oscillator_batch,
    GmmaOscillatorStream,
)

data = np.asarray(close_values, dtype=np.float64)

oscillator, signal = gmma_oscillator(
    data,
    gmma_type="guppy",
    smooth_length=1,
    signal_length=13,
    anchor_minutes=0,
    interval_minutes=None,
    kernel="auto",
)

stream = GmmaOscillatorStream(
    gmma_type="super_guppy",
    smooth_length=3,
    signal_length=21,
    anchor_minutes=60,
    interval_minutes=5,
)
print(stream.update(data[-1]))

batch = gmma_oscillator_batch(
    data,
    gmma_type="guppy",
    smooth_length_range=(1, 3, 1),
    signal_length_range=(9, 21, 6),
    anchor_minutes=0,
    interval_minutes=None,
    kernel="auto",
)

print(batch["oscillator"].shape)
print(batch["signal_lengths"])
print(batch["gmma_types"])

JavaScript/WASM Bindings

The WASM surface exposes scalar, batch, and into-buffer entry points. The scalar function returns an object with `oscillator` and `signal` arrays. The batch function returns flattened oscillator and signal matrices plus the tested parameter combinations and output dimensions.

import init, {
  gmma_oscillator_js,
  gmma_oscillator_batch_js,
} from "@vectoralpha/vector_ta";

await init();

const single = gmma_oscillator_js(
  close,
  "guppy",
  1,
  13,
  0,
  null,
);

console.log(single.oscillator);
console.log(single.signal);

const batch = gmma_oscillator_batch_js(close, {
  gmma_type: "super_guppy",
  smooth_length_range: [1, 3, 1],
  signal_length_range: [9, 21, 6],
  anchor_minutes: 60,
  interval_minutes: 5,
});

console.log(batch.oscillator);
console.log(batch.signal);
console.log(batch.rows, 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