D14-F03-A02 / Released engineering topic

Resampled Efficient Frontier: Measure Weight Instability Without Losing Frontier Rank

Averages optimizer weights across seeded bootstrap resamples at matching return rank, so the reported allocation reflects estimation noise rather than one sample.

Resampled Efficient Frontier: Measure Weight Instability Without Losing Frontier RankD14 / D14-F03

A portfolio can look precise because its weights have many decimal places. That precision says nothing about stability. Change a few observations, and the mean-variance optimizer may send the portfolio somewhere else.

Resampled Efficient Frontier (REF) makes that sensitivity visible. We resample the return rows, estimate new moments, rebuild a frontier for each replicate, and average comparable portfolios. The last phrase is the heart of the method: we must preserve frontier rank. Minimum-variance portfolios are comparable to minimum-variance portfolios; middle-ranked portfolios are comparable to middle-ranked portfolios. Averaging every optimized weight together produces a number, but not a resampled efficient frontier.

This tutorial gives you a deterministic, inspectable version. You will see the exact sampled rows, moment estimates, rank targets, inner weights, correct average, and wrong global-average shortcut.

Starting toolkit

Read Black–Litterman’s toolkit for the simplex and portfolio-variance notation. Here the new idea is sampling variability: the optimizer receives different estimated moments when the observed rows change. You should leave able to reconstruct every replicate and explain precisely what is being averaged.

Why rebuild the optimizer?

Mean-variance weights depend on estimated means and covariances. Those estimates depend on the particular observations in the sample. REF treats the frontier as a statistical object: if plausible resamples produce very different portfolios at the same rank, the original weights deserve skepticism.

The method does not magically create information. It reorganizes information already in the sample and reveals how the optimizer reacts. A bad split, omitted cash dividend, impossible mark, stale security mapping, or inconsistent FX conversion will be resampled too. More replications can make a data error look consistently wrong.

What “same rank” means

Each replicate has its own global-minimum-variance (GMV) expected return and its own maximum asset mean. For rank q between zero and one, define

βb(q)=βGMV,b+q(βmax,b−βGMV,b)\beta_b(q)=\beta_{GMV,b}+q(\beta_{max,b}-\beta_{GMV,b})

At q=0, solve the replicate's GMV portfolio. At q=1, solve its maximum-mean endpoint. At q=0.5, solve halfway through that replicate's feasible return interval. This mapping lets us associate portfolios by economic position even when raw expected returns move between samples.

Rank-matched resampled frontiers and the correct averaging direction

The two computed curves use their own moments. Dots mark the selected common rank; the final average is an average of weights, not chart coordinates.

Rank association and weight averaging are described in the MOSEK estimation-error chapter and the original public method record. The linear return-rank mapping, population divisor, and explicit resample indices here are declared implementation choices.

The canonical algorithm

For each explicit bootstrap index row:

  1. Select source rows with replacement.
  2. Calculate the replicate mean and population covariance (ddof=0).
  3. Solve the long-only GMV portfolio.
  4. Find the replicate maximum mean.
  5. Map every requested q to beta_b(q).
  6. Solve minimum variance subject to that return target.
  7. Store the rank label, target, weights, and diagnostics.

Then average only weights with the same q:

wREF(q)=1B∑b=1Bwb(q)w_{REF}(q)=\frac1B\sum_{b=1}^B w_b(q)

Because every inner portfolio lies in the same convex simplex, its rank-wise average also lies in the simplex. If a later layer imposes nonconvex cardinality or lot constraints, that property no longer guarantees feasibility and any repair must be named separately.

A fixture you can calculate

The example uses six synthetic two-asset decimal-return rows:

Plain text
[0.00, 0.00]
[0.20, 0.00]
[0.10, 0.10]
[0.10, 0.00]
[0.05, 0.10]
[0.15, 0.05]

We do not ask a random generator to hide the evidence. The two resample index rows are stored explicitly:

Plain text
[0,1,2,3]
[0,1,4,5]

For the first replicate, mu=[0.10,0.025] and Sigma=[[0.005,0],[0,0.001875]]. The GMV weights are [0.2727272727,0.7272727273]. For the second, mu=[0.10,0.0375] and Sigma=[[0.00625,-0.000625],[-0.000625,0.00171875]]; the GMV weights are [0.2542372881,0.7457627119].

