openquant.portfolio
Compiled from Rust (crates/pyopenquant). Signatures and types are from the shipped .pyi stubs; descriptions are the functions’ own docstrings, as help() shows them. All Python modules.
Functions
allocate_efficient_risk
allocate_efficient_risk(
prices: Sequence[Sequence[float]],
target_return: float,
bounds: Sequence[tuple[int, float, float]] | None = None,
tuple_bounds: tuple[float, float] | None = None,
) -> tuple[list[float], float, float, float]Least-risk portfolio from a price matrix whose expected return is at least a target.
Solves min w' Sigma w subject to mu' w >= target_return, sum(w) = 1 and the bounds. A target below the minimum-variance portfolio's return yields that portfolio. Returns are simple returns and both mu and Sigma are annualised with 252 periods a year (daily rows), so target_return is an annual figure. Markowitz (1952); AFML chapter 16.
Parameters
prices: list[list[float]]Price matrix, one inner list per date (ascending in time, at least 2 rows) and one column per asset.
target_return: floatMinimum annualised expected return (a floor, not an equality).
bounds: list[tuple[int, float, float]] | None, default NonePer-asset
(asset_index, lower, upper)weight bounds; they overridetuple_boundsfor those assets. Out-of-range indices are ignored.tuple_bounds: tuple[float, float] | None, default None(lower, upper)bounds for every asset not inbounds; None means(0, 1). Upper bounds above 1 are treated as 1; negative lower bounds allow shorting.
Returns
- tuple[list[float], float, float, float]
(weights, portfolio_risk, portfolio_return, portfolio_sharpe): weights per asset in column order summing to 1, annualised volatility, annualised expected return, andportfolio_return / portfolio_risk(0 when the risk is 0).
Raises
- ValueError
If
pricesis empty or ragged, or the core rejects the input (e.g. fewer than two rows, a zero price, bounds that cannot sum to 1, or an unreachabletarget_return).
allocate_from_inputs
allocate_from_inputs(
expected_returns: Sequence[float],
covariance: Sequence[Sequence[float]],
solution: str,
risk_free_rate: float = 0.0,
target_return: float = 0.0,
bounds: Sequence[tuple[int, float, float]] | None = None,
tuple_bounds: tuple[float, float] | None = None,
) -> tuple[list[float], float, float, float]Allocate a mean-variance portfolio from given expected returns and covariance.
Same solutions as allocate_with_solution ("inverse_variance", "min_volatility", "max_sharpe", "efficient_risk"), but no returns are estimated and nothing is annualised: the units are the caller's, and expected_returns, covariance, risk_free_rate and target_return must agree (an annual mu with a daily covariance overstates the Sharpe ratio by sqrt(252)). Markowitz (1952); AFML chapter 16.
Parameters
expected_returns: list[float]Expected return per asset.
covariance: list[list[float]]Square covariance matrix in the same asset order, one inner list per row.
solution: str"inverse_variance","min_volatility","max_sharpe"or"efficient_risk".risk_free_rate: float, default 0.0Risk-free rate for
"max_sharpe"and the reported Sharpe ratio.target_return: float, default 0.0Minimum expected return for
"efficient_risk"; ignored otherwise.bounds: list[tuple[int, float, float]] | None, default NonePer-asset
(asset_index, lower, upper)weight bounds; they overridetuple_boundsfor those assets. Out-of-range indices are ignored.tuple_bounds: tuple[float, float] | None, default None(lower, upper)bounds for every asset not inbounds; None means(0, 1). Upper bounds above 1 are treated as 1; negative lower bounds allow shorting.
Returns
- tuple[list[float], float, float, float]
(weights, portfolio_risk, portfolio_return, portfolio_sharpe)in the caller's units: weights summing to 1,sqrt(w' Sigma w),mu' w, and(portfolio_return - risk_free_rate) / portfolio_risk(0 when the risk is 0).
Raises
- ValueError
If
covarianceis empty or ragged, or the core rejects the input (e.g. a non-square covariance or one whose size differs fromexpected_returns, an unknownsolution, bounds that cannot sum to 1, an unreachabletarget_return, or no asset aboverisk_free_ratefor"max_sharpe").
allocate_inverse_variance
allocate_inverse_variance(
prices: Sequence[Sequence[float]],
) -> tuple[list[float], float, float, float]Inverse-variance portfolio from a price matrix, long-only and fully invested.
Weights are w_i = (1 / Sigma_ii) / sum_j(1 / Sigma_jj) (correlation ignored), where Sigma is the sample covariance of simple returns p_t / p_{t-1} - 1. Expected returns and covariance are annualised with 252 periods a year, so the reported figures are annual for daily rows. The 252 assumes daily rows: for one-minute rows multiply the return by 390 and the risk by sqrt(390); the weights do not depend on it. Markowitz (1952); see AFML chapter 16 for why mean-variance portfolios are fragile.
Parameters
prices: list[list[float]]Price matrix, one inner list per date (ascending in time, at least 2 rows) and one column per asset.
Returns
- tuple[list[float], float, float, float]
(weights, portfolio_risk, portfolio_return, portfolio_sharpe): weights per asset in column order summing to 1, annualised volatilitysqrt(w' Sigma w), annualised expected returnmu' w, andportfolio_return / portfolio_risk(0 when the risk is 0).
Raises
- ValueError
If
pricesis empty or ragged, or the core rejects the input (e.g. fewer than two rows, a zero price used as a return denominator, or an asset with zero return variance).
allocate_max_sharpe
allocate_max_sharpe(
prices: Sequence[Sequence[float]],
risk_free_rate: float | None = None,
bounds: Sequence[tuple[int, float, float]] | None = None,
tuple_bounds: tuple[float, float] | None = None,
) -> tuple[list[float], float, float, float]Maximum-Sharpe portfolio from a price matrix, with weight bounds.
Maximises (mu' w - rf) / sqrt(w' Sigma w) subject to sum(w) = 1 and the bounds, via the substitution y = kappa w. Returns are simple returns and both mu and Sigma are annualised with 252 periods a year, so risk_free_rate is an annual rate. The 252 assumes daily rows; openquant.pipeline.run_mid_frequency_pipeline rescales for other bar frequencies with periods_per_year. The Sharpe ratio is in-sample. Markowitz (1952); AFML chapter 16.
Parameters
prices: list[list[float]]Price matrix, one inner list per date (ascending in time, at least 2 rows) and one column per asset.
risk_free_rate: float | None, default NoneAnnual risk-free rate; None means 0.0.
bounds: list[tuple[int, float, float]] | None, default NonePer-asset
(asset_index, lower, upper)weight bounds; they overridetuple_boundsfor those assets. Out-of-range indices are ignored.tuple_bounds: tuple[float, float] | None, default None(lower, upper)bounds for every asset not inbounds; None means(0, 1). Upper bounds above 1 are treated as 1; negative lower bounds allow shorting.
Returns
- tuple[list[float], float, float, float]
(weights, portfolio_risk, portfolio_return, portfolio_sharpe): weights per asset in column order summing to 1, annualised volatility, annualised expected return, and(portfolio_return - risk_free_rate) / portfolio_risk.
Raises
- ValueError
If
pricesis empty or ragged, or the core rejects the input (e.g. fewer than two rows, a zero price, bounds that cannot sum to 1, no asset with an expected return aboverisk_free_rate, or a degenerate solution).
allocate_min_vol
allocate_min_vol(
prices: Sequence[Sequence[float]],
bounds: Sequence[tuple[int, float, float]] | None = None,
tuple_bounds: tuple[float, float] | None = None,
) -> tuple[list[float], float, float, float]Minimum-volatility portfolio from a price matrix, min w' Sigma w with weight bounds.
Solves the quadratic programme subject to sum(w) = 1 and the bounds (the bounds are part of the problem, not applied afterwards). Returns are simple returns and both the expected returns and covariance are annualised with 252 periods a year (daily rows; the weights do not depend on the factor). Markowitz (1952); AFML chapter 16.
Parameters
prices: list[list[float]]Price matrix, one inner list per date (ascending in time, at least 2 rows) and one column per asset.
bounds: list[tuple[int, float, float]] | None, default NonePer-asset
(asset_index, lower, upper)weight bounds; they overridetuple_boundsfor those assets. Out-of-range indices are ignored.tuple_bounds: tuple[float, float] | None, default None(lower, upper)bounds for every asset not inbounds; None means(0, 1). Upper bounds above 1 are treated as 1; negative lower bounds allow shorting.
Returns
- tuple[list[float], float, float, float]
(weights, portfolio_risk, portfolio_return, portfolio_sharpe): weights per asset in column order summing to 1, annualised volatility, annualised expected return, andportfolio_return / portfolio_risk(0 when the risk is 0).
Raises
- ValueError
If
pricesis empty or ragged, or the core rejects the input (e.g. fewer than two rows, a zero price, bounds that cannot sum to 1, or no feasible or convergent solution).
allocate_with_solution
allocate_with_solution(
prices: Sequence[Sequence[float]],
solution: str,
risk_free_rate: float = 0.0,
target_return: float = 0.0,
bounds: Sequence[tuple[int, float, float]] | None = None,
tuple_bounds: tuple[float, float] | None = None,
resample_by: str | None = None,
returns_method: str | None = None,
) -> tuple[list[float], float, float, float]Allocate a mean-variance portfolio from a price matrix with a named solution.
Estimates annualised expected returns mu and covariance Sigma from simple returns of the (optionally resampled) prices, both scaled by 252 / step, then solves one of: "inverse_variance" (weights proportional to 1 / Sigma_ii, projected onto the bounds), "min_volatility", "max_sharpe" or "efficient_risk" (least risk with mu' w >= target_return). Weights are fully invested; risk_free_rate and target_return are annual. Markowitz (1952); AFML chapter 16.
Parameters
prices: list[list[float]]Price matrix, one inner list per date (ascending in time) and one column per asset.
solution: str"inverse_variance","min_volatility","max_sharpe"or"efficient_risk".risk_free_rate: float, default 0.0Annual risk-free rate for
"max_sharpe"and for the reported Sharpe ratio.target_return: float, default 0.0Minimum annual expected return for
"efficient_risk"; ignored otherwise.bounds: list[tuple[int, float, float]] | None, default NonePer-asset
(asset_index, lower, upper)weight bounds; they overridetuple_boundsfor those assets. Out-of-range indices are ignored.tuple_bounds: tuple[float, float] | None, default None(lower, upper)bounds for every asset not inbounds; None means(0, 1). Upper bounds above 1 are treated as 1; negative lower bounds allow shorting.resample_by: str | None, default None"W"/"week"/"weekly"keeps every 5th row,"M"/"month"/"monthly"every 21st (case-insensitive); anything else, or None, keeps every row.returns_method: str | None, default None"mean"/"mean_historical"(arithmetic mean, the default) or"exponential"/"exponential_historical"(exponentially weighted mean, span 500); case-insensitive.
Returns
- tuple[list[float], float, float, float]
(weights, portfolio_risk, portfolio_return, portfolio_sharpe): weights per asset in column order summing to 1, annualised volatility, annualised expected return, and(portfolio_return - risk_free_rate) / portfolio_risk(0 when the risk is 0).
Raises
- ValueError
If
pricesis empty or ragged,returns_methodis unknown, or the core rejects the input (e.g. an unknownsolution, fewer than two rows after resampling, a zero price, bounds that cannot sum to 1, an unreachabletarget_return, no asset aboverisk_free_ratefor"max_sharpe", or a zero variance for"inverse_variance").