Squeeze Momentum Indicator (TTM Squeeze)

Parameters: length_bb = 20 | mult_bb = 2 (1.5–2.5) | length_kc = 20 | mult_kc = 1.5 (1–2)

Overview

The Squeeze Momentum Indicator identifies periods when volatility contracts sharply before explosive breakouts by comparing Bollinger Bands width to Keltner Channels width. When the Bollinger Bands squeeze completely inside the Keltner Channels, it signals a volatility compression that typically precedes significant price moves. The indicator combines this squeeze detection with a momentum histogram derived from linear regression, showing whether momentum accelerates bullishly or bearishly as the squeeze resolves. During the squeeze phase, the indicator alerts traders to prepare for a breakout, while the momentum histogram provides directional guidance once price begins moving. The squeeze condition reflects market indecision where both statistical volatility and average true range contract simultaneously, creating tension that eventually releases through strong directional movement. Traders watch for the squeeze to end and the momentum histogram to shift from declining to rising values, indicating the direction and strength of the impending breakout.

Defaults (VectorTA): length_bb=20, mult_bb=2.0, length_kc=20, mult_kc=1.5.

Implementation Examples

Get squeeze state, momentum, and signal from candles or HLC slices:

use vectorta::indicators::squeeze_momentum::{
    squeeze_momentum, SqueezeMomentumInput, SqueezeMomentumOutput, SqueezeMomentumParams
};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with candles (defaults: length_bb=20, mult_bb=2.0, length_kc=20, mult_kc=1.5)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = SqueezeMomentumInput::with_default_candles(&candles);
let out: SqueezeMomentumOutput = squeeze_momentum(&input)?;

// Or from H/L/C slices with custom params
let (high, low, close) = (
    vec![..].as_slice(),
    vec![..].as_slice(),
    vec![..].as_slice(),
);
let params = SqueezeMomentumParams { length_bb: Some(20), mult_bb: Some(2.0), length_kc: Some(20), mult_kc: Some(1.5) };
let input = SqueezeMomentumInput::from_slices(high, low, close, params);
let out = squeeze_momentum(&input)?;

// Access outputs by index
for i in 0..out.momentum.len() {
    println!(
        "squeeze={}, momentum={}, signal={}",
        out.squeeze[i], out.momentum[i], out.momentum_signal[i]
    );
}

API Reference

Input Methods
// From H/L/C slices
SqueezeMomentumInput::from_slices(&[f64], &[f64], &[f64], SqueezeMomentumParams) -> SqueezeMomentumInput

// From candles (uses high/low/close columns)
SqueezeMomentumInput::from_candles(&Candles, SqueezeMomentumParams) -> SqueezeMomentumInput

// From candles with default params (20/2.0 and 20/1.5)
SqueezeMomentumInput::with_default_candles(&Candles) -> SqueezeMomentumInput
Parameters Structure
pub struct SqueezeMomentumParams {
    pub length_bb: Option<usize>, // Default: 20
    pub mult_bb: Option<f64>,     // Default: 2.0
    pub length_kc: Option<usize>, // Default: 20
    pub mult_kc: Option<f64>,     // Default: 1.5
}
Output Structure
pub struct SqueezeMomentumOutput {
    pub squeeze: Vec<f64>,         // -1 = on, 0 = neutral, 1 = off
    pub momentum: Vec<f64>,        // Linear regression of RAW over length_kc
    pub momentum_signal: Vec<f64>, // ±1 accelerating, ±2 decelerating (NaN during warmup)
}
Validation, Warmup & NaNs
  • length_bb > 0 and length_kc > 0; otherwise SqueezeMomentumError::InvalidLength.
  • All of high/low/close must have equal length; otherwise InconsistentDataLength.
  • If all inputs are NaNAllValuesNaN. Leading NaNs delay first valid index.
  • Need at least max(length_bb, length_kc) valid points after the first finite value; else NotEnoughValidData.
  • Warmups: squeeze = max(length_bb, length_kc) − 1, momentum = length_kc − 1, signal = length_kc; indices before warmup are NaN.
Error Handling
use vectorta::indicators::squeeze_momentum::{squeeze_momentum, SqueezeMomentumError};

match squeeze_momentum(&input) {
    Ok(out) => use_results(out),
    Err(SqueezeMomentumError::EmptyData) => eprintln!("no data provided"),
    Err(SqueezeMomentumError::InvalidLength { length, data_len }) =>
        eprintln!("invalid length={} for data_len={}", length, data_len),
    Err(SqueezeMomentumError::InconsistentDataLength) => eprintln!("H/L/C length mismatch"),
    Err(SqueezeMomentumError::AllValuesNaN) => eprintln!("all values are NaN"),
    Err(SqueezeMomentumError::NotEnoughValidData { needed, valid }) =>
        eprintln!("need {} valid points, only {}", needed, valid),
}

