TTM Squeeze

Parameters: length = 20 | bb_mult = 2 | kc_mult = 1.5

Overview

TTM Squeeze detects periods of low volatility followed by explosive breakouts by comparing two volatility measurement systems simultaneously. The indicator overlays Bollinger Bands and Keltner Channels to identify squeeze conditions where volatility contracts tightly, often preceding significant price moves. When the Bollinger Bands squeeze inside the Keltner Channels, the market is consolidating energy for a potential breakout. The indicator pairs this volatility assessment with a momentum oscillator derived from linear regression, helping traders anticipate both when a move will occur and in which direction. Multi-level squeeze detection classifies compression intensity as low, mid, or high, providing nuanced insight into the magnitude of potential breakouts. Traders watch for the squeeze to fire, marking the transition from compression to expansion as volatility returns. Default parameters use a 20 period length with Bollinger Band multiplier of 2.0 and graduated Keltner Channel thresholds at 1.0, 1.5, and 2.0.

Implementation Examples

Compute momentum and squeeze vectors from slices or candles:

use vectorta::indicators::ttm_squeeze::{ttm_squeeze, TtmSqueezeInput, TtmSqueezeParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using OHLC slices
let high = vec![101.0, 102.0, 103.0, 104.0];
let low  = vec![ 99.0, 100.0, 100.5, 101.0];
let close= vec![100.5, 101.5, 102.0, 103.0];
let params = TtmSqueezeParams { length: Some(20), bb_mult: Some(2.0), kc_mult_high: Some(1.0), kc_mult_mid: Some(1.5), kc_mult_low: Some(2.0) };
let input = TtmSqueezeInput::from_slices(&high, &low, &close, params);
let out = ttm_squeeze(&input)?; // out.momentum, out.squeeze

// Using Candles with defaults (length=20, bb=2.0, kc_high=1.0, kc_mid=1.5, kc_low=2.0)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = TtmSqueezeInput::with_default_candles(&candles);
let out = ttm_squeeze(&input)?;

for (m, s) in out.momentum.iter().zip(out.squeeze.iter()) {
    println!("momentum={:.4}, squeeze_state={}", m, s);
}

API Reference

Input Methods
// From OHLC slices
TtmSqueezeInput::from_slices(&[f64], &[f64], &[f64], TtmSqueezeParams) -> TtmSqueezeInput

// From candles
TtmSqueezeInput::from_candles(&Candles, TtmSqueezeParams) -> TtmSqueezeInput

// From candles with default params
TtmSqueezeInput::with_default_candles(&Candles) -> TtmSqueezeInput
Parameters Structure
pub struct TtmSqueezeParams {
    pub length: Option<usize>,     // Default: 20
    pub bb_mult: Option<f64>,      // Default: 2.0
    pub kc_mult_high: Option<f64>, // Default: 1.0
    pub kc_mult_mid: Option<f64>,  // Default: 1.5
    pub kc_mult_low: Option<f64>,  // Default: 2.0
}
Output Structure
pub struct TtmSqueezeOutput {
    pub momentum: Vec<f64>, // Momentum oscillator
    pub squeeze: Vec<f64>,  // Squeeze state: 0=No, 1=Low, 2=Mid, 3=High
}
Validation, Warmup & NaNs
  • Slice lengths must match (high/low/close); otherwise TtmSqueezeError::InconsistentSliceLengths.
  • length > 0 and length ≤ len(close); otherwise TtmSqueezeError::InvalidPeriod.
  • If all closes are NaN, returns TtmSqueezeError::AllValuesNaN.
  • Must have at least length valid points after the first finite close; else TtmSqueezeError::NotEnoughValidData.
  • Warmup index is first + length − 1; outputs before this are NaN.
  • ttm_squeeze_into_slices requires destination slices equal to source length; otherwise TtmSqueezeError::NotEnoughValidData is returned with needed=len.
  • Invalid multipliers (non‑finite or ≤ 0) are rejected via TtmSqueezeError::SmaError / LinRegError pathways.
Error Handling
use vectorta::indicators::ttm_squeeze::{ttm_squeeze, TtmSqueezeError};

match ttm_squeeze(&input) {
    Ok(out) => handle(out.momentum, out.squeeze),
    Err(TtmSqueezeError::EmptyInputData) => println!("Input data is empty"),
    Err(TtmSqueezeError::AllValuesNaN) => println!("All input values are NaN"),
    Err(TtmSqueezeError::InvalidPeriod { period, data_len }) =>
        println!("Invalid period {} for length {}", period, data_len),
    Err(TtmSqueezeError::NotEnoughValidData { needed, valid }) =>
        println!("Need {} valid points, only {}", needed, valid),
    Err(TtmSqueezeError::InconsistentSliceLengths { high, low, close }) =>
        println!("Length mismatch: h={}, l={}, c={}", high, low, close),
    Err(TtmSqueezeError::SmaError(e)) => println!("SMA error: {}", e),
    Err(TtmSqueezeError::LinRegError(e)) => println!("LinReg error: {}", e),
}

Python Bindings

Basic Usage

Compute TTM Squeeze momentum and squeeze arrays using NumPy:

import numpy as np
from vectorta import ttm_squeeze  # function name: ttm_squeeze(high, low, close, ...)

high = np.array([101.0, 102.0, 103.0], dtype=np.float64)
low  = np.array([ 99.0, 100.0, 101.0], dtype=np.float64)
close= np.array([100.5, 101.0, 102.0], dtype=np.float64)

momentum, squeeze = ttm_squeeze(
    high, low, close,
    length=20, bb_mult=2.0,
    kc_mult_high=1.0, kc_mult_mid=1.5, kc_mult_low=2.0,
    kernel=None  # or "auto", "scalar", "avx2", "avx512" if available
)

print(momentum.shape, squeeze.shape)
Streaming Real-time Updates
from vectorta import TtmSqueezeStream

stream = TtmSqueezeStream(length=20, bb_mult=2.0, kc_mult_high=1.0, kc_mult_mid=1.5, kc_mult_low=2.0)
for (h, l, c) in ohlc_feed:
    out = stream.update(h, l, c)
    if out is not None:
        momentum, squeeze = out
        handle(momentum, squeeze)
Batch Processing
import numpy as np
from vectorta import ttm_squeeze_batch

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

res = ttm_squeeze_batch(
    high, low, close,
    length_range=(20, 24, 1),
    bb_mult_range=(2.0, 2.0, 0.0),
    kc_high_range=(1.0, 1.2, 0.1),
    kc_mid_range=(1.5, 1.7, 0.1),
    kc_low_range=(2.0, 2.2, 0.1),
    kernel=None
)

# res is a dict with numpy arrays: "momentum" and "squeeze" shaped [rows, cols]
print(res["momentum"].shape, res["squeeze"].shape)
CUDA Acceleration

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

# Coming soon: CUDA-accelerated TTM Squeeze calculations

JavaScript/WASM Bindings

Memory-Efficient Operations

Use zero-copy operations with the exported WASM helpers:

import { ttm_squeeze_alloc, ttm_squeeze_free, ttm_squeeze_into, memory } from 'vectorta-wasm';

// Prepare your OHLC data
const high = new Float64Array([/* ... */]);
const low  = new Float64Array([/* ... */]);
const close= new Float64Array([/* ... */]);
const len = close.length;

// Allocate output buffers in WASM memory
const outMom = ttm_squeeze_alloc(len);
const outSqz = ttm_squeeze_alloc(len);

// Compute directly into output buffers
ttm_squeeze_into(high, low, close, 20, 2.0, 1.0, 1.5, 2.0,
                 new Float64Array(memory.buffer, outMom, len),
                 new Float64Array(memory.buffer, outSqz, len));

// Read results (slice() to copy out)
const momentum = new Float64Array(memory.buffer, outMom, len).slice();
const squeeze  = new Float64Array(memory.buffer, outSqz, len).slice();

// Free buffers when done
ttm_squeeze_free(outMom, len);
ttm_squeeze_free(outSqz, len);
Batch Processing

Test multiple parameter combinations via a unified batch API:

import { ttm_squeeze_batch } from 'vectorta-wasm';

const cfg = {
  length_range: [20, 24, 1],
  bb_mult_range: [2.0, 2.0, 0.0],
  kc_high_range: [1.0, 1.2, 0.1],
  kc_mid_range: [1.5, 1.7, 0.1],
  kc_low_range: [2.0, 2.2, 0.1],
};

// Returns a packed object with values(rows*cols*2), rows, cols, combos
const js = ttm_squeeze_batch(high, low, close, cfg);

// Unpack (pairs of rows per combo: momentum then squeeze)
const rows = js.rows;
const cols = js.cols;
const values = js.values; // Float64Array
// Example: first combo momentum and squeeze
const mom0 = values.slice(0, cols);
const sqz0 = values.slice(cols, 2 * cols);

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators