Skip to content

Portfolio API

Classical Optimizers

Mean-Variance

mean_variance

Mean-variance portfolio optimization using CVXPY.

Implements Markowitz (1952) mean-variance optimization: - Minimum variance - Maximum return subject to variance constraint - Maximum Sharpe ratio

Objective

Bases: str, Enum

MVResult dataclass

Result from mean-variance optimization.

mean_variance(mu, cov, objective=Objective.MIN_VARIANCE, risk_free_rate=0.0, max_variance=None, min_return=None, long_only=True, max_weight=1.0, cardinality=None)

Solve a mean-variance portfolio optimization.

Parameters

mu : NDArray Expected returns vector, shape (N,). cov : NDArray Covariance matrix, shape (N, N). objective : Objective Which objective to optimize. risk_free_rate : float Risk-free rate for Sharpe calculation. max_variance : float | None Upper bound on portfolio variance (for MAX_RETURN). min_return : float | None Lower bound on expected return (for MIN_VARIANCE). long_only : bool If True, weights >= 0. max_weight : float Maximum weight per asset. cardinality : int | None Maximum number of assets (makes problem MIQP — slower). Returns


MVResult

Black-Litterman

black_litterman

Black-Litterman model for portfolio allocation.

Combines market equilibrium returns with investor views to produce a posterior expected return vector.

BLResult dataclass

Black-Litterman posterior result.

black_litterman(cov, market_caps, risk_aversion=2.5, tau=0.05, P=None, Q=None, omega=None)

Compute Black-Litterman posterior expected returns.

Parameters

cov : NDArray Covariance matrix of returns, shape (N, N). market_caps : NDArray Market capitalizations, shape (N,). Used to derive equilibrium weights. risk_aversion : float Risk aversion coefficient (lambda). Typical range: 1-4. tau : float Scalar controlling uncertainty in the prior (equilibrium). Typical: 0.01-0.1. P : NDArray | None Views matrix, shape (K, N). Each row is one view linking assets. Q : NDArray | None Views vector, shape (K,). Expected return of each view. omega : NDArray | None Uncertainty of views, shape (K, K). If None, uses proportional-to-variance heuristic: omega_ii = tau * P_i @ cov @ P_i^T.

Returns

BLResult

Risk Parity

risk_parity

Risk parity (equal risk contribution) portfolio optimization.

Finds weights such that each asset contributes equally to total portfolio risk. Also provides inverse-volatility weighting as a fast heuristic baseline.

RiskParityResult dataclass

Result from risk parity optimization.

risk_parity(cov, budget=None, max_iter=1000)

Compute risk parity portfolio.

Parameters

cov : NDArray Covariance matrix, shape (N, N). budget : NDArray | None Risk budget vector, shape (N,). Default is equal risk (1/N each). max_iter : int Max optimization iterations.

Returns

RiskParityResult

HRP

hrp

Hierarchical Risk Parity (Lopez de Prado, 2016).

Tree-based portfolio construction that doesn't require covariance matrix inversion, making it more robust than mean-variance.

HRPResult dataclass

Result from HRP allocation.

hrp(returns, linkage_method='single')

Compute Hierarchical Risk Parity weights.

Parameters

returns : NDArray Return matrix, shape (T, N). linkage_method : str Linkage method for hierarchical clustering: 'single', 'complete', 'average', 'ward'.

Returns

HRPResult

QUBO Formulation

qubo

Portfolio QUBO formulation with realistic constraints.

Builds the Markowitz QUBO with optional cardinality, sector, turnover, and transaction-cost penalty terms. Supports one-hot and binary encodings.

References

Brandhofer et al., arXiv:2207.10555 — portfolio QAOA benchmarking. arXiv:2601.03278 — slack ancilla for inequality constraints.

PortfolioQUBO dataclass

QUBO formulation for portfolio optimization.

Parameters

