← Back to Axfolio
Methodology

Efficient Frontier Optimizer

How Axfolio computes the risk-return frontier for your portfolio: the inputs we trust, the inputs we shrink, the constraints we honor, and the gaps we don't paper over.

Version 2.0 Updated April 2026 Audience: Investment advisors, CFAs, compliance reviewers

01 Overview

The Axfolio Efficient Frontier shows the set of risk-return outcomes achievable by reweighting your client's current holdings — no new tickers introduced. Each grey dot in the scatter is one hypothetical reweighting; the upper envelope of the cloud is the frontier. Three target portfolios are computed analytically: the highest-return portfolio at the current volatility, the lowest-volatility portfolio at the current return, and the portfolio with the highest Sharpe ratio.

We are explicit about what this is and what it is not. It is a fast, transparent, advisor-facing portfolio optimizer with institutional-grade statistical estimators (Ledoit-Wolf shrinkage, Black-Litterman blending) and a real quadratic-program solver under realistic constraints (long-only, position cap, asset-class limits). It is not a multi-factor risk model, a tax-aware optimizer, or a robust-optimization engine. We tell you exactly which estimators are running in the diagnostics line on the chart, and we list every limitation in section 11.

Why a frontier, not a single answer?

There is no single "optimal" portfolio. Optimality depends on the client's risk tolerance, liquidity needs, tax situation, and IPS bands. The frontier reframes the question from "what's optimal?" to "what's achievable?" — making the conversation about the trade-off, not the answer.

02 Inputs & Data

What we use

What we don't use

History window

The advisor selects the lookback window (3 years, 5 years, 10 years, or all available history). Returns prior to the window are excluded before computing means and covariance. This lets you test stability — a frontier that shifts dramatically between a 3-year and 10-year window is a warning sign about input noise, and you should see that.

Minimum data requirement: each ticker must have at least 60 aligned trading observations for the frontier to render at all.

03 Covariance Shrinkage (Ledoit-Wolf)

The sample covariance matrix Σ̂ from a finite return series is an unbiased but high-variance estimator of the true covariance. In a portfolio optimizer this is a known disaster — small sampling noise produces wildly different "optimal" portfolios. This is the canonical Markowitz error-maximization problem: the optimizer concentrates weight in tickers whose pairwise covariance estimates happened to be misleadingly low.

The fix: shrinkage to a structured target

We replace the sample covariance with a convex combination of the sample and a structured target matrix F:

Σ_shrunk = δ · F + (1 − δ) · Σ̂

where F has the same diagonal as Σ̂ but every off-diagonal entry is replaced with r̄ · σᵢ · σⱼ — that is, the constant-correlation target where r̄ is the average pairwise correlation across all ticker pairs. This is the well-known "constant-correlation target" of Ledoit and Wolf (2003).

How we choose δ

The shrinkage intensity δ ∈ [0, 1] is computed analytically per portfolio using the Ledoit-Wolf optimal formula:

δ* = clip( π̂ / (γ̂ · T) , 0 , 1 )

When the sample covariance is already close to the target, γ̂ is small and δ* shrinks toward 0 (we trust the sample). When the sample is noisy and far from the target, δ* is larger (we trust the prior). The diagnostics line shows the active δ as a percentage.

In plain English
We don't fully trust each pairwise correlation estimated from limited data. We blend toward a "they all move together about the same amount" assumption — biased, but with much lower variance. The math chooses the optimal trade-off automatically.

04 Mean Shrinkage (Black-Litterman style)

Historical mean returns are the noisiest input to Markowitz optimization. Five years of AAPL daily data produces a 95% confidence interval on AAPL's annual expected return that's wider than ±10% — essentially uninformative. This is why pure mean-variance optimization with sample means is so unreliable in practice.

The fix: blend with a forward-looking prior

For each ticker i, we replace the sample mean with a convex combination of historical and a forward-looking prior derived from the ticker's asset class:

μ_i = (1 − τ) · μ̂_i  +  τ · μ_prior(asset_class_i)

with τ = 0.5 by default — half-weight to historical, half to prior.

