The optimiser on the partial-fit seam

An optimiser takes the online step in two verbs and one forward. partial_fit!(opt, rd) folds the observations of a carrier into the prior alone and records the rest of the carrier in a PortfolioOptimisers.ReturnsBufferState; optimise(opt) with no returns rebuilds the carrier from the state, swaps the folded prior for its read-out and runs the ordinary batch path, so everything above the prior — the clustering estimator, the constraint estimators, every uncertainty set, a meta-optimiser's inner optimisers — is fitted exactly as batch fits it. The read-out is pure, so the fallback chain walks unchanged. ADR 0137 records the decision.

PortfolioOptimisers.partial_fit!Method
partial_fit!(opt::JuMPOptimisationEstimator, rd::ReturnsResult)
partial_fit!(opt::Union{<:HierarchicalRiskParity, <:HierarchicalEqualRiskContribution, <:SchurComplementHierarchicalRiskParity}, rd::ReturnsResult)
partial_fit!(opt::Union{<:JuMPOptimiser, <:HierarchicalOptimiser, <:InverseVolatility, <:NestedClustered, <:Stacking, <:SubsetResampling}, rd::ReturnsResult)
partial_fit!(opt::Union{<:EqualWeighted, <:RandomWeighted}, rd::ReturnsResult)
partial_fit!(opt::PreviousWeights, rd::ReturnsResult)
partial_fit!(opt::FiniteAllocationOptimisationEstimator, rd::ReturnsResult)
partial_fit!(td::TD_OptE_Opt, rd::ReturnsResult)

Folds observations into an optimiser, without solving.

The optimiser's online step, decided by #867: two verbs and one forward. This verb folds and returns the estimator; optimise(opt) with no returns reads the state out and solves once. The step forwards the observations to the prior alone, through fold_prior, and records the rest of the carrier in a ReturnsBufferState so the read-out can rebuild the carrier the batch path reads. Everything above the prior — the clustering estimator, the constraint estimators, every uncertainty set, a meta-optimiser's inner optimisers — is untouched by the step and fitted by the read-out exactly as batch fits it.

The arity mirrors the batch verb: optimise(opt, rd) takes a carrier, so the step takes one, holding one observation or a block of them, and unpacks it on the way down to the prior's own arity. A JuMP head forwards to the JuMPOptimiser it holds and a hierarchical head to its HierarchicalOptimiser; the bundle is the host of the prior and the context. The three meta-optimisers and InverseVolatility hold their prior directly and are their own host. EqualWeighted and RandomWeighted hold no prior but read the observations — each derives the Coverage Universe of its own window — so they are the bottom of their chain and their context keeps the rows itself. PreviousWeights reads nothing at all, so its step is the identity and it carries no context.

Two families are refused by name. A finite allocation converts a weight vector and prices into share counts and reads no returns window, so it has no online step and needs none: optimise(da, w, p) is its whole verb. A TimeDependent schedule of optimisers has no step of its own, because a schedule swaps the optimiser that carries the state and no loop resolves a schedule before stepping: a schedule reaches stateless fields only, and the fold loop's online arm refuses one at warm-up (assert_online_entry).

The identity the step keeps is the seam's: after t observations, optimise(opt) equals optimise(opt, rd[1:t]) — exactly for the carrier the read-out rebuilds, and to the moment layer's own tolerance for the weights.

Arguments

  • opt: The optimiser to fold into.
  • rd: The carrier holding the observations to fold, observations × assets. A one-row carrier is one step of a walk-forward; a block is a warm-up.

Validation

Returns

  • opt: The optimiser, with its prior folded and its context recorded.

Related

source
PortfolioOptimisers.optimiseMethod
optimise(opt::OptimisationEstimator; kwargs...)

Reads a folded optimiser out: rebuilds the carrier from the state, and solves once over it.

The read-out verb of the online step, and the fold-less entry of every optimiser. With no returns to fit, an optimiser answers from what it holds: a prior that is already a result, which is the batch configuration this entry has always served, or the state its partial_fit! steps wrote, which online_readout turns into a batch estimator and the carrier of the observations folded so far. The ordinary optimise(opt, rd) then runs — the constraint estimators, the clustering, every uncertainty set and every inner optimiser fitted from that carrier exactly as batch fits them — and the fallback chain walks unchanged, because the read-out is pure and a failed solve leaves the state where the last fold put it.

mr = MeanRisk(; opt = JuMPOptimiser(; pe = EmpiricalPrior(), slv = slv))mr = update_online_estimator(mr)          # warm-up: seeds any Online bufferfor t in 1:warmup    mr = partial_fit!(mr, port_opt_view(rd, t:t, :))   # folds; solves nothingendres = optimise(mr)                        # reads out; solves once

Arguments

  • opt: The optimiser to read out.
  • kwargs...: Keyword arguments of the batch verb — dims, str_names, save — forwarded to it.

Returns

  • res::OptimisationResult: The result the batch verb gives over the observations folded so far.

Related

source