Skip to main content

Constructing minimum variance portfolios

Project description

fast-minimum-variance: Solving Minimum Variance Portfolios Fast

Python License Rhiza

Overview

fast-minimum-variance is a Python library for computing minimum variance and mean-variance portfolios without ever forming the sample covariance matrix. By operating directly on the returns matrix $R \in \mathbb{R}^{T \times N}$, it exposes a clean hierarchy of solvers — from an exact direct KKT solve to matrix-free Krylov methods — that scale gracefully as $N$ grows.

The core insight is that minimising portfolio variance is equivalent to minimising $|Rw|^2$, which can be evaluated using two matrix-vector products $w \mapsto R^\top(Rw)$ without constructing $R^\top R$ explicitly. This reframing connects the portfolio optimisation literature directly to Krylov subspace methods.

Linear equality and inequality constraints ($A^\top w = b$, $C^\top w \leq d$) are handled via an active-set method: violated inequalities are promoted to equalities one outer iteration at a time, and the process terminates in at most $p$ iterations where $p$ is the number of inequality constraints.

Solvers

All solvers are methods on the Problem class:

Method Approach Notes
solve_kkt() Direct KKT via numpy.linalg.solve Exact; baseline for accuracy comparisons
solve_minres() MINRES on the indefinite KKT system Matrix-free; handles indefiniteness correctly
solve_cg() CG in the constraint-reduced space Positive-definite reduced system; fastest for large $N$
solve_cvxpy() General-purpose convex solver via CVXPY Reference implementation; requires [convex] extra

All solvers return (w, n_iters) where $w \in \mathbb{R}^N$ satisfies $\sum_i w_i = 1$ and $w_i \geq 0$.

Quick Start

import numpy as np
from fast_minimum_variance import Problem

# Returns matrix: 500 daily returns, 20 assets
R = np.random.default_rng(42).standard_normal((500, 20))
p = Problem(R)

# Solve with any of the available solvers
w_kkt,    _ = p.solve_kkt()    # exact KKT solve
w_minres, _ = p.solve_minres() # MINRES on the indefinite KKT system
w_cg,     _ = p.solve_cg()    # CG in the constraint-reduced space

# All solutions satisfy the portfolio constraints
assert abs(w_kkt.sum() - 1.0) < 1e-8
assert (w_kkt >= 0).all()

The Problem Dataclass

Problem bundles all problem data and exposes the solvers as methods:

Field Type Default Description
X ndarray (T, N) required Returns matrix
A ndarray (N, m) ones((N,1)) Equality constraint matrix: $A^\top w = b$
b ndarray (m,) [1.0] Equality RHS (budget constraint by default)
C ndarray (N, p) -eye(N) Inequality constraint matrix: $C^\top w \leq d$
d ndarray (p,) zeros(N) Inequality RHS (long-only by default)
rho float 0.0 Return tilt strength for mean-variance
mu ndarray (N,) None Expected returns vector
gamma float 0.0 L2 regularisation (e.g. Ledoit-Wolf shrinkage)

The defaults recover the long-only minimum variance problem. Pass custom A, b, C, d for arbitrary linear equality and inequality constraints.

The KKT System

The equality-constrained minimum variance problem yields the $(N+m) \times (N+m)$ KKT system:

$$\begin{pmatrix} 2(R^\top R + \gamma I) & A \cr A^\top & 0 \end{pmatrix} \begin{pmatrix} w \cr \lambda \end{pmatrix} = \begin{pmatrix} \rho,\mu \cr b \end{pmatrix}$$

where $A \in \mathbb{R}^{N \times m}$ collects the active equality and inequality constraints. With the defaults ($A = \mathbf{1}$, $b = 1$, $\gamma = 0$, $\rho = 0$) this reduces to the familiar $(N+1) \times (N+1)$ budget-constraint system.

This system is symmetric but indefinite — the zero bottom-right block introduces negative eigenvalues. This rules out standard CG on the full system, but it opens the door to MINRES. Alternatively, the CG solver eliminates the constraints entirely by parameterising $w = w_0 + Pv$ where $P$ spans the null space of $A^\top$, yielding a positive-definite reduced system of size $(N-m) \times (N-m)$.

Installation

pip install fast-minimum-variance

To use the CVXPY reference solver:

pip install fast-minimum-variance[convex]

For development:

git clone https://github.com/Jebel-Quant/fast_minimum_variance
cd fast_minimum_variance
make install

Requirements

  • Python 3.11+
  • numpy
  • scipy
  • cvxpy (optional, only required for solve_cvxpy)

Citing

If you use this library in academic work or research, please cite:

@software{fast_minimum_variance,
  author  = {Schmelzer, Thomas},
  title   = {fast-minimum-variance: Solving Minimum Variance Portfolios Fast},
  url     = {https://github.com/Jebel-Quant/fast_minimum_variance},
  year    = {2026},
  license = {MIT}
}

License

MIT License — see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fast_minimum_variance-0.4.0.tar.gz (5.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fast_minimum_variance-0.4.0-py3-none-any.whl (8.9 kB view details)

Uploaded Python 3

File details

Details for the file fast_minimum_variance-0.4.0.tar.gz.

File metadata

  • Download URL: fast_minimum_variance-0.4.0.tar.gz
  • Upload date:
  • Size: 5.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fast_minimum_variance-0.4.0.tar.gz
Algorithm Hash digest
SHA256 97e5bc2d3473712742ae9e21e63a02d5a01cbefd5e28c01abd70afac2a92f043
MD5 8b82438071bf4fd774df0373babd3bef
BLAKE2b-256 d86b4a8dae0a6e32616004686aa5ca01fd3958693bb5cb893656b632e54443fa

See more details on using hashes here.

Provenance

The following attestation bundles were made for fast_minimum_variance-0.4.0.tar.gz:

Publisher: rhiza_release.yml on Jebel-Quant/fast_minimum_variance

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fast_minimum_variance-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for fast_minimum_variance-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c37a9c3db885e5805951916000f0e5b120f22e3a79348aec1ec9e3467f8ce5c3
MD5 0459d323eaf1d39ac22f34a16870586f
BLAKE2b-256 c2e1a57b783cb35e77db8f21f9e3552ebd79f12345821f82c5e9b8e1995cdc23

See more details on using hashes here.

Provenance

The following attestation bundles were made for fast_minimum_variance-0.4.0-py3-none-any.whl:

Publisher: rhiza_release.yml on Jebel-Quant/fast_minimum_variance

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page