Low Pass Channel (LPC)

Parameters: cutoff_type = adaptive | fixed_period = 20 | max_cycle_limit = 60 | cycle_mult = 1 | tr_mult = 1

Overview

The Low Pass Channel (LPC) attenuates higher frequency price oscillations and volatility with minimal lag using a single pole IIR low pass filter that smooths the price series while preserving trend information. Channel bands are constructed by applying the same filter to the True Range values and then adding and subtracting this filtered volatility from the smoothed price centerline. When operating in adaptive mode, the indicator uses John Ehlers' Instantaneous Frequency Measurement technique to detect the dominant price cycle through Hilbert Transform calculations, automatically adjusting the filter cutoff to match market conditions. Traders employ LPC to identify smooth trend direction while the channel bands provide dynamic support and resistance levels that expand and contract based on filtered volatility. Unlike simple moving average channels, LPC's single pole filter design offers superior frequency response characteristics, effectively removing market noise while maintaining responsiveness to genuine price movements.

Defaults: cutoff_type='adaptive', fixed_period=20, max_cycle_limit=60, cycle_mult=1.0, tr_mult=1.0.

Implementation Examples

Compute LPC from candles or raw OHLC + source slices:

use vectorta::indicators::lpc::{lpc, LpcInput, LpcParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using raw slices (high, low, close, src)
let high = vec![101.0, 103.0, 102.0];
let low = vec![ 99.0, 100.0, 100.5];
let close = vec![100.5, 102.0, 101.2];
let src = close.clone();
let params = LpcParams { cutoff_type: Some("adaptive".into()), fixed_period: Some(20), max_cycle_limit: Some(60), cycle_mult: Some(1.0), tr_mult: Some(1.0) };
let input = LpcInput::from_slices(&high, &low, &close, &src, params);
let out = lpc(&input)?;

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

// Access components
for i in 0..out.filter.len() {
    println!("filter={}, high={}, low={}", out.filter[i], out.high_band[i], out.low_band[i]);
}

API Reference

Input Methods
// From raw OHLC + source slices
LpcInput::from_slices(&[f64], &[f64], &[f64], &[f64], LpcParams) -> LpcInput

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

// From candles with defaults (source = "close")
LpcInput::with_default_candles(&Candles) -> LpcInput
Parameters Structure
#[derive(Debug, Clone, Default)]
pub struct LpcParams {
    pub cutoff_type: Option<String>, // Default: "adaptive"
    pub fixed_period: Option<usize>, // Default: 20
    pub max_cycle_limit: Option<usize>, // Default: 60
    pub cycle_mult: Option<f64>,    // Default: 1.0
    pub tr_mult: Option<f64>,       // Default: 1.0
}
Output Structure
pub struct LpcOutput {
    pub filter: Vec<f64>,
    pub high_band: Vec<f64>,
    pub low_band: Vec<f64>,
}
Validation, Warmup & NaNs
  • All four inputs must have equal length; otherwise LpcError::MissingData.
  • cutoff_type must be 'adaptive' or 'fixed'; otherwise LpcError::InvalidCutoffType.
  • fixed_period > 0 and fixed_period ≤ data_len; otherwise LpcError::InvalidPeriod.
  • There must be at least 2 valid points after the first finite bar; otherwise LpcError::NotEnoughValidData.
  • If any input slice is empty: LpcError::EmptyInputData. If all values are NaN: LpcError::AllValuesNaN.
  • Warmup: indices before the first fully‑finite bar are NaN. At the first valid index, filter = src[first] and bands are seeded from high[first]-low[first].
Error Handling
use vectorta::indicators::lpc::{lpc, LpcError};

match lpc(&input) {
    Ok(output) => process(output.filter, output.high_band, output.low_band),
    Err(LpcError::EmptyInputData) =>
        eprintln!("lpc: input slice is empty"),
    Err(LpcError::AllValuesNaN) =>
        eprintln!("lpc: all values are NaN"),
    Err(LpcError::InvalidPeriod { period, data_len }) =>
        eprintln!("lpc: invalid period: {} (len = {})", period, data_len),
    Err(LpcError::NotEnoughValidData { needed, valid }) =>
        eprintln!("lpc: need {} valid points, got {}", needed, valid),
    Err(LpcError::InvalidCutoffType { cutoff_type }) =>
        eprintln!("lpc: invalid cutoff type: {}", cutoff_type),
    Err(LpcError::MissingData) =>
        eprintln!("lpc: required OHLC data missing or mismatched lengths"),
}

Python Bindings

Basic Usage

Compute LPC from NumPy arrays (defaults: adaptive, period 20, max_cycle_limit 60, multipliers 1.0):

import numpy as np
from vectorta import lpc

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

# Defaults (adaptive cutoff)
filt, hi, lo = lpc(high, low, close, src)

# Fixed cutoff with period 20
filt, hi, lo = lpc(high, low, close, src,
                   cutoff_type='fixed', fixed_period=20,
                   max_cycle_limit=60, cycle_mult=1.0, tr_mult=1.0,
                   kernel='auto')

print(f"filter[0:3] = {filt[:3]}")
Streaming Real-time Updates
from vectorta import LpcStream

stream = LpcStream(cutoff_type='fixed', fixed_period=20, max_cycle_limit=60, cycle_mult=1.0, tr_mult=1.0)
for (h, l, c, s) in ohlc_src_feed:
    result = stream.update(h, l, c, s)
    if result is not None:
        filt, hi, lo = result
        handle(filt, hi, lo)
Batch Parameter Optimization

Sweep fixed periods and multipliers across a grid:

import numpy as np
from vectorta import lpc_batch

high, low, close, src = ...  # NumPy float64 arrays of equal length

res = lpc_batch(
    high, low, close, src,
    fixed_period_range=(10, 20, 5),
    cycle_mult_range=(1.0, 1.0, 0.0),
    tr_mult_range=(1.0, 1.5, 0.5),
    cutoff_type='fixed', max_cycle_limit=60, kernel='auto'
)

print(res['values'].shape)  # (rows, cols) with rows = combos*3 (filter, high, low)
print(res['fixed_periods'], res['cycle_mults'], res['tr_mults'])
print(res['order'])  # ['filter', 'high', 'low']
CUDA Acceleration

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

# Coming soon: CUDA-accelerated LPC calculations
# Planned kernel parameter remaining consistent with CPU/AVX paths.

JavaScript / WASM

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

const high = new Float64Array([101.0, 103.0, 102.0]);
const low = new Float64Array([ 99.0, 100.0, 100.5]);
const close = new Float64Array([100.5, 102.0, 101.2]);
const src = close;

// Returns flat array: [filter..., high..., low...]
const values = lpc(high, low, close, src, 'fixed', 20, 60, 1.0, 1.0);
const len = src.length;
const filter = values.slice(0, len);
const highBand = values.slice(len, 2*len);
const lowBand = values.slice(2*len, 3*len);
Memory-Efficient Operations

Use zero-copy *_alloc/*_into for large arrays:

import { lpc_alloc, lpc_free, lpc_into, memory } from 'vectorta-wasm';

const len = src.length;
// Allocate output buffers in WASM memory
const fPtr = lpc_alloc(len);
const hPtr = lpc_alloc(len);
const lPtr = lpc_alloc(len);

// Copy inputs into WASM memory
const hIn = lpc_alloc(len), lIn = lpc_alloc(len), cIn = lpc_alloc(len), sIn = lpc_alloc(len);
new Float64Array(memory.buffer, hIn, len).set(high);
new Float64Array(memory.buffer, lIn, len).set(low);
new Float64Array(memory.buffer, cIn, len).set(close);
new Float64Array(memory.buffer, sIn, len).set(src);

// Compute directly into pre-allocated buffers
lpc_into(hIn, lIn, cIn, sIn, fPtr, hPtr, lPtr, len, 'fixed', 20, 60, 1.0, 1.0);

// Read results
const filter = new Float64Array(memory.buffer, fPtr, len).slice();
const highBand = new Float64Array(memory.buffer, hPtr, len).slice();
const lowBand = new Float64Array(memory.buffer, lPtr, len).slice();

// Free
[fPtr, hPtr, lPtr, hIn, lIn, cIn, sIn].forEach(ptr => lpc_free(ptr, len));
Batch Processing

Sweep parameter ranges and get a rows×cols matrix:

import { lpc_batch } from 'vectorta-wasm';

const cfg = {
  fixed_period_range: [10, 20, 5],
  cycle_mult_range: [1.0, 1.0, 0.0],
  tr_mult_range: [1.0, 1.5, 0.5],
  cutoff_type: 'fixed',
  max_cycle_limit: 60,
};

const out = lpc_batch(high, low, close, src, cfg);
console.log(out.rows, out.cols, out.order); // order: ['filter','high','low']
console.log(out.fixed_periods, out.cycle_mults, out.tr_mults);

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators