Cross-Sectional Regression: private API

Types

PortfolioOptimisers.AbstractCrossSectionalSolveAlgorithmType
abstract type AbstractCrossSectionalSolveAlgorithm <: AbstractRegressionAlgorithm

Abstract supertype for all cross-sectional solve algorithm types.

A member decides what CrossSectionalLinearRegression does when the weighted design of an observation is rank deficient, and the members differ on two axes: whether a rank test runs at all, and what happens when it fails. The decision is a real one, because Julia's \ answers a deficient design in three different ways. A square design goes to an LU factorisation that throws LinearAlgebra.SingularException on an exactly zero pivot. A non-square one goes to a column-pivoted QR whose solve completes the orthogonal factorisation, so it returns the minimum-norm solution and agrees with a pseudo-inverse. A design that is only nearly dependent passes the rank test of every member and returns a badly conditioned answer that a pseudo-inverse would truncate.

Related

source

Functions

PortfolioOptimisers.cross_sectional_design_maskFunction
cross_sectional_design_mask(
    Z::AbstractArray{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}, 3},
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    W::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
) -> BitMatrix

Return the eligibility mask of a cross-sectional design, after checking its three arrays.

An (observation, asset) pair is eligible when its cross-sectional weight is positive. The weight is the one contract: a pair excluded by a zero weight may carry a missing return or a missing exposure, and a pair with a positive weight may not.

Arguments

  • Z::Arr3Num: Exposure tensor observations × assets × factors.
  • X::MatNum: Asset returns matrix observations × assets.
  • W::MatNum: Cross-sectional weights matrix observations × assets.

Validation

  • !isempty(Z), !isempty(X) and !isempty(W).
  • size(Z, 1) == size(X, 1) and size(Z, 2) == size(X, 2).
  • size(W) == size(X).
  • all(isfinite, W) and all(x -> x >= 0, W).
  • Every pair with a positive weight carries a finite return and finite exposures. The IsNonFiniteError names the observation, the asset and the weight.

Returns

  • act::BitMatrix: Eligibility mask observations × assets, true where the weight is positive.

Related

source
PortfolioOptimisers.cross_sectional_coefficientsFunction
cross_sectional_coefficients(
    cre::CrossSectionalLinearRegression,
    A::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    y::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    w::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    t::Integer
) -> Any

Return the factor returns of one observation, through the member's own solve.

CrossSectionalLinearRegression scales the design and the target by sqrt.(w) and hands them to cross_sectional_solve. CrossSectionalTargetRegression hands the unscaled pair to the target, with w as the observation weights, and refuses an empty cross-section.

Arguments

  • cre: Cross-sectional regression estimator.
  • A::MatNum: Exposures of the eligible assets, eligible assets × factors, already demeaned when an intercept is fitted.
  • y::VecNum: Returns of the eligible assets, already demeaned when an intercept is fitted.
  • w::VecNum: Cross-sectional weights of the eligible assets.
  • t::Integer: Index of the observation.

Validation

  • Under CrossSectionalTargetRegression, !isempty(y). An external target has no cross-section to fit when no asset is eligible, and the ArgumentError names the observation.

Returns

  • f::VecNum: Factor returns of the observation, of length size(A, 2).

Related

source
PortfolioOptimisers.cross_sectional_rankFunction
cross_sectional_rank(
    A::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
) -> Int64

Return the numerical rank of A, read off the diagonal of a column-pivoted QR.

The test is written out rather than delegated to LinearAlgebra.rank(::QRPivoted), which needs Julia 1.12 while this package supports 1.11.

Algorithm

  1. Return 0 when A has no row or no column, because a factorisation of it has no pivot to read.
  2. Take the column-pivoted LinearAlgebra.qr of A. The magnitudes of the diagonal of its R are non-increasing, so they rank the columns by how much each adds to the span.
  3. Count the leading diagonal entries above min(size(A)...) * eps(real(eltype(R))) * abs(R[1, 1]), giving the numerical rank. The tolerance is the one LinearAlgebra.rank applies to a pivoted QR.

Arguments

  • A::MatNum: Weighted design of one observation, eligible assets × factors.

Returns

  • r::Int: Numerical rank of A.

Related

source
PortfolioOptimisers.cross_sectional_solveFunction
cross_sectional_solve(alg::AbstractCrossSectionalSolveAlgorithm, A::MatNum, y::VecNum,
                      t::Integer) -> VecNum

Solve the weighted design A against the weighted target y through the branch alg selects.

Algorithm

  1. UncheckedSolve returns A \ y, with no rank test.
  2. MinimumNormSolve returns LinearAlgebra.pinv(A) * y, with no rank test.
  3. PseudoInverseFallback takes cross_sectional_rank. It returns A \ y when the rank equals the factor count, and LinearAlgebra.pinv(A) * y otherwise.
  4. RankDeficiencyRefusal takes cross_sectional_rank, refuses when the rank falls short of the factor count, and returns A \ y otherwise.

Arguments

  • alg: Cross-sectional solve algorithm.
  • A::MatNum: Weighted design of one observation, eligible assets × factors.
  • y::VecNum: Weighted target of one observation, of length eligible assets.
  • t::Integer: Index of the observation, named by the refusal of RankDeficiencyRefusal.

Validation

  • Under RankDeficiencyRefusal, the rank of A equals size(A, 2). The ArgumentError names the observation, the rank, the factor count and the count of eligible assets.

Returns

  • f::VecNum: Factor returns of the observation, of length size(A, 2).

Related

source
PortfolioOptimisers.cross_sectional_systematicFunction
cross_sectional_systematic(f::MatNum, b::Option{<:VecNum}, Z::Arr3Num) -> MatNum

Return the systematic part of a cross-sectional regression, observations × assets.

Mathematical definition

\[\begin{align} \hat{x}_{t,i} &= b_{t} + \boldsymbol{z}_{t,i}^{\intercal} \boldsymbol{f}_{t}\,. \end{align}\]

Where:

  • $\hat{x}_{t,i}$: Systematic return of asset $i$ at observation $t$.
  • $\boldsymbol{z}_{t,i}$: Exposures of asset $i$ at observation $t$.
  • $\boldsymbol{f}_{t}$: Factor returns of observation $t$.
  • $b_{t}$: Intercept of observation $t$. The term is zero when no intercept was fitted.

Arguments

  • f::MatNum: Factor returns matrix observations × factors.
  • b::Option{<:VecNum}: Intercept vector, or nothing when none was fitted.
  • Z::Arr3Num: Exposure tensor observations × assets × factors. The asset axis may differ from the one the fit saw; the observation and factor axes may not.

Validation

  • size(Z, 1) == size(f, 1) and size(Z, 3) == size(f, 2).

Returns

  • Xh::MatNum: Systematic returns, observations × assets.

Related

source