Expected Returns
PortfolioOptimisers.ExpectedReturn — Type
struct ExpectedReturn{__T_settings, __T_rt} <: NonOptimisationRiskMeasureReturn-based risk measure.
ExpectedReturn is a risk measure that uses the expected portfolio return as its risk metric. This is useful for algorithms or analyses where the risk is defined as the expected return, used in portfolio performance analysis.
Mathematical definition
\[\begin{align} \mathrm{ER}(\boldsymbol{w}) &= \sum_{j \,:\, e_{j}} s_j\, R_j(\boldsymbol{w})\,. \end{align}\]
Where:
- $\mathrm{ER}(\boldsymbol{w})$: Expected portfolio return, used here as the risk metric.
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
- $R_j$: Expected return of the $j$-th term in
rt, net offees. - $s_j$:
settings.scaleof the $j$-th term. A singlerthas one term. - $e_j$:
settings.rteof the $j$-th term. The sum runs over the terms whose flag istrue.
Fields
settings: Risk measure settings.
rt: Returns estimator, or a vector of them. A vector is summed at its terms'settings.scaleweights, skipping any term whosesettings.rteisfalse. There is no scalariser on the return axis.
Constructors
ExpectedReturn(; settings::HierarchicalRiskMeasureSettings = HierarchicalRiskMeasureSettings(), rt::JRE_VecJRE = ArithmeticReturn()) -> ExpectedReturnKeywords correspond to the struct's fields.
Multiplicity
rt takes one return term or a vector of them, so ExpectedReturn(; rt = res.ret) works after a solve against several terms. A vector is summed at its terms' settings.scale weights, skipping any term whose settings.rte is false.
The type gains no scalariser: the return axis sums and never scalarises.
Validation
- If
rtis a vector:!isempty(rt).
Propagated parameters
When factory is called on this type, the following @fprop-tagged fields are automatically propagated:
rt: Recursively updated viafactory.
Examples
julia> ExpectedReturn()ExpectedReturn settings ┼ HierarchicalRiskMeasureSettings │ scale ┴ Float64: 1.0 rt ┼ ArithmeticReturn │ settings ┼ JuMPReturnsSettings │ │ scale ┼ Float64: 1.0 │ │ lb ┼ nothing │ │ rte ┼ Bool: true │ │ fee ┼ Bool: true │ │ mic ┴ Bool: true │ ucs ┼ nothing │ mu ┴ nothingRelated
PortfolioOptimisers.ExpectedReturnRiskRatio — Type
struct ExpectedReturnRiskRatio{__T_settings, __T_rt, __T_rk, __T_sca, __T_rf} <: NonOptimisationRiskMeasureRatio-based risk measure.
ExpectedReturnRiskRatio is a risk measure that computes the risk-adjusted return ratio, such as the Sharpe ratio, for a portfolio. It combines a return estimator, a risk measure, and a risk-free rate to produce a ratio metric, used in portfolio performance analysis.
Mathematical definition
\[\begin{align} \mathrm{ERRR}(\boldsymbol{w}) &= \frac{\sum_{j \,:\, e_{j}} s_j\, R_j(\boldsymbol{w}) - r_f}{\mathrm{sca}\left(\left\{c_i\, \rho_i(\boldsymbol{w})\right\}\right)}\,. \end{align}\]
Where:
- $\mathrm{ERRR}(\boldsymbol{w})$: Expected return to risk ratio.
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
- $R_j$: Expected return of the $j$-th term in
rt, net offees. - $s_j$:
settings.scaleof the $j$-th term ofrt. - $e_j$:
settings.rteof the $j$-th term ofrt. The sum runs over the terms whose flag istrue. - $r_f$: Risk-free rate, the
rffield. - $\rho_i$: The $i$-th risk measure in
rk. - $c_i$:
settings.scaleof the $i$-th risk measure. - $\mathrm{sca}$: Scalariser held in
sca, which reduces the risk axis to one number.
Fields
settings: Risk measure settings.
rt: Returns estimator, or a vector of them. A vector is summed at its terms'settings.scaleweights, skipping any term whosesettings.rteisfalse. There is no scalariser on the return axis.
rk: Risk measure for ratio computation, or a vector of them scalarised bysca.
sca: Scalariser combining the risk measures inrkinto one number. Inert whenrkholds a single measure. The field beats ascakeyword supplied at the call site.
rf: Risk-free rate.
Constructors
ExpectedReturnRiskRatio(; settings::HierarchicalRiskMeasureSettings = HierarchicalRiskMeasureSettings(), rt::JRE_VecJRE = ArithmeticReturn(), rk::BaseRM_VecBaseRM = Variance(), sca::Scalariser = SumScalariser(), rf::Number = 0.0,) -> ExpectedReturnRiskRatioKeywords correspond to the struct's fields.
Multiplicity
Both axes widen, and only the risk axis carries a scalariser. rt sums its terms at their own settings.scale weights, skipping any term whose settings.rte is false; rk is scalarised into one number by sca. The ratio is then aggregate over aggregate. The return axis sums and never scalarises.
The field beats a caller's sca keyword. A sca passed at the call site flows no further than this type, so a figure reported from an ExpectedReturnRiskRatio is always the one the type names.
Validation
- If
rtis a vector:!isempty(rt). - If
rkis a vector:!isempty(rk). isfinite(rf).
Propagated parameters
When factory is called on this type, the following @fprop-tagged fields are automatically propagated:
Examples
julia> ExpectedReturnRiskRatio()ExpectedReturnRiskRatio settings ┼ HierarchicalRiskMeasureSettings │ scale ┴ Float64: 1.0 rt ┼ ArithmeticReturn │ settings ┼ JuMPReturnsSettings │ │ scale ┼ Float64: 1.0 │ │ lb ┼ nothing │ │ rte ┼ Bool: true │ │ fee ┼ Bool: true │ │ mic ┴ Bool: true │ ucs ┼ nothing │ mu ┴ nothing rk ┼ Variance │ settings ┼ RiskMeasureSettings │ │ scale ┼ Float64: 1.0 │ │ ub ┼ nothing │ │ rke ┴ Bool: true │ sigma ┼ nothing │ chol ┼ nothing │ rc ┼ nothing │ alg ┴ SquaredSOCRiskExpr() sca ┼ SumScalariser() rf ┴ Float64: 0.0Related
PortfolioOptimisers.PerformanceSummaryResult — Type
struct PerformanceSummaryResult{__T_n_periods, __T_periods_per_year, __T_alpha, __T_compound, __T_ann_return, __T_ann_volatility, __T_sharpe, __T_sharpe_stderr, __T_sortino, __T_calmar, __T_max_drawdown, __T_cvar} <: AbstractResultThe headline performance statistics of a realised return series.
PerformanceSummaryResult is what performance_summary returns. It carries the seven numbers plot_performance_summary draws, the standard error of the Sharpe ratio, and the four inputs that produced them, so a summary can be tabulated, compared across runs, asserted on in a test, or fed to a search without a plotting package installed.
Sign convention
max_drawdown and cvar are reported in return space, so both are non-positive for a portfolio that ever loses. This is the opposite sign to the ConditionalValueatRisk risk measure, which reports a positive loss: ConditionalValueatRisk(; alpha = alpha)(ret) == -summary.cvar.
Fields
n_periods: Number of observations in the return series.
periods_per_year: Annualisation factor. 252 for daily, 52 for weekly, 12 for monthly returns.
alpha: Tail probability used for the CVaR, $\alpha \in (0, 1)$.
compound: Whether the wealth path behind the drawdown statistics was compounded.
ann_return: Annualised arithmetic mean return.
ann_volatility: Annualised sample standard deviation.
sharpe: Annualised Sharpe ratio at a zero risk-free rate.NaNif the volatility is zero.
sharpe_stderr: Standard error ofsharpe, corrected for the skewness and excess kurtosis of the returns, and not for their serial dependence. A series scored under a Weight Drift is serially dependent through the weights it held, so this figure understates the true standard error of such a series.
sortino: Annualised Sortino ratio, at a zero minimum acceptable return.NaNif the downside deviation is zero.
calmar: Annualised return divided by the absolute maximum drawdown.NaNif there is no drawdown.
max_drawdown: Maximum drawdown, in return space, so it is non-positive.
cvar: Conditional Value-at-Risk atalpha, in return space, so a tail loss is negative.
Constructors
PerformanceSummaryResult( n_periods, periods_per_year, alpha, compound, ann_return, ann_volatility, sharpe, sharpe_stderr, sortino, calmar, max_drawdown, cvar) -> PerformanceSummaryResultArguments correspond to the struct's fields, in the order they are declared. The type is a Result, so performance_summary builds it and a caller reads it; there is no keyword constructor, and the type validates nothing of its own. The four inputs it carries first are the arguments that produced the statistics after them.
Related
PortfolioOptimisers.expected_risk — Function
expected_risk(r::ExpectedReturn, w::VecNum, pr::AbstractPriorResult,
fees::Option{<:Fees} = nothing; kwargs...)Compute the expected risk for a portfolio using a return-based risk measure.
expected_risk returns the expected portfolio return as the risk metric, using the specified return estimator in the ExpectedReturn. This is useful for algorithms where risk is defined as expected return. The closed form is the one ExpectedReturn states, and the method delegates to expected_return on the measure's rt field.
Arguments
r:ExpectedReturncontaining a return estimator.w: Portfolio weights.pr: Prior result.fees: Optional transaction fees.kwargs...: Additional keyword arguments.
Returns
risk::Number: Expected portfolio return (net of fees if provided).
Related
PortfolioOptimisers.expected_risk — Function
expected_risk(r::ExpectedReturnRiskRatio, w::VecNum, pr::AbstractPriorResult,
fees::Option{<:Fees} = nothing; kwargs...)Compute the expected risk for a portfolio using a ratio-based risk measure.
expected_risk returns the risk-adjusted return ratio (e.g., Sharpe ratio) for the portfolio, using the specified return estimator, risk measure, and risk-free rate in the ExpectedReturnRiskRatio. The closed form is the one ExpectedReturnRiskRatio states, and the method delegates to expected_ratio on the measure's fields.
The measure's own sca and rf are pinned after kwargs... is splatted, so a sca or an rf supplied at the call site loses to the field. A figure reported from an ExpectedReturnRiskRatio is therefore always the one the type names.
Arguments
r:ExpectedReturnRiskRatiocontaining a return estimator, risk measure, and risk-free rate.w: Portfolio weights.pr: Prior result.fees: Optional transaction fees.kwargs...: Additional keyword arguments.
Returns
risk::Number: Risk-adjusted return ratio.
Related
PortfolioOptimisers.performance_summary — Function
performance_summary(ret::VecNum; periods_per_year::Number = 252, alpha::Number = 0.05,
compound::Bool = false) -> PerformanceSummaryResult
performance_summary(w::ArrNum, X::MatNum, fees::Option{<:Fees} = nothing;
periods_per_year, alpha, compound) -> PerformanceSummaryResult
performance_summary(w::ArrNum, rd::ReturnsResult, fees::Option{<:Fees} = nothing;
kwargs...) -> PerformanceSummaryResult
performance_summary(res::OptimisationResult, rd::ReturnsResult;
kwargs...) -> PerformanceSummaryResult
performance_summary(pred::PredictionResult; kwargs...) -> PerformanceSummaryResult
performance_summary(pred::MultiPeriodPredictionResult;
kwargs...) -> PerformanceSummaryResultSummarise a realised return series as a PerformanceSummaryResult.
The weight-and-returns methods net the returns through calc_net_returns first, so a summary of a portfolio accounts for its fees.
The Precomputed-returns contract: the series ret must be finite. No method takes a finiteness check, because every internal caller hands one a finite series: the prediction methods read the fold's own funnel output, and a scan on a long series would be paid by all of them. One non-finite entry makes the mean, the volatility and every ratio non-finite, and the tail figure answers a finite wrong number rather than a NaN, because partialsort orders a NaN after every real. A caller who holds a gapped series drops the gaps first with x[isfinite.(x)], and a caller who holds a gapped panel scores it through predict(res::NonFiniteAllocationOptimisationResult, rd::ReturnsResult) instead, which filters the Held Gaps once.
Mathematical definition
Let $m$ and $s$ be the sample mean and the sample standard deviation of the periodic returns $\boldsymbol{r}$, let $p$ be periods_per_year, and let $\boldsymbol{d}$ be the drawdown path of the cumulative wealth series.
\[\begin{align} \mathrm{ann\_return} &= m p\\ \mathrm{ann\_volatility} &= s \sqrt{p}\\ \mathrm{sharpe} &= \dfrac{\mathrm{ann\_return}}{\mathrm{ann\_volatility}}\\ \mathrm{sortino} &= \dfrac{\mathrm{ann\_return}}{\sqrt{p \, \mathbb{E}\left[\min(\boldsymbol{r}, 0)^{2}\right]}}\\ \mathrm{calmar} &= \dfrac{\mathrm{ann\_return}}{\left\lvert \min_{t} d_{t} \right\rvert}\,. \end{align}\]
The downside deviation of the Sortino ratio divides by $T$ rather than by the count of losing periods, which is the Sortino and Price convention and the one that keeps the ratio comparable across series with different loss frequencies.
The standard error of the Sharpe ratio is the Bailey and Lopez de Prado [146] expression, which corrects for the skewness $g_{1}$ and the excess kurtosis $g_{2}$ of the returns:
\[\begin{align} \mathrm{sharpe\_stderr} &= \sqrt{\dfrac{p}{T - 1} \left(1 - g_{1} \dfrac{m}{s} + \dfrac{g_{2} + 2}{4} \dfrac{m^{2}}{s^{2}}\right)}\,. \end{align}\]
The kurtosis term is $(g_{2} + 2)/4$ because the source states it as $(\gamma_{2} - 1)/4$ on the raw fourth standardised moment $\gamma_{2}$, and StatsBase.kurtosis returns the excess moment $g_{2} = \gamma_{2} - 3$. The two forms agree, and only this one reduces to the naive $\sqrt{(1 + \mathrm{SR}^{2}/2)/T}$ on a normal series: over 200,000 samples of 250 normal returns at a per-period Sharpe ratio of 0.5, the sample standard deviation of the Sharpe ratio is 0.06728323, against 0.06721661 from this expression evaluated at the population moments and 0.06337243 from the same expression without the $+2$.
A non-normal return series makes a Sharpe ratio less precise than the naive expression suggests, and negative skew makes it worse. That is the case that matters for a real portfolio, which is why the standard error ships beside the ratio.
The expression corrects for the third and fourth moments and not for serial dependence, so it reads every observation as independent of the others. A series scored under a Weight Drift is serially dependent through the weights it held, because a position that grew weighs the next observation more, so on such a series this figure understates the true standard error. The rigorous alternative is a long-run variance estimator, which needs a bandwidth the library would have to defend on every sample. The library does not build one, and it applies no guard and no threshold here: the figure is reported as it stands, and this paragraph is the caveat that rides with it.
Algorithm
The five other methods reduce to the ret::VecNum method, which runs the steps. A method that takes weights nets the returns through calc_net_returns first, and a method that takes a prediction result takes the first series when it carries several.
- Check
alphaandperiods_per_year, as# Validationstates. - Take
T, the number of periods, andmands, the sample mean and the corrected sample standard deviation ofret. - Annualise the two, giving
ann_retandann_vol. - Divide, giving
sharpe. A non-positiveann_volgives aNaNin its place. - Clip every positive entry of
retto zero, square, average over allTperiods and annualise, giving the downside deviationddev. Divide, givingsortino. A non-positiveddevgives aNaN. - Build the cumulative wealth series with
cumulative_returns, take its drawdown path withdrawdowns, and readmax_dd, the minimum of that path. Divide, givingcalmar. A non-negativemax_ddgives aNaN. - Evaluate
ConditionalValueatRiskatalphaonretand negate it, givingcvar_valin return space. - Form the per-period Sharpe ratio
sr_p, correct its variancevar_srby the sample skewness and the sample excess kurtosis ofret, and annualise the square root, givingsharpe_se. A non-positivevar_srgives aNaN. - Collect the four inputs and the eight statistics into a
PerformanceSummaryResult.
Arguments
ret: Periodic portfolio returns vector.w: Portfolio weights.X: Data matrixobservations × assetsif thedimskeyword does not exist ordims = 1,assets × observationswhendims = 2.fees: Optional transaction fees.rd:ReturnsResultcarrying the asset returns.res: AnOptimisationResult, whose weights and fees meet the caller'srdon the result's investable universe throughresult_investable_view.periods_per_year: Annualisation factor. 252 for daily, 52 for weekly, 12 for monthly returns.alpha: Tail probability used for the CVaR, $\alpha \in (0, 1)$.compound: Whether the wealth path behind the drawdown statistics was compounded.
Validation
0 < alpha < 1.periods_per_year > 0.
Returns
summary::PerformanceSummaryResult: The computed summary.
Related
References
- [146] D. H. Bailey and M. Lopez de Prado. The Sharpe ratio efficient frontier. Journal of Risk 15, 3–44 (2012).
PortfolioOptimisers.expected_risk — Function
expected_risk(r::PrRM, w::VecVecNum, pr::AbstractPriorResult,
fees::Option{<:Fees} = nothing; kwargs...)Evaluate a prior-reading risk measure over a vector of weight vectors, such as the points of a Frontier.
A PrRM reads the prior result itself and not a returns matrix — it declares no risk_input_kind for that reason. The generic VecVecNum method resolves the measure and then hands the loop pr.X, which is right for a measure whose kernel takes the matrix and strips what this family needs. So the family resolves once here and maps with the prior still in hand.
Algorithm
- Resolve the measure against the prior once, with
factory, giving a measure whose slots are filled. - Evaluate the resolved measure on each weight vector
wiofw, with the prior itself as the input rather thanpr.X. - Collect one expected risk per weight vector, in the order of
w.
Arguments
r:ExpectedReturnorExpectedReturnRiskRatio.w: Vector of portfolio weight vectors.pr: Prior result.fees: Optional transaction fees.kwargs...: Additional keyword arguments.
Returns
risk::VecNum: One expected risk per weight vector.
Related
PortfolioOptimisers.expected_risk — Method
expected_risk(r::PrRM,
pred::PredictionResult{<:Any,
<:PredictionReturnsResult{<:Any, <:VecNum}};
kwargs...)
expected_risk(r::PrRM,
pred::PredictionResult{<:Any,
<:PredictionReturnsResult{<:Any, <:VecVecNum}};
kwargs...)
expected_risk(r::PrRM, pred::MultiPeriodPredictionResult; kwargs...)
expected_risk(r::PrRM, pred::PopulationPredictionResult; kwargs...)Refuse to evaluate a prior-reading risk measure against a prediction result.
A PrRM reads a prior result: it takes the expected returns the prior states and contracts them with the weights. A prediction result carries a realised return series instead, and it states no expected returns, so there is nothing for these two measures to read. The four methods exist to say so, and each of them raises rather than returning a figure computed from the wrong input.
The caller has two routes. To measure the realised series, name MeanReturn or MeanReturnRiskRatio in place of the measure and call expected_risk(alt, pred). To measure the prior instead, call expected_risk(r, w, pr) with the prior result and the weights, which is the route the first two methods of this file document.
Arguments
r:ExpectedReturnorExpectedReturnRiskRatio.pred: The prediction result the caller supplied.kwargs...: Additional keyword arguments. They are read by none of the four methods.
Validation
- Every method raises
ArgumentError, with the messageprrm_prediction_messagebuilds. There is no input on which any of them returns.
Related
PrRMprrm_prediction_message: builds the message of the raise.MeanReturn: the replacement for anExpectedReturn.MeanReturnRiskRatio: the replacement for anExpectedReturnRiskRatio.PredictionResultMultiPeriodPredictionResultPopulationPredictionResult
PortfolioOptimisers.expected_return — Function
expected_return(ret::ArithmeticReturn, w::VecNum, pr::AbstractPriorResult,
fees::Option{<:Fees} = nothing; strict::Bool = false, kwargs...)
expected_return(ret::LogarithmicReturn, w::VecNum, pr::AbstractPriorResult,
fees::Option{<:Fees} = nothing; strict::Bool = false, kwargs...)
expected_return(ret::NoReturn, w::VecNum, pr::AbstractPriorResult,
fees::Option{<:Fees} = nothing; kwargs...)
expected_return(ret::JRE_VecJRE, w::VecVecNum, pr::AbstractPriorResult,
fees::Option{<:Fees} = nothing; strict::Bool = false, kwargs...)Compute the expected portfolio return using the specified return estimator.
expected_return computes the expected return for a portfolio given its weights, a prior result, and optional transaction fees. fees is positional and follows pr, on every method.
The prior route reduces to the Investable Mask
A prior result lives on the full asset universe, and an asset it could not estimate carries NaN in mu and down its column of pr.X. So dot(w, mu) and X * w are NaN at any weight, the optimiser's own zero included. Each method therefore reduces the prior, the weights and the fees once at its entry, through investable_reduction — the rule for an optimiser, taken at the value-level door. A held non-investable asset warns and its weight is dropped, or raises under strict. NoReturn answers zero at every weight, so it reads nothing and reduces nothing.
The reduction is a no-op on a prior that is already reduced, because a reduced prior's moments are finite everywhere and investable_mask then answers nothing. That is what keeps a vector of terms, a population, and a composite such as expected_ratio to exactly one diagnostic.
Each method is the scalar twin of the ret expression that set_return_constraints! builds for the same estimator, so the two sides charge the same fee. NoReturn builds a zero expression and the model charges it nothing, so settings.fee is inert on that term and the scalar twin charges nothing either.
The fourth method takes a vector of weight vectors, applies one of the first three to each of them, and returns one expected return per weight vector.
Mathematical definition
\[\begin{align} R_{\mathrm{a}}(\boldsymbol{w}) &= \boldsymbol{w}^{\intercal} \boldsymbol{\mu} - F(\boldsymbol{w})\\ R_{\mathrm{l}}(\boldsymbol{w}) &= \dfrac{\sum\limits_{t=1}^{T} v_{t} \ln\left(1 + \boldsymbol{x}_{t}^{\intercal} \boldsymbol{w}\right)}{\sum\limits_{t=1}^{T} v_{t}} - F(\boldsymbol{w})\\ R_{\mathrm{n}}(\boldsymbol{w}) &= 0\,. \end{align}\]
Where:
- $R_{\mathrm{a}}(\boldsymbol{w})$: Expected return of an
ArithmeticReturn. - $R_{\mathrm{l}}(\boldsymbol{w})$: Expected return of a
LogarithmicReturn, the mean log-growth rate of the portfolio. - $R_{\mathrm{n}}(\boldsymbol{w})$: Expected return of a
NoReturn. It carries no fee term, so nofeesargument moves it away from zero. - $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
- $\boldsymbol{\mu}$: Expected returns vector $N \times 1$.
- $\boldsymbol{x}_t$: Asset returns for observation $t$, the $t$-th row of the returns matrix.
- $T$: Number of observations.
- $v_{t}$: Observation weight of observation $t$, the $t$-th entry of the
wfield of aLogarithmicReturn. Every weight is one when that field isnothing, and the quotient is then the unweighted mean. - $F(\boldsymbol{w})$: Fee term of
term_fees. It iscalc_feesofwandfeeswhen the term'ssettings.feeistrue, and zero otherwise.
Arguments
ret: Return estimator, or a vector of them on the fourth method.w: Portfolio weights, or a vector of portfolio weight vectors on the fourth method.pr: Prior result. It resolves a Deferred Quantity ofret, and it suppliesmuto anArithmeticReturnwhose ownmuisnothing.fees: Optional fees.kwargs...: Additional keyword arguments passed to underlying routines.
Returns
rt::Number: Expected portfolio return, net of fees, on the first three methods.rt::VecNum: One expected portfolio return per weight vector, on the fourth method.
Related
ArithmeticReturnLogarithmicReturnNoReturnJuMPReturnsEstimatorJRE_VecJREAbstractPriorResultVecNumVecVecNumNum_VecNumOptionexpected_ratioexpected_risk_ret_ratioexpected_sricexpected_risk_ret_sricterm_fees: Charges $F(\boldsymbol{w})$, and holds the rule that decides whether it is charged.calc_fees
expected_return(
ret::AbstractVector{<:JuMPReturnsEstimator},
w::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
pr::AbstractPriorResult;
...
) -> Any
expected_return(
ret::AbstractVector{<:JuMPReturnsEstimator},
w::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
pr::AbstractPriorResult,
fees::Union{Nothing, Fees};
strict,
kwargs...
) -> Any
Sum the value-level returns of several return terms, each at its own scale.
The scalar twin of the model's ret expression, which is the same weighted sum over the same terms. A term whose settings.rte is false is skipped, exactly as it is skipped in the model.
This is the one carve-out from the rule that the value-level expected_* family stays singular: NearOptimalCentering's barrier needs the aggregate scalar, and this function declares itself the scalar twin of the ret expression. The rest of the family, and the whole risk side, still take one measure and one term.
Mathematical definition
\[\begin{align} R(\boldsymbol{w}) &= \sum_{j \,:\, e_{j}} s_{j} \, R_{j}(\boldsymbol{w})\,. \end{align}\]
Where:
- $R(\boldsymbol{w})$: Aggregate expected return of the whole vector of terms.
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
- $R_{j}(\boldsymbol{w})$: Expected return of the $j$-th term, net of its own fee.
- $s_{j}$:
settings.scaleof the $j$-th term. - $e_{j}$:
settings.rteof the $j$-th term. The sum runs over the terms whose flag istrue.
Algorithm
- Set the accumulator
rtto a zero of the element type ofw. - Take the next term
ret_iofret. Skip it when itssettings.rteisfalse. - Compute the value-level return of
ret_iwithexpected_return, scale it by the term'ssettings.scale, and add it tort. - Repeat steps 2 and 3 over the remaining terms, then return
rt.
Arguments
ret: Vector of return estimators.w: Portfolio weights.pr: Prior result.fees: Optional fees.kwargs...: Additional keyword arguments passed to underlying routines.
Returns
rt::Number: Aggregate expected portfolio return of the terms whosesettings.rteistrue.
Related
PortfolioOptimisers.expected_ratio — Function
expected_ratio(r::BaseRM_VecBaseRM, ret::JRE_VecJRE, w::VecNum,
pr::AbstractPriorResult, fees::Option{<:Fees} = nothing;
rf::Number = 0, sca::Scalariser = SumScalariser(), kwargs...)Compute the expected risk-adjusted return ratio for a portfolio.
expected_ratio computes the ratio of expected portfolio return (net of fees and risk-free rate) to expected portfolio risk, using the specified risk measure and return estimator.
Mathematical definition
\[\begin{align} \mathrm{ratio}(\boldsymbol{w}) &= \dfrac{R(\boldsymbol{w}) - r_{f}}{\mathrm{sca}\left(\left\{c_{i}\, \rho_{i}(\boldsymbol{w})\right\}\right)}\,. \end{align}\]
Where:
- $\mathrm{ratio}(\boldsymbol{w})$: Risk-adjusted return ratio.
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
- $R(\boldsymbol{w})$: Aggregate expected return of
ret, the sum of its terms at their ownsettings.scaleweights, each net offees. - $r_{f}$: Risk-free rate, the
rfkeyword. - $\rho_{i}$: The $i$-th risk measure in
r. - $c_{i}$:
settings.scaleof the $i$-th risk measure. - $\mathrm{sca}$: Scalariser held in
sca, which reduces the risk axis to one number.
Multiplicity
Both axes take one item or several, and the ratio is aggregate over aggregate: the risk axis is scalarised by sca into one number, the return axis is summed at its terms' own settings.scale weights, and the two aggregates are divided.
Only the risk axis carries a scalariser. The return axis sums and never scalarises.
Resolution
Both axes are handed pr itself, so both apply the same precedence rule: a stated slot wins, then the slot's own estimator, then the prior's field. The risk axis enters expected_risk's prior route, which resolves the measure through resolve_risk_inputs once per call — a Deferred Quantity is fitted against pr, and an unstated slot takes the prior's field.
Arguments
r: Risk measure, or a vector of them.ret: Return estimator, or a vector of them.w: Portfolio weights.pr: Prior result.fees: Optional transaction fees.rf: Risk-free rate.sca: Scalariser combining a vectorrinto one number. Inert on a single measure.kwargs...: Additional keyword arguments.
Returns
ratio::Number: Risk-adjusted return ratio.
Related
PortfolioOptimisers.expected_risk_ret_ratio — Function
expected_risk_ret_ratio(r::BaseRM_VecBaseRM, ret::JRE_VecJRE, w::VecNum,
pr::AbstractPriorResult, fees::Option{<:Fees} = nothing;
rf::Number = 0, sca::Scalariser = SumScalariser(), kwargs...)Compute expected risk, expected return, and risk-adjusted return ratio for a portfolio.
expected_risk_ret_ratio returns a tuple containing the expected portfolio risk, expected portfolio return, and the risk-adjusted return ratio, using the specified risk measure and return estimator.
Mathematical definition
\[\begin{align} \mathrm{risk}(\boldsymbol{w}) &= \mathrm{sca}\left(\left\{c_{i}\, \rho_{i}(\boldsymbol{w})\right\}\right)\\ \mathrm{return}(\boldsymbol{w}) &= R(\boldsymbol{w})\\ \mathrm{ratio}(\boldsymbol{w}) &= \dfrac{\mathrm{return}(\boldsymbol{w}) - r_{f}}{\mathrm{risk}(\boldsymbol{w})}\,. \end{align}\]
Where:
- $\mathrm{risk}(\boldsymbol{w})$, $\mathrm{return}(\boldsymbol{w})$, $\mathrm{ratio}(\boldsymbol{w})$: The three entries of the returned tuple, in that order.
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
- $R(\boldsymbol{w})$: Aggregate expected return of
ret, the sum of its terms at their ownsettings.scaleweights, each net offees. - $r_{f}$: Risk-free rate, the
rfkeyword. - $\rho_{i}$: The $i$-th risk measure in
r. - $c_{i}$:
settings.scaleof the $i$-th risk measure. - $\mathrm{sca}$: Scalariser held in
sca, which reduces the risk axis to one number.
Multiplicity
Both axes take one item or several, and the ratio is aggregate over aggregate, exactly as in expected_ratio: the risk axis is scalarised by sca into one number, the return axis is summed at its terms' own settings.scale weights, and the two aggregates are divided. The returned risk and return are those two aggregates, never a per-element breakdown.
Only the risk axis carries a scalariser. The return axis sums and never scalarises.
Reporting a result
This is the documented route out of an optimisation result. A result carries the measure and the scalariser it ran under, so it can name them itself:
rk, rt, rr = expected_risk_ret_ratio(res.r, res.ret, res.w, res.pr; sca = res.sca, rf = rf)res.r and res.ret are stored resolved, so this route reports the aggregates the optimisation actually optimised, whether either axis held one item or several. Naming the measures by hand instead is still supported, but the figure then matches the optimisation only if the caller names the same measures and the same scalariser; fees and rf carry the same responsibility.
A result with no r — one on the NonRiskJuMPOptimisationResult half — resolved no measure, so there is nothing to pass back and the caller must name one.
Arguments
r: Risk measure, or a vector of them.ret: Return estimator, or a vector of them.w: Portfolio weights.pr: Prior result.fees: Optional transaction fees.rf: Risk-free rate.sca: Scalariser combining a vectorrinto one number. Inert on a single measure.kwargs...: Additional keyword arguments.
Returns
risk::Number: Expected portfolio risk.return::Number: Expected portfolio return.ratio::Number: Risk-adjusted return ratio.
Related
PortfolioOptimisers.expected_sric — Function
expected_sric(r::BaseRM_VecBaseRM, ret::JRE_VecJRE, w::VecNum,
pr::AbstractPriorResult, fees::Option{<:Fees} = nothing;
rf::Number = 0, sca::Scalariser = SumScalariser(), kwargs...)Compute the risk-adjusted ratio information criterion (SRIC) for a portfolio.
expected_sric computes the SRIC, which adjusts the risk-adjusted return ratio for estimation error, penalizing overfitting in portfolio optimization. The SRIC is computed as the risk-adjusted return ratio minus a penalty term based on the number of assets and sample size.
Mathematical definition
\[\begin{align} \mathrm{SRIC}(\boldsymbol{w}) &= \mathrm{ratio}(\boldsymbol{w}) - P\left(\mathrm{ratio}(\boldsymbol{w})\right)\,. \end{align}\]
Where:
- $\mathrm{SRIC}(\boldsymbol{w})$: Sharpe ratio information criterion.
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
- $\mathrm{ratio}(\boldsymbol{w})$: Risk-adjusted return ratio of
expected_ratio, which states its own closed form. - $P$: Estimation-error penalty of
sric_penalty, which states its own closed form.
Multiplicity
The ratio it penalises is expected_ratio's, so it inherits the same rule: aggregate over aggregate. The risk axis is scalarised by sca, the return axis is summed at its terms' own settings.scale weights, and the penalty is applied once to the resulting scalar — not per element.
Arguments
r: Risk measure, or a vector of them.ret: Return estimator, or a vector of them.w: Portfolio weights.pr: Prior result.fees: Optional transaction fees.rf: Risk-free rate.sca: Scalariser combining a vectorrinto one number. Inert on a single measure.kwargs...: Additional keyword arguments.
Returns
sric::Number: Sharpe Ratio Information Criterion.
Related
PortfolioOptimisers.expected_risk_ret_sric — Function
expected_risk_ret_sric(r::BaseRM_VecBaseRM, ret::JRE_VecJRE, w::VecNum,
pr::AbstractPriorResult, fees::Option{<:Fees} = nothing;
rf::Number = 0, sca::Scalariser = SumScalariser(), kwargs...)Compute expected risk, expected return, and SRIC for a portfolio.
expected_risk_ret_sric returns a tuple containing the expected portfolio risk, expected portfolio return, and the Sharpe Ratio Information Criterion (SRIC), which adjusts the risk-adjusted return ratio for estimation error.
Mathematical definition
\[\begin{align} \mathrm{SRIC}(\boldsymbol{w}) &= \mathrm{ratio}(\boldsymbol{w}) - P\left(\mathrm{ratio}(\boldsymbol{w})\right)\,. \end{align}\]
Where:
- $\mathrm{SRIC}(\boldsymbol{w})$: Sharpe ratio information criterion, the third entry of the returned tuple. The first two entries are those of
expected_risk_ret_ratio, which states their closed forms. - $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
- $\mathrm{ratio}(\boldsymbol{w})$: Risk-adjusted return ratio of
expected_risk_ret_ratio. - $P$: Estimation-error penalty of
sric_penalty, which states its own closed form.
Multiplicity
The tuple is expected_risk_ret_ratio's with the penalty applied to its third element, so it inherits the same rule: aggregate over aggregate. The returned risk and return are the two aggregates, never a per-element breakdown, and the penalty is applied once to the resulting scalar.
Arguments
r: Risk measure, or a vector of them.ret: Return estimator, or a vector of them.w: Portfolio weights.pr: Prior result (must contain asset return matrixX).fees: Optional transaction fees.rf: Risk-free rate (default: 0).sca: Scalariser combining a vectorrinto one number. Inert on a single measure.kwargs...: Additional keyword arguments.
Returns
risk::Number: Expected portfolio risk.return::Number: Expected portfolio return.sric::Number: Sharpe Ratio Information Criterion.
Related
PortfolioOptimisers.factory — Method
factory(a::Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}, args...; kwargs...) -> a
factory(a::AbstractVector{<:Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}}, args...; kwargs...) -> VectorNo-op factory function for constructing objects with a uniform interface.
Defining methods which dispatch on the first argument allows for a consistent factory interface across different types.
factory and port_opt_view are the two propagation mechanisms in this library. They are duals: factory threads runtime values (prior moments, observation weights, previous portfolio weights) down through a composed struct tree; port_opt_view threads an index selection (a subset of assets or observations) down through the same tree.
The vector method is the one forwarding contract for every vector-valued propagation field: it applies factory to each element and forwards args... and kwargs... unchanged, so a family that admits a vector of estimators, algorithms, or results needs no method of its own. A family that needs more than the forward, such as a concrete element type (concrete_typed_array_if_abstract), defines its own more specific method.
Algorithm
The scalar method:
- Return
aunchanged, and dropargs...andkwargs.... This method is the leaf of the recursion, and it is what makes an untagged type safe to call the verb on.
The vector method:
- For each element
aiofa, callfactoryonai, and forwardargs...andkwargs...unchanged. - Collect the results into a new vector, in the order of
a, and return it.
A @propagatable struct with at least one @fprop- or @wprop-tagged field carries a generated method that dominates the scalar method. That method rebuilds the struct with its keyword constructor, sending each @fprop field through factory_child and each @wprop field through _wprop.
Arguments
a: Indicates no object should be constructed, or a vector whose elements are rebuilt one by one.args...: Arbitrary positional arguments (ignored by the scalar method, forwarded by the vector method).kwargs...: Arbitrary keyword arguments (ignored by the scalar method, forwarded by the vector method).
Returns
a: The input unchanged.v::Vector: The element-wise rebuilds, for the vector method.
Examples
julia> factory(nothing, 1, 2; x = 3)julia> factory(MeanValue())MeanValue w ┴ nothingRelated
factory(
rs::AbstractBaseRiskMeasure,
args...;
kwargs...
) -> AverageDrawdown{RiskMeasureSettings{__T_scale, __T_ub, __T_rke}} where {__T_scale, __T_ub, __T_rke}
Return the risk measure rs unchanged.
Identity pass-through used when a risk measure is provided in a context that calls factory.
Related
PortfolioOptimisers.factory — Method
factory(a::Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}, args...; kwargs...) -> a
factory(a::AbstractVector{<:Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}}, args...; kwargs...) -> VectorNo-op factory function for constructing objects with a uniform interface.
Defining methods which dispatch on the first argument allows for a consistent factory interface across different types.
factory and port_opt_view are the two propagation mechanisms in this library. They are duals: factory threads runtime values (prior moments, observation weights, previous portfolio weights) down through a composed struct tree; port_opt_view threads an index selection (a subset of assets or observations) down through the same tree.
The vector method is the one forwarding contract for every vector-valued propagation field: it applies factory to each element and forwards args... and kwargs... unchanged, so a family that admits a vector of estimators, algorithms, or results needs no method of its own. A family that needs more than the forward, such as a concrete element type (concrete_typed_array_if_abstract), defines its own more specific method.
Algorithm
The scalar method:
- Return
aunchanged, and dropargs...andkwargs.... This method is the leaf of the recursion, and it is what makes an untagged type safe to call the verb on.
The vector method:
- For each element
aiofa, callfactoryonai, and forwardargs...andkwargs...unchanged. - Collect the results into a new vector, in the order of
a, and return it.
A @propagatable struct with at least one @fprop- or @wprop-tagged field carries a generated method that dominates the scalar method. That method rebuilds the struct with its keyword constructor, sending each @fprop field through factory_child and each @wprop field through _wprop.
Arguments
a: Indicates no object should be constructed, or a vector whose elements are rebuilt one by one.args...: Arbitrary positional arguments (ignored by the scalar method, forwarded by the vector method).kwargs...: Arbitrary keyword arguments (ignored by the scalar method, forwarded by the vector method).
Returns
a: The input unchanged.v::Vector: The element-wise rebuilds, for the vector method.
Examples
julia> factory(nothing, 1, 2; x = 3)julia> factory(MeanValue())MeanValue w ┴ nothingRelated
factory(
rs::AbstractBaseRiskMeasure,
args...;
kwargs...
) -> AverageDrawdown{RiskMeasureSettings{__T_scale, __T_ub, __T_rke}} where {__T_scale, __T_ub, __T_rke}
Return the risk measure rs unchanged.
Identity pass-through used when a risk measure is provided in a context that calls factory.
Related
PortfolioOptimisers.brinson_attribution — Function
brinson_attribution(X::TimeArray, w::VecNum, wb::VecNum,
asset_classes::DataFrame, col, date0 = nothing, date1 = nothing)Compute Brinson performance attribution aggregated per asset class [3].
brinson_attribution generates a DataFrame summarizing the Brinson performance attribution, decomposing total excess return into asset allocation, security selection, interaction, and total effect for each asset class. The calculation is performed over the specified date range, using the provided asset weights, benchmark weights, and asset class assignments.
date0 and date1 are positional, and they follow col. Both must be given for the filter to apply: a call that names only one of them attributes the whole of X.
X holds prices, not returns. The period return of an asset is the ratio of its last value in the range to its first value, less one, which is a return only when the entries are prices.
The function is defined over one evaluation period, between the two dates, and it reads no weight path. w and wb are held fixed over that period, so a fund whose holdings drifted inside it is attributed at the weights it opened with. A drift-aware multi-period attribution needs a scheme for linking the single-period effects into a cumulative one, and this library has chosen none, so no such method exists. Attribute each period on its own, and link the results outside the library.
Both class returns divide by the class weight, and neither division is guarded. A class holding zero portfolio weight makes its $r_{i}$ a NaN, and a class holding zero benchmark weight makes its $r_{i}^{b}$ a NaN. The two spread differently, because $\mathrm{AA}_{i}$ reads only $r_{i}^{b}$. A zero portfolio weight leaves $\mathrm{AA}_{i}$ finite and makes the other three rows of that class a NaN; a zero benchmark weight makes all four a NaN. The Total column is a row sum, so a row is a NaN exactly when one of its class entries is. Drop the empty class from asset_classes to remove it.
Mathematical definition
\[\begin{align} r_{j} &= \dfrac{P_{j,\,1}}{P_{j,\,0}} - 1\\ r^{b} &= \sum_{j} w_{j}^{b}\, r_{j}\\ r_{i} &= \dfrac{\sum_{j \in \mathcal{C}_{i}} w_{j}\, r_{j}}{\sum_{j \in \mathcal{C}_{i}} w_{j}}\\ r_{i}^{b} &= \dfrac{\sum_{j \in \mathcal{C}_{i}} w_{j}^{b}\, r_{j}}{\sum_{j \in \mathcal{C}_{i}} w_{j}^{b}}\\ \mathrm{AA}_{i} &= \left(w_{i} - w_{i}^{b}\right) \left(r_{i}^{b} - r^{b}\right)\\ \mathrm{SS}_{i} &= w_{i}^{b} \left(r_{i} - r_{i}^{b}\right)\\ \mathrm{I}_{i} &= \left(w_{i} - w_{i}^{b}\right) \left(r_{i} - r_{i}^{b}\right)\\ \mathrm{TER}_{i} &= \mathrm{AA}_{i} + \mathrm{SS}_{i} + \mathrm{I}_{i}\,. \end{align}\]
Where:
- $\mathcal{C}_{i}$: The $i$-th asset class, the set of assets whose
colentry is that class. - $P_{j,\,0}$, $P_{j,\,1}$: Price of asset $j$ at the first and at the last observation of the range.
- $r_{j}$: Return of asset $j$ over the range.
- $w_{j}$, $w_{j}^{b}$: Portfolio and benchmark weight of asset $j$.
- $w_{i} = \sum_{j \in \mathcal{C}_{i}} w_{j}$, $w_{i}^{b} = \sum_{j \in \mathcal{C}_{i}} w_{j}^{b}$: Portfolio and benchmark weight of the class.
- $r_{i}$, $r_{i}^{b}$: Portfolio and benchmark return of the class. Each is the average of its members' returns, normalised by the weight of the class, so a class return is a weighted average and not a weighted sum.
- $r^{b}$: Benchmark return of the whole universe.
- $\mathrm{AA}_{i}$: Asset allocation of the class, the part of the excess return that the class weight decision earned.
- $\mathrm{SS}_{i}$: Security selection of the class, the part that the choice of assets inside the class earned.
- $\mathrm{I}_{i}$: Interaction of the class, the product of the two differences.
- $\mathrm{TER}_{i}$: Total excess return of the class, the sum of the three.
Algorithm
- Filter the date range. When
date0anddate1are both given, find the first and the last observation ofXinside the range, givingidx1andidx2. Otherwise take the whole ofX, soidx1is1andidx2islength(X). - Form
ret, the period return of each asset, from the values atidx1andidx2. - Contract
retwithwb, givingret_b, the benchmark return of the whole universe. - Read the class of each asset from column
colofasset_classes, and buildsets_mat, the membership matrix whose entry istruewhen the asset belongs to the class. - Take the next class. Sum the two weight vectors over its members, giving
w_iandwb_i, and form its two weight-normalised returnsret_iandret_b_i. - Compute
AA_i,SS_i,I_iandTER_iof that class, and write the four numbers as its column ofdf. - Repeat steps 5 and 6 over the remaining classes.
- Append the
Totalcolumn, the row-wise sum over the class columns.
Arguments
X: TimeArray of asset prices.w: Vector of portfolio weights.wb: Vector of benchmark weights.asset_classes: DataFrame containing asset class assignments for each asset.col: Column inasset_classesspecifying the class for each asset.date0: (Optional) Start date for the attribution period.date1: (Optional) End date for the attribution period.
Returns
df::DataFrame: DataFrame with rows for asset allocation, security selection, interaction, and total effect, and columns for each asset class and the total.
Related
VecNumperformance_summary: summarises one realised return series, where this function splits one period across the classes.calc_net_returns: the net return series of a portfolio, which charges fees. This function charges none.AssetSetsMatrixEstimator: the library's own group-membership matrix. Step 4 builds its own from a DataFrame column instead.
References
- [3] G. P. Brinson and N. Fachler. Measuring non-US. equity portfolio performance. The Journal of Portfolio Management 11, 73–76 (1985).
PortfolioOptimisers.needs_previous_weights — Method
needs_previous_weights(r::AbstractBaseRiskMeasure) -> Bool
needs_previous_weights(r::VecBaseRM) -> BoolReturn whether risk measure r requires previous portfolio weights as input.
The default returns false. Override to true for risk measures such as turnover or tracking constraints that depend on prior weights. The VecBaseRM overload returns true if any element returns true.
Returns
Bool:trueif prior weights are required;falseotherwise.
Related
needs_previous_weights(
_::Union{Nothing, AbstractAlgorithm, AbstractEstimator, AbstractResult}
) -> Any
Return false.
nothing never requires previous portfolio weights.
Related
References
- [3]
- G. P. Brinson and N. Fachler. Measuring non-US. equity portfolio performance. The Journal of Portfolio Management 11, 73–76 (1985).
- [146]
- D. H. Bailey and M. Lopez de Prado. The Sharpe ratio efficient frontier. Journal of Risk 15, 3–44 (2012).