The prior: JP Morgan LTCMA 2025

We use the 2025 vintage of JP Morgan's Long-Term Capital Market Assumptions, an industry-standard 15-year forward expected-return projection used by institutional consultants. Asset-class values:

Asset ClassExpected Return (annual)Expected Vol (annual)
Equity 7.8%15.0%
Fixed Income 5.1%5.5%
Alternatives 8.5%14.0%
Cash 4.2%0.5%

Why this is "Black-Litterman style", not full Black-Litterman

Full Black-Litterman (1992) requires equilibrium implied returns derived from market-cap weights and a P matrix encoding investor views with an Ω uncertainty matrix. The full machinery is most useful when the advisor wants to express explicit tactical views (e.g. "I think tech will outperform energy by 200bps").

Our use case is different: we want to tame noise, not express views. Convex shrinkage toward an institutional prior captures that need with far less implementation overhead and more transparency. We say "Black-Litterman style" rather than claim full Black-Litterman.

Why JPM specifically?
JP Morgan's LTCMA is published annually, methodology is public, and is one of the most-cited asset-class capital market assumptions in the institutional consulting world. Other valid priors (BlackRock, Vanguard, Research Affiliates) could be substituted; we standardized on JPM for consistency with our Monte Carlo module.

05 Sampling the Feasible Region

We draw 4,000 random portfolios from the feasible region defined by all constraints, compute (volatility, return, Sharpe) for each, and plot them as the grey scatter. This is what the advisor and client see as the visual cloud of "what's possible."

How we sample

Constraint enforcement

Each draw is checked against the position cap (clipped and redistributed) and asset-class constraints (rejection sampling). The diagnostics line reports the fraction of draws that survived constraint checks — a low acceptance rate (under 30%) signals that the constraint set is tight and the cloud may be sparse.

Reproducibility

The random number generator is seeded deterministically from a fingerprint of the ticker list and current weights (FNV-1a hash → Mulberry32 PRNG). The same portfolio always produces the same scatter — flipping between cards or refreshing the page doesn't cause the cloud to "jitter."

06 Analytical QP Solver

Sampling shows the feasible region; for the three target portfolios we want the exact analytical optimum. We solve the Markowitz quadratic program using the Goldfarb-Idnani active-set algorithm via the quadprog JavaScript port, running server-side as a Vercel serverless function at /api/efficient-frontier.

The optimization problem

minimize:    ½ · wᵀ Σ w  −  λ · μᵀ w

subject to:  Σᵢ wᵢ = 1          (full investment)
             wᵢ ≥ 0              (long-only)
             wᵢ ≤ cap            (position limit)
             cᵢ_min ≤ Σ_{j ∈ class i} wⱼ ≤ cᵢ_max   (asset-class limits)
             μᵀ w ≥ r_floor      (return floor — sameReturn target)
             wᵀ Σ w ≤ v_cap      (variance cap — sameVol target)

The parameter λ traces out the entire frontier as it sweeps from 0 (minimum-variance portfolio) to large positive values (return-maximizing portfolio). For each target we use a different search strategy:

Why server-side?

QP solves with constraints can take 50–500ms even for small problems. Running on Vercel keeps the browser thread responsive and lets us scale to richer constraint sets (sector limits, factor exposure caps, etc.) without browser-CPU concerns. The frontend renders sampled targets immediately, then upgrades to analytical results when the API responds — typically in under 500ms warm. If the API is unreachable, the UI silently falls back to sampled targets and the diagnostics badge reads "Sampled (fallback)".

Numerical safeguards

07 Local Refinement (Pairwise Coordinate Ascent)

After identifying the best sampled candidate for each target, we run a pairwise coordinate-ascent refinement as a sanity-check layer on top of (or in place of, when the QP API is unreachable) the analytical solver:

  1. For step sizes {5%, 2%, 1%, 0.5%, 0.2%}, in order:
  2. For every directed pair (i, j): try transferring step weight from i to j.
  3. If the transfer improves the objective and still satisfies all constraints, accept.
  4. Repeat until no improving transfer is found at the current step size; halve the step.

