Portfolio Optimization with Investment Bands API Reference
Data
Data model for Portfolio Optimization with Investment Bands use case.
PoIbtvData
Bases: UcData
Data for the Portfolio Optimization with Investment Bands use case.
Models a Markowitz-style mean-variance portfolio optimization where each asset has a minimum and maximum investment band. Continuous allocations are discretised using binary variables so the problem is suitable for QUBO/quantum solvers.
Attributes:
-
name(Literal['portfolio_optimization_with_investment_bands']) –Identifier for this data type.
-
log_returns(list[float]) –Expected log-return per asset. Positive values indicate expected gains, negative values indicate expected losses.
-
covariance_matrix(NumPyArray) –Symmetric positive semi-definite covariance matrix of shape (n_assets, n_assets). Entry [i,j] is the covariance between asset i and asset j. Diagonal entries are variances.
-
investment_bands(list[tuple[float, float]]) –(lower, upper) investment bounds per asset. Each allocation is constrained to lie within [lower, upper].
-
risk_aversion(float) –Risk aversion parameter lambda in the Markowitz objective: Higher values penalise variance more strongly. Default: 1.0.
-
max_budget(float) –Maximum total budget that can be allocated across all assets: sum_i investment[i] <= max_budget.
-
n_bits(int) –Number of binary bits used to discretise each asset's allocation within its investment band. Higher values give finer resolution but increase the number of binary variables (n_assets * n_bits).
plot(*, ax: Axes | None = None) -> Axes
Plot investment bands and expected returns per asset.
Shows a horizontal bar for each asset's investment band, colored per asset. The expected log-return is annotated to the right of each bar.
Parameters:
-
ax(Axes | None, default:None) –Matplotlib axes to draw on. Creates a new figure if
None.
Returns:
-
Axes–The axes with the plot.
to_string() -> str
Return a human-readable description of the problem data.
Returns:
-
str–String representation of the data including all parameters and per-asset investment bands and expected returns.
from_values(log_returns: list[float], covariance_matrix: np.ndarray, investment_bands: list[tuple[float, float]], max_budget: float, n_bits: int = 3, risk_aversion: float = 1.0) -> PoIbtvData
staticmethod
Create a PoIbtvData instance from explicit problem parameters.
Parameters:
-
log_returns(list[float]) –Expected log-return per asset. Positive values indicate expected gains, negative values indicate expected losses.
-
covariance_matrix(ndarray) –Symmetric positive semi-definite covariance matrix of shape (n_assets, n_assets). Entry [i,j] represents the covariance between asset i and asset j. Diagonal entries are variances.
-
investment_bands(list[tuple[float, float]]) –(lower, upper) investment bounds per asset. Each allocation is constrained to [lower, upper]. Must have length n_assets.
-
max_budget(float) –Maximum total budget across all assets: sum_i investment[i] <= max_budget.
-
n_bits(int, default:3) –Number of binary bits used to discretise each asset's allocation within its band. Higher values give finer resolution but increase the number of variables exponentially. Default: 3.
-
risk_aversion(float, default:1.0) –Risk aversion parameter lambda in the Markowitz objective: Higher values penalise variance more strongly. Default: 1.0.
Returns:
-
PoIbtvData–A validated data instance ready for optimisation.
Raises:
-
ValueError–If any of the following conditions are violated:
covariance_matrixis not squarecovariance_matrixshape does not matchlen(log_returns)investment_bandslength does not matchlen(log_returns)- Any lower bound exceeds its upper bound
max_budgetis not positiven_bitsis not positiverisk_aversionis not positive
Examples:
>>> import numpy as np
>>> data = PoIbtvData.from_values(
... log_returns=[0.05, 0.08, 0.03],
... covariance_matrix=np.array(
... [
... [0.04, 0.01, 0.005],
... [0.01, 0.09, 0.02],
... [0.005, 0.02, 0.03],
... ]
... ),
... investment_bands=[(0.1, 0.5), (0.1, 0.4), (0.1, 0.6)],
... max_budget=1.0,
... n_bits=3,
... risk_aversion=1.0,
... )
generate_random(n_assets: int = 3, n_bits: int = 3, risk_aversion: float = 1.0, seed: int | None = None) -> PoIbtvData
staticmethod
Generate a random PO-IBTV instance.
Creates a random portfolio problem with plausible return and covariance structure. Investment bands are sampled uniformly, and the covariance matrix is constructed to be positive semi-definite.
Parameters:
-
n_assets(int, default:3) –Number of assets, by default 3.
-
n_bits(int, default:3) –Number of binary bits per asset for discretisation, by default 3.
-
risk_aversion(float, default:1.0) –Risk aversion parameter lambda controlling the trade-off between expected return and variance. Default: 1.0.
-
seed(int | None, default:None) –Random seed for reproducibility, by default None.
Returns:
-
PoIbtvData–A randomly generated data instance.
Examples:
Formulation
Formulation for Portfolio Optimization with Investment Bands use case.
PoIbtvFormulation
Bases: UcFormulation[PoIbtvData, PoIbtvSolution]
Formulation for Portfolio Optimization with Investment Bands.
Each asset's allocation is discretised within its investment band using
n_bits binary variables. The objective is the Markowitz mean-variance
formulation: maximise expected return minus risk-aversion-weighted variance.
The continuous allocation per asset is encoded as: investment[i] = lower[i] + step[i] * sum_q (2^q * x[i,q]) where step[i] = (upper[i] - lower[i]) / (2^n_bits - 1)
This allows a quantum solver to search over discretised allocations within the investment bands without requiring continuous variables.
Mathematical Formulation
Decision Variables: x[i,q] in {0,1} for asset i, bit q in 0..n_bits-1
Derived investment: investment[i] = lower[i] + step[i] * sum_q (2^q * x[i,q]) where step[i] = (upper[i] - lower[i]) / (2^n_bits - 1)
Objective (maximize): sum_i log_returns[i] * investment[i] - risk_aversion * sum_{i,j} sigma[i][j] * investment[i] * investment[j] where risk_aversion = 1 / (2 * target_volatility^2)
Constraints: 1. Budget: sum_i investment[i] <= max_budget
to_string(data: PoIbtvData) -> str
staticmethod
Format the formulation as a string.
Parameters:
-
data(PoIbtvData) –The problem data.
Returns:
-
str–String representation of the formulation.
formulate(data: PoIbtvData) -> Model
staticmethod
Formulate PO-IBTV as an optimization model.
Expands the continuous Markowitz objective fully into binary variables by substituting the discretised investment expression. The resulting objective contains linear and quadratic terms in x[i,q].
Parameters:
-
data(PoIbtvData) –The problem data containing returns, covariance, bands, and parameters.
Returns:
-
Model–A Luna optimization model representing the investment band portfolio problem.
interpret(solution: Solution, data: PoIbtvData) -> PoIbtvSolution
staticmethod
Extract solution from solver result.
Reconstructs the continuous allocation per asset from the binary variables and computes portfolio return, volatility, and validity.
Parameters:
-
solution(Solution) –The solution containing variable assignments.
-
data(PoIbtvData) –The original problem data.
Returns:
-
PoIbtvSolution–A structured solution object with: - allocations: continuous allocation per asset - portfolio_return: expected return of the portfolio - portfolio_volatility: standard deviation of the portfolio - is_valid: whether investment bands and budget are satisfied
Raises:
-
NoSolutionFoundError–If the solver did not find a solution.
Solution
Solution model for Portfolio Optimization with Investment Bands use case.
PoIbtvSolution
Bases: UcSolution
Solution for the Portfolio Optimization with Investment Bands use case.
Attributes:
-
name(Literal['portfolio_optimization_with_investment_bands']) –Identifier for this solution type.
-
allocations(list[float]) –Allocation fraction per asset (discretised).
-
portfolio_return(float) –Expected portfolio return.
-
portfolio_volatility(float) –Portfolio volatility (standard deviation).
-
is_valid(bool) –Whether the solution satisfies all constraints (allocations within bands, budget met).
plot(data: PoIbtvData, *, ax: Axes | None = None) -> Axes
Plot the portfolio allocation against investment bands.
Mirrors the data plot layout: horizontal bars show the investment bands (background) with the selected allocation overlaid as a marker, making it easy to see where each allocation falls within its band.
Parameters:
-
data(PoIbtvData) –The original problem data containing investment bands.
-
ax(Axes | None, default:None) –Matplotlib axes to draw on. Creates a new figure if
None.
Returns:
-
Axes–The axes with the plot.
to_string() -> str
Return a string describing the PO-IBTV solution.
Instance
Instance model for PoIbtv use case.
PoIbtvInstance
Bases: UcInstance[PoIbtvData, PoIbtvFormulation, PoIbtvSolution]
Instance combining data and formulation for PoIbtv.
Collection
Collection of PO Investment Bands instances.
PoIbtvCollection
Bases: UcInstanceCollection[PoIbtvInstance]
Collection of PO Investment Bands instances.
This collection provides methods to generate benchmark instances with various characteristics for testing and evaluation.
from_random(min_assets: int = 2, max_assets: int = 5, n_bits: int = 3, num_instances: int = 1, *, seed: int | None = None) -> PoIbtvCollection
classmethod
Generate random PO-IBTV instances.
Parameters:
-
min_assets(int, default:2) –Minimum number of assets per instance (default: 2).
-
max_assets(int, default:5) –Maximum number of assets per instance (default: 5).
-
n_bits(int, default:3) –Number of binary bits per asset (default: 3).
-
num_instances(int, default:1) –Number of instances per asset count (default: 1).
-
seed(int | None, default:None) –Random seed for reproducibility (default: None).
Returns:
-
PoIbtvCollection–Collection containing generated instances.