Average those two rank-zero portfolios and we get [0.2634822804,0.7365177196]. Both maximum-mean endpoints are [1,0], so the rank-one average is [1,0].

Now consider the wrong shortcut: mix both endpoints and both GMV portfolios into one global average. The result no longer answers any named rank. It blends two different investor positions and discards the mapping that defines REF.

Reproducibility needs more than a seed

The reference package includes a small seeded bootstrap generator, but the fixture stores the full index matrix. That distinction matters. Different languages and libraries can turn the same integer seed into different streams. If you need exact replay, serialize the selected row indices, estimator convention, ranks, constraints, solver tolerance, and source-row identity.

Reordering the original return table while keeping the same numeric indices does not preserve a sample; the indices now select different observations. Either keep a stable row identifier or remap the indices to the same original rows.

The data contract comes first

In portfolio work, the return matrix is not just numbers. Every row needs an observation period and availability date. Every column needs a point-in-time security identity. The full panel needs a base currency, FX timestamp and quote direction, split/dividend treatment, missing-data policy, and survivorship rule.

I care about this because old portfolios often contain exactly the data defects that optimization amplifies: quantities entered with the wrong unit, marks that do not belong to that instrument or date, and holdings from several currencies compared before conversion to the portfolio's reporting currency. REF is a sensitivity tool after those checks—not a substitute for them.

Resampling assumptions are part of the model

An ordinary row bootstrap treats observations as exchangeable; the common justification is independent, identically distributed observations. Serial dependence, changing regimes, and overlapping returns weaken that justification. A block bootstrap samples consecutive stretches together to retain some temporal structure. It needs a declared block length and a new index-generation policy; it is not implemented by the simple IID helper.

Draw whole rows so asset returns stay aligned. Sampling each column independently destroys contemporaneous dependence. The teaching fixture uses four observations from six source rows, a deliberate mm-out-of-nn example. The conventional nn-out-of-nn bootstrap would draw six. Neither two replicates nor these artificial rows establish a useful sampling distribution in practice.

Our moments use divisor mm (ddof=0). Changing to m−1m-1 rescales a replicate covariance uniformly; under the same constraints it does not change its minimum-variance weights, but it changes reported variances and must be recorded.

What the rank average actually says

At rank 0.5, replicate A weights are [0.6363636364,0.3636363636]; replicate B weights are [0.6271186441,0.3728813559]. Their mean asset-A weight is 0.6317411402. Their population standard deviation is 0.0046224961, or 0.46225 percentage points of weight. This is dispersion across the two specified replicates, not a confidence interval.

Compare that with mixing rank 1 from replicate A and rank 0.5 from replicate B: (1+0.6271186441)/2=0.8135593220(1+0.6271186441)/2=0.8135593220. The arithmetic is valid; the association is wrong. Conversely, averaging both assets across every portfolio trivially gives 0.5 because each portfolio sums to one. That tautology cannot test rank matching.

Evaluate the final averaged weights against one common full-panel mean and covariance before plotting a resulting portfolio on a common risk-return chart. In general,

wˉTΣwˉ≠1B∑bwbTΣwb.\bar w^T\Sigma\bar w\ne\frac1B\sum_b w_b^T\Sigma w_b.

The curve of averaged weights need not itself be efficient for the full-panel moments. The word “frontier” in the method name does not prove that property.

Equal means and singular samples

If all means equal the target, the return equality adds no information to the budget equation. The solver must still minimize variance. With equal means and covariance diag(0.005,0.005), every rank returns [0.5,0.5].

A bootstrap can also produce singular covariance. The two rows [0.1,-0.1] and [-0.1,0.1] admit a zero-variance hedge at [0.5,0.5]. Discarding the singular face and reporting a corner as optimal is wrong. The reference now solves scaled KKT equations with redundant constraints and free variables, checks residuals, and supports positive-semidefinite sample covariances without adding hidden jitter. Numerically unresolved problems fail explicitly.

How many replications are enough?

There is no universal adequate BB. Compare rank-specific mean weights and dispersion at increasing BB, replay several fixed seeds, and report the maximum change in weight at the investor’s chosen rank. Stable bootstrap averages can still be stably wrong under a misspecified resampling model. Out-of-sample evaluation and costs answer a different question from numerical convergence of the bootstrap average.

Try it: repeat one replicate twice. Its own rank weights should be unchanged by averaging. Then permute the source rows and remap the stored indices. The answer should again be unchanged.