mu : NDArray Expected returns, shape (N,). cov : NDArray Covariance matrix, shape (N, N). gamma : float Risk aversion parameter. cardinality : int | None Exactly K assets must be selected (one-hot encoding). sector_map : dict[int, int] | None Mapping asset index -> sector index. sector_caps : dict[int, int] | None Max assets per sector. turnover_penalty : float Penalty coefficient for turnover from previous_weights. transaction_cost : float Per-asset transaction cost coefficient. previous_weights : NDArray | None Previous portfolio (binary selection vector) for turnover/cost. budget_penalty : float | None Penalty for budget constraint (sum x_i = 1 or K). If None, auto-scaled from Q matrix magnitude. encoding : str "one_hot" (1 qubit per asset) or "binary" (log-bits per asset). bits_per_asset : int Bits per asset in binary encoding (ignored for one_hot).

n_qubits property

Total qubits needed for the chosen encoding.

build_matrix()

Build the QUBO Q matrix for minimization: min x^T Q x.

Returns

NDArray of shape (n_qubits, n_qubits)

evaluate(bitstring)

Evaluate the QUBO objective for a given bitstring.

decode_weights(bitstring)

Decode a bitstring into portfolio weights.

For one-hot: weights are the binary selection (equal weight among selected). For binary: weights are decoded integer levels, normalized to sum to 1.

feasibility_check(bitstring)

Check which constraints a bitstring satisfies.

Quantum Optimizers

QAOA

qaoa

QAOA portfolio optimizer (X-mixer and XY-ring/Dicke for cardinality).

References

Farhi, Goldstone, Gutmann, arXiv:1411.4028. Hadfield et al., Algorithms 12:34 (2019) — Quantum Alternating Operator Ansatz. Wang, Rubin, Dominy, Rieffel, PRA 101:012320 (2020) — XY mixers. Bartschi et al., npj QI (2024) — Dicke initial state for cardinality. Brandhofer et al., arXiv:2207.10555 — portfolio QAOA benchmarking.

QAOAPortfolio

QAOA solver for the cardinality-constrained Markowitz QUBO.

run()

Optimize QAOA parameters and return the best portfolio.

QAOAConfig dataclass

QAOAResult dataclass

Bases: Result

VQE

vqe

VQE portfolio optimizer with CVaR objective.

Uses a TwoLocal hardware-efficient ansatz with CVaR expectation as the objective function for combinatorial optimization.

References

Barkoutsos et al., Quantum 4, 256 (2020) — CVaR-VQE. Kandala et al., Nature 549, 242 (2017) — hardware-efficient ansatz.

VQEPortfolio

VQE solver for the cardinality-constrained Markowitz QUBO.

Uses a TwoLocal hardware-efficient ansatz (Kandala et al. 2017) with CVaR objective (Barkoutsos et al. 2020).

run()

Optimize VQE parameters and return the best portfolio.

VQEConfig dataclass

Configuration for VQE portfolio optimizer.

VQEResult dataclass

Bases: Result

Result from VQE portfolio optimization.

Exhaustive

exhaustive

Exhaustive (brute-force) QUBO solver for small problems.

Enumerates all 2^n bitstrings and returns the optimal one. Only practical for n <= 20 qubits.

ExhaustiveResult dataclass

Bases: Result

exhaustive_solve(qubo, return_all=False)

Brute-force solve: enumerate all 2^n bitstrings.

Parameters

qubo : PortfolioQUBO The QUBO to solve. return_all : bool If True, return all (bitstring, objective) pairs in all_objectives.

Raises

ValueError If n_qubits > 20 (too large for brute force).

Multi-Period

multi_period

Multi-period portfolio optimization with turnover penalties.

Extends single-period QUBO/MVO to T-period rebalancing with turnover and holding-cost penalties. Supports sequential QAOA (quantum) and sequential classical MVO modes.

References

Boyd et al., "Multi-Period Trading via Convex Optimization" (2017). Brandhofer et al., arXiv:2207.10555 — portfolio QAOA benchmarking.

MultiPeriodConfig dataclass

Configuration for multi-period portfolio optimization.

Parameters

n_periods : int Number of rebalancing periods. turnover_penalty : float Penalty coefficient for changing positions between periods. holding_cost : float Per-period holding cost applied to the portfolio. method : str "sequential" (solve period-by-period, QAOA if backend provided, else classical MVO) or "classical" (always classical MVO). risk_aversion : float Risk aversion parameter for the Markowitz objective. cardinality : int | None Maximum number of assets to hold (None = unconstrained). qaoa_depth : int Number of QAOA layers (p parameter) when using quantum solver. shots : int Number of measurement shots for QAOA. seed : int Random seed for reproducibility.

MultiPeriodResult dataclass

Result from multi-period portfolio optimization.

Attributes

allocations : list[NDArray[np.float64]] Portfolio weights for each period, length T. objectives : list[float] Objective value achieved at each period. turnovers : list[float] Turnover between consecutive periods, length T-1. total_turnover : float Sum of all inter-period turnovers. total_objective : float Sum of all per-period objectives.

multi_period_optimize(mu_series, cov_series, config=None, backend=None)

Run multi-period portfolio optimization.

Parameters

mu_series : list[NDArray] List of T expected-return vectors, one per rebalancing period. cov_series : list[NDArray] List of T covariance matrices, one per rebalancing period. config : MultiPeriodConfig | None Optimisation configuration. Defaults to MultiPeriodConfig(). backend : object | None Quantum backend (e.g. QiskitAerBackend). When None and method is "sequential", classical MVO is used instead.

Returns

MultiPeriodResult

multi_period_backtest(prices, allocations, rebalance_dates)

Backtest a multi-period allocation schedule against realised prices.

Parameters

prices : NDArray Price matrix of shape (T_total, N_assets). Each row is a trading day, each column an asset price. allocations : list[NDArray] Portfolio weight vectors, one per rebalancing event. rebalance_dates : list[int] Row indices into prices at which each rebalancing occurs. Must be sorted in ascending order and have the same length as allocations.

Returns

dict Keys: "portfolio_values", "returns", "sharpe", "max_drawdown", "total_turnover".

compute_turnover(w_old, w_new)

Compute turnover between two weight vectors.

Turnover is defined as half the L1 distance between the two weight vectors, i.e. sum(|w_new - w_old|) / 2.

Parameters

w_old : NDArray Previous portfolio weights. w_new : NDArray New portfolio weights.

Returns

float Turnover value in [0, 1].

ADMM

admm

ADMM-based QUBO decomposition for large portfolio problems.

Splits a large QUBO (>50 assets) into sub-problems of manageable size and solves them iteratively via the Alternating Direction Method of Multipliers (ADMM) with consensus constraints.

References

Boyd et al., "Distributed Optimization and Statistical Learning via the Alternating Direction Method of Multipliers," Found. & Trends in Machine Learning 3(1):1-122 (2011). Gambella et al., "Multi-block ADMM Heuristics for Mixed-Binary Optimization on Classical and Quantum Computers," IEEE Trans. Quantum Eng. 1:1-15 (2020).

ADMMConfig dataclass

Configuration for the ADMM portfolio optimizer.

Parameters

sub_problem_size : int Maximum number of qubits per sub-problem. max_iterations : int Maximum number of ADMM outer iterations. rho : float Augmented Lagrangian penalty parameter. rho_update : bool Whether to adaptively update rho based on residual balance. tol_primal : float Convergence tolerance for the primal residual. tol_dual : float Convergence tolerance for the dual residual. sub_solver : str Solver for sub-problems: "qaoa" or "exhaustive". qaoa_depth : int QAOA circuit depth (p parameter) when using the QAOA sub-solver. shots : int Number of measurement shots per sub-problem evaluation. seed : int Random seed for reproducibility.

ADMMResult dataclass

Bases: Result

Result of the ADMM portfolio optimization.

Attributes

