Range Filter

Parameters: range_size = 2.618 (0.1–10) | range_period = 14 | smooth_range = true | smooth_period = 27

Overview

The Range Filter creates a smooth price line that follows significant movements while filtering out minor fluctuations by restricting price changes to a volatility based channel around the previous filtered value. This indicator measures market volatility through the exponential moving average of absolute price changes, then uses this dynamic range to determine whether new price movements are significant enough to update the filter or should be ignored as noise. When price moves beyond the established range boundaries, the filter updates to follow the trend, but smaller movements within the range get absorbed, creating a step like pattern that clearly shows genuine trend changes. The optional range smoothing adds another layer of filtering that prevents the bands from reacting too quickly to volatility spikes. Traders apply the Range Filter for trend identification and as a trailing stop mechanism, since it naturally follows price at a distance determined by market volatility rather than fixed parameters.

Implementation Examples

Get started with the Range Filter in a few lines:

use vectorta::indicators::range_filter::{range_filter, RangeFilterInput, RangeFilterParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with price data slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = RangeFilterParams {
    range_size: Some(2.618),
    range_period: Some(14),
    smooth_range: Some(true),
    smooth_period: Some(27),
};
let input = RangeFilterInput::from_slice(&prices, params);
let out = range_filter(&input)?;

// Using with Candles (defaults: source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = RangeFilterInput::with_default_candles(&candles);
let out = range_filter(&input)?;

// Access the outputs
for (f, h, l) in itertools::izip!(out.filter, out.high_band, out.low_band) {
    println!("filter={}, high={}, low={}", f, h, l);
}

API Reference

Input Methods
// From price slice
RangeFilterInput::from_slice(&[f64], RangeFilterParams) -> RangeFilterInput

// From candles with explicit source
RangeFilterInput::from_candles(&Candles, &str, RangeFilterParams) -> RangeFilterInput

// With defaults (source = "close"; params = defaults)
RangeFilterInput::with_default_candles(&Candles) -> RangeFilterInput
Parameters Structure
#[derive(Debug, Clone)]
pub struct RangeFilterParams {
    pub range_size: Option<f64>,     // Default: 2.618 (must be > 0)
    pub range_period: Option<usize>, // Default: 14 (>= 1)
    pub smooth_range: Option<bool>,  // Default: true
    pub smooth_period: Option<usize>,// Default: 27 (>= 1 when smooth_range)
}
Output Structure
#[derive(Debug, Clone)]
pub struct RangeFilterOutput {
    pub filter: Vec<f64>,
    pub high_band: Vec<f64>,
    pub low_band: Vec<f64>,
}
Validation, Warmup & NaNs
  • range_size must be finite and > 0; otherwise RangeFilterError::InvalidRangeSize.
  • range_period ≥ 1 and ≤ len; if smooth_range=true, then smooth_period ≥ 1 and ≤ len; else RangeFilterError::InvalidPeriod.
  • Leading NaNs are skipped; if all values are NaN, returns RangeFilterError::AllValuesNaN.
  • Warmup: indices [0 .. first + max(range_period, smooth_period if smooth_range)] are set to NaN in all three outputs.
  • If there aren’t enough valid points after the first finite value, returns RangeFilterError::NotEnoughValidData with required vs valid counts.
Error Handling
use vectorta::indicators::range_filter::{range_filter, RangeFilterError};

match range_filter(&input) {
    Ok(out) => handle(out),
    Err(RangeFilterError::EmptyInputData) => eprintln!("empty input"),
    Err(RangeFilterError::AllValuesNaN) => eprintln!("all values are NaN"),
    Err(RangeFilterError::InvalidRangeSize { range_size }) =>
        eprintln!("invalid range_size: {}", range_size),
    Err(RangeFilterError::InvalidPeriod { period, data_len }) =>
        eprintln!("invalid period={} for len={}", period, data_len),
    Err(RangeFilterError::NotEnoughValidData { needed, valid }) =>
        eprintln!("need {} valid points, only {}", needed, valid),
}

Python Bindings

Basic Usage

Calculate the Range Filter on NumPy arrays (defaults: 2.618 / 14 / smooth=true / 27):

import numpy as np
from vectorta import range_filter

prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5])

# Defaults
f, h, l = range_filter(prices)

# Custom parameters and kernel selection
f, h, l = range_filter(
    prices,
    range_size=2.618,
    range_period=14,
    smooth_range=True,
    smooth_period=27,
    kernel="avx2"  # or "auto"
)

print(f"Filter: {f}
High: {h}
Low: {l}")
Streaming Real-time Updates

Use the Python stream class to process price ticks:

from vectorta import RangeFilterStream

# Constructor requires explicit parameters
stream = RangeFilterStream(
    range_size=2.618,
    range_period=14,
    smooth_range=True,
    smooth_period=27,
)

for price in price_feed:
    value = stream.update(price)  # (filter, high, low) or None during warmup
    if value is not None:
        f, h, l = value
        # ... use f/h/l ...
Batch Parameter Optimization

Sweep multiple combinations and get columnar metadata:

import numpy as np
from vectorta import range_filter_batch

prices = np.array([...])

res = range_filter_batch(
    prices,
    range_size_start=1.5, range_size_end=3.0, range_size_step=0.5,
    range_period_start=10, range_period_end=20, range_period_step=5,
    smooth_range=True, smooth_period=27, kernel="auto"
)

print(res['filter'].shape, res['rows'], res['cols'])
print(res['range_sizes'], res['range_periods'])

JavaScript / WASM

Quick Start

Compute filter and bands from a Float64Array:

import { range_filter_js } from 'vectorta-wasm';

const prices = new Float64Array([/* prices */]);

// Optional params (use null for defaults)
const result = range_filter_js(prices, 2.618, 14, true, 27);
// result is a JSON-like object with { filter, high, low, rows, cols?, combos? } in this build
console.log(result);
Memory-Efficient Operations

Use flat output buffers to avoid extra allocations:

import { range_filter_alloc, range_filter_free, range_filter_into_flat, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* your data */]);
const n = prices.length;

// Allocate WASM memory for input and a single flat output buffer (3 * n)
const inPtr = range_filter_alloc(n);
const outPtr = range_filter_alloc(3 * n);

// Copy input into WASM memory
new Float64Array(memory.buffer, inPtr, n).set(prices);

// Compute directly into flat buffer: [filter..., high..., low...]
range_filter_into_flat(inPtr, outPtr, n, 2.618, 14, true, 27);

// Read back slices
const outFlat = new Float64Array(memory.buffer, outPtr, 3 * n).slice();
const filter = outFlat.slice(0, n);
const high   = outFlat.slice(n, 2 * n);
const low    = outFlat.slice(2 * n, 3 * n);

// Free memory
range_filter_free(inPtr, n);
range_filter_free(outPtr, 3 * n);
Batch Processing

Evaluate multiple parameter sets in one call:

import { range_filter_batch } from 'vectorta-wasm';

const prices = new Float64Array([/* prices */]);

const res = range_filter_batch(
  prices,
  1.5, 3.0, 0.5,  // range_size: start, end, step
  10,  20,  5,    // range_period: start, end, step
  true, 27        // smoothing controls
);

// res contains flat arrays and metadata fields in this build
console.log(res);

CUDA

CUDA Acceleration

CUDA support for Range Filter is coming soon.

# Coming soon: CUDA-accelerated Range Filter calculations
# The API will mirror other CUDA-enabled indicators in this project.

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators