Non-Stationarity
Unlike audio signals, market data exhibits regime changes where statistical properties shift dramatically. Filters must adapt or maintain robustness across these transitions.
Like master craftsmen who understand not just how to use their tools but why they work, this guide unveils the mathematical foundations underlying our curated collection of digital signal processing filters. Here, we bridge the gap between academic DSP theory and practical trading applications.
Financial markets generate time series data with unique characteristics that challenge traditional DSP approaches:
Non-Stationarity
Unlike audio signals, market data exhibits regime changes where statistical properties shift dramatically. Filters must adapt or maintain robustness across these transitions.
Heavy Tails
Price movements follow distributions with fatter tails than Gaussian, requiring filters that handle extreme events gracefully without distortion.
Multi-Scale Dynamics
Markets operate on multiple timescales simultaneously — from microsecond arbitrage to decade-long cycles. Effective filters must isolate relevant scales.
The Discrete Fourier Transform (DFT) decomposes price series into frequency components:
X(k) = Σ(n=0 to N-1) x(n) × e^(-j2πkn/N)
Where:
x(n)
= price at time nX(k)
= frequency component kN
= number of samplesThis reveals the cyclical patterns hidden in price data.
Filters are characterised by their transfer function H(z):
H(z) = Y(z)/X(z) = (b₀ + b₁z⁻¹ + ... + bₘz⁻ᵐ)/(1 + a₁z⁻¹ + ... + aₙz⁻ⁿ)
The poles and zeros of H(z) determine:
Key frequency relationships in trading:
Low-pass filters attenuate high-frequency noise while preserving trend information. In trading, these create smoothed price representations for trend following.
The general form of an Infinite Impulse Response (IIR) low-pass filter:
y[n] = Σ(i=0 to M) bᵢ×x[n-i] - Σ(j=1 to N) aⱼ×y[n-j]
Where:
bᵢ
= feedforward coefficientsaⱼ
= feedback coefficientsM, N
= filter orders2-Pole Butterworth Design
Critical frequency: ωc = √2 × π / periodPole location: a₁ = e^(-ωc)
Transfer function:H(z) = K × (1 + 2z⁻¹ + z⁻²)/(1 - 2a₁cos(ωc)z⁻¹ + a₁²z⁻²)
Where K ensures unity gain at DC
Characteristics:
Zero-Lag Through Subtraction
Concept: LPF = APF - HPF
2-Pole Implementation:ω = √2 × π / perioda₁ = e^(-ω)
HPF coefficients:c₀ = (1 + 2a₁cos(ω) - a₁²) / 4c₁ = 2a₁cos(ω)c₂ = -a₁²
Final output subtracts HPF from input
Unique Properties:
Parametric Second-Order Section
Normalised frequency: fc = 0.5 / periodAngular frequency: ω = 2π × fc
Q-factor influence:α = sin(ω)/(2Q)
Coefficients:b₀ = b₂ = (1 - cos(ω))/2 × 1/(1+α)b₁ = (1 - cos(ω)) × 1/(1+α)a₁ = -2cos(ω) × 1/(1+α)a₂ = (1 - α) × 1/(1+α)
Q-Factor Effects:
MESA Adaptive Moving Average (MAMA)
MAMA adjusts its smoothing based on the dominant market cycle:
1. Hilbert Transform extracts phase: HT(x) = 0.0962×x + 0.5769×x[2] - 0.5769×x[4] - 0.0962×x[6]
2. Homodyne Discriminator finds period: Phase difference → Instantaneous period
3. Adaptive alpha: α = FastLimit / DeltaPhase (constrained by SlowLimit)
4. Dual-speed smoothing: MAMA = α×price + (1-α)×MAMA[1] FAMA = α/2×MAMA + (1-α/2)×FAMA[1]
The result adapts to market conditions, tightening during trends and loosening during cycles.
High-pass filters remove trend components to isolate short-term fluctuations, creating oscillators for mean reversion strategies.
Transfer Function:H(z) = K × (1 - z⁻¹)/(1 - αz⁻¹)
Where:ω = π / periodα = e^(-ω)K = (1 - α)/2
Implementation:y[n] = K×(x[n] - x[n-1]) + α×y[n-1]
Properties:
Enhanced attenuation:ω = √2 × π / perioda₁ = e^(-ω)
Coefficients:c₀ = (1 + 2a₁cos(ω) - a₁²)/4c₁ = 2a₁cos(ω)c₂ = -a₁²
Output:y[n] = c₀×(x[n] - 2×x[n-1] + x[n-2]) + c₁×y[n-1] + c₂×y[n-2]
Advantages:
Band-pass filters extract specific frequency ranges, ideal for identifying market cycles.
A band-pass filter combines high-pass and low-pass characteristics:
Centre frequency: f₀ = 1/periodBandwidth: BW (in frequency units)Quality factor: Q = f₀/BW
Transfer function peak at f₀Attenuation outside passband
Centre frequency: fc = 0.5/periodω = 2π × fc
Bandwidth to Q conversion:Q = 1/(2×sinh(ln(2)/2 × BW × ω/sin(ω)))
Filter coefficients:α = sin(ω)×sinh(ln(2)/2 × Q × ω/sin(ω))
b₀ = sin(ω)/2 × 1/(1+α)b₁ = 0b₂ = -b₀a₁ = -2cos(ω) × 1/(1+α)a₂ = (1-α) × 1/(1+α)
Combined approach with AGC:
1. Highpass preprocessing2. Bandpass filtering: BP = 0.5×(1-α)×(HP - HP[2]) + β×(1+α)×BP[1] - α×BP[2]
3. Automatic Gain Control: Peak = 0.991×Peak[1] if |BP| > Peak: Peak = |BP| Normalised = BP/Peak
Results in consistent amplitude output.
Cascaded design for ultra-clean extraction:
Stage 1: 2-pole Butterworth HPF (remove trend)Stage 2: 2-pole SuperSmoother (remove noise)
Result: Pristine cycle componentPhase distortion: MinimalLag: Balanced across frequency range
Mathematical Basis The Voss filter achieves negative group delay through anticipatory coupling:
Filter order: N = 3 × prediction_horizon
Output computation:y[n] = 0.5×(3+N)×x[n] - Σ(i=0 to N-1)((i+1)/N × y[n-N+i])
Key insight: Weighted feedback creates phase leadLimitation: Input must be band-limitedPractical range: 1-3 bars prediction
Critical Understanding: Prediction is only valid for band-limited signals. Raw price cannot be meaningfully predicted; only filtered components.
Group Delay Formula:
τg(ω) = -d(∠H(e^jω))/dω
Measures filter delay at each frequency. Zero-lag filters achieve τg ≈ 0 in passband.
Variance Reduction:
SR = σ²(input)/σ²(output)
Higher ratios indicate better noise suppression.
Magnitude Response:
|H(e^jω)| across frequency spectrum
Ideal: Unity in passband, zero in stopband, sharp transition.
# Recalculate only on parameter changeif period != period[1]: omega = calculate_omega(period) update_coefficients(omega)
# Use cached coefficients for filteringoutput = apply_filter(input, cached_coefficients)
# Efficient circular buffer for filter statesclass FilterState: def __init__(self, order): self.buffer = [0] * order self.index = 0
def update(self, value): self.buffer[self.index] = value self.index = (self.index + 1) % len(self.buffer)
# Prevent coefficient overflowdef stabilise_poles(poles): return [p * 0.999 if abs(p) >= 1 else p for p in poles]
# Handle startup conditionsdef initialise_filter(source): return source if bar_index < warmup_period else filtered_value
Cascade architectures, or serial processing chains, allow complex signal processing by chaining multiple filters together:
Noise → Trend → Cycle:
Adaptive Cascade:
Predictive Chain:
While not implemented in FiltersToolkit, wavelet concepts inspire multi-scale approaches:
Price = Trend + Cycles + Noise
Trend: Ultimate Smoother (long period)Cycles: Multiple band-pass filtersNoise: Residual after filtering
Adaptive decomposition philosophy:
Similar results achievable with adaptive filter banks.
Cryptocurrency
Forex
Equities
This mathematical reference is part of our commitment to sharing institutional-grade DSP knowledge with the trading community. By documenting these foundations, we enable traders to:
Related Open Source Resources:
“Mathematics is the language in which the universe speaks. In financial markets, filters are our translators—converting the cacophony of price action into comprehensible signals. Master the mathematics, master the message.” 📊