best_bitstring : str Best binary solution found. weights : NDArray[np.float64] Decoded portfolio weights from the best bitstring. objective : float QUBO objective value of the best solution. n_iterations : int Number of ADMM iterations executed. primal_residuals : list[float] Primal residual history across iterations. dual_residuals : list[float] Dual residual history across iterations. sub_problem_objectives : list[list[float]] Per-iteration, per-sub-problem objective values. converged : bool Whether ADMM converged within tolerance.

Hybrid

hybrid

Hybrid classical-quantum optimizer: SDP relaxation + QAOA refinement.

Combines classical continuous optimization with quantum combinatorial search: solve the relaxed problem classically, round to a feasible binary solution, then refine with QAOA warm-started from the relaxation.

References

Egger et al., Quantum 5, 479 (2021) — Warm-starting quantum optimization. Goemans & Williamson, JACM 42(6), 1995 — SDP relaxation for MAX-CUT. Brandhofer et al., arXiv:2207.10555 — portfolio QAOA benchmarking.

HybridOptimizer

Hybrid classical-quantum portfolio optimizer.

Pipeline: 1. Solve continuous relaxation of the QUBO (classical, fast). 2. Round the relaxed solution to a feasible binary solution. 3. Warm-start QAOA from the relaxation to refine the solution. 4. Return the best solution found across both stages.

run()

Run the hybrid optimization pipeline.

HybridConfig dataclass

Configuration for hybrid classical-quantum optimizer.

HybridResult dataclass

Bases: Result

Result from hybrid optimization.

Robust (CVaR QUBO)

robust

Robust portfolio optimization with worst-case CVaR under uncertainty.

Implements a robust counterpart of the Markowitz mean-variance model using ellipsoidal uncertainty sets for expected returns. The worst-case expected return under the uncertainty set is maximized, yielding a minimax formulation that guards against estimation error.

The QUBO formulation embeds the robust counterpart so it can be solved on quantum hardware via QAOA or VQE, while a classical comparison is provided via CVXPY's second-order cone programming.

References

Goldfarb & Iyengar, "Robust Portfolio Selection Problems," Mathematics of Operations Research 28(1):1-38 (2003). Tutuncu & Koenig, "Robust Asset Allocation," Annals of Operations Research 132:157-187 (2004). Ben-Tal, El Ghaoui, Nemirovski, "Robust Optimization," Princeton University Press (2009).

RobustPortfolioOptimizer

Worst-case CVaR portfolio optimizer with QUBO formulation.

Solves the robust Markowitz problem:

min  gamma * w^T Sigma w  -  min_{mu in U} mu^T w
s.t. sum(w) = 1, w >= 0

For the ellipsoidal uncertainty set U, the inner min has a closed-form worst case:

min_{mu in U} mu^T w = mu_hat^T w - epsilon * sqrt(w^T Sigma_mu w)

The QUBO formulation linearizes the sqrt term via a first-order Taylor expansion around equal weights, yielding a penalty that can be embedded into the standard QUBO matrix.

Parameters

uncertainty : EllipsoidalUncertaintySet The uncertainty set for expected returns. cov : NDArray Asset return covariance matrix, shape (N, N). gamma : float Risk aversion parameter. cardinality : int | None If set, select exactly this many assets. budget_penalty : float | None Penalty for budget constraint. Auto-scaled if None.

build_qubo()

Build a QUBO with robust (worst-case) adjusted returns.

Returns

PortfolioQUBO A standard QUBO object with mu replaced by the worst-case adjusted returns.

build_matrix()

Build the robust QUBO Q matrix directly.

Returns

NDArray of shape (n_assets, n_assets)

solve_exhaustive()

Solve the robust QUBO by exhaustive enumeration.

Only practical for small problems (N <= 20).

Returns

RobustPortfolioResult

EllipsoidalUncertaintySet dataclass

Ellipsoidal uncertainty set for expected returns.

The true mean mu lies in the set

{ mu : (mu - mu_hat)^T Sigma_mu_inv (mu - mu_hat) <= epsilon^2 }

