Back to Publications
Signal Processing // Algorithmic Finance

Denoising HFT Noise:
Causal Rolling Fourier Filters

An advanced mathematical signal engine and interactive visualization dashboard tailored for cleaning High-Frequency Trading (HFT) microstructure noise in real-time. By transforming tick-by-tick prices into the frequency domain, we isolate and mute high-frequency bid-ask bouncing while reconstructing clean, lag-free trend signals.

T
Tensor R&D Lab
June 23, 2026
8 Min Read

01/ The Microstructure Noise Bottleneck

In algorithmic finance and high-frequency trading (HFT), tick-by-tick orderbook data is plagued by rapid, artificial price fluctuations. This is known as microstructure noise, and it is primarily driven by bid-ask bouncing, order cancellations, and execution fragmentation.

Traditional linear filters, such as the Simple Moving Average (SMA) or Exponential Moving Average (EMA), attempt to smooth this noise by averaging historical prices. However, these time-domain convolutions introduce a substantial <strong>phase lag</strong> $\tau$ directly proportional to their window size:

$$\tau_{\text{SMA}} = \frac{L - 1}{2}$$

This delay degrades the performance of execution algorithms. By the time a lagged moving average detects a structural market breakout, the optimal entry price has frequently disappeared, leading to severe slippage.

02/ Frequency-Domain Spectral Filters

Rather than averaging prices in the time domain, we translate our price series into the frequency domain. Because microstructure bid-ask bounce occurs at rapid speeds (high frequencies) and macroscopic trends drift slowly (low frequencies), we can isolate and mute noise using a spectral mask.

The uniform price series $x_n$ of length $N$ is first decomposed into frequency coefficients $X_k$ using the <strong>Discrete Fourier Transform (DFT)</strong>:

$$X_k = \sum_{n=0}^{N-1} x_n e^{-i \frac{2\pi}{N} k n}, \quad k = 0, \dots, N-1$$

We then apply a low-pass binary frequency mask $H(f_k)$ to the coefficients, muting any signal component whose frequency exceeds our cutoff frequency $f_c$:

$$H(f_k) = \begin{cases} 1 & |f_k| \le f_c \\ 0 & |f_k| > f_c \end{cases}$$
$$\tilde{X}_k = X_k \cdot H(f_k)$$

Finally, we map the filtered spectrum back into the time domain via the Inverse DFT (IDFT):

$$\tilde{x}_n = \frac{1}{N} \sum_{k=0}^{N-1} \tilde{X}_k e^{i \frac{2\pi}{N} k n}, \quad n = 0, \dots, N-1$$

03/ Spectral Leakage & Mirrored Windowing

A major challenge of applying the FFT to rolling financial price data is spectral leakage. The FFT assumes that the input window is periodic. When prices drift, this boundary mismatch creates a sharp step discontinuity, which causes severe edge distortion at the boundaries—distorting our most critical value: the current price.

Rectangular Window

No tapering. Subject to extreme edge warping at the boundary.

Hamming Taper

Tapering resolves boundary leakage but attenuates active price amplitude.

Mirrored Hann (Optimal)

Symmetrically mirrors window around current price where attenuation is 0.

The Mirrored Windowing technique achieves zero attenuation at the current price boundary:

$$z = [x_0, x_1, \dots, x_{W-1}, x_{W-2}, \dots, x_1], \quad w[W-1] = 1.0$$

Interactive Signal Sandbox

Active Engine
Fourier MSE
0.1420
SMA MSE
1.4820
Lag Reduction
~72% Less Lag
Noise Muted
94.8%
Observed Price (Noisy)
Causal Rolling FFT
SMA (Lagged)
True Trend

05/ Causal Execution & Backtesting

In live production, the signal engine operates strictly in a rolling window buffer. Each incoming tick is pushed to a buffer of size $W$, the mirroring is applied, and the forward/inverse FFT operates strictly on this historic slice, preventing future data leakage.

06/ Python Production Engine

import numpy as np

class FourierFilter:
    """
    A causal rolling Fast Fourier Transform (FFT) noise-reduction filter.
    """
    def __init__(self, window_size: int, cutoff_hz: float, fs: float, window_type: str = 'mirror'):
        self.window_size = window_size
        self.cutoff_hz = cutoff_hz
        self.fs = fs
        self.window_type = window_type.lower()

    def filter_window(self, window_data: np.ndarray) -> float:
        W = len(window_data)
        if self.window_type == 'mirror':
            mirrored = window_data[-2:0:-1]
            z = np.concatenate([window_data, mirrored])
            M = len(z)
            w = np.hanning(M)
            mean_val = np.mean(z)
            fft_vals = np.fft.fft((z - mean_val) * w)
            freqs = np.fft.fftfreq(M, d=1.0/self.fs)
            mask = np.abs(freqs) <= self.cutoff_hz
            reconstructed = np.fft.ifft(fft_vals * mask).real
            center_idx = W - 1
            return (reconstructed[center_idx] / w[center_idx]) + mean_val