Skip to content

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 Type Description
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:

Name Type Description Default
ax Axes | None

Matplotlib axes to draw on. Creates a new figure if None.

None

Returns:

Type Description
Axes

The axes with the plot.

to_string() -> str

Return a human-readable description of the problem data.

Returns:

Type Description
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:

Name Type Description Default
log_returns list[float]

Expected log-return per asset. Positive values indicate expected gains, negative values indicate expected losses.

required
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.

required
investment_bands list[tuple[float, float]]

(lower, upper) investment bounds per asset. Each allocation is constrained to [lower, upper]. Must have length n_assets.

required
max_budget float

Maximum total budget across all assets: sum_i investment[i] <= max_budget.

required
n_bits int

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.

3
risk_aversion float

Risk aversion parameter lambda in the Markowitz objective: Higher values penalise variance more strongly. Default: 1.0.

1.0

Returns:

Type Description
PoIbtvData

A validated data instance ready for optimisation.

Raises:

Type Description
ValueError

If any of the following conditions are violated:

  • covariance_matrix is not square
  • covariance_matrix shape does not match len(log_returns)
  • investment_bands length does not match len(log_returns)
  • Any lower bound exceeds its upper bound
  • max_budget is not positive
  • n_bits is not positive
  • risk_aversion is 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:

Name Type Description Default
n_assets int

Number of assets, by default 3.

3
n_bits int

Number of binary bits per asset for discretisation, by default 3.

3
risk_aversion float

Risk aversion parameter lambda controlling the trade-off between expected return and variance. Default: 1.0.

1.0
seed int | None

Random seed for reproducibility, by default None.

None

Returns:

Type Description
PoIbtvData

A randomly generated data instance.

Examples:

>>> data = PoIbtvData.generate_random(n_assets=4, n_bits=3, seed=42)

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:

Name Type Description Default
data PoIbtvData

The problem data.

required

Returns:

Type Description
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:

Name Type Description Default
data PoIbtvData

The problem data containing returns, covariance, bands, and parameters.

required

Returns:

Type Description
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:

Name Type Description Default
solution Solution

The solution containing variable assignments.

required
data PoIbtvData

The original problem data.

required

Returns:

Type Description
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:

Type Description
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 Type Description
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:

Name Type Description Default
data PoIbtvData

The original problem data containing investment bands.

required
ax Axes | None

Matplotlib axes to draw on. Creates a new figure if None.

None

Returns:

Type Description
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:

Name Type Description Default
min_assets int

Minimum number of assets per instance (default: 2).

2
max_assets int

Maximum number of assets per instance (default: 5).

5
n_bits int

Number of binary bits per asset (default: 3).

3
num_instances int

Number of instances per asset count (default: 1).

1
seed int | None

Random seed for reproducibility (default: None).

None

Returns:

Type Description
PoIbtvCollection

Collection containing generated instances.