Implementation without a mystery solver

The Python implementation and TypeScript implementation use a small active-set enumerator. For each candidate set of positive weights, they solve the equality-constrained minimum-variance equations, discard negative or residual-failing candidates, and keep the feasible minimum. This is readable and independently checkable for at most 12 assets.

Production universes need a reliable quadratic solver. Preserve per-cell solver statuses. One inaccurate inner solution does not become accurate because it is averaged with other solutions; an incomplete matrix of ranks and replicates must be reported as partial.

Learn by stepping through the lab

Open the rank-matching playground. The initial view shows the computed rank-0.5 result; the input stage exposes the synthetic rows. Step through row selection, moment estimation, endpoint construction, rank alignment, and averaging. Switch to equal means or the singular hedge to inspect the degenerate cases. The failure case introduces an invalid index and shows the calculation stop before a frontier is drawn.

What to notice: the average is the final operation, not the first. Every member of an average must carry its rank and feasibility evidence.

What the tests prove

The shared JSON fixture reproduces both replicate means, covariances, GMV weights, maximum-mean endpoints, and averaged weights. Tests cover B=1, seeded determinism, invalid ranks and indices, the anti-global-mean regression, and Python/TypeScript parity.

They prove that the implementation follows the selected algorithm. They do not prove that resampled weights have higher Sharpe ratio, lower realized risk, or better performance after costs. Those are empirical questions requiring point-in-time out-of-sample data.

Boundaries and historical evidence

The historical-example decision is deferred. A named market panel would need licensed data, point-in-time constituents, corporate-action and FX basis, an estimator record, and an out-of-sample comparison with costs. The public US patent record is useful for method history and rank/vector-averaging evidence; its public metadata currently labels the record “Expired - Lifetime.” That is not a statistical result or legal opinion. This package uses original code and short factual citations.

Run the calculation yourself

From this topic folder, use Python 3.10 or later. No third-party numerical library is required:

Shell
python examples/python_example.py
python -m unittest discover -s tests -p "test_*.py"

The key call is shown below. For an interactive Python session, first make the local implementation importable:

Python
import sys
sys.path.insert(0, "implementations/python")
from resampled_efficient_frontier import resampled_efficient_frontier
R=[[0,0],[.2,0],[.1,.1],[.1,0],[.05,.1],[.15,.05]]
print(resampled_efficient_frontier(R,[[0,1,2,3],[0,1,4,5]],[0,.5,1]).to_dict())

The TypeScript example uses the same inputs. See the runnable commands for compilation and execution. Shared cases in the shared regression cases exercise boundary states in both languages; strings NaN and Infinity are test sentinels, not valid numerical inputs.

Summary

REF is valuable when it keeps the entire evidence chain: selected rows, replicate moments, replicate endpoints, common ranks, inner weights, and the rank-wise average. Its best lesson is not “averaging is safer.” It is that an optimized portfolio can be unstable, and stability must be measured without changing the object being compared.

Next, Robust Mean-Variance replaces repeated samples with an explicit set of possible mean vectors and asks for the best objective under the weakest member of that set.

Choose the family method by the decision you face

Your real questionStart withWhat changesWhat it does not solve
How should uncertain views modify a market prior?Black-LittermanExpected returns through a prior and viewsSampling stability by itself
How unstable are optimized weights across plausible samples?Resampled Efficient FrontierRows, replicate moments, and rank-matched frontiersStructural errors repeated in every sample
How should a bounded error in the mean affect allocation?Robust Mean-VarianceMean vector inside a declared ellipsoidResampling or posterior beliefs
Which portfolio ranks best under a declared distribution class and loss event?Distributionally Robust PortfolioThe admissible return distributionUniversal or assumption-free safety
Which scenario mix maximizes long-run log growth?Kelly AllocationObjective and scenario wealth factorsWeight-stability evidence

Related concepts and learning handoff

The reviewed The Fintech Builder chapter KKT checks and limits of an optimality claim (17:33–18:43) supports the inner frontier solver review: a feasible target and a stationarity check matter before any replicate weight is averaged. It does not derive resampling or justify the rank grid.

resampled frontier flow

ReferencesPrimary sources and evidence notes

Expand the source trail, evidence role, and limitations behind the engineering choices.

