I kept seeing "quantum computing will revolutionize finance" takes with zero running code behind them, so I built the smallest honest version of the experiment: take a real portfolio of stocks (AAPL, NVDA, JPM, ...), solve the "pick the best K of N" problem with a real quantum algorithm (QAOA), and check it against the exact answer.
Two findings, both honest:
- Noiseless, QAOA ties the classical optimizer. It makes the optimal portfolio the single most likely outcome. It does not beat classical.
- Add the noise real quantum computers actually have, and the edge evaporates. Optimizing just 8 stocks already needs ~84 two-qubit gates, and at today's ~0.5-1% error rates a typical answer decays toward a coin flip.
pip install -r requirements.txt
python demo.py # runs everything, no quantum computer neededThe core is just NumPy/SciPy, no cloud account, runs in a couple seconds. Real
prices come cached in data/prices.csv (refresh with python fetch_data.py).
There's an optional Amazon Braket path for the noisy sim and real hardware.
A portfolio optimizer decides which assets to hold for the best return at a given risk. Add one realistic rule, "hold exactly K of N assets", and it stops being a tidy equation and becomes a subset search (NP-hard). That combinatorial bit is the only reason quantum is interesting here.
prices -> mu, Sigma (Ledoit-Wolf shrinkage)
-> QUBO (yes/no variables, one per stock)
-> Ising model
-> QAOA circuit (emitted as real OpenQASM 3.0)
-> hybrid loop (classical optimizer tunes the circuit angles)
-> read the answer, compare to the brute-force optimum
1. Prices to moments (data.py). Returns and covariance, with Ledoit-Wolf
shrinkage on the covariance. Raw sample covariance is too noisy to optimize
against, and honestly this step matters more for real results than the quantum
part does.
2-3. Build the QUBO (qubo.py):
maximize mu . x - q * x^T Sigma x
subject to sum(x) = K, x_i in {0,1}
Fold the constraint in as a penalty, flip the sign so it's a minimization, and
since x_i is binary (x_i^2 == x_i) the whole thing becomes x^T Q x, a
QUBO. Substitute x_i = (1 - z_i)/2 to get an Ising model (h fields, J
couplings), which is what QAOA wants.
4-5. Build the circuit (qaoa.py, openqasm.py). QAOA starts every qubit
in superposition, then alternates a cost layer exp(-i*gamma*H_C) and a mixer
exp(-i*beta*sum X_i), p times. One qubit per stock. The circuit is written
out as OpenQASM 3.0 in results/qaoa_circuit.qasm, so it isn't locked to any
one vendor.
6. The hybrid loop. A classical optimizer (COBYLA) tunes the gamma/beta
angles while the quantum circuit scores them. On real hardware this loop is
where the bill adds up. Two things I had to get right (see run_qaoa):
normalizing the cost so the phase rotations don't wrap around and wreck the
landscape, and using random restarts because the angle landscape is bumpy.
7. Read the answer. Sample the state, drop the portfolios that don't hold exactly K stocks, keep the best.
8. Compare honestly (classical.py). For small N I brute-force every subset
to get the true optimum, then put QAOA next to it.
classical energy -20.16 return 0.335 vol 0.209 sharpe 1.60 ['AAPL','NVDA','JNJ']
QAOA energy -20.16 return 0.335 vol 0.209 sharpe 1.60 ['AAPL','NVDA','JNJ']
matched the optimum.
Noiseless QAOA reaches the optimum, doesn't beat it. If anyone's selling you a quantum trading edge on hardware like this in 2026, they're overselling.
make_noise_figure.py runs the tuned circuit on Braket's density-matrix
simulator across a range of two-qubit error rates and measures the quantum edge:
how many times more likely than random the chip is to output the optimal
portfolio.
- Noiseless: the optimum is ~4-5x more likely than chance (and the single most likely of all 256 outcomes).
- At ~0.5-1% two-qubit error (where real QPUs are today): the edge drops to ~1.5-2x.
- By ~4%: it's gone. The state is basically random.
Noise drives the quantum state toward maximally mixed (uniform), and an 84-gate circuit for eight stocks is already enough to feel it. That's the real reason "quantum portfolio optimization" isn't beating your laptop yet.
pip install amazon-braket-sdk
aws configure
python run_on_hardware.py --device sim # managed simulator, cheap
python run_on_hardware.py --device ionq # real QPU ($$$)It tunes the circuit locally (free) and submits only the final circuit, so you pay for one run (a few $), not the whole loop. Compare the printed advantage against the simulated ~4-5x to see what the hardware noise cost you.
Cost warning: real QPUs bill per task + per shot. Check the Braket cost estimator before confirming.
does-quantum-beat-quant/
├── demo.py # run the whole thing
├── fetch_data.py # refresh real prices -> data/prices.csv
├── make_figures.py # comparison chart
├── make_noise_figure.py # the noise chart
├── make_gif.py # the terminal gif
├── run_on_hardware.py # submit to a real QPU
├── src/quantum_portfolio/
│ ├── data.py # prices -> mu, Sigma (shrinkage)
│ ├── qubo.py # constraints -> QUBO -> Ising
│ ├── qaoa.py # QAOA statevector sim + readout
│ ├── openqasm.py # emit OpenQASM 3.0
│ ├── classical.py # brute-force optimum + metrics
│ └── braket_runner.py # Braket: local / noisy / real QPU
├── tests/test_workflow.py
├── assets/ # charts + gif
├── data/prices.csv # real cached prices
└── results/
- Brute-forcing the optimum only works for small N. That's deliberate, it's how I can prove how close QAOA got. Crank N to feel the wall.
- The noise numbers come from a depolarizing model on Braket's density-matrix simulator, not a specific real device. The real-hardware script is there if you want true QPU numbers.
- v1 picks which stocks (equal weight). Continuous weights need a binary expansion per asset and more qubits, the obvious next step.
Not financial advice, not a money printer. It's a learning/research thing. PRs welcome.
MIT, see LICENSE.