where mu_hat is the estimated mean, Sigma_mu is the uncertainty shape matrix (typically proportional to the covariance), and epsilon controls the size of the uncertainty region.

Parameters

mu_hat : NDArray Estimated (nominal) expected returns, shape (N,). sigma_mu : NDArray Uncertainty shape matrix, shape (N, N). Positive semidefinite. epsilon : float Uncertainty radius. 0 means no uncertainty (standard MVO).

robust_classical(mu, cov, uncertainty, gamma=1.0, long_only=True, max_weight=1.0)

Solve the robust portfolio problem classically via CVXPY.

Solves the second-order cone program (SOCP):

min  gamma * w^T Sigma w  -  mu_hat^T w  +  epsilon * ||L^T w||_2
s.t. sum(w) = 1
     w >= 0 (if long_only)
     w <= max_weight

where L is the Cholesky factor of sigma_mu.

Parameters

mu : NDArray Nominal expected returns. cov : NDArray Return covariance matrix. uncertainty : EllipsoidalUncertaintySet Uncertainty set specification. gamma : float Risk aversion parameter. long_only : bool If True, enforce w >= 0. max_weight : float Maximum weight per asset.

Returns

RobustPortfolioResult

Szegedy Quantum Walk

quantum_walk

Szegedy quantum walk optimizer for portfolio optimization.

Implements a discrete-time quantum walk on the portfolio state graph, where vertices represent candidate portfolios (bitstrings) and transition probabilities are derived from the QUBO objective via a Boltzmann-like weighting. Low-energy states are marked and amplified through the quantum walk search framework.

References

Szegedy, FOCS 2004 -- Quantum speed-up of Markov chain based algorithms. Magniez et al., SIAM J. Comput. 40(4):1220-1240, 2011 -- Search via quantum walk. Portugal, Quantum Walks and Search Algorithms, Springer (2013).

SzegedyWalkOptimizer

Szegedy quantum walk optimizer for portfolio QUBO problems.

Uses a discrete-time quantum walk on the portfolio state graph to search for low-energy portfolio configurations. The walk operates on a Markov chain whose stationary distribution is biased toward low-energy states via Boltzmann weighting.

Parameters

qubo : PortfolioQUBO Portfolio QUBO formulation. config : SzegedyWalkConfig Walk optimizer configuration. backend : Backend Quantum backend (used for metadata; walk is simulated via matrix operations for small instances).

run()

Run the Szegedy walk optimizer and return the best portfolio.

Returns

SzegedyWalkResult Optimization result including best bitstring, objective, and decoded portfolio weights.

SzegedyWalkConfig dataclass

Configuration for the Szegedy quantum walk optimizer.

Parameters

n_walk_steps : int Number of quantum walk iterations (analogous to Grover iterations). temperature : float Boltzmann temperature for converting QUBO energies to transition probabilities. Lower values sharpen the distribution toward low-energy states. energy_threshold : float | None States with energy below this threshold are marked. If None, the median energy across all states is used. shots : int Number of measurement shots for the final circuit. seed : int | None Random seed for reproducibility.

SzegedyWalkResult dataclass

Bases: Result

Result from Szegedy quantum walk portfolio optimization.

classical_random_walk(qubo, n_steps=1000, temperature=1.0, seed=42)

Classical random walk baseline on the same Markov chain.

Performs a classical Metropolis-Hastings walk on the QUBO energy landscape and tracks the best state found.

Parameters

qubo : PortfolioQUBO Portfolio QUBO formulation. n_steps : int Number of random walk steps. temperature : float Boltzmann temperature for acceptance probabilities. seed : int | None Random seed.

Returns

tuple of (best_bitstring, best_energy, energy_trace)

Factor Models

factor_models

Fama-French factor model integration for portfolio optimization.

Estimates factor exposures via OLS, constructs factor-model covariance matrices, and decomposes portfolio risk into systematic and idiosyncratic components.

FactorModelResult dataclass