Python Bindings

Basic Usage

Compute squeeze, momentum, and signal with NumPy arrays (defaults shown):

import numpy as np
from vectorta import squeeze_momentum

# Prepare OHLC arrays
high = np.array([...], dtype=float)
low = np.array([...], dtype=float)
close = np.array([...], dtype=float)

# Run SMI (returns three arrays)
sq, mo, si = squeeze_momentum(high, low, close, length_bb=20, mult_bb=2.0, length_kc=20, mult_kc=1.5, kernel=None)

print(sq.shape, mo.shape, si.shape)
Streaming Real-time Updates

Use the Python stream class to update with (high, low, close):

from vectorta import SqueezeMomentumStream

stream = SqueezeMomentumStream(length_bb=20, mult_bb=2.0, length_kc=20, mult_kc=1.5)

for h, l, c in ohlc_feed:
    sq, mo, si = stream.update(h, l, c)
    if sq is not None:
        act_on((sq, mo, si))
Batch Parameter Optimization

Sweep ranges; momentum matrix returned as values:

import numpy as np
from vectorta import squeeze_momentum_batch

high = np.array([...], dtype=float)
low = np.array([...], dtype=float)
close = np.array([...], dtype=float)

out = squeeze_momentum_batch(
    high, low, close,
    length_bb_range=(10, 30, 5),
    mult_bb_range=(1.5, 2.5, 0.5),
    length_kc_range=(10, 30, 5),
    mult_kc_range=(1.0, 2.0, 0.5),
    kernel=None
)

values = out['values']     # shape: (rows, len)
squeeze = out['squeeze']   # optional: squeeze state per row
signal = out['signal']     # optional: acceleration signal per row
length_bb = out['length_bb']
mult_bb = out['mult_bb']
length_kc = out['length_kc']
mult_kc = out['mult_kc']
CUDA Acceleration

CUDA support for SMI is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.

JavaScript/WASM Bindings

Basic Usage

Compute SMI in JavaScript/TypeScript (WASM):

import { squeeze_momentum_js } from 'vectorta-wasm';

// High/Low/Close as Float64Array
const high = new Float64Array([/* ... */]);
const low = new Float64Array([/* ... */]);
const close = new Float64Array([/* ... */]);

// Returns a flat array: [squeeze..., momentum..., signal...]
const values = squeeze_momentum_js(high, low, close, 20, 2.0, 20, 1.5);
const n = close.length;
const squeeze = values.slice(0, n);
const momentum = values.slice(n, 2*n);
const signal = values.slice(2*n, 3*n);
Memory-Efficient Operations

Use zero-copy buffers for large datasets:

import {
  squeeze_momentum_alloc, squeeze_momentum_free, squeeze_momentum_into, memory
} from 'vectorta-wasm';

const n = close.length;

// Allocate combined input buffer: [high..., low..., close...]
const inPtr = squeeze_momentum_alloc(3*n);
const outSq = squeeze_momentum_alloc(n);
const outMo = squeeze_momentum_alloc(n);
const outSi = squeeze_momentum_alloc(n);

// Fill input buffer
new Float64Array(memory.buffer, inPtr, n).set(high);
new Float64Array(memory.buffer, inPtr + n*8, n).set(low);
new Float64Array(memory.buffer, inPtr + 2*n*8, n).set(close);

// Compute into pre-allocated outputs
squeeze_momentum_into(inPtr, outSq, outMo, outSi, n, 20, 2.0, 20, 1.5);

// Read results (copy out if needed)
const sq = new Float64Array(memory.buffer, outSq, n).slice();
const mo = new Float64Array(memory.buffer, outMo, n).slice();
const si = new Float64Array(memory.buffer, outSi, n).slice();

// Free buffers
squeeze_momentum_free(inPtr, 3*n);
squeeze_momentum_free(outSq, n);
squeeze_momentum_free(outMo, n);
squeeze_momentum_free(outSi, n);
Batch Processing

Sweep parameter combinations and get momentum matrices:

import { squeeze_momentum_batch } from 'vectorta-wasm';

const config = {
  length_bb_range: [10, 30, 5],
  mult_bb_range: [1.5, 2.5, 0.5],
  length_kc_range: [10, 30, 5],
  mult_kc_range: [1.0, 2.0, 0.5]
};

const out = squeeze_momentum_batch(high, low, close, config);
// out: { values, rows, cols, length_bb, mult_bb, length_kc, mult_kc }
const rows = out.rows;
const cols = out.cols;
const combos = rows; // number of parameter combinations

// Reshape flat momentum array into matrix
const matrix: number[][] = [];
for (let r = 0; r < rows; r++) {
  const start = r * cols;
  matrix.push(out.values.slice(start, start + cols));
}

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators