Skip to main content

Squeeze Kernel Covariance Estimator

CI PyPI Python License: MIT DOI Paper

A streaming covariance estimator for panels of financial returns that beats every fitted baseline out of sample, costs half a millisecond to ten milliseconds a day, and has one parameter.

from squeeze_kernel import SqueezeKernel

sk = SqueezeKernel(lam=0.996)        # the entire public surface
for r_t in returns:                  # NaN marks missing assets
    sk.update(r_t)
cov = sk.covariance()                # positive semi-definite by construction, every day
  • State of the art, measured. On thirty years of S&P 500 constituents it leads ten baselines fitted to the data (CM-IEWMA, DCC, DCC-NL, nonlinear shrinkage, Ledoit–Wolf, OAS, RMT denoising, Gerber, IEWMA, EWMA) at every universe size from 50 to 300, every Diebold–Mariano p below 10⁻⁷, and it is the sole member of the 90% model confidence set at every size. With its configuration frozen on data to 2021 it stays first on 2022–2026, on twenty-four sealed small- and mid-cap universes it had never seen, on a 121-instrument futures panel across eight asset classes, and on the public Fama–French 49 panel on the competing authors' own metric.
  • Fast, with nothing that can fail. One O(Kn²) state update, one Cholesky factorisation of the blend and K positive-definiteness checks a day. No rolling window, no likelihood refit, no daily solver. 0.54 ms a day at n = 50, 10.1 ms at n = 300. The closest competitor on accuracy, CM-IEWMA as released by its authors, takes 18 to 51 times as long and spends most of it in a convex programme that is infeasible on about one scored day in five at n = 300.
  • One number, no tuning. Everything else is derived from lam, fixed by a structural argument, or computed online from the estimator's own state. Listings, delistings and halts enter as NaN. Only dependency: NumPy.

Paper: Kende (2026), The Squeeze Kernel Covariance Estimator, SSRN 6455918. Every number on this page is taken from the tables of the paper's September 2026 revision, A Diagonal-Congruence Flow in Per-Asset Trading Time, which describes the 3.x estimator and carries the protocol, the proofs and the full ablation ledger.

Benchmarks · See the difference · Installation · Quickstart · How it works · Citation

Why

Markets do not keep calendar time. Following Mandelbrot, the estimator treats a panel as a collection of partially coupled markets, each advancing on its own activity-driven clock, and reads those clocks from the panel's own correlation structure: a hot cluster (say precious metals and FX) advances its correlation state while an idle one (agriculture) does not, and nobody has to identify the cluster. On those clocks it runs a single recursion that learns in market time and forgets in calendar time, shrinks itself from two online statistics, and re-weights its three timescales by the gradient of its own log score. Nothing is fitted but lam.

Against the field it is scored on:

Squeeze Kernel CM-IEWMA (as released) DCC, DCC-NL Window shrinkage (LW, OAS, NLS, RMT)
Clock per-asset market clocks calendar calendar calendar window
Daily work O(Kn²) update, one Cholesky, no solver daily convex programme GARCH recursion re-estimate on the window
Tuned in the paper nothing: lam = 0.996, fixed on data to 2021 fitted on a tuning block, re-fitted to each sealed universe fitted on a tuning block, re-fitted to each sealed universe fitted on a tuning block, re-fitted to each sealed universe
Milliseconds a day, n = 50 → 300 0.54 → 10.1 18.8 → 182.2 0.05 → 0.83, 0.09 → 1.83 not timed
Days the solver fails, n = 300 no solver 1,196 of 6,439 scored — —

Benchmarks

The score is the one-step-ahead Gaussian negative log-likelihood (NLL) of the next day's returns under each estimator's forecast, averaged per day; lower is better. Every baseline is fitted on a tuning block before it is scored. The ranking holds under the Student-t and density-power scores as well.

Thirty years of S&P 500 constituents, 1995–2026

Each baseline's row is its excess NLL per day over the Squeeze Kernel. Every Diebold–Mariano p is below 10⁻⁷. The Squeeze Kernel is the sole member of the 90% model confidence set at every size.

n = 50 n = 100 n = 200 n = 300
Squeeze Kernel, average NLL −371.28 −753.21 −1539.32 −2334.88
CM-IEWMA +1.63 +4.58 +12.45 +20.70
DCC +2.04 +5.31 +13.49 +25.38
DCC-NL +2.21 +5.53 +13.90 +24.67
RMT denoising +6.60 +14.72 +32.39 +58.94
Gerber (PSD) +7.00 +15.61 +38.82 +62.98
Nonlinear shrinkage +6.29 +16.10 +42.84 +73.22
IEWMA +3.98 +16.56 +79.79 +224.73
EWMA +7.10 +19.92 +58.71 +80.81
Ledoit–Wolf +6.75 +20.44 +60.42 +98.94
OAS +8.44 +23.96 +66.04 +106.56

Out of sample, configuration frozen on data to 2021

n = 50 n = 100 n = 200 n = 300
S&P constituents, 2022–2026: lead over the best baseline (CM-IEWMA at every size), NLL per day 1.72 3.72 11.12 19.16
Twenty-four sealed small- and mid-cap universes, six per size, 2022–2026, field fitted to each universe: universes on which it is first 6 of 6 6 of 6 6 of 6 6 of 6
lead over the best baseline, range 0.1–1.2 2.7–4.0 8.6–10.9 13.2–17.5
sole member of the 90% model confidence set 1 of 6 6 of 6 6 of 6 6 of 6

Carried zero-shot to other panels, same lam:

Panel Result
121 futures, eight asset classes, held out from mid-2018, baselines calibrated on that panel's own history first; leads DCC-NL by 11.3 NLL per day (p = 10⁻⁸), IEWMA by 14.4, DCC by 18.2, EWMA by 36.0, Ledoit–Wolf by 121.4
Fama–French 49 industries, scored with the CM-IEWMA authors' own regret metric (lower is better) 16.35, against CM-IEWMA 16.96, IEWMA 18.20, EWMA 19.39

Cost

Milliseconds per forecast day, single-threaded, over the full thirty-year pass:

n = 50 n = 100 n = 200 n = 300
Squeeze Kernel 0.54 0.91 2.80 10.1
CM-IEWMA as released 18.8 46.4 106.8 182.2
of which the daily convex programme 16.1 39.9 85.4 142.1
days on which the programme fails 0 50 294 1,196
peak memory, GB 2.0 6.4 22.4 45.3
DCC 0.05 0.11 0.28 0.83
DCC-NL 0.09 0.25 0.92 1.83

Two honest footnotes. The released CM-IEWMA code overstates what the method has to cost: its linear algebra alone, with the programme treated as free, would run in 60% to 121% of the Squeeze Kernel's time. The cost is the solver, and so is the fragility. And DCC and DCC-NL are cheaper than both; they are also 13 to 25 NLL per day behind at n = 200 and 300.

See the difference

A passive strategy any allocator would recognise: long-only minimum-variance over 300 liquid US stocks, scaled to a 15% volatility target, rebalanced monthly, 5 bps costs. Two runs on identical data; the only difference is the covariance matrix. The Squeeze Kernel arm runs SqueezeKernel(lam=0.996), nothing tuned on this panel.

Vol-targeted long-only minimum-variance portfolio on 300 US equities: Squeeze Kernel vs Ledoit-Wolf equity curves, drawdown, realized volatility, and risk/return profile
Method CAGR Vol Sharpe MaxDD Calmar Vol-target RMSE
Squeeze Kernel (default) 12.6% 13.6% 0.92 -34.3% 0.37 7.05%
Ledoit-Wolf (252d) 11.7% 14.6% 0.80 -38.7% 0.30 7.51%

