Low Pass Channel (LPC)
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_typemust be'adaptive'or'fixed'; otherwiseLpcError::InvalidCutoffType.fixed_period > 0andfixed_period ≤ data_len; otherwiseLpcError::InvalidPeriod.- There must be at least
2valid points after the first finite bar; otherwiseLpcError::NotEnoughValidData. - If any input slice is empty:
LpcError::EmptyInputData. If all values areNaN:LpcError::AllValuesNaN. - Warmup: indices before the first fully‑finite bar are
NaN. At the first valid index,filter = src[first]and bands are seeded fromhigh[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
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05