Numerical validation¶
jaxfolio's optimizers are validated against independent reference solvers — the two code paths share nothing but the mathematics, so agreement is meaningful. This page is the "does it compute the right answer?" evidence: which method is checked against which reference, on what kind of problem, and how close they agree.
How it works¶
- References. SciPy
SLSQP(a general-purpose constrained optimizer), a CVXPY linear program for CVaR, and PyPortfolioOpt for HRP. Where a method targets an exact mathematical property (e.g. equal risk contributions), we assert that property directly — it is a stronger reference than a second iterative solver. - Problem conditions. Each method is exercised on well-conditioned inputs, ill-conditioned inputs (near-singular / collinear covariance, condition number ≳ 10⁴), and degenerate inputs (single asset, zero-variance asset, infeasible bounds). The contract on hard inputs is robustness: stay finite and feasible, and match the reference objective where the optimum is defined.
- Executable. Every row is backed by a test in
tests/validation/and runs in CI (thevalidationjob). The table below is regenerated byexamples/validation/run_matrix.py.
Reproduce it yourself:
uv sync --all-extras
uv run pytest tests/validation -v
uv run python examples/validation/run_matrix.py # regenerates the table below
Validation matrix¶
| Method | Reference | Condition | Metric | jaxfolio | Reference value | Agreement |
|---|---|---|---|---|---|---|
| Minimum variance | SciPy SLSQP | well-conditioned | variance | 4.8074e-05 | 4.8074e-05 | PASS |
| Maximum Sharpe | SciPy SLSQP | well-conditioned | Sharpe | 0.042967 | 0.042967 | PASS |
| Maximum diversification | SciPy SLSQP | well-conditioned | div. ratio | 2.5347 | 2.5347 | PASS |
| Kelly (log-growth) | SciPy SLSQP | well-conditioned | E[log-growth] | 0.00044781 | 0.00044781 | PASS |
| Risk parity (ERC) | Analytic ERC property | well-conditioned | max|rcᵢ − 1/N| | 8.12e-09 | 0 (exact) | PASS |
| Minimum CVaR | CVXPY LP (exact) | well-conditioned | CVaR₉₅ | 0.01729 | 0.01353 | ⚠️ +27.84% (see notes) |
| HRP | PyPortfolioOpt | well-conditioned | L1 weight dist. | — | — | PASS (L1=1.11e-07) |
| Multi-period MV (linear cost) | CVXPY QP (exact) | well-conditioned | path objective | -0.0045116 | -0.0045114 | PASS |
| Multi-period MV (impact) | CVXPY QP (exact) | well-conditioned | path objective | -0.0042534 | -0.0042533 | PASS |
| Minimum variance | SciPy SLSQP | ill-conditioned (κ≈6.0e+04) | variance | 1.029e-04 | 1.029e-04 | PASS |
| Risk parity (ERC) | finite / feasible | ill-conditioned (κ≈6.0e+04) | Σw, finite | feasible | — | PASS |
| Minimum variance | exact (trivial) | degenerate: single asset | w = [1] | 1.0 | 1.0 | PASS |
| Minimum variance | finite / feasible | degenerate: zero-variance asset | finite w | finite | — | PASS |
Known limitations¶
- Minimum CVaR. The smooth, Adam-based projected-gradient solver
min_cvardefaults to — an explicitly configured optax optimizer is honored instead — converges to a point that is systematically ~26–33% above the exact CVaR optimum found by the CVXPY LP on multi-asset panels, and additional iterations do not close the gap. The direction is correct (it is never below the true optimum) and single-asset / tiny problems are fine, but the multi-asset CVaR result should be treated as approximate. This is tracked by a strict-xfailtest (tests/validation/test_reference_solvers.py::test_min_cvar_matches_cvxpy_lp) that will flip to a failure the moment the solver is improved to match the LP — a built-in reminder to remove the caveat. Contributions welcome.
Scope¶
This matrix covers the classical/convex optimizers and HRP, where an external
ground truth exists. Learning-based (deep_sharpe, online_gradient) and
LLM-driven strategies are validated by property and integration tests rather
than reference-solver equivalence — there is no canonical "correct" allocation to
compare against. LLM strategies are additionally
experimental.