Optimization

Mean-variance objectives, risk parity and efficient-frontier construction with diagnostics.

See the usage guide for task-oriented examples. Signatures below are generated from the public headers at the same commit as the rest of this site.

struct efficient_frontier_result

Efficient-frontier points and endpoint portfolios.

Public Members

std::vector<double> frontier_returns

Annualized expected return of each point, from the minimum-variance return to the maximum-return endpoint.

std::vector<double> frontier_volatilities

Annualized volatility of each point.

std::vector<double> frontier_sharpe

Sharpe ratio of each point; 0 where volatility is negligible.

std::vector<std::vector<double>> frontier_weights

Asset weights of each point, in asset_names order.

portfolio_point min_variance

Minimum-variance endpoint portfolio.

portfolio_point max_sharpe

Maximum-Sharpe portfolio.

double max_sharpe_ratio = 0.0

Sharpe ratio of max_sharpe; 0 where volatility is negligible.

std::vector<std::string> asset_names

Asset names, in returns column order.

double periods_per_year = 252.0

Annualization factor copied from the configuration.

frontier_point_diagnostics min_variance_diagnostics

Residuals of the minimum-variance endpoint.

frontier_point_diagnostics max_sharpe_diagnostics

Residuals of the max-Sharpe portfolio.

std::vector<frontier_point_diagnostics> frontier_diagnostics

Per-point residuals, parallel to frontier_returns.

struct frontier_config

Efficient-frontier computation settings.

Public Members

std::size_t n_points = 50U

Number of frontier points; must be at least 2.

double risk_free_rate = 0.0

Annualized risk-free rate used for Sharpe ratios; must be finite.

bool short_allowed = false

Allow weights down to -max_weight instead of 0.

double max_weight = 1.0

Per-asset upper bound; must be finite and greater than 0.

double periods_per_year = 252.0

Annualization factor; must be positive and finite.

struct frontier_point_diagnostics

Constraint residuals measured for one frontier point.

Public Members

frontier_solver solver = frontier_solver::projected_gradient

Solver used for the point.

double sum_residual = 0.0

Absolute deviation of the weight sum from 1.

double lower_bound_residual = 0.0

Largest lower-bound violation; 0 when feasible.

double upper_bound_residual = 0.0

Largest upper-bound violation; 0 when feasible.

double target_return_residual = 0.0

Absolute target-return deviation; 0 when no target was imposed.

struct portfolio_optimization_config

Inputs for optimize_portfolio.

Public Members

portfolio_objective objective = portfolio_objective::max_sharpe

Objective to optimize.

double risk_free_rate = 0.0

Annualized risk-free rate used by max_sharpe and the reported Sharpe ratio; must be finite.

std::optional<double> target_return

Annualized expected return required by target_return; must be finite when used.

std::optional<double> target_volatility

Annualized volatility required by target_risk; must be finite and attainable.

bool short_allowed = false

Allow weights down to -max_weight when min_weight is unset.

min_variance without sector_constraints is solved over the unit simplex, so that combination yields a long-only book regardless of this flag.

double max_weight = 1.0

Per-asset upper bound; must be finite and greater than 0.

std::optional<double> min_weight

Optional per-asset lower bound; must not exceed max_weight and overrides the short_allowed default.

std::vector<sector_allocation_constraint> sector_constraints

Sector weight bounds; requires sector_map, and sectors without a mapped asset are ignored.

std::map<std::string, std::string> sector_map

Asset name to sector name mapping used by sector_constraints.

struct portfolio_optimization_result

Optimized weights and their annualized summary statistics.

Public Members

std::vector<double> weights

Asset weights in asset_names order; they sum to 1.

double expected_return = 0.0

Annualized expected return, using 252 periods per year.

double volatility = 0.0

Annualized volatility, using 252 periods per year.

double sharpe = 0.0

(expected_return - risk_free_rate) / volatility; 0 where volatility is negligible.

std::vector<std::string> asset_names

Asset names, in returns column order.

portfolio_objective objective = portfolio_objective::max_sharpe

Objective that produced the result.

struct portfolio_point

One portfolio on the efficient frontier.

Public Members

std::vector<double> weights

Asset weights in asset_names order; they sum to 1.

double expected_return = 0.0

Annualized expected return.

double volatility = 0.0

Annualized volatility.

struct risk_parity_result

Risk-parity weights and their risk contributions.

Public Members