PATENT-6003018 — Portfolio optimization by means of resampled efficient frontiers

  • Organization or authors: US patent record; inventors Richard O. Michaud and Robert Michaud.
  • Source type: Official public patent record.
  • Publication or effective date: 14 December 1999; priority 27 March 1998.
  • Version: US6003018A, current record labels legal status “Expired - Lifetime”.
  • URL or DOI: Google Patents record.
  • Accessed: 2026-09-15.
  • Jurisdiction: United States intellectual-property record.
  • Supports: Recalculation on resampled inputs, index/rank association, and vector averaging of associated portfolios.
  • Limitations: Legal-status labels include database caveats; the record does not prove statistical superiority or grant permission to copy protected text/code.

MOSEK-REF — Resampled optimization

  • Organization or authors: MOSEK ApS.
  • Source type: Maintained official technical documentation.
  • Publication or effective date: Online edition accessed 2026-09-15.
  • Version: Portfolio Optimization Cookbook, estimation-error chapter.
  • URL or DOI: Resampled optimization section.
  • Accessed: 2026-09-15.
  • Jurisdiction: None.
  • Supports: Parametric and nonparametric resampling, matching portfolios across simulated frontiers, averaging, and computational limitations.
  • Limitations: Vendor documentation is a secondary technical explanation; package behavior is defined by the explicit contract and tests.

MICHAUD1998 — Efficient Asset Management

  • Organization or authors: Richard O. Michaud.
  • Source type: Original book-length method exposition.
  • Publication or effective date: 1998.
  • Version: Public author-hosted PDF copy; edition pagination should be checked before quoting.
  • URL or DOI: New Frontier Advisors PDF.
  • Accessed: 2026-09-15.
  • Jurisdiction: None.
  • Supports: Historical context for simulated efficient frontiers and rank-associated portfolio averaging.
  • Limitations: No text or code is reproduced; the source does not make the package's synthetic fixture empirical evidence.
resampled-efficient-frontier.ts
export class ContractError extends Error {
}
type Vector = number[];
type Matrix = number[][];
const dot = (a: Vector, b: Vector) => a.reduce((s, x, i) => s + x * b[i], 0);
const mv = (a: Matrix, x: Vector) => a.map(r => dot(r, x));
function* combos(n: number, k: number, s = 0, p: number[] = []): Generator<number[]> { if (p.length === k) {
    yield p;
    return;
} for (let i = s; i < n; i++)
    yield* combos(n, k, i + 1, [...p, i]); }
