Solve portfolio optimization (minimum variance) in Python

Portfolio optimization — choosing how to weight a set of assets to minimize risk (variance) for a target expected return — is the textbook Markowitz mean-variance problem, and it is a quadratic program (QP): a convex quadratic objective under linear constraints. With quicopt it is a few lines in Python.

The naive approach

A common tutorial move is Monte-Carlo: draw thousands of random weight vectors, compute each one's risk and return, and keep the best. It only approximates the efficient frontier, degrades fast as the number of assets grows, and never proves optimality. The problem is a convex QP — so solve it exactly.

Model it as a QP

A continuous weight per asset, fully invested (sum(w) == 1), long-only (w >= 0), meeting a target return. The objective is the portfolio variance wᵀΣw, where Σ is the covariance matrix:

portfolio.py
from ortools.math_opt.python import mathopt
from quicopt import Client

# Minimum-variance portfolio (Markowitz): choose asset weights minimizing
# variance w'Σw, fully invested, meeting a target expected return.
Sigma = [[0.10, 0.02, 0.04, 0.00],
         [0.02, 0.08, 0.01, 0.02],
         [0.04, 0.01, 0.12, 0.03],
         [0.00, 0.02, 0.03, 0.06]]
mu = [0.10, 0.08, 0.12, 0.07]
target = 0.09
N = len(mu)

model = mathopt.Model(name="portfolio")
w = [model.add_variable(lb=0.0, name=f"w_{i}") for i in range(N)]
model.add_linear_constraint(sum(w) == 1.0)
model.add_linear_constraint(sum(mu[i] * w[i] for i in range(N)) >= target)
model.minimize(sum(Sigma[i][j] * w[i] * w[j] for i in range(N) for j in range(N)))

client = Client("https://try.quicoptapi.pgi.fz-juelich.de")
print(client.solve(model).display)
$ python portfolio.py
├── status:     optimal
├── feasible:   true
├── objective:  0.03552848683416981
├── x:          w_0=0.2632, w_1=0.2362, w_2=0.1949, w_3=0.3058  (4 variables)
└── solve_time: 0.0073 s

What you get

status: optimal means these weights are the proven minimum-variance portfolio for the target return — variance 0.0355, fully invested, meeting the 9% return floor, in a few milliseconds. No sampling, no frontier scan.

The same model scales from four assets to hundreds — you change the covariance matrix and expected returns, not the method. (This is an illustrative optimization example, not financial advice.)

Next

Reference: H. Markowitz, Portfolio Selection, The Journal of Finance, 1952.

A bigger portfolio — or a different problem?

Tell us what you're optimizing. We'll help you model it and point you at the right approach.

Talk to us →

Frequently asked questions

Why not Monte-Carlo sample random weights and pick the best?

Random sampling only approximates the efficient frontier and needs enormous sample counts to get close in more than a few assets. The QP returns the provably minimum-variance portfolio directly.

Can I add a maximum weight per asset, sector limits, or no short-selling?

Yes — weight bounds, group limits and long-only (w >= 0) are linear constraints on the same QP.

What about a limit on the number of assets held?

A cardinality limit turns it into a mixed-integer QP (MINLP) with binary hold/skip variables — a related model quicopt also solves.

Is quicopt free to use?

Yes — pip install quicopt and your first call sets up a free key, no license.