2-Pole High‑Pass Filter

Parameters: period = 48 | k = 0.707 (0.1–1)

Overview

The 2-Pole High Pass Filter isolates short term price oscillations by systematically removing trend components, revealing the cyclical patterns and noise that remain after the dominant directional bias gets filtered out. Using second order recursive filtering with two feedback loops, the indicator achieves sharper frequency separation than single pole filters, creating a cleaner distinction between trend and cycle components in price data. The resulting output oscillates around zero, with positive values indicating price sits above its trend component and negative values showing price below trend, effectively detrending the series to expose pure momentum dynamics. Traders employ this filter to identify overbought and oversold conditions within trending markets, as extreme readings suggest price has deviated too far from its underlying trend and may revert. The cutoff factor k controls the filter's selectivity, with the default 0.707 providing critical damping that balances response sharpness against stability, preventing the ringing artifacts that plague more aggressive filter designs. Advanced traders combine the high pass output with other indicators to separate signal from noise, using the filtered values to confirm whether price movements represent genuine momentum shifts or merely temporary fluctuations around the prevailing trend.

Implementation Examples

Compute the 2‑pole high‑pass filter from a slice or candles:

use vectorta::indicators::highpass_2_pole::{highpass_2_pole, HighPass2Input, HighPass2Params};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// From a price slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = HighPass2Params { period: Some(48), k: Some(0.707) }; // defaults
let input = HighPass2Input::from_slice(&prices, params);
let out = highpass_2_pole(&input)?;

// From candles (default params; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = HighPass2Input::with_default_candles(&candles);
let out = highpass_2_pole(&input)?;

// Values
for v in out.values { println!("hp2: {}", v); }

API Reference

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

// From candles with custom source
HighPass2Input::from_candles(&Candles, &str, HighPass2Params) -> HighPass2Input

// From candles with default params (close, period=48, k=0.707)
HighPass2Input::with_default_candles(&Candles) -> HighPass2Input
Parameters Structure
pub struct HighPass2Params {
    pub period: Option<usize>, // Default: 48
    pub k: Option<f64>,        // Default: 0.707
}
Output Structure
pub struct HighPass2Output {
    pub values: Vec<f64>, // High‑pass values
}
Validation, Warmup & NaNs
  • period ≥ 2 and period ≤ data.len(); otherwise HighPass2Error::InvalidPeriod.
  • k > 0 and finite; otherwise HighPass2Error::InvalidK.
  • After the first finite input, need at least period valid points; else HighPass2Error::NotEnoughValidData.
  • Indices before the first finite value are NaN. First two finite outputs seed from input; full values accrue after warmup.
  • Streaming: HighPass2Stream::update returns None until period samples are processed.
Error Handling
use vectorta::indicators::highpass_2_pole::{highpass_2_pole, HighPass2Error};

match highpass_2_pole(&input) {
    Ok(output) => consume(output.values),
    Err(HighPass2Error::EmptyInputData) => eprintln!("empty input"),
    Err(HighPass2Error::AllValuesNaN) => eprintln!("all values are NaN"),
    Err(HighPass2Error::InvalidPeriod { period, data_len }) =>
        eprintln!("invalid period {} for len {}", period, data_len),
    Err(HighPass2Error::InvalidK { k }) => eprintln!("invalid k {}", k),
    Err(HighPass2Error::NotEnoughValidData { needed, valid }) =>
        eprintln!("need {} valid points, have {}", needed, valid),
    Err(HighPass2Error::OutputLengthMismatch { .. }) => eprintln!("buffer length mismatch"),
}

Python Bindings

Basic Usage
import numpy as np
from vectorta import highpass_2_pole

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

# Defaults period=48, k=0.707
hp = highpass_2_pole(prices, period=48, k=0.707)

# Specify kernel ('auto', 'scalar', 'avx2', 'avx512') if desired
hp = highpass_2_pole(prices, period=48, k=0.707, kernel="auto")

print(hp)
Streaming Real‑time Updates
from vectorta import HighPass2Stream

stream = HighPass2Stream(period=48, k=0.707)
for price in price_feed:
    val = stream.update(price)
    if val is not None:
        handle(val)
Batch Parameter Optimization
import numpy as np
from vectorta import highpass_2_pole_batch

prices = np.array([...], dtype=float)

results = highpass_2_pole_batch(
    prices,
    period_range=(24, 64, 8),
    k_range=(0.5, 1.0, 0.1),
    kernel="auto",
)

vals = results["values"]           # shape: (num_combos, len(prices))
periods = results["periods"]       # tested period values
ks = results["k"]                   # tested k values
CUDA Acceleration

CUDA device APIs for HP2 (development names):

import numpy as np
from vectorta import (
    highpass_2_pole_cuda_batch_dev,
    highpass_2_pole_cuda_many_series_one_param_dev,
)

# One series, many params (Float32 input on device)
data_f32 = np.asarray([...], dtype=np.float32)
dev_arr = highpass_2_pole_cuda_batch_dev(
    data_f32,
    period_range=(48, 48, 0),
    k_range=(0.707, 0.707, 0.0),
    device_id=0,
)

# Many series (time‑major), one parameter set
tm = np.asarray([[...]], dtype=np.float32)  # shape [T, N]
dev_arr = highpass_2_pole_cuda_many_series_one_param_dev(
    tm, period=48, k=0.707, device_id=0
)

JavaScript/WASM Bindings

Basic Usage
import { highpass_2_pole_js } from 'vectorta-wasm';

const prices = new Float64Array([100, 102, 101.5, 103, 105, 104.5]);
const hp = highpass_2_pole_js(prices, 48, 0.707);
console.log('HP2:', hp);
Memory‑Efficient Operations
import { highpass_2_pole_alloc, highpass_2_pole_free, highpass_2_pole_into, memory } from 'vectorta-wasm';

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

new Float64Array(memory.buffer, inPtr, n).set(prices);
highpass_2_pole_into(inPtr, outPtr, n, 48, 0.707);
const hp = new Float64Array(memory.buffer, outPtr, n).slice();

highpass_2_pole_free(inPtr, n);
highpass_2_pole_free(outPtr, n);
Batch Processing
import { highpass_2_pole_batch } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);
const config = { period_range: [24, 64, 8], k_range: [0.5, 1.0, 0.1] };
const res = highpass_2_pole_batch(prices, config);

// res = { values: Float64Array, combos: Array<{ period?: number, k?: number }>, rows: number, cols: number }
// Example: reshape into matrix of rows x cols
const rows = res.rows, cols = res.cols;
const mat: number[][] = [];
for (let r = 0; r < rows; r++) {
  const start = r * cols;
  mat.push(Array.from(res.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