Full factor model output.

Attributes:

Name Type Description
expected_returns NDArray[float64]

Factor-implied expected returns, shape (n_assets,).

factor_cov NDArray[float64]

Factor covariance matrix, shape (n_factors, n_factors).

cov NDArray[float64]

Full asset covariance via factor model, shape (n_assets, n_assets).

exposures FactorExposureResult

Underlying exposure estimation result.

FactorExposureResult dataclass

Result of factor exposure estimation.

Attributes:

Name Type Description
betas NDArray[float64]

Factor loadings, shape (n_assets, n_factors).

alpha NDArray[float64]

Regression intercepts, shape (n_assets,).

r_squared NDArray[float64]

Coefficient of determination per asset, shape (n_assets,).

residual_cov NDArray[float64]

Diagonal residual covariance, shape (n_assets, n_assets).

factor_names list[str]

Human-readable factor labels.

build_factor_model(returns, factor_returns, window=None, factor_names=None)

Build a complete factor model from return data.

Convenience wrapper that estimates exposures, computes factor covariance from the factor return series, and assembles the full asset covariance and expected return vector.

Parameters:

Name Type Description Default
returns NDArray[float64]

Asset return series, shape (T, n_assets).

required
factor_returns NDArray[float64]

Factor return series, shape (T, n_factors).

required
window int | None

If provided, use only the last window observations for both exposure estimation and factor covariance.

None
factor_names list[str] | None

Optional factor labels.

None

Returns:

Type Description
FactorModelResult

FactorModelResult with expected returns, factor covariance,

FactorModelResult

full asset covariance, and exposure details.

estimate_factor_exposures(returns, factor_returns, window=None, factor_names=None)

Estimate factor exposures via OLS regression.

Runs per-asset regressions: r_i = alpha_i + beta_i^T * f + epsilon_i

Parameters:

Name Type Description Default
returns NDArray[float64]

Asset return series, shape (T, n_assets).

required
factor_returns NDArray[float64]

Factor return series, shape (T, n_factors).

required
window int | None

If provided, use only the last window observations.

None
factor_names list[str] | None

Optional factor labels. Defaults to ["factor_0", "factor_1", ...].

None

Returns:

Type Description
FactorExposureResult

FactorExposureResult with estimated betas, alphas, R-squared values,

FactorExposureResult

and diagonal residual covariance.

factor_model_cov(exposures, factor_cov)

Compute full asset covariance via factor model.

Sigma = B @ F @ B^T + D

Parameters:

Name Type Description Default
exposures FactorExposureResult

Factor exposure result containing betas and residual_cov.

required
factor_cov NDArray[float64]

Factor covariance matrix, shape (n_factors, n_factors).

required

Returns:

Type Description
NDArray[float64]

Asset covariance matrix, shape (n_assets, n_assets).

factor_expected_returns(exposures, factor_premium)

Compute expected returns from factor model.

E[r] = alpha + B @ factor_premium

Parameters:

Name Type Description Default
exposures FactorExposureResult

Factor exposure result containing alphas and betas.

required
factor_premium NDArray[float64]

Expected factor risk premia, shape (n_factors,).

required

Returns:

Type Description
NDArray[float64]

Expected asset returns, shape (n_assets,).

risk_decomposition(weights, exposures, factor_cov)

Decompose portfolio variance into systematic and idiosyncratic parts.

Parameters:

Name Type Description Default
weights NDArray[float64]

Portfolio weights, shape (n_assets,).

required
exposures FactorExposureResult

Factor exposure result with betas and residual_cov.

required
factor_cov NDArray[float64]

Factor covariance matrix, shape (n_factors, n_factors).

required

Returns:

Type Description
dict[str, float | NDArray[float64]]

Dictionary with keys:

dict[str, float | NDArray[float64]]
  • total_variance: Total portfolio variance (float).
dict[str, float | NDArray[float64]]
  • systematic_variance: Variance from factor exposures (float).