Reproduce from the repo alone (the 300-stock panel ships as a parquet; survivorship and provenance are documented in the script):

pip install squeeze-kernel pandas pyarrow scikit-learn matplotlib
python examples/vol_targeted_portfolio.py    # ~1 minute for the 26-year stream

Installation

pip install squeeze-kernel          # NumPy only
pip install "squeeze-kernel[full]"  # + SciPy (faster Cholesky factorisations for the mixture)

Quickstart

import numpy as np
from squeeze_kernel import SqueezeKernel, estimate_squeeze_cov

returns = np.random.default_rng(42).normal(0.0, 0.01, size=(500, 30))

sk = SqueezeKernel()                 # lam=0.996 (anchor half-life ~173 days)
for r_t in returns:
    w = sk.update(r_t)               # returns the day's kernel weight
cov, corr = sk.covariance(), sk.correlation()
sk.state()                           # kernel scale, per-timescale effective sizes, mixture tilt

# batch mode: full panel in, covariance path out
cov_path, corr_path, weights = estimate_squeeze_cov(returns, with_weights=True)

Missing values: pass NaN (or mask= on update). Newly listed, delisted or halted assets need no imputation and no complete-case subsetting.

Units: pass returns in decimal units (0.01 = 1%). The numerical floors are absolute (ε = 1e-8 in squared-return units), so percent-unit returns give a different path. A panel whose first day carries many exact-zero returns starts with a transient that the slow timescale remembers; start a day later.

How it works

flowchart LR
    R["return r_t<br/>(NaN = missing)"] --> V["variance ladder<br/>3 rungs per asset,<br/>pooled panel-wide"]
    V --> Z["standardised<br/>surprise z_t"]
    Z --> W["per-asset clocks<br/>from the correlation<br/>neighbourhood"]
    W --> Q["diagonal-congruence update<br/>of 3 correlation timescales,<br/>outer product scaled by n/m"]
    Q --> S["per-timescale shrinkage<br/>raw / equicorrelation /<br/>Hadamard square"]
    S --> B["blend across timescales<br/>by the gradient of<br/>its own log score"]
    B --> O["Σ_t"]

