Median Absolute Deviation Risk: private API
PortfolioOptimisers.MedianCenteringFunction — Type
abstract type MedianCenteringFunction <: AbstractAlgorithmAbstract supertype for centering functions used in the Median Absolute Deviation risk measure.
Related
PortfolioOptimisers.MedAbsDevMu — Type
const MedAbsDevMu = Union{<:MuSlot, <:MedianCenteringFunction}Union of valid centring-target types for MedianAbsoluteDeviation.
Accepts a numeric scalar/vector target, an estimator that computes one (a Deferred Quantity — see MuSlot), or a MedianCenteringFunction (e.g. mean or median centering).
The field has two resolution points, and both are forced. A centring strategy resolves in calc_moment_target, at the point of use, because it centres the portfolio series and so needs the asset weights. A Deferred Quantity resolves in factory, because it needs the returns matrix, which calc_moment_target never sees. There is no Nothing state: the default is MedianCentering().
The two are different quantities, not two spellings of one. The median is not linear, so median(w'X) ≠ w' * median(X) — MedianCentering() and mu = MedianExpectedReturns() do not agree. Nor does MeanCentering() agree with a mean estimator when fees are set: it centres net of fees, a resolved vector is gross.
Related
PortfolioOptimisers.resolve_deferred_quantities — Method
resolve_deferred_quantities(x, ) -> StandardDeviation
resolve_deferred_quantities(x, , ) -> StandardDeviation
Resolve every Deferred Quantity held by x against prior result pr, returning a struct of the same type whose deferred slots hold plain values.
This resolves the deferred state and nothing else. A slot left unstated stays nothing, so whichever fallback the consumer already applies — sel on the factory path, chol_sigma_selector and its siblings on the JuMP path — keeps working unchanged. The two paths are separate: a JuMP model builder reads the risk measure's slots directly and never calls factory, so both entry points resolve.
Given a prior result the rule has two halves. Container recursion is derived from deferred_slots, so a type that only holds children needs no method at all. A type that resolves a quantity of its own defines a method, which overrides the derived one. Writing that half per type — rather than per field — is what lets slots that travel together be resolved together: a deferred sigma supplies chol from the same fit, so the pair is never mixed across two sources.
slv is the effective solver, and it is what a Calibration Rule in the same struct reads. It carries the value the optimisation settled on, so a rule resolves against one solver on both routes. On the factory route the @cprop selection has already put that solver on the struct, so the argument stays at its default. On the JuMP route no selection runs, so set_risk_constraints! reads the solver off the estimator and threads it here. A type that carries a solver of its own settles it locally as sel(x.slv, slv), beside the observation weights it already settles that way, and a type that carries none gives its rules none on either route.
Algorithm
- Return
xunchanged. This method is the arm for a second argument that is not a prior result: with no prior in hand nothing can be fitted, so the deferred state travels on.
A more specific method dominates this one on a prior result: the one that deferred_slots derives for a container, and the hand-written one of a type that resolves a quantity of its own.
Related
resolve_deferred_quantities(x, pr::AbstractPriorResult, slv = nothing)Resolve the children that deferred_slots declared and the slots that calibration_slots declared, and return x itself when none of them changed.
This is the derived half of the resolution rule. A container declares its children once and both entry points follow: factory reaches them through @fprop, and the JuMP builders reach them through this method. Neither needs a forwarding method per container.
A type that resolves a quantity of its own overrides this with its own method, which is more specific. So the derivation carries container recursion alone, and never guesses how a matrix, a tensor or the centre a moment was taken about comes out of a fit.
Both channels end in one rebuild: a measure that carries both kinds of slot must not be rebuilt twice. resolve_calibration_slots states the calibration half and returns its resolved slots rather than a rebuilt object, and the two answers merge here. The deferred half merges last, so it wins a key both channels declare. A container names one child in both, and the child the recursion resolved is the one to keep.
slv is the effective solver, and the recursion threads it to every child. A container states no solver of its own, so it changes none: each child settles the one it was handed against the one it carries.
Algorithm
- Read the slots
xdeclares withdeferred_slots, givingslots. - Read the resolved calibration slots with
resolve_calibration_slots, givingcalibrated. - Return
xunchanged when both are empty. A type with neither kind of slot needs no method of its own. - Resolve every entry of
slotswithresolve_deferred_child, threadingprandslvto each, givingresolved. - Refuse a slot the recursion left unresolved with
assert_declared_slot_resolver. - Hand
merge(calibrated, resolved)torebuild_with_slots, which returnsxitself when no entry moved and a rebuilt copy when one did.
Returns
xitself when no slot moved, and a rebuilt copy ofxwhen one did.
Related
resolve_deferred_quantities(
r::MedianAbsoluteDeviation,
pr::AbstractPriorResult
) -> Any
resolve_deferred_quantities(
r::MedianAbsoluteDeviation,
pr::AbstractPriorResult,
) -> Any
Resolve a Deferred Quantity in MedianAbsoluteDeviation's mu slot against prior result pr.
This is the only thing factory does to mu. The field is tagged @vprop and not @pprop, so the prior never fills it — a bare MedianAbsoluteDeviation() inside a JuMPOptimiser keeps median-centring rather than silently taking pr.mu.
Related
PortfolioOptimisers.nothing_scalar_array_view — Method
nothing_scalar_array_view(
x::MedianCenteringFunction,
_
) -> MedianCenteringFunction
Return the MedianCenteringFunction x unchanged.
Identity pass-through: centering functions are not sliced by asset index.
Related
PortfolioOptimisers.calc_moment_target — Method
calc_moment_target(::MedianAbsoluteDeviation{<:Any, Nothing, <:MeanCentering, ...}, ::Any, x::VecNum)
calc_moment_target(r::MedianAbsoluteDeviation{<:Any, <:ObsWeights, <:MeanCentering, ...}, ::Any, x::VecNum)
calc_moment_target(::MedianAbsoluteDeviation{<:Any, Nothing, <:MedianCentering, ...}, ::Any, x::VecNum)
calc_moment_target(r::MedianAbsoluteDeviation{<:Any, <:ObsWeights, <:MedianCentering, ...}, ::Any, x::VecNum)
calc_moment_target(r::MedianAbsoluteDeviation{<:Any, <:Any, <:VecNum, ...}, w::VecNum, ::Any)
calc_moment_target(r::MedianAbsoluteDeviation{<:Any, <:Any, <:Number, ...}, ::Any, ::Any)
calc_moment_target(r::MedianAbsoluteDeviation{<:Any, <:Any, <:VecScalar, ...}, w::VecNum, ::Any)Compute the centering target for MedianAbsoluteDeviation risk measure calculations.
Dispatches on the type of r.w and r.mu:
- No weights +
MeanCentering: arithmetic mean ofx. ObsWeights+MeanCentering: weighted mean ofx.- No weights +
MedianCentering: median ofx. ObsWeights+MedianCentering: weighted median ofx.VecNummu: dot product $\boldsymbol{w}^\intercal \boldsymbol{\mu}$.Numbermu: the scalarr.mudirectly.VecScalarmu: $\boldsymbol{w}^\intercal \boldsymbol{\mu}_v + \mu_s$.
Related
PortfolioOptimisers.calc_deviations_vec — Function
calc_deviations_vec(
r::MedianAbsoluteDeviation,
w::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
) -> Any
calc_deviations_vec(
r::MedianAbsoluteDeviation,
w::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
fees::Union{Nothing, Fees}
) -> Any
Compute the vector of deviations from the centering target for MedianAbsoluteDeviation risk measures.
Related
PortfolioOptimisers.calc_deviations_vec — Method
calc_deviations_vec(
r::MedianAbsoluteDeviation,
x::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
) -> Any
Compute the vector of deviations from the centering target for a precomputed returns series for MedianAbsoluteDeviation.
Single-argument form used by the precomputed-returns functor r(x::VecNum).
Related
PortfolioOptimisers.weight_independent_target — Method
weight_independent_target(
_::MedianCenteringFunction
) -> Bool
Return true: MedianCenteringFunction targets are weight-independent centering functions and can be evaluated on a bare return series.
Related
PortfolioOptimisers.supports_precomputed_returns — Method
supports_precomputed_returns(
r::MedianAbsoluteDeviation
) -> Any
Return whether MedianAbsoluteDeviation r supports precomputed-return evaluation.
Delegates to weight_independent_target on r.mu: true iff the target is Nothing, a Number, or a MedianCenteringFunction; false for per-asset targets.
Related