dict[str, float | NDArray[float64]]
  • idiosyncratic_variance: Variance from residuals (float).
dict[str, float | NDArray[float64]]
  • systematic_pct: Fraction of variance that is systematic (float).
dict[str, float | NDArray[float64]]
  • factor_contributions: Per-factor variance contribution, shape (n_factors,).

Sector Rotation

sector_rotation

Sector rotation with quantum classifiers for regime detection.

Detects market regimes (risk_on, risk_off, crisis) from macro features using a variational quantum classifier, then rotates sector allocations based on the predicted regime.

References

Nystrup, Hansen, Madsen, Lindstrom, Journal of Banking & Finance (2017). Schuld, Bocharov, Svore, Killoran, PRA 101, 032308 (2020).

SectorRotator

Map a regime prediction to sector allocation weights.

Parameters

sectors : list[str] | None Sector names. Defaults to 11 GICS sectors. profiles : SectorWeightProfile | None Per-regime weight maps. None uses sensible defaults.

allocate(regime)

Return sector weights for the given regime.

Missing sectors are filled so weights sum to 1.0.

Parameters

regime : int or Regime enum value.

Returns

dict mapping sector name to weight (sums to 1.0).

allocate_array(regime)

Return weights as an array aligned with self.sectors.

allocate_timeseries(regimes)

Return weight matrix of shape (T, n_sectors) for a regime time series.

RegimeDetector

Detect market regimes from macro features using a VQC ensemble.

The detector uses one-vs-rest binary VQC classifiers for three-class classification (risk_on / risk_off / crisis). Features are normalised to [0, pi] for angle encoding.

Parameters

config : RegimeDetectorConfig Hyperparameters. backend : Backend Quantum backend for circuit execution.

build_features(vix, yield_curve_slope, pmi, credit_spread) staticmethod

Stack macro indicators into a feature matrix.

Parameters

vix : 1-D array VIX index values. yield_curve_slope : 1-D array 10Y-2Y yield spread (bps). pmi : 1-D array Purchasing Managers' Index. credit_spread : 1-D array Investment-grade credit spread (bps).

Returns

NDArray of shape (n_samples, 4)

label_regimes(vix, pmi, *, crisis_vix=30.0, risk_off_vix=20.0, risk_off_pmi=50.0) staticmethod

Heuristic labelling based on VIX thresholds and PMI.

Parameters

vix, pmi : 1-D arrays of the same length. crisis_vix : VIX threshold above which we declare crisis. risk_off_vix : VIX threshold for risk_off (when PMI < 50). risk_off_pmi : PMI threshold below which conditions are risk-off.

Returns

1-D int array with values in {0, 1, 2} (Regime enum).

fit(X, y)

Train one-vs-rest VQC classifiers.

Parameters

X : array of shape (n_samples, n_features) y : array of shape (n_samples,) with values in {0, 1, 2}

Returns

self

predict(X)

Predict regime labels for feature matrix X.

Uses the one-vs-rest classifier with the highest positive-class probability.

Parameters

X : array of shape (n_samples, n_features)

Returns

1-D int array of predicted regime labels.

predict_regime_name(X)

Predict regime labels as human-readable strings.

backtest_sector_rotation(sector_returns, regimes, sectors=None, profiles=None, *, annual_periods=252, risk_free_rate=0.0)

Backtest a sector rotation strategy against equal-weight buy-and-hold.

Parameters

sector_returns : array of shape (T, n_sectors) Daily (or periodic) sector returns. regimes : array of shape (T,) Regime label for each period. sectors : list[str] | None Sector names matching columns of sector_returns. profiles : SectorWeightProfile | None Custom weight profiles. None uses defaults. annual_periods : int Number of periods per year (252 for daily). risk_free_rate : float Annualised risk-free rate for Sharpe calculation.

Returns

BacktestResult

Mixers

mixers

QAOA mixer Hamiltonians: X, XY-ring, Grover, Dicke initial state.

References