One daily update, in words:

  1. Volatility with self-selected memory. Each asset's variance runs on a three-rung ladder of half-lives (h/16, h/4, h), pooled panel-wide by weights that move on saturated evidence, so one spike day cannot hand a stale rung weeks of weight. The day's return is standardised by it.
  2. Per-asset clocks. Each asset reads its activity as the neighbourhood mean of squared surprises, weighted by the Hadamard square of yesterday's correlation (under conditional Gaussianity that square is the correlation of squared returns, so the neighbourhood is the asset's own cluster, no labels needed). A saturating kernel maps activity to a clock increment in [0, 1): an unbounded shock is squeezed into at most one unit of trading time.
  3. Magnitude once, direction once. The day's Mahalanobis norm under the estimator's own forecast, m = r'Σ⁻¹r, sets the clocks; the innovation then enters with weight n/m, so a day's direction moves the state and its size only says how much trading time it carried. Score-driven and robust estimators down-weight such a day as contamination; this one reads it as time.
  4. Diagonal-congruence flow on three timescales. Each correlation state advances by a congruence plus a rank-one term on the clocks, with decays (lam⁴, lam, lam^¼). In closed form it is an exponentially weighted Gram matrix of clock-weighted returns: pairs accrue at the geometric mean of their two clock increments, which is exactly the Cauchy–Schwarz bound on how far two clocks can overlap and the most positive semi-definiteness allows. No eigenvalue clipping, no nearest-PSD repair, any missing-data pattern.
  5. Self-tuning, label-free shrinkage. Each timescale's shrunk correlation is a pool of its raw correlation, its equicorrelation level and its Hadamard square. The Hadamard square suppresses cross-cluster noise quadratically (0.10 → 0.01) while keeping intra-cluster blocks (0.60 → 0.36). The pool's prior weights come from a closed-form intensity rule read off two online statistics; the blend gradient corrects them.
  6. Blend by the gradient of the blend's own score. The timescale weights follow an exponentiated-gradient step on the blend's log score (not any single timescale's, which would select rather than blend), at a temperature calibrated so that uninformative evidence leaves the mix within a factor e of its prior. Nothing in either mixture is fitted.

The paper gives the derivations (natural-gradient and Gram-matrix forms of the flow, the Fisher–Rao form of the mixture), the guarantees (PSD at every step, a conditioning floor, strict positive definiteness whenever a positively weighted timescale has its intensity strictly inside (0, 1), exact reductions to the published special cases) and the full evaluation.

What derives from lam

quantity value
timescale ladder decays (lam⁴, lam, lam^¼) — half-lives (h/4, h, 4h), h = -1/log2(lam)
kernel scale state: κ_t = ⅓ · EWMA(activity) at the anchor rate
shrinkage intensity per timescale, α = min(1,c) · g̃²/(g̃² + (1−g̃)²·max(0, 1/c − 1)) from the online concentration c = n/ν and target-fit g̃
timescale weights prior ∝ √h, moved by exponentiated gradient on the blend log score at the null temperature, fast-rung memory
shrinkage split per timescale, a learned pool of the raw correlation, its equicorrelation level and its Hadamard square; prior (1−α, α(1−α), α²), moved by the blend gradient
observation weight state: ω_t = n_t/m_t, the day's observed count over its Mahalanobis distance under yesterday's forecast, from the mixture's own factorisation
volatility memory ladder (h/16, h/4, h), pooled panel-wide by the same rule on tanh-saturated evidence, uniform prior
structural constants K=3, b=4, θ=½, κ-scale ⅓, Schur power 2, intensity exponent 2 — each bracketed by ablation in the paper
fitted constants lam itself (0.996, chosen on data to 2021); nothing else is fitted

Development

uv sync --extra full --extra dev
uv run python -m pytest        # test suite
uv run python -m ruff check .  # lint
uv run mypy                      # strict type check (src/squeeze_kernel)
uv build                       # build sdist + wheel

Citation

@article{kende2026squeeze,
  title  = {The Squeeze Kernel Covariance Estimator},
  author = {Kende, Robert},
  year   = {2026},
  note   = {Working paper. Available at SSRN: \url{https://ssrn.com/abstract=6455918}}
}

See also CITATION.cff.

License

MIT

Metadata

Release files for squeeze-kernel 4.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for squeeze-kernel 4.0.0
File Size Uploaded Provenance Actions
squeeze_kernel-4.0.0.tar.gz 17.0 kB With provenance Details for squeeze_kernel-4.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for squeeze-kernel 4.0.0
File Interpreter ABI Platform Actions
squeeze_kernel-4.0.0-py3-none-any.whl Python 3 none any Details for squeeze_kernel-4.0.0-py3-none-any.whl

Total release size: 34.7 kB

Release files / squeeze_kernel-4.0.0.tar.gz

Download URL squeeze_kernel-4.0.0.tar.gz
Size 17.0 kB
Tags Source
SHA-256 checksum
How to use checksums
89bc39522c99bf422d9c58120647098065ad8cac54f45131ed3c3ad06f39aa81
BLAKE2b-256 checksum
How to use checksums
e208cd9f37353b01e2c5650200716dcb4f06c0224d5e75e534afa913558ea5ea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / squeeze_kernel-4.0.0-py3-none-any.whl

Download URL squeeze_kernel-4.0.0-py3-none-any.whl
Size 17.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
def31a4e65fd05c9c5c8d8b1b9bfeb98eb2ddfcbe744449584677b6dbb78a703
BLAKE2b-256 checksum
How to use checksums
df1f671d5718d6d86882f693493493b04a846b078dcc958d27c69373ee193260
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Security for squeeze-kernel 4.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for squeeze_kernel-4.0.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page