Time-dependent estimators: private API
PortfolioOptimisers.TD_Option — Type
const TD_Option{X} = Union{Nothing, <:TimeDependent, X}Alias for an optimiser field that accepts nothing, a static value of type X, or a per-fold TimeDependent schedule.
The set of fields whose constructor signatures use this alias is the single source of truth for which optimiser inputs may vary over folds.
Related
PortfolioOptimisers.TD — Type
const TD{X} = Union{<:TimeDependent, X}Alias for a required optimiser field that accepts a static value of type X or a per-fold TimeDependent schedule, but not nothing.
The problem-definition fields that always carry a value — the prior estimator, the returns model, the scalariser, the clustering estimator, the weight finaliser — are time-dependent through this alias rather than TD_Option, so nothing stays inadmissible where it was never a legal static value. Such a field still has a static default, so a schedule in one resets to that default on a fold-less solve, unlike the optimiser-valued fields (see TD_OptE_Opt).
Related
PortfolioOptimisers.TD_OptE_Opt — Type
const TD_OptE_Opt = Union{TimeDependent{<:AbstractVector{<:OptE_Opt}},
TimeDependent{<:TimeDependentOptimiserCallable},
TimeDependent{<:PreviousWeightsFunction},
TimeDependent{<:Base.Callable}}The TimeDependent forms admissible in an optimiser-valued field — where the scheduled thing is the optimiser itself, not one of its inputs.
Two of the four are statically checked: a vector schedule whose entries are all OptE_Opt (an optimiser or a precomputed result — a mixed schedule is allowed, fold i optimising or predicting depending on what entry i is), and a TimeDependentOptimiserCallable, which declares its output kind in its type. The other two — a bare ctx -> optimiser and a PreviousWeightsFunction wrapping one — cannot be checked before they run, so their output is checked when the fold loop swaps it into the field, by the host's own keyword constructor.
Because an optimiser-valued field is required, a schedule in one has no static default to reset to on a fold-less solve and must supply default (see NoDefault, TimeDependentDefaultError).
Related
PortfolioOptimisers.TDO_Option — Type
const TDO_Option{X} = Union{Nothing, <:TDO_OptE_Opt, X}Alias for an optional optimiser-valued field (e.g. a fallback) that accepts nothing, a static value of type X, or a per-fold schedule of optimisers whose entries may be nothing (see TDO_OptE_Opt).
A required optimiser-valued field spells its union out — Union{<:X, <:TD_OptE_Opt} — since nothing is not one of its values.
Related
PortfolioOptimisers.OptE_TD — Type
const OptE_TD = Union{<:NonFiniteAllocationOptimisationEstimator, <:TD_OptE_Opt}Alias for an optimisation estimator, or a TimeDependent schedule standing in its place.
This is the entry-point type of the cross-validation fold loops that fit: a schedule handed straight to cross_val_predict is the optimiser, and fold i runs entry i. Precomputed results are excluded because a bare result takes the predict-only path, which has no fold loop to resolve a schedule against — but a schedule whose entries are results is admissible here, and each such entry takes the predict-only path per fold (see OptE_Opt_TD).
Related
PortfolioOptimisers.OptE_Opt_TD — Type
const OptE_Opt_TD = Union{<:OptE_Opt, <:TD_OptE_Opt}Alias for an optimisation estimator or a precomputed result, or a TimeDependent schedule standing in their place.
The entry-point type of the fold loops that accept a precomputed result as well as an estimator. A schedule's entries are OptE_Opt, so a mixed schedule is admissible: fold i optimises when entry i is an estimator and predicts when it is a result, which the single-fold fit_and_predict methods already distinguish by dispatch.
Related
PortfolioOptimisers.VecOptE_Opt_TD — Type
const VecOptE_Opt_TD = AbstractVector{<:OptE_Opt_TD}Alias for a vector of optimisation estimators or results in which individual elements may be TimeDependent schedules.
This is the element-level admission of schedules, needed where a vector-valued field's elements are themselves optimiser positions consumed by a fold loop one at a time — Stacking.opti, whose inner cross-validation is entered per candidate. It is a superset of VecOptE_Opt, so every method taking it continues to accept plain vectors.
Related
PortfolioOptimisers.TD_VecOptE_Opt — Type
const TD_VecOptE_Opt = Union{TimeDependent{<:AbstractVector{<:VecOptE_Opt_TD}},
TimeDependent{<:TimeDependentOptimiserCallable},
TimeDependent{<:PreviousWeightsFunction},
TimeDependent{<:Base.Callable}}The TimeDependent forms admissible in a vector-of-optimisers field (Stacking.opti): a vector schedule whose entries are per-fold optimiser vectors, or a callable returning the fold's vector.
Entry i is fold i's complete vector of candidates, so a field-level schedule varies the whole candidate set per fold; an entry's own elements may in turn be schedules (a VecOptE_Opt_TD), which the consuming host's inner fold loop resolves as usual. Only bind = :outermost is admissible at the field level — see the host's constructor for why.
Related
PortfolioOptimisers.TDO_OptE_Opt — Type
const TDO_OptE_Opt = Union{<:TD_OptE_Opt,
<:TimeDependent{<:AbstractVector{<:Option{<:OptE_Opt}}}}The TimeDependent forms admissible in an optional optimiser-valued field (a fallback): every TD_OptE_Opt form, plus a vector schedule whose entries may be nothing.
nothing was always a legal static value of an optional field, and the TimeDependent contract says a vector entry may be nothing, giving the field nothing for that fold — so an optional optimiser field admits TimeDependent([mr, nothing]), a fallback switched off on some folds. A required optimiser position (the optimiser itself) never admits nothing, statically or per fold, so it stays on the strict TD_OptE_Opt bound.
Related
PortfolioOptimisers.VecOptE_Opt — Type
const VecOptE_Opt = AbstractVector{<:OptE_Opt}Alias for a vector of optimisation estimators or results.
Represents a collection of OptE_Opt objects for batch processing.
Related
PortfolioOptimisers.assert_special_nco_requirements — Method
assert_special_nco_requirements(
opt::AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:(Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}), var"#s7443"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}}
)
Assert special NCO requirements for each element of a vector of optimisation estimators or results.
Related
PortfolioOptimisers.assert_nearest_optimiser_schedule — Function
assert_nearest_optimiser_schedule(x, field::Symbol, cv, host::Symbol)Validate a bind = :nearest TimeDependent schedule in an optimiser-valued position that a host's inner cross-validation does consume.
Two construction-time requirements, both consequences of the position's double consumer: the inner cross-validation leg resolves the schedule per fold, while the full-sample leg (the meta's wi fit, or the per-cluster optimise) always resolves it fold-lessly to its default.
- An explicit
defaultis required — without one, every solve would throw aTimeDependentDefaultErrorwhen the full-sample leg reaches the schedule, so the error is moved to construction. This deliberately departs from the rule that a defaultless schedule is legal at construction, for this position only. cv !== nothingis required — without an inner cross-validation there is no inner fold loop, so the schedule could only ever be itsdefault: silently inert.
No-op for anything that is not a bind = :nearest TimeDependent.
Related
PortfolioOptimisers.inner_fold_fields — Function
inner_fold_fields(opt)Field names of opt that the host hands across a fold loop it opens itself, so the loop that merely reaches the host is never the nearest one for them.
The default is the empty tuple: an ordinary host opens no inner fold loop, so the loop reaching it is both outermost and nearest for every field. A meta-optimiser whose inner cross-validation consumes a field directly declares that field here (e.g. NestedClustered's opti, entered per cluster as cross_val_predict(opti, …; cols = cl)), and every generic pass — time_dependent_fields, and through it update, reset and the fold-count assertion — then leaves a bind = :nearest schedule in that field for the host's own inner loop. Without the reset leg of this rule, the fold-less reset at the top of _optimise would replace a :nearest optimiser schedule with its default before the inner cross-validation ever saw it.
Related
PortfolioOptimisers.time_dependent_value — Function
time_dependent_value(
td::TimeDependent,
ctx::TimeDependentContext
) -> Any
Resolve a time-dependent constraint to its value for the fold described by ctx.
Vector values index entry ctx.i; callables are invoked with ctx.
Related
PortfolioOptimisers.time_dependent_candidate_fields — Function
time_dependent_candidate_fields(opt)Field names of opt whose type admits a TimeDependent value — the candidate set time_dependent_fields narrows by value.
Whether a field can hold a schedule is decidable from fieldtype alone: a host built through the widened constructor signatures (see TD_Option) records a schedule in the field's type parameter, so a field that holds no schedule cannot have a type intersecting TimeDependent. The tuple is therefore computed once per host type by a generated function, and a fold-invariant scan over a wide static host such as JuMPOptimiser, whose fields number in the dozens, folds to an empty tuple at compile time rather than walking every field dynamically on every split and _optimise.
This stays derived from the field types — no hand-maintained list — so the constructor signatures remain the single source of truth for which fields may vary over folds.
Related
PortfolioOptimisers.time_dependent_fields — Function
time_dependent_fields(opt, all_binds::Bool = true)Return the tuple of field names of opt whose values are TimeDependent.
The scan is generic over the host's fields, so the widened constructor signatures (see TD_Option) remain the single source of truth for which fields may vary over folds — there is no hand-maintained list. Only the fields whose type admits a schedule are visited (see time_dependent_candidate_fields); the rest are ruled out at compile time, so a static host returns an empty tuple without touching its fields.
The all_binds argument
all_binds encodes something the schedule's own bind field cannot: it is a property of the recursion position, not of the schedule. A TimeDependent's bind (:outermost / :nearest) says which fold loop the schedule wants; all_binds says whether the loop currently recursing is entitled to consume nearest-bound schedules at this depth. The second fact is not on the schedule.
Why position matters: under outer CV loop → meta → (meta's inner CV loop) → inner estimator with a :nearest field, the same :nearest field is visited by two loops. The outer loop recurses through the meta (mandatory — that recursion is how an inner estimator's :outermost field is resolved against the outer folds) and must skip the :nearest field, because it is not the nearest enclosing loop. The meta's inner CV loop drives the same estimator directly and must consume it, because it is. Same field, same bind, opposite actions — the difference is whether a nearer fold-loop boundary was crossed to reach it, which is exactly what all_binds carries.
So all_binds is true at every ordinary (outermost/standalone) fold loop — which is both outermost and nearest, and therefore takes everything remaining, including :nearest. It is forced to false only where a meta-optimiser recurses into the estimators its own inner CV owns, leaving their :nearest schedules for that inner loop. With all_binds = false, only fields with bind === :outermost are returned.
Entitlement is refined per field by inner_fold_fields: even at all_binds = true, a :nearest schedule in a field the host hands across its own inner fold loop is left alone — the host's inner loop, not the scanning one, is nearest for that field (see entitled).
Related
PortfolioOptimisers.time_dependent_entries — Function
time_dependent_entries(td::TimeDependent) -> Any
Return the statically inspectable entries of a TimeDependent schedule: the per-fold values of a vector schedule, plus its default when it has one. A callable schedule contributes nothing — its per-fold values cannot be inspected before it runs.
Related
PortfolioOptimisers.time_dependent_entry_needs_previous_weights — Function
time_dependent_entry_needs_previous_weights(x)Return true if a per-fold entry value of a TimeDependent requires the previous optimisation's weights.
Delegates to needs_previous_weights for the value types that support the trait (turnover, fees, tracking); every other value contributes false.
Related
PortfolioOptimisers.assert_time_dependent_substitution — Function
assert_time_dependent_substitution(
_::Type{T},
args::NamedTuple,
defaults::NamedTuple
)
Test-substitute every vector entry of the TimeDependent-valued fields in args through the keyword constructor of T.
args holds the host constructor's arguments; defaults the static defaults of the fields that may be time-dependent (see time_dependent_field_defaults). Each per-fold entry, and an explicit default, is substituted into its field — with every other time-dependent field standing at a value of its own (see time_dependent_stand_in) — and the constructor re-run, surfacing type and cross-field errors at construction time instead of mid-backtest. Substituted calls contain no TimeDependent values, so the recursion terminates.
Validation is skipped when a time-dependent field has no stand-in at all — a callable schedule in a required field, whose value only exists once a fold context does.
Related
PortfolioOptimisers.substitute_time_dependent_entries — Function
substitute_time_dependent_entries(::Type{T}, base::NamedTuple, f::Symbol, td::TimeDependent) where {T}
substitute_time_dependent_entries(::Type, ::NamedTuple, ::Symbol, ::Any)Re-run the keyword constructor of T with every vector entry of the schedule td, and its explicit default, substituted into the field f of base; a field that holds no schedule substitutes nothing.
The filter of assert_time_dependent_substitution keeps the schedules alone, and the dispatch says so to a static analyser at a call whose arguments hold no schedule at all, where an assertion would read as a certain failure.
Related
PortfolioOptimisers.time_dependent_stand_in — Function
time_dependent_stand_in(
td::TimeDependent,
defaults::NamedTuple,
field::Symbol
) -> Union{Nothing, Some}
Return a valid static value for a TimeDependent-valued field, wrapped in Some, or nothing if none exists.
Used by assert_time_dependent_substitution to stand the other time-dependent fields at a valid value while it test-substitutes one of them: the schedule's default, else the field's static default, else the schedule's first entry. A callable schedule in a field with no default of either kind has no stand-in — its value exists only inside a fold — so it returns nothing and validation is skipped.
Unlike time_dependent_reset_value this never throws: a schedule without a fold-less value is legitimate at construction time and only fails if it reaches a fold-less solve.
Related
PortfolioOptimisers.time_dependent_reset_value — Function
time_dependent_reset_value(
td::TimeDependent,
defaults::NamedTuple,
field::Symbol,
opt
) -> Any
Return the value a TimeDependent-valued field takes outside every fold loop.
The schedule's own default wins; absent one (NoDefault), the host's static default for field is used (time_dependent_field_defaults, nothing for fields it omits). Throws a TimeDependentDefaultError when neither exists — a schedule in a required field that never said what a fold-less solve should do.
Related
PortfolioOptimisers.assert_time_dependent_optimiser — Function
assert_time_dependent_optimiser(
_::Union{NonFiniteAllocationOptimisationEstimator, NonFiniteAllocationOptimisationResult}
)
Assert that a TimeDependent schedule in an optimiser position resolved to something that can be optimised or predicted.
The vector and TimeDependentOptimiserCallable forms of a schedule declare their output kind in their type and are checked statically (see TD_OptE_Opt). The two callable forms — a bare ctx -> optimiser and a PreviousWeightsFunction wrapping one — cannot be, so their output is checked here, when the fold loop swaps it in.
Related
PortfolioOptimisers.assert_time_dependent_fold_count — Function
assert_time_dependent_fold_count(opt, n::Integer, all_binds::Bool = true)Assert that every vector-valued time-dependent constraint in opt has exactly n entries.
Called by the cross-validation fold loops immediately after split, before any fold runs. The default is a no-op; hosts scan their time_dependent_fields and wrapper optimisers recurse. When all_binds is false, bind === :nearest schedules are skipped — they are validated by the nearest enclosing fold loop against its own fold count instead (see TimeDependent).
Related
PortfolioOptimisers.assert_time_dependent_fold_count — Function
assert_time_dependent_fold_count(
td::Union{TimeDependent{<:AbstractVector{<:Union{Nothing, var"#s7440"} where var"#s7440"<:(Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult})}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}},
n::Integer
)
assert_time_dependent_fold_count(
td::Union{TimeDependent{<:AbstractVector{<:Union{Nothing, var"#s7440"} where var"#s7440"<:(Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult})}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}},
n::Integer,
all_binds::Bool
)
Assert that a TimeDependent schedule standing in for an optimiser has one entry per fold, and that the schedules within each entry are sized to the same fold loop.
Entry i runs at fold i of this loop, so its own :outermost schedules bind here too (see update_time_dependent_estimator) and are validated against this loop's fold count. The default is not — it runs only outside a fold loop, where its schedules reset instead.
Skipped when all_binds is false and the schedule is not :outermost-bound — the fold loop the host opens validates it against its own fold count instead.
Related
PortfolioOptimisers.assert_time_dependent_fold_count — Function
assert_time_dependent_fold_count(
opt::AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:(Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}), var"#s7443"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}},
n::Integer
)
assert_time_dependent_fold_count(
opt::AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:(Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}), var"#s7443"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}},
n::Integer,
all_binds::Bool
)
Apply assert_time_dependent_fold_count element-wise to a vector of optimisation estimators or results.
Related
PortfolioOptimisers.assert_time_dependent_fields_fold_count — Function
assert_time_dependent_fields_fold_count(opt, n::Integer)
assert_time_dependent_fields_fold_count(
opt,
n::Integer,
all_binds::Bool
)
Assert the fold count of every TimeDependent-valued field of a host optimiser.
Related
PortfolioOptimisers.rebuild_estimator — Function
rebuild_estimator(x, repl::NamedTuple) -> Pipeline
Rebuild an estimator through its keyword constructor with the fields in repl replaced.
All remaining fields are carried through unchanged. Because the rebuild goes through the validated keyword constructor, every construction invariant re-runs.
Related
rebuild_estimator(p::Pipeline, repl::NamedTuple)Rebuilds a Pipeline with the fields in repl replaced, through the positional constructor.
The keyword constructor derives the names from the steps. A rebuild that seeds a state must keep the names as they are, and only the positional constructor takes them as given.
Related
PortfolioOptimisers.is_time_dependent — Method
is_time_dependent(opt)Return true if the optimiser carries time-dependent constraints.
The default returns false. Hosts return true when any of their fields holds a TimeDependent (see time_dependent_fields); wrapper optimisers recurse into their inner optimiser and fallback.
Arguments
opt: Optimisation estimator, result, or vector thereof.
Returns
Bool:trueif the estimator is time-dependent.
Related
PortfolioOptimisers.is_time_dependent — Method
is_time_dependent(
opt::AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:(Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}), var"#s7443"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}}
) -> Any
Return true if any element of the vector of optimisation estimators or results is time-dependent.
Related
PortfolioOptimisers.is_time_dependent — Method
is_time_dependent(opt::BaseOptimisationEstimator) -> Any
Return true if the base optimiser configuration carries time-dependent constraints.
Related
PortfolioOptimisers.is_time_dependent — Method
is_time_dependent(_::TimeDependent) -> Bool
A TimeDependent schedule is time-dependent by construction.
Related
PortfolioOptimisers.is_time_dependent — Method
is_time_dependent(
opt::AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:(Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}), var"#s7443"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}}
) -> Any
Return true if any element of the vector of optimisation estimators or results is time-dependent.
Related
PortfolioOptimisers.update_time_dependent_estimator — Function
update_time_dependent_estimator(
opt::BaseOptimisationEstimator,
ctx::TimeDependentContext
) -> Any
update_time_dependent_estimator(
opt::BaseOptimisationEstimator,
ctx::TimeDependentContext,
all_binds::Bool
) -> Any
Resolve the time-dependent constraints of a base optimiser configuration for the fold described by ctx.
Rebuilds the configuration through its validated keyword constructor with each TimeDependent-valued field replaced by its resolved per-fold value, so the result is an ordinary static configuration. When all_binds is false, bind === :nearest fields are left in place for the nearest enclosing fold loop to consume.
Related
update_time_dependent_estimator(opt, ctx::TimeDependentContext, all_binds::Bool = true)Resolve the time-dependent constraints of opt for the fold described by ctx.
The default returns the estimator unchanged. Hosts rebuild themselves through their validated keyword constructor with each TimeDependent-valued field replaced by its resolved per-fold value, so the result is an ordinary static estimator; wrapper optimisers recurse.
Arguments
opt: Optimisation estimator or result.ctx::TimeDependentContext: The fold's context.all_binds::Bool: Whenfalse,bind === :nearestschedules are skipped, leaving them for the nearest enclosing fold loop to consume. Meta-optimisers passfalsewhen recursing into the estimators their internal fold loop processes; fold loops call with the defaulttrue.
Returns
- Updated estimator.
Related
update_time_dependent_estimator(
td::Union{TimeDependent{<:AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}},
ctx::TimeDependentContext
) -> Any
update_time_dependent_estimator(
td::Union{TimeDependent{<:AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}},
ctx::TimeDependentContext,
all_binds::Bool
) -> Any
Resolve a TimeDependent schedule standing in for an optimiser to the optimiser of fold ctx.i.
Entry i may be an estimator or a precomputed result, so a mixed schedule optimises on some folds and predicts on others. After the swap the resolved estimator is recursed into with the same context, so its own :outermost schedules bind to this fold loop rather than going unresolved.
Returns the schedule unchanged when all_binds is false and it is not :outermost-bound — a :nearest schedule in an optimiser position is consumed by a fold loop the host itself opens, not by the loop that reached the host.
Related
update_time_dependent_estimator(
opt::AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:(Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}), var"#s7443"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}},
ctx::TimeDependentContext
) -> Any
update_time_dependent_estimator(
opt::AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:(Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}), var"#s7443"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}},
ctx::TimeDependentContext,
all_binds::Bool
) -> Any
Apply update_time_dependent_estimator element-wise to a vector of optimisation estimators or results.
Related
update_time_dependent_estimator(
opt::NaiveOptimisationEstimator,
ctx::TimeDependentContext
) -> Any
update_time_dependent_estimator(
opt::NaiveOptimisationEstimator,
ctx::TimeDependentContext,
all_binds::Bool
) -> Any
Resolve the time-dependent fields of a naive optimiser for the fold that ctx describes.
The answer is a static optimiser, built through the keyword constructor, so the constructor checks every resolved value.
Algorithm
- Return
optunchanged whenis_time_dependentisfalse. - Replace each
TimeDependentfield ofoptwith its value for the fold, withupdate_time_dependent_fields, givingopt. - Resolve the fallback
opt.fbfor the same fold, and rebuildoptwith it.
Related
update_time_dependent_estimator(
opt::ClusteringOptimisationEstimator,
ctx::TimeDependentContext
) -> NestedClustered
update_time_dependent_estimator(
opt::ClusteringOptimisationEstimator,
ctx::TimeDependentContext,
all_binds::Bool
) -> NestedClustered
Replace every TimeDependent schedule of a clustering optimiser with its value at the fold that ctx describes.
The fallback is resolved after the optimiser's own fields, so a fallback that a schedule selects for this fold is itself resolved. NestedClustered overrides this with its own method.
Algorithm
- Return
optunchanged whenis_time_dependentisfalse. - Replace each scheduled field of
optwith its value at the fold, throughupdate_time_dependent_fields. This gives the newopt. - Resolve the inner optimiser
opt.optand the fallbackopt.fbof the newoptwith the samectxandall_binds. - Rebuild
optwith the two resolved estimators, throughrebuild_estimator.
Arguments
opt: Clustering optimiser that holds the fieldsoptandfb.ctx: Fold context of the current fold.all_binds: Whether the fold loop resolves the schedules bound to:nearestas well as those bound to:outermost.
Returns
opt: The optimiser with no schedule left for this fold loop, of the same type as the input.
Related
update_time_dependent_estimator(
opt::JuMPOptimisationEstimator,
ctx::TimeDependentContext
) -> Any
update_time_dependent_estimator(
opt::JuMPOptimisationEstimator,
ctx::TimeDependentContext,
all_binds::Bool
) -> Any
Resolve the time-dependent fields of the optimiser for the fold that ctx describes.
The optimiser's own scheduled fields take their value for the fold first. The fallback can be one of those fields, so the recursion into the fallback reads the value that the first step put in place.
Algorithm
- Return
optunchanged whenis_time_dependentisfalse. - Replace each scheduled field of
optwith its value for the fold, throughupdate_time_dependent_fields, giving the newopt. - Resolve the inner optimiser
opt.optand the fallbackopt.fbof the newoptwith the samectx. - Rebuild
optwith the two resolved values.
update_time_dependent_estimator(
opt::NestedClustered,
ctx::TimeDependentContext
) -> NestedClustered
update_time_dependent_estimator(
opt::NestedClustered,
ctx::TimeDependentContext,
all_binds::Bool
) -> NestedClustered
Resolve time-dependent constraints for the fold described by ctx by recursing into the inner optimiser, outer optimiser, and fallback.
update_time_dependent_estimator(
opt::Stacking,
ctx::TimeDependentContext
) -> Stacking
update_time_dependent_estimator(
opt::Stacking,
ctx::TimeDependentContext,
all_binds::Bool
) -> Stacking
Resolve time-dependent constraints for the fold described by ctx by recursing into the inner optimisers, outer optimiser, and fallback.
update_time_dependent_estimator(
opt::SubsetResampling,
ctx::TimeDependentContext
) -> SubsetResampling
update_time_dependent_estimator(
opt::SubsetResampling,
ctx::TimeDependentContext,
all_binds::Bool
) -> SubsetResampling
Resolve time-dependent constraints for the fold described by ctx by recursing into the base optimiser and fallback.
update_time_dependent_estimator(
p::Pipeline,
ctx::TimeDependentContext
) -> Pipeline
update_time_dependent_estimator(
p::Pipeline,
ctx::TimeDependentContext,
all_binds::Bool
) -> Pipeline
Resolve the time-dependent steps of a Pipeline for the fold described by ctx.
The swap happens in the fold loop, outside fit entirely: it maps update_time_dependent_step over the steps (names preserved), so by the time fit runs on the fold's training window every schedule step is already a plain optimiser or precomputed result and injection (inject_context / maybe_inject_step) never sees a schedule — fit and run_step never learn about folds.
Related
PortfolioOptimisers.update_time_dependent_fields — Function
update_time_dependent_fields(
opt,
ctx::TimeDependentContext
) -> Any
update_time_dependent_fields(
opt,
ctx::TimeDependentContext,
all_binds::Bool
) -> Any
Rebuild a host optimiser with each TimeDependent-valued field replaced by its per-fold value for ctx.
Shared implementation behind the hosts' update_time_dependent_estimator methods. Returns opt unchanged when no field is time-dependent.
Related
PortfolioOptimisers.reset_time_dependent_estimator — Method
reset_time_dependent_estimator(opt)Replace every TimeDependent-valued field of opt with its static default, recursing through wrapper optimisers.
A time-dependent constraint is defined only over the folds of a cross-validation scheme, so a fold-less solve runs with the affected fields at their static defaults (see time_dependent_field_defaults). Called at the top of the _optimise methods; per-fold estimators produced by update_time_dependent_estimator contain no TimeDependent values, so they pass through unchanged. The default returns the estimator unchanged; hosts rebuild themselves, wrapper optimisers recurse.
Related
PortfolioOptimisers.reset_time_dependent_estimator — Method
reset_time_dependent_estimator(
opt::BaseOptimisationEstimator
) -> Any
Replace the time-dependent constraints of a base optimiser configuration with their static defaults (see time_dependent_field_defaults).
Related
PortfolioOptimisers.reset_time_dependent_estimator — Method
reset_time_dependent_estimator(
td::Union{TimeDependent{<:AbstractVector{<:Union{var"#s7444", var"#s7443"} where {var"#s7444"<:NonFiniteAllocationOptimisationEstimator, var"#s7443"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}
) -> Any
Return the optimiser a TimeDependent schedule takes outside every fold loop.
An optimiser position is required — there is no static default to fall back to — so the schedule must supply its own default, and one that does not throws a TimeDependentDefaultError. The fold-less optimiser is itself reset, so its own schedules resolve to their defaults too.
Related
PortfolioOptimisers.reset_time_dependent_fields — Function
reset_time_dependent_fields(opt) -> Any
Rebuild a host optimiser with each TimeDependent-valued field replaced by its fold-less value (see time_dependent_reset_value).
Shared implementation behind the hosts' reset_time_dependent_estimator methods. Returns opt unchanged when no field is time-dependent.
Related
PortfolioOptimisers.assert_no_nearest_bind_optimiser_schedule — Method
assert_no_nearest_bind_optimiser_schedule(x, field::Symbol, host::Symbol)Reject a bind = :nearest TimeDependent schedule in an optimiser-valued position no inner fold loop consumes.
bind picks which fold loop supplies the schedule's index. :nearest therefore says something different from :outermost only where the host opens a fold loop of its own and hands the field across it — NestedClustered.opti (its inner cross-validation is entered per cluster) and Stacking.opti[k] (entered per candidate), the positions declared by inner_fold_fields. Everywhere else the loop that reaches the host is the nearest one, and the two binds would name the same loop.
The positions this guards have no such inner loop:
- A fallback (
fb), on every host. The fallback walk is a retry chain within a single fold's solve — it has no fold indices of its own — so:nearestthere is either redundant with:outermostor, behind a meta's inner cross-validation, silently wrong: it would resolve against the inner loop's fold numbers (tuning folds) instead of the backtest's periods, changing meaning with nesting depth. A per-fold fallback is fully expressible with:outermost, includingnothingentries to switch it off on some folds (seeTDO_OptE_Opt). - The outer optimisers (
opto). They consume the combined inner output, once per solve. SubsetResampling.opt. Its internal loop is over randomly drawn asset subsets, not time folds.
So a :nearest schedule in any of them has no nearest fold loop to bind to, and is rejected at construction rather than resolving against a loop the caller did not mean. No-op for anything that is not a TimeDependent.
Related
PortfolioOptimisers.entitled — Function
entitled(opt, f::Symbol, all_binds::Bool)Return true when the recursion position described by all_binds may consume a bind = :nearest schedule in field f of opt.
Entitlement is per-field, not per-host: a loop scanning with all_binds = true takes :nearest schedules everywhere except in the fields the host hands across its own inner fold loop (see inner_fold_fields) — for those, the host's inner loop is the nearest one, whatever loop is doing the scanning. A field is taken by a pass iff entitled(opt, f, all_binds) || bind === :outermost.
Related