std::vector<double> weights

Asset weights in asset_names order, normalized to sum to 1.

std::vector<double> risk_contributions

w_i * (Sigma w)_i / volatility per asset; the values sum to the portfolio volatility.

double volatility = 0.0

Annualized portfolio volatility, using 252 periods per year.

std::vector<std::string> asset_names

Asset names, in returns column order.

struct sector_allocation_constraint

Bounds on the summed weights of the assets mapped to one sector.

Public Members

std::string sector

Sector name matched against portfolio_optimization_config::sector_map values.

double lower

Lower bound on the summed sector weight.

double upper

Upper bound on the summed sector weight.

enum class fincore::frontier_solver

Solver that produced one frontier point.

Values:

enumerator min_variance_degenerate

Minimum-variance weights reused for every point because the attainable return range is degenerate.

enumerator min_variance

First point, solved as a variance minimization.

enumerator projected_gradient

Interior target-return point or the max-Sharpe portfolio, solved by projected gradient descent.

enumerator bounded_linear_max_return

Last point, filled greedily by descending expected return up to the weight bounds.

enum class fincore::portfolio_objective

Portfolio optimization objective.

Values:

enumerator max_sharpe

Maximize the Sharpe ratio using risk_free_rate.

enumerator min_variance

Minimize portfolio variance.

enumerator target_return

Minimize variance subject to the expected return equaling target_return.

enumerator target_risk

Maximize expected return subject to the volatility equaling target_volatility.

efficient_frontier_result fincore::efficient_frontier(const dataframe &returns, const frontier_config &config = {})

Computes an efficient frontier from daily simple returns.

Points are evenly spaced in expected return between the minimum-variance portfolio and a maximum-return endpoint built by filling assets in descending mean-return order up to the weight bounds. Returns and volatilities are annualized with config.periods_per_year, and Sharpe ratios use config.risk_free_rate. Solvers report constraint residuals in frontier_diagnostics rather than a convergence status.

Parameters:
  • returns – Daily simple returns with one finite double column per asset and at least two observations.

  • config – Frontier settings; n_points must be at least 2.

Throws:
  • input_error – If returns is empty or too short, fewer than two assets are present, values are non-finite, or a configuration value is invalid.

  • optimization_error – If the covariance matrix is not positive semidefinite, the weight bounds cannot fund a fully invested portfolio, or a solver returns infeasible weights.

Returns:

Frontier points, endpoint portfolios and per-point diagnostics.

portfolio_optimization_result fincore::optimize_portfolio(const dataframe &returns, const portfolio_optimization_config &config = {})

Optimizes portfolio weights for one supported objective.

Returns are annualized with a fixed 252-period year. Sector constraints apply only to assets listed in sector_map, and infeasible targets are reported by throwing rather than through a status field.

Parameters:
  • returns – Daily simple returns with one finite double column per asset and at least two observations.

  • config – Objective, bounds and optional sector constraints.

Throws:
  • input_error – If returns is empty, too short or non-finite, an objective-specific target is missing, weight bounds are invalid, sector constraints lack a sector map, or the objective is unknown.

  • optimization_error – If the weight bounds cannot produce a fully invested portfolio, target_volatility lies outside the attainable range, or the solver returns infeasible weights.

Returns:

Optimized weights, annualized return, volatility, Sharpe ratio and asset names.

risk_parity_result fincore::risk_parity(const dataframe &returns, std::optional<std::vector<double>> risk_budget = std::nullopt, std::size_t max_iter = 1000U)

Computes risk-parity weights with equal or user-supplied risk budgets.

The solver minimizes squared deviations between realized risk-contribution shares and the normalized risk budget, then renormalizes the weights to sum to 1. Returns and volatility use a fixed 252-period year. Weights are bounded to [1e-6, 1.0] during the solve.

Parameters:
  • returns – Daily simple returns with one finite double column per asset and at least two observations.

  • risk_budget – Optional per-asset budget with finite, non-negative entries and a positive sum; it is normalized internally. A uniform budget is used when omitted.

  • max_iter – Maximum projected-gradient iterations per penalty stage; must be at least 1.

Throws:
  • input_error – If returns is empty, too short or non-finite, max_iter is zero, or the risk budget is malformed.

  • optimization_error – If the solved weights cannot be normalized to a positive sum.

Returns:

Risk-parity weights, risk contributions, annualized volatility and asset names.