This converges to a local optimum on the frontier. For Markowitz under linear inequality constraints (a convex problem), the local optimum equals the global optimum modulo numerical precision. The complexity is O(N² × iterations) — fast for typical RIA portfolios with under 50 holdings.

08 The Three Targets

The "How to reach the frontier" panel below the chart shows three portfolios. Each is a recommendation for how the current holdings could be reweighted — no new tickers added.

More Return, Same Risk

The portfolio with the highest expected return whose volatility does not exceed the current portfolio's volatility (within a ±2% tolerance band, widened to ±4% if no candidate is found). Answers: "If I'm comfortable with the current risk level, what's the most I can earn?"

Less Risk, Same Return

The portfolio with the lowest volatility whose expected return is at least the current portfolio's return (within a ±1% tolerance band, widened to ±2%). Answers: "If I want to keep my expected return, how much can I de-risk?"

Best Risk-Adjusted (Maximum Sharpe)

The portfolio with the highest Sharpe ratio across the entire feasible region. Answers: "Ignoring my current portfolio, what's the most efficient mix of these holdings?"

What each card displays

09 Constraints

All constraints are advisor-configurable via the controls panel above the chart. They are enforced uniformly during sampling, refinement, and the analytical QP solve.

Always-on constraints

Configurable constraints

History window

3 years / 5 years / 10 years / All available. Controls the slice of daily returns used for mean/covariance estimation.

Single-position cap

25% / 40% / 60% / no cap. Default 40% — matches typical IPS-driven concentration limits.

Asset-class minimums and maximums

Per-class lower and upper bounds for equity, fixed income, alts, and cash. Leave blank for no constraint. Useful for IPS-coupled optimization and conservative or growth-tilted mandates.

Solver mode

Toggle between exact analytical QP (default) and sampled-only mode. The sampled mode can be useful for fast offline demos or when the API endpoint is unavailable.

10 Diagnostics & Transparency

Every render produces a diagnostics line below the controls panel. It is intentionally verbose — a CFA reviewer should be able to reproduce or stress-test what's running without guesswork.

Analytical (QP) · Window: 5.0y (1260d) · Cov shrinkage (Ledoit-Wolf): 18% · Mean shrinkage: 50% to JPM prior · 73% of samples passed constraints

Field reference

11 Limitations

We list these explicitly because every quantitative model has them, and a methodology sheet that doesn't list them isn't really a methodology sheet.

Tax and transaction costs are not modeled
Realized capital gains, wash-sale rules, bid-ask spreads, commissions, and ETF tracking differences are all ignored. The recommended reweights are frictionless mathematical optima, not after-tax recommendations. For taxable accounts, a meaningful portion of the projected return improvement may be eroded by realized gains taxes. The advisor must apply judgment.
Returns are assumed IID
Daily returns are treated as independent and identically distributed when annualized (multiply mean by 252, vol by √252). Real returns exhibit volatility clustering, fat tails, and serial correlation — particularly during regime shifts. Annualized volatility from this method understates tail risk.
No multi-factor risk model
We compute asset-class tilt (equity / fixed / alts / cash) but do not currently decompose portfolios into Fama-French / Barra-style risk factors (market, size, value, momentum, profitability, investment). A multi-factor regression endpoint is planned (see Roadmap).
No robust optimization
We do not currently apply Michaud resampling, worst-case ellipsoidal optimization, or scenario-based stress testing. Our shrinkage estimators tame the input noise, but they do not replace robust optimization for use cases where input uncertainty is dominant (e.g. very short return histories, illiquid assets).
Tolerance bands can widen silently
If no sample within ±2% volatility (or ±1% return) clears the current portfolio, we widen to ±4% / ±2% and surface a small badge on the affected target card. Read the badge — a widened band means "Same Risk" or "Same Return" is loose for this portfolio.
Sample window matters
Use the History Window control to test sensitivity. If the frontier shifts substantially between a 3-year and 10-year window, the inputs are noisier than the model can fully compensate for, and the recommendations should be treated as directional rather than precise.

12 References & Further Reading