function moments(rows: Matrix): [
    Vector,
    Matrix
] { const t = rows.length, n = rows[0].length, mu = Array.from({ length: n }, (_, j) => rows.reduce((s, r) => s + r[j], 0) / t); return [mu, Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => rows.reduce((s, r) => s + (r[i] - mu[i]) * (r[j] - mu[j]), 0) / t))]; }
function consistentSolve(a: number[][], b: number[]): number[] {
    const g = a.map((row, i) => { const s = Math.max(...row.map(Math.abs), Math.abs(b[i]), 1e-300); return [...row.map(x => x / s), b[i] / s]; });
    const columns = a[0].length, pivots: number[] = [];
    let pr = 0;
    for (let col = 0; col < columns && pr < g.length; col++) {
        let p = pr;
        for (let r = pr + 1; r < g.length; r++)
            if (Math.abs(g[r][col]) > Math.abs(g[p][col]))
                p = r;
        if (Math.abs(g[p][col]) < 1e-12)
            continue;
        [g[pr], g[p]] = [g[p], g[pr]];
        const z = g[pr][col];
        g[pr] = g[pr].map(x => x / z);
        for (let r = 0; r < g.length; r++)
            if (r !== pr) {
                const f = g[r][col];
                g[r] = g[r].map((x, j) => x - f * g[pr][j]);
            }
        pivots.push(col);
        pr++;
    }
    if (g.some(row => Math.max(...row.slice(0, -1).map(Math.abs)) < 1e-10 && Math.abs(row[columns]) > 1e-9))
        throw new ContractError("inconsistent KKT system");
    const x = Array(columns).fill(0);
    pivots.forEach((col, r) => x[col] = g[r][columns]);
    return x;
}
/** PSD face enumeration with scaled, possibly redundant equality constraints. */
function minVariance(mu: number[], sigma: number[][], target: number | null): number[] {
    const n = mu.length, scale = Math.max(...sigma.flat().map(Math.abs), 1e-300);
    let best: {
        v: number;
        w: number[];
    } | undefined;
    for (let size = 1; size <= n; size++)
        for (const active of combos(n, size)) {
            const constraints = [Array(size).fill(1)], rhs = [1];
            if (target !== null) {
                const spread = Math.max(...active.map(i => Math.abs(mu[i] - target)));
                if (spread > 1e-14) {
                    constraints.push(active.map(i => (mu[i] - target) / spread));
                    rhs.push(0);
                }
            }
            const k = constraints.length, a = active.map((i, pos) => [...active.map(j => sigma[i][j] / scale), ...constraints.map(c => c[pos])]);
            a.push(...constraints.map(c => [...c, ...Array(k).fill(0)]));
            const b = [...Array(size).fill(0), ...rhs];
            let z: number[];
            try {
                z = consistentSolve(a, b);
            }
            catch {
                continue;
            }
            if (z.some(x => !Number.isFinite(x)) || Math.min(...z.slice(0, size)) < -1e-10)
                continue;
            if (Math.max(...a.map((row, i) => Math.abs(dot(row, z) - b[i]))) > 1e-8)
                continue;
            const w = Array(n).fill(0);
            active.forEach((i, pos) => w[i] = Math.max(0, z[pos]));
            if (Math.abs(w.reduce((s, x) => s + x, 0) - 1) > 1e-9 || (target !== null && Math.abs(dot(mu, w) - target) > 1e-10))
                continue;
            const v = dot(w, mv(sigma, w));
            if (!best || v < best.v - 1e-14 * scale)
                best = { v, w };
        }
    if (!best)
        throw new ContractError("frontier target infeasible or numerically unresolved");
    return best.w;
}
export interface Replicate {
    indices: number[];
    mean: Vector;
    covariance: Matrix;
    gmvReturn: number;
    maxReturn: number;
    weightsByRank: Vector[];
}
export interface ResampledFrontierResult {
    ranks: Vector;
    averagedWeights: Vector[];
    replicates: Replicate[];
    status: "optimal";
}
export function resampledEfficientFrontier(returns: Matrix, resampleIndices: number[][], ranks: Vector): ResampledFrontierResult { const rows = returns.map(r => r.map(Number)); if (rows.length < 2 || !rows[0]?.length || rows.some(r => r.length !== rows[0].length) || rows.flat().some(v => !Number.isFinite(v)))
    throw new ContractError("returns must be a finite rectangular matrix with at least two rows"); if (rows[0].length > 12)
    throw new ContractError("reference solver supports at most 12 assets"); if (!ranks.length || ranks.some(q => !Number.isFinite(q) || q < 0 || q > 1))
    throw new ContractError("ranks must lie in [0,1]"); if (!resampleIndices.length)
    throw new ContractError("at least one resample is required"); const replicates = resampleIndices.map(raw => { const indices = raw.map(Number); if (indices.length < 2 || indices.some(i => !Number.isInteger(i) || i < 0 || i >= rows.length))
    throw new ContractError("resample index is out of range"); const [mean, covariance] = moments(indices.map(i => rows[i])); const gmv = minVariance(mean, covariance, null), gmvReturn = dot(mean, gmv), maxReturn = Math.max(...mean); const weightsByRank = ranks.map(q => minVariance(mean, covariance, gmvReturn + q * (maxReturn - gmvReturn))); return { indices, mean, covariance, gmvReturn, maxReturn, weightsByRank }; }); const averagedWeights = ranks.map((_, ri) => Array.from({ length: rows[0].length }, (_, j) => replicates.reduce((s, r) => s + r.weightsByRank[ri][j], 0) / replicates.length)); return { ranks: [...ranks], averagedWeights, replicates, status: "optimal" }; }
export function seededBootstrapIndices(rowCount: number, sampleSize: number, replications: number, seed: number): number[][] { if ([rowCount, sampleSize, replications].some(x => !Number.isSafeInteger(x) || x <= 0) || !Number.isInteger(seed) || seed < 0 || seed > 4294967295)
    throw new ContractError("bootstrap sizes must be positive"); let state = seed >>> 0; return Array.from({ length: replications }, () => Array.from({ length: sampleSize }, () => { state = (Math.imul(1664525, state) + 1013904223) >>> 0; return Math.floor((state / 4294967296) * rowCount); })); }
Full-height labplaygroundOpen full screen
Written by

Fintech engineer building market-data and financial systems, and the author of every article, glossary record, and reference implementation on The Fintech Builder.