Portfolio API
The pyvallocation.portfolioapi module provides the public entry points
used across the tutorials and examples:
pyvallocation.portfolioapi.AssetsDistributionstores parametric or scenario-based return descriptions while preserving labels.pyvallocation.portfolioapi.PortfolioWrapperexposes efficient frontiers (mean-variance, CVaR, relaxed risk parity, robust optimisation) with a uniform interface for constraints, turnover costs, and selectors.pyvallocation.portfolioapi.PortfolioFrontierencapsulates solved frontiers and offers convenience selectors such astangency()andat_risk().
All helpers preserve pandas alignment and cooperate with the ensembling and discrete allocation utilities.
- class pyvallocation.portfolioapi.AssetsDistribution(mu=None, cov=None, scenarios=None, probabilities=None, asset_names=None)[source]
Bases:
objectAn immutable container for asset return distributions.
This class validates and stores the statistical properties of assets, which can be represented either parametrically (mean and covariance) or non-parametrically (scenarios and their probabilities). It automatically handles both NumPy arrays and pandas Series/DataFrames, ensuring data consistency.
- Parameters:
mu (Optional[Union[npt.NDArray[np.floating], pd.Series]])
cov (Optional[Union[npt.NDArray[np.floating], pd.DataFrame]])
scenarios (Optional[Union[npt.NDArray[np.floating], pd.DataFrame]])
probabilities (Optional[Union[npt.NDArray[np.floating], pd.Series]])
asset_names (Optional[List[str]])
- mu
A 1D array or pandas.Series of expected returns for each asset (N,).
- Type:
Optional[Union[npt.NDArray[np.floating], pd.Series]]
- cov
A 2D covariance matrix of asset returns (N, N).
- Type:
Optional[Union[npt.NDArray[np.floating], pd.DataFrame]]
- scenarios
A 2D array or pandas.DataFrame of shape (T, N), where each row is a market scenario.
- Type:
Optional[Union[npt.NDArray[np.floating], pd.DataFrame]]
- probabilities
A 1D array or pandas.Series of probabilities corresponding to each scenario (T,).
- Type:
Optional[Union[npt.NDArray[np.floating], pd.Series]]
- asset_names
A list of names for the assets. If not provided, inferred from pandas inputs.
- Type:
Optional[List[str]]
- N
The number of assets, inferred from the input data.
- Type:
int
- T
The number of scenarios, inferred from the input data. None if parametric distribution is used.
- Type:
Optional[int]
- Assumptions & Design Choices:
If “scenarios” are provided without “probabilities”, probabilities are assumed to be uniform across all scenarios.
If scenarios are provided but mu and cov are not, the mean and covariance will be estimated from the scenarios, accompanied by a warning.
If provided “probabilities” do not sum to 1.0, they are automatically normalized with a warning. This choice ensures downstream solvers receive valid probability distributions.
If pandas objects are used for inputs, asset names are inferred from their indices or columns. It is assumed that the order and names are consistent across all provided pandas objects.
- N: int
- T: int | None
- asset_names: List[str] | None = None
- cov: numpy.typing.NDArray.numpy.floating | pandas.DataFrame | None = None
- mu: numpy.typing.NDArray.numpy.floating | pandas.Series | None = None
- probabilities: numpy.typing.NDArray.numpy.floating | pandas.Series | None = None
- scenarios: numpy.typing.NDArray.numpy.floating | pandas.DataFrame | None = None
- class pyvallocation.portfolioapi.PortfolioFrontier(weights, returns, risks, risk_measure, asset_names=None, metadata=None, alternate_risks=<factory>)[source]
Bases:
objectRepresents an efficient frontier of optimal portfolios.
This immutable container holds the results of an optimization run that generates a series of efficient portfolios. It provides methods to easily query and analyze specific portfolios on the frontier.
- Parameters:
weights (npt.NDArray[np.floating])
returns (npt.NDArray[np.floating])
risks (npt.NDArray[np.floating])
risk_measure (str)
asset_names (Optional[List[str]])
metadata (Optional[List[Dict[str, Any]]])
alternate_risks (Dict[str, np.ndarray])
- weights
A 2D NumPy array of shape (N, M), where N is the number of assets and M is the number of portfolios on the frontier. Each column represents the weights of an optimal portfolio.
- Type:
npt.NDArray[np.floating]
- returns
A 1D NumPy array of shape (M,) containing the expected returns for each portfolio on the frontier.
- Type:
npt.NDArray[np.floating]
- risks
Primary risk vector of length
M(e.g., volatility, CVaR, estimation risk).- Type:
npt.NDArray[np.floating]
- risk_measure
Label describing the primary risk measure.
- Type:
str
- asset_names
An optional list of names for the assets. If provided, enables pandas Series/DataFrame output for portfolio weights.
- Type:
Optional[List[str]]
- metadata
Optional per-portfolio diagnostics. Each entry maps diagnostic field names (e.g.,
target_multiplier) to their values.- Type:
Optional[List[Dict[str, Any]]]
- alternate_risks
Optional auxiliary risk grids keyed by label (e.g.,
"Volatility"when the frontier is built on CVaR). Shapes must match(M,). Access viarisk_label=...parameters.- Type:
Dict[str, np.ndarray]
- alternate_risks: Dict[str, numpy.ndarray]
- as_discrete_allocation(column, latest_prices, total_value, *, method='greedy', lot_sizes=None, **kwargs)[source]
Convert a selected frontier portfolio into a discrete allocation.
- Parameters:
column – Frontier column index to discretize.
latest_prices – Series or mapping of asset prices.
total_value – Total portfolio value available.
method – Discrete allocation method (default
"greedy").lot_sizes – Optional lot sizes per asset.
**kwargs – Extra arguments forwarded to the allocator.
- Returns:
DiscreteAllocationResult – Allocation result with share counts.
- Parameters:
column (int)
latest_prices (pandas.Series | Mapping[str, float])
total_value (float)
method (str)
lot_sizes (pandas.Series | Mapping[str, int] | None)
- Return type:
- asset_names: List[str] | None = None
- at_percentile(percentile, *, risk_label=None)[source]
Select the portfolio closest to a risk percentile (0-1 or 0-100).
- Parameters:
percentile – Percentile in
[0, 1]or[0, 100].risk_label – Risk label to use for ranking.
- Returns:
Tuple[pd.Series, float, float] – Weights, return, and risk value.
- Parameters:
percentile (float)
risk_label (str | None)
- Return type:
Tuple[pandas.Series, float, float]
- at_return(min_return, *, risk_label=None)[source]
Finds the portfolio that minimizes risk for a given expected return target.
This method identifies the portfolio on the frontier that has the lowest risk, subject to its return being greater than or equal to min_return.
- Parameters:
min_return (float) – The minimum required expected return.
risk_label – Optional alternate risk measure used for the minimisation.
- Returns:
Tuple[pd.Series, float, float] –
- A tuple containing:
weights (
pandas.Series): The weights of the portfolio.returns (float): The expected return of the portfolio.
risk (float): The risk of the portfolio.
- Parameters:
min_return (float)
risk_label (str | None)
- Return type:
Tuple[pandas.Series, float, float]
- at_risk(max_risk, *, risk_label=None)[source]
Finds the portfolio that maximizes return for a given risk tolerance.
This method identifies the portfolio on the frontier that has the highest return, subject to its risk being less than or equal to max_risk.
- Parameters:
max_risk (float) – The maximum allowable risk.
risk_label – Optional alternate risk measure name (see
available_risk_measures()). Defaults to the primary risk.
- Returns:
Tuple[pd.Series, float, float] –
- A tuple containing:
weights (
pandas.Series): The weights of the portfolio.returns (float): The expected return of the portfolio.
risk (float): The risk of the portfolio.
- Parameters:
max_risk (float)
risk_label (str | None)
- Return type:
Tuple[pandas.Series, float, float]
- available_risk_measures()[source]
Return list of risk measure labels carried by this frontier.
- Returns:
list[str] – Primary and alternate risk labels.
- Return type:
List[str]
- closest_risk(target_risk, *, risk_label=None)[source]
Select the portfolio whose risk is nearest
target_risk(L1 distance).Useful for aligning risk levels across frontiers built with different models.
- Parameters:
target_risk (float)
risk_label (str | None)
- Return type:
Tuple[pandas.Series, float, float]
- index_at_risk_percentile(percentile, *, risk_label=None)[source]
Return the column index closest to a risk percentile.
- Parameters:
percentile – Percentile in
[0, 1]or[0, 100].risk_label – Risk label to use for ranking.
- Returns:
int – Column index on the frontier.
- Parameters:
percentile (float)
risk_label (str | None)
- Return type:
int
- metadata: List[Dict[str, Any]] | None = None
- min_risk(*, risk_label=None)[source]
Finds the portfolio with the minimum risk on the efficient frontier.
- Parameters:
risk_label – Optional alternate risk measure name (see
available_risk_measures()). Defaults to the primary risk.- Returns:
Tuple[pd.Series, float, float] –
- A tuple containing:
weights (
pandas.Series): The weights of the minimum risk portfolio.returns (float): The expected return of the minimum risk portfolio.
risk (float): The risk of the minimum risk portfolio.
- Parameters:
risk_label (str | None)
- Return type:
Tuple[pandas.Series, float, float]
- returns: numpy.typing.NDArray.numpy.floating
- risk_measure: str
- risk_percentiles(risk_label=None)[source]
Return per-portfolio risk percentiles on
[0, 1].- Parameters:
risk_label – Risk label to use for ranking. Defaults to primary risk.
- Returns:
np.ndarray – Percentile ranks in
[0, 1].- Parameters:
risk_label (str | None)
- Return type:
numpy.ndarray
- risks: numpy.typing.NDArray.numpy.floating
- tangency(risk_free_rate)[source]
Calculates the tangency portfolio, which represents the portfolio with the maximum Sharpe ratio.
The Sharpe ratio is defined as (portfolio_return - risk_free_rate) / portfolio_risk.
- Parameters:
risk_free_rate (float) – The risk-free rate of return.
- Returns:
Tuple[pd.Series, float, float] –
- A tuple containing:
weights (
pandas.Series): The weights of the tangency portfolio.returns (float): The expected return of the tangency portfolio.
risk (float): The risk of the tangency portfolio.
- Parameters:
risk_free_rate (float)
- Return type:
Tuple[pandas.Series, float, float]
- to_frame(columns=None, *, column_labels=None)[source]
Return the frontier weights as a pandas DataFrame.
- Parameters:
columns – Optional iterable of column indices selecting specific portfolios.
column_labels – Optional labels for the resulting DataFrame columns.
- Returns:
A DataFrame whose rows correspond to assets and whose columns correspond to efficient portfolios.
- Raises:
ValueError – If
column_labelsis supplied with a length that does not match the number of selected portfolios.- Parameters:
columns (Iterable[int] | None)
column_labels (Sequence[str] | None)
- Return type:
pandas.DataFrame
- to_samples(columns=None, *, as_frame=True)[source]
Return the frontier weights as either a DataFrame or raw NumPy samples.
- Parameters:
columns – Optional iterable selecting specific portfolio indices.
as_frame – When
True(default) return a pandas DataFrame. WhenFalsereturn(matrix, asset_names)suitable for downstream NumPy consumption.
- Parameters:
columns (Iterable[int] | None)
as_frame (bool)
- Return type:
pandas.DataFrame | Tuple[numpy.ndarray, List[str] | None]
- class pyvallocation.portfolioapi.PortfolioWrapper(distribution, *, constraints=None, costs=None, long_only=True, total_weight=1.0, bounds=None)[source]
Bases:
objectHigh-level interface for portfolio construction and optimization.
Factory classmethods cover every user level:
Newbie:
PortfolioWrapper.from_prices(price_df)Parametric:
PortfolioWrapper.from_moments(mu, cov)Scenario-based:
PortfolioWrapper.from_scenarios(scenarios)Meucci Prayer (P3→P4):
PortfolioWrapper.from_invariants(invariants, reprice=..., horizon=52)Bayesian robust:
PortfolioWrapper.from_robust_posterior(posterior)
Then compute frontiers, select portfolios, and analyze:
frontier = port.variance_frontier() w, ret, risk = frontier.tangency(risk_free_rate=0.04)
Constraints and costs can be passed to factories or per-method.
- Parameters:
distribution (AssetsDistribution)
constraints (Union['Constraints', Dict[str, Any], None])
costs (Optional['TransactionCosts'])
long_only (bool)
total_weight (float)
bounds (Optional[Any])
- assemble_ensembles(specs, **kwargs)[source]
Proxy to
pyvallocation.ensembles.assemble_portfolio_ensemble().- Parameters:
specs – Sequence of ensemble specifications.
**kwargs – Forwarded to
assemble_portfolio_ensemble().
- Returns:
EnsembleResult – Aggregated ensemble output.
- Parameters:
specs (Sequence[EnsembleSpec])
kwargs (Any)
- Return type:
- cvar_frontier(num_portfolios=10, alpha=0.05, seed=None, *, constraints=None, costs=None, enforce_convexity=True)[source]
Compute the Mean-CVaR efficient frontier.
- Implementation Notes:
This method requires scenarios. If only
muandcovare provided, it makes a strong modeling assumption to simulate scenarios from a multivariate normal distribution.
- Parameters:
num_portfolios – The number of portfolios to compute. Defaults to 10.
alpha – The tail probability for CVaR. Defaults to 0.05.
seed – Random seed for scenario simulation reproducibility.
constraints – Optional
Constraintsor dict overriding the instance constraints.costs – Optional
TransactionCostsoverriding instance costs.enforce_convexity – Apply monotonicity and convex-envelope correction to the frontier (default
True). SetFalseto return raw solver output.
- Returns:
A
PortfolioFrontierobject whose columns are sorted by non-decreasing CVaR. An auxiliaryalternate_risks['Volatility']is attached for variance-based selection.- Parameters:
num_portfolios (int)
alpha (float)
seed (int | None)
enforce_convexity (bool)
- Return type:
PortfolioFrontier
- classmethod from_invariants(invariants, *, horizon=1, n_simulations=5000, p=None, reprice=None, seed=None, constraints=None, costs=None, long_only=True, total_weight=1.0, bounds=None)[source]
Create a wrapper via Meucci’s Prayer: project invariants, reprice to P&L.
Chains P3 Projection (bootstrap invariants over
horizonsteps) and P4 Pricing (reprice to P&L) to produce simple-return scenarios ready for optimisation (P6).- Parameters:
invariants – Historical invariant scenarios
(T, K). DataFrame or array of risk-driver observations (log-returns, yield changes, implied-vol changes, …).horizon – Investment horizon in invariant time steps.
n_simulations – Number of projected P&L scenarios.
p – Flexible probabilities for the invariants (e.g. posterior from
FlexibleViewsProcessor).reprice – P4 repricing callable. Default
reprice_exp()(exp(x)-1, suitable for log-return invariants).callable: applied to the full
(n_sim, K)projected matrix.dict
{col_name: callable}: per-column repricing (requires DataFrame invariants).
seed – Random seed for reproducibility.
constraints – Constraint specification.
costs – Transaction cost specification.
long_only – Shortcut for long-only constraint. Defaults to
True.total_weight – Shortcut for weight-sum constraint. Defaults to
1.0.bounds – Shortcut for per-asset bounds.
- Parameters:
invariants (Union[npt.NDArray[np.floating], pd.DataFrame])
horizon (int)
n_simulations (int)
p (Optional[Union[npt.NDArray[np.floating], pd.Series]])
reprice (Optional[Any])
seed (Optional[int])
constraints (Union[Constraints, Dict[str, Any], None])
costs (Optional[TransactionCosts])
long_only (bool)
total_weight (float)
bounds (Optional[Any])
- Return type:
PortfolioWrapper
Examples
>>> # Equities (log-return invariants, default reprice_exp) >>> log_rets = np.log(prices / prices.shift(1)).dropna() >>> wrapper = PortfolioWrapper.from_invariants(log_rets, horizon=52, seed=42) >>> frontier = wrapper.cvar_frontier()
>>> # Mixed portfolio (per-instrument repricing) >>> wrapper = PortfolioWrapper.from_invariants( ... invariants_df, ... reprice={"SPY": reprice_exp, ... "TLT": lambda dy: reprice_taylor(dy, delta=-7.2, gamma=55)}, ... horizon=52, ... )
- classmethod from_moments(mu, cov, *, constraints=None, costs=None, long_only=True, total_weight=1.0, bounds=None, return_type='simple')[source]
Create a ready-to-use wrapper from mean and covariance.
Applies
ensure_psd_matrixto the covariance and sets constraints in one call. Whenconstraintsis provided (as aConstraintsobject or dict), it overrides thelong_only/total_weight/boundsshortcuts.- Parameters:
return_type –
"simple"(default) or"log". When"log", moments are converted to simple-return space vialog2simple()so the optimizer works in P&L space per Meucci’s Prayer.- Parameters:
mu (Union[npt.NDArray[np.floating], pd.Series])
cov (Union[npt.NDArray[np.floating], pd.DataFrame])
constraints (Union[Constraints, Dict[str, Any], None])
costs (Optional[TransactionCosts])
long_only (bool)
total_weight (float)
bounds (Optional[Any])
return_type (str)
- Return type:
PortfolioWrapper
Examples
>>> wrapper = PortfolioWrapper.from_moments(mu, cov) >>> frontier = wrapper.variance_frontier()
- classmethod from_prices(prices, *, constraints=None, costs=None, long_only=True, total_weight=1.0, bounds=None)[source]
Create a wrapper from a price history.
Computes simple returns
(P_t / P_{t-1}) - 1and builds the distribution from the resulting scenario matrix. This is the easiest entry point for users who have raw price data.- Parameters:
prices – Price matrix
(T+1, N)as a 2-D array or DataFrame. Rows are chronological observations, columns are assets.constraints – Constraint specification.
costs – Transaction cost specification.
long_only – Shortcut for long-only constraint. Defaults to
True.total_weight – Shortcut for weight-sum constraint. Defaults to
1.0.bounds – Shortcut for per-asset bounds.
- Raises:
ValueError – If prices contains fewer than 2 rows, NaN, or non-positive values.
- Parameters:
prices (Union[npt.NDArray[np.floating], pd.DataFrame])
constraints (Union[Constraints, Dict[str, Any], None])
costs (Optional[TransactionCosts])
long_only (bool)
total_weight (float)
bounds (Optional[Any])
- Return type:
PortfolioWrapper
Examples
>>> wrapper = PortfolioWrapper.from_prices(price_df) >>> frontier = wrapper.variance_frontier()
- classmethod from_robust_posterior(posterior, *, constraints=None, costs=None, long_only=True, total_weight=1.0, bounds=None, annualization_factor=1.0)[source]
Create a wrapper configured for robust optimisation from a Bayesian posterior.
Maps the
RobustBayesPosteriorfields to the robust optimiser inputs per Meucci (2005, Eq. 9.155):dist.mu←posterior.mu(posterior mean \(\mu_1\))dist.cov←posterior.sigma(return covariance \(\Sigma_{ce}\))_uncertainty_cov←posterior.s_mu(mean-uncertainty scatter \(S_\mu\))
The resulting wrapper is ready for
robust_lambda_frontier()orsolve_robust_gamma_portfolio().- Parameters:
posterior – A
RobustBayesPosterior(typically fromRobustBayesPosterior.from_niw).constraints – Constraint specification.
costs – Transaction cost specification.
long_only – Shortcut for long-only constraint. Defaults to
True.total_weight – Shortcut for weight-sum constraint. Defaults to
1.0.bounds – Shortcut for per-asset bounds.
annualization_factor – Horizon scaling (e.g. 52 for weekly → annual). Mean scales by
h, scatter scales byh^2.
- Parameters:
posterior (RobustBayesPosterior)
constraints (Union[Constraints, Dict[str, Any], None])
costs (Optional[TransactionCosts])
long_only (bool)
total_weight (float)
bounds (Optional[Any])
annualization_factor (float)
- Return type:
PortfolioWrapper
Examples
>>> from pyvallocation import PortfolioWrapper >>> from pyvallocation.bayesian import RobustBayesPosterior >>> posterior = RobustBayesPosterior.from_niw(...) >>> wrapper = PortfolioWrapper.from_robust_posterior(posterior, annualization_factor=52) >>> frontier = wrapper.robust_lambda_frontier()
- classmethod from_scenarios(scenarios, *, probabilities=None, constraints=None, costs=None, long_only=True, total_weight=1.0, bounds=None, return_type='simple')[source]
Create a ready-to-use wrapper from scenario data.
- Parameters:
return_type –
"simple"(default) or"log". When"log", scenarios are converted to simple returns viaexp(x) - 1so the optimizer works in P&L space per Meucci’s Prayer.- Parameters:
scenarios (Union[npt.NDArray[np.floating], pd.DataFrame])
probabilities (Optional[Union[npt.NDArray[np.floating], pd.Series]])
constraints (Union[Constraints, Dict[str, Any], None])
costs (Optional[TransactionCosts])
long_only (bool)
total_weight (float)
bounds (Optional[Any])
return_type (str)
- Return type:
PortfolioWrapper
Examples
>>> wrapper = PortfolioWrapper.from_scenarios(returns_df) >>> frontier = wrapper.variance_frontier()
- make_ensemble_spec(name, *, optimiser='mean_variance', optimiser_kwargs=None, selector='tangency', selector_kwargs=None, frontier_selection=None, metadata=None)[source]
Convenience wrapper around
pyvallocation.ensembles.make_portfolio_spec()that reuses the wrapper’s distribution.- Parameters:
name (str)
optimiser (Union[str, Callable[[PortfolioWrapper], PortfolioFrontier], Callable[..., PortfolioFrontier]])
optimiser_kwargs (Optional[Dict[str, Any]])
selector (Union[str, Callable[[PortfolioFrontier], Union[pd.Series, Tuple[Any, ...], np.ndarray]]])
selector_kwargs (Optional[Dict[str, Any]])
frontier_selection (Optional[Sequence[int]])
metadata (Optional[Dict[str, Any]])
- Return type:
- min_cvar_at_return(return_target, alpha=0.05, seed=None, *, constraints=None, costs=None)[source]
Solve for the minimum CVaR portfolio that achieves a given expected return.
This method directly solves the optimization problem for a specific target return, rather than interpolating from a pre-computed frontier.
- Parameters:
return_target (float) – The desired minimum expected return.
alpha (float) – The tail probability for CVaR. Defaults to 0.05.
seed – Random seed for scenario simulation reproducibility.
constraints – Optional
Constraintsor dict overriding the instance constraints.costs – Optional
TransactionCostsoverriding instance costs.
- Returns:
Tuple[pd.Series, float, float] –
- A tuple containing:
weights (
pandas.Series): The weights of the optimal portfolio.return (float): The expected return of the portfolio.
risk (float): The CVaR of the portfolio.
- Raises:
ValueError – If scenarios cannot be used or generated.
- Parameters:
return_target (float)
alpha (float)
seed (int | None)
- Return type:
Tuple[pandas.Series, float, float]
- min_variance_at_return(return_target, *, constraints=None, costs=None)[source]
Solve for the minimum-variance portfolio achieving the target return.
This method directly solves the optimization problem for a specific target return, rather than interpolating from a pre-computed frontier. This is more accurate and efficient if only a single portfolio is of interest.
- Parameters:
return_target (float) – The desired minimum expected return.
constraints – Optional
Constraintsor dict overriding the instance constraints.costs – Optional
TransactionCostsoverriding instance costs.
- Returns:
Tuple[pd.Series, float, float] –
- A tuple containing:
weights (
pandas.Series): The weights of the optimal portfolio.return (float): The expected return of the portfolio.
risk (float): The volatility (standard deviation) of the portfolio.
- Raises:
ValueError – If mu and cov are not available in the distribution.
- Parameters:
return_target (float)
- Return type:
Tuple[pandas.Series, float, float]
- relaxed_risk_parity_frontier(num_portfolios=10, max_multiplier=1.6, *, lambda_reg=0.2, target_multipliers=None, include_risk_parity=True, risk_budgets=None, constraints=None, costs=None)[source]
Build a relaxed risk parity frontier by sweeping target-return multipliers.
Each frontier column corresponds to a distinct multiplier \(m\) applied to the benchmark risk parity return \(r_{RP}\) to generate the target \(R = m \cdot \max(r_{RP}, 0)\). The method solves the regulated RP programme for each multiplier using the shared \(\lambda\) value and stores per-point diagnostics (effective target after clipping, objective value, cone slack variables, solver warnings) in
PortfolioFrontier.metadata.- Parameters:
num_portfolios – Number of grid points when
target_multipliersis omitted. Must be positive; includes the upper endpointmax_multiplier.max_multiplier – Upper bound for the auto-generated multiplier grid. Ignored if
target_multipliersis supplied.lambda_reg – Regulator coefficient \(\\lambda\). Applies to every relaxed point; the optional RP anchor always uses \(\\lambda = 0\).
target_multipliers – Explicit iterable of multipliers. When provided, the method skips automatic grid generation and uses the supplied values verbatim.
include_risk_parity – If
True(default) the frontier prepends the pure risk parity solution so downstream plots can intercept the anchor directly.constraints – Optional
Constraintsor dict overriding the instance constraints.costs – Optional
TransactionCostsoverriding instance costs.
- Returns:
PortfolioFrontier – Object containing weights
(n, k), realised returns, volatility proxy (standard deviation), and diagnostic metadata for each node.- Parameters:
num_portfolios (int)
max_multiplier (float)
lambda_reg (float)
target_multipliers (Optional[Sequence[float]])
include_risk_parity (bool)
risk_budgets (Optional[npt.NDArray[np.floating]])
- Return type:
PortfolioFrontier
- relaxed_risk_parity_portfolio(*, lambda_reg=0.2, target_multiplier=1.2, return_target=None, risk_budgets=None, constraints=None, costs=None)[source]
Compute a relaxed risk parity allocation.
Returns the same triple as other portfolio methods: (weights, return, risk). For full diagnostics, use
relaxed_risk_parity_portfolio_with_diagnostics().Args/Returns: See
relaxed_risk_parity_portfolio_with_diagnostics().- Parameters:
lambda_reg (float)
target_multiplier (Optional[float])
return_target (Optional[float])
risk_budgets (Optional[npt.NDArray[np.floating]])
- Return type:
Tuple[pd.Series, float, float]
- relaxed_risk_parity_portfolio_with_diagnostics(*, lambda_reg=0.2, target_multiplier=1.2, return_target=None, risk_budgets=None, constraints=None, costs=None)[source]
Compute a single relaxed risk parity allocation and expose solver diagnostics.
The routine first solves the baseline risk parity programme (lambda = 0) to obtain the benchmark return \(r_{RP} = \mu^{\top}x^{RP}\). Unless an explicit
return_targetis supplied, the relaxed model is then solved with the adaptive target \(R = m \cdot \max(r_{RP}, 0)\) wheremistarget_multiplier. Infeasible targets are clipped via a backtracking line-search toward the RP return before falling back to the unconstrained problem if necessary.- Parameters:
lambda_reg – Non-negative regulator coefficient \(\\lambda\). Setting
0recovers the pure risk parity allocation. Defaults to0.2.target_multiplier – Optional multiplier \(m\) governing the adaptive target-return rule. Ignored when
return_targetis provided. Must beNonewhen pairing withlambda_reg == 0. Defaults to1.2.return_target – Explicit target return \(R\). When supplied, overrides the adaptive rule. The method clips \(R\) down to the feasible region if necessary. Defaults to
None.constraints – Optional
Constraintsor dict overriding the instance constraints.costs – Optional
TransactionCostsoverriding instance costs.
- Returns:
Tuple[pd.Series, float, float, Dict[str, Any]] – Optimal portfolio weights (Series), achieved return, portfolio volatility, and rich diagnostics including variance, marginal risks, risk contributions, target information, and any solver warning emitted during target clipping.
- Parameters:
lambda_reg (float)
target_multiplier (Optional[float])
return_target (Optional[float])
risk_budgets (Optional[npt.NDArray[np.floating]])
- Return type:
Tuple[pd.Series, float, float, Dict[str, Any]]
- robust_lambda_frontier(num_portfolios=10, max_lambda=2.0, *, lambdas=None, return_cov=None, constraints=None, costs=None)[source]
Computes a robust frontier based on uncertainty in expected returns.
- Assumptions & Design Choices:
This method follows Meucci (2005, Eq. 9.158). The
muandcovon theAssetsDistributionmust be:mu— the posterior mean \(\\mu_1\) (or any point estimate).cov— the mean-uncertainty scatter \(S_\\mu\), not the posterior covariance \(\\Sigma_1\). For an NIW posterior useRobustBayesPosterior.s_mu. If you want to supply \(\\Sigma_1\) directly, scale it: \(S_\\mu = \\frac{1}{T_1}\\frac{\\nu_1}{\\nu_1-2}\\Sigma_1\).
- Parameters:
num_portfolios – The number of portfolios to compute. Defaults to 10.
max_lambda – Maximum penalty weight \(\\lambda\). Higher values shrink toward the minimum-uncertainty portfolio. Defaults to 2.0.
lambdas – Optional explicit \(\\lambda\) grid. Overrides
num_portfolios/max_lambda.return_cov – Optional return covariance (for a volatility overlay). This is distinct from
dist.cov(the mean-uncertainty scatter).constraints – Optional
Constraintsor dict overriding the instance constraints.costs – Optional
TransactionCostsoverriding instance costs.
- Returns:
A
PortfolioFrontierobject.- Parameters:
num_portfolios (int)
max_lambda (float)
lambdas (Optional[Sequence[float]])
return_cov (Optional[Union[npt.NDArray[np.floating], pd.DataFrame]])
- Return type:
PortfolioFrontier
- solve_robust_gamma_portfolio(gamma_mu, gamma_sigma_sq, *, constraints=None, costs=None)[source]
Solve for a single robust portfolio with explicit uncertainty constraints.
Uses \(\gamma_\mu\) (Meucci Eq. 9.156) as the penalty weight on mean-uncertainty radius, and caps the squared radius at \(\gamma_{\sigma}^2\).
- Parameters:
gamma_mu – Penalty weight on \(\|S_\mu^{1/2}w\|_2\). Obtain from
RobustBayesPosterior.cred_radius_mu(p_mu).gamma_sigma_sq – Upper bound on \(\|S_\mu^{1/2}w\|_2^2\).
constraints – Optional
Constraintsor dict overriding the instance constraints.costs – Optional
TransactionCostsoverriding instance costs.
- Returns:
A tuple containing the portfolio weights, nominal return, and squared uncertainty radius.
- Parameters:
gamma_mu (float)
gamma_sigma_sq (float)
- Return type:
Tuple[pandas.Series, float, float]
- variance_frontier(num_portfolios=10, *, constraints=None, costs=None)[source]
Compute the classical mean-variance efficient frontier.
- Parameters:
num_portfolios – The number of portfolios to compute. Defaults to 10.
constraints – Optional
Constraintsor dict overriding the instance constraints.costs – Optional
TransactionCostsoverriding instance costs.
- Returns:
A PortfolioFrontier object. When scenarios are available on the distribution, a CVaR overlay is added to
alternate_risksto allow CVaR-based selection on the same weights.- Parameters:
num_portfolios (int)
- Return type:
PortfolioFrontier
- class pyvallocation.portfolioapi.TransactionCosts(initial_weights, market_impact_costs=None, proportional_costs=None)[source]
Bases:
objectImmutable container for transaction cost parameters.
- Parameters:
initial_weights (Union[npt.NDArray[np.floating], pd.Series])
market_impact_costs (Optional[Union[npt.NDArray[np.floating], pd.Series]])
proportional_costs (Optional[Union[npt.NDArray[np.floating], pd.Series]])
- initial_weights
Current portfolio weights (required).
- Type:
numpy.typing.NDArray.numpy.floating | pandas.Series
- market_impact_costs
Quadratic cost coefficients for mean-variance.
- Type:
numpy.typing.NDArray.numpy.floating | pandas.Series | None
- proportional_costs
Linear cost coefficients for CVaR and robust models.
- Type:
numpy.typing.NDArray.numpy.floating | pandas.Series | None
- initial_weights: numpy.typing.NDArray.numpy.floating | pandas.Series
- market_impact_costs: numpy.typing.NDArray.numpy.floating | pandas.Series | None = None
- proportional_costs: numpy.typing.NDArray.numpy.floating | pandas.Series | None = None
Portfolio Optimization: For advanced portfolio optimization
Portfolio Views: For applying investment views
Statistical Moments Module: For statistical analysis