Squeeze Momentum Indicator (TTM Squeeze)
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 > 0andlength_kc > 0; otherwiseSqueezeMomentumError::InvalidLength.- All of
high/low/closemust have equal length; otherwiseInconsistentDataLength. - If all inputs are
NaN→AllValuesNaN. LeadingNaNs delay first valid index. - Need at least
max(length_bb, length_kc)valid points after the first finite value; elseNotEnoughValidData. - Warmups: squeeze =
max(length_bb, length_kc) − 1, momentum =length_kc − 1, signal =length_kc; indices before warmup areNaN.
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
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05