Hadfield et al., Algorithms 12:34 (2019) — QAOA+. Wang, Rubin, Dominy, Rieffel, PRA 101:012320 (2020) — XY mixers. Bartschi et al., npj QI (2024) — Dicke state alignment.

XMixer

Bases: Mixer

Standard X-mixer (sum of Pauli-X on each qubit).

Does NOT preserve Hamming weight — use for unconstrained problems.

XYRingMixer

Bases: Mixer

XY-ring mixer preserving Hamming weight (for cardinality constraints).

Implements nearest-neighbor XX+YY interactions in a ring topology. This keeps the total number of 1s constant, naturally enforcing cardinality K when initialized from a Dicke state |D_n^k>.

XYFullMixer

Bases: Mixer

Fully-connected XY mixer (all-to-all XX+YY).

More expressive than ring but deeper circuits. Preserves Hamming weight.

GroverMixer

Bases: Mixer

Grover-style mixer: reflects about the uniform superposition.

Used in the original Grover-QAOA variant. Does not preserve Hamming weight.

circuit(beta)

exp(-i * beta * D) where D = 2|s> = |+>^n.

Implemented as: R_s(beta) = I - (1 - e^{-2i*beta}) |s><0|.

DickeInitialState

Prepare the Dicke state |D_n^k> as initial state for cardinality-K QAOA.

The Dicke state is the uniform superposition over all n-qubit states with exactly k ones. Used with XY-ring or XY-full mixers to maintain cardinality throughout the QAOA evolution.

References

Bartschi & Eidenbenz, arXiv:1904.07358 — deterministic Dicke state preparation.

circuit()

Prepare |D_n^k> using the Bärtschi & Eidenbenz SCS algorithm.

The algorithm works by iterating through positions n-1 down to 1. At each position j, a split operation distributes Hamming weight from position j to position j using a controlled rotation.

get_mixer(name, n_qubits, **kwargs)

Factory function to create a mixer by name.

Parameters

name : str One of "x", "xy_ring", "xy_full", "grover". n_qubits : int Number of qubits.

Encodings

encodings

Qubit encoding schemes for portfolio variables.

Supports one-hot (binary inclusion), binary (integer weights), and unary encoding. Documents qubit cost for each.

One-hot: N qubits for N assets (1 qubit per asset, binary in/out). Binary: N * ceil(log2(K)) qubits for N assets with K weight levels. Unary: N * K qubits (thermometer encoding, simple but expensive).

References

Hodson et al., arXiv:1911.05296 — QAOA portfolio rebalancing with various encodings.

one_hot_encoding(n_assets)

One-hot (binary inclusion) encoding: 1 qubit per asset.

Each qubit indicates whether the asset is selected (1) or not (0). All selected assets receive equal weight = 1/K where K is the number of selected assets.

binary_encoding(n_assets, bits_per_asset=3)

Binary (integer weight) encoding.

Each asset uses bits_per_asset qubits. The decoded weight for asset i is: w_i = (sum_b 2^b * x_{i,b}) / (2^bits - 1).

Parameters

n_assets : int Number of assets. bits_per_asset : int Bits per asset. 3 bits -> 8 levels (0, 1/7, 2/7, ..., 1).

unary_encoding(n_assets, n_levels=4)

Unary (thermometer) encoding.

Each asset uses n_levels qubits. The weight is proportional to the number of qubits set to 1 (thermometer code). Simple but qubit-expensive.

decode_one_hot(bitstring)

Decode a one-hot bitstring to equal-weight portfolio.

Returns weights: 1/K for selected assets, 0 for unselected.

decode_binary(bitstring, n_assets, bits_per_asset)

Decode a binary-encoded bitstring to portfolio weights.

Weights are normalized to sum to 1.

qubit_cost_table(n_assets_list)

Generate a qubit cost comparison table for different encodings.

Parameters

n_assets_list : list[int] List of asset counts to compare.

Returns

List of dicts with columns: n_assets, one_hot, binary_3b, binary_5b, unary_4.