Detone
Financial data is often responds to broad market conditions. This market-wide behaviour can obscure specific correlation signals. By removing the largest n eigenvalues, the idiosyncratic relationships between assets are allowed to shine through [9].
Detoned matrices may be non-positive definite, so they can be unsuitable for traditional optimisations, but they can be quite effective for clustering ones.
PortfolioOptimisers.AbstractDetoneEstimator — Type
abstract type AbstractDetoneEstimator <: AbstractEstimatorAbstract supertype for all detoning estimators.
All concrete and/or abstract types representing detoning estimators should be subtypes of AbstractDetoneEstimator.
Interfaces
In order to implement a new detoning estimator which will work seamlessly with the library, subtype AbstractDetoneEstimator with all necessary parameters as part of the struct, and implement the following methods:
detone!(dt::AbstractDetoneEstimator, X::MatNum) -> MatNum: In-place detoning.detone(dt::AbstractDetoneEstimator, X::MatNum) -> MatNum: Optional out-of-place detoning. A fallback method copiesXand callsdetone!, so it is only needed if the copy can be avoided.
Arguments
dt: Matrix detoning estimator.X: Covariance-like or correlation-like matrixassets × assets.
Returns
X::MatNum: The detoned input matrixX.
Examples
We can create a dummy detoning estimator as follows:
julia> struct MyDetoneEstimator <: PortfolioOptimisers.AbstractDetoneEstimator endjulia> function PortfolioOptimisers.detone!(dt::MyDetoneEstimator, X::PortfolioOptimisers.MatNum) # Implement your in-place detoning estimator here. println("Detoning matrix in-place...") return X endjulia> function PortfolioOptimisers.detone(dt::MyDetoneEstimator, X::PortfolioOptimisers.MatNum) X = copy(X) println("Copy X...") detone!(dt, X) return X endjulia> detone!(MyDetoneEstimator(), [1.0 2.0; 2.0 1.0])Detoning matrix in-place...2×2 Matrix{Float64}: 1.0 2.0 2.0 1.0julia> detone(MyDetoneEstimator(), [1.0 2.0; 2.0 1.0])Copy X...Detoning matrix in-place...2×2 Matrix{Float64}: 1.0 2.0 2.0 1.0Related
PortfolioOptimisers.Detone — Type
struct Detone{__T_pdm, __T_n} <: AbstractDetoneEstimatorRemoves the largest n principal components (market modes) from a covariance or correlation matrix. Applied by detone! and detone.
For financial data, the leading principal components often represent market-wide movements that can obscure asset-specific signals. The Detone estimator allows users to specify the number of these leading components to remove, thereby enhancing the focus on idiosyncratic relationships between market members [9].
Detoned matrices may not be suitable for non-clustering optimisations because it can make the matrix non-positive definite. However, they can be quite effective for clustering optimsations.
Mathematical definition
The $n$ largest eigenmodes are subtracted, and the remainder is rescaled to unit diagonal:
\[\begin{align} \mathbf{C} &= \mathbf{X} - \sum_{k=N-n+1}^{N} \lambda_k \boldsymbol{v}_k \boldsymbol{v}_k^\intercal\,, \\ \tilde{X}_{ij} &= \frac{C_{ij}}{\sqrt{C_{ii} C_{jj}}}\,. \end{align}\]
Where:
- $\mathbf{C}$: Remainder after the market modes are subtracted.
- $\tilde{\mathbf{X}}$: Detoned matrix.
- $\mathbf{X}$: Original correlation or covariance matrix.
- $\lambda_k$: $k$-th largest eigenvalue of $\mathbf{X}$.
- $\boldsymbol{v}_k$: $k$-th largest eigenvector of $\mathbf{X}$.
- $n$: Number of eigenmodes (market modes) to remove.
- $N$: Number of assets.
Subtracting a set of eigenmodes takes the diagonal of $\mathbf{C}$ below one, so the rescaling is not cosmetic: it changes every entry. The subtraction can also take an eigenvalue of $\mathbf{C}$ below zero, which is the reason a detoned matrix may not be positive definite.
Algorithm
The steps that detone! runs under this estimator.
- Read
dt.ninton, and check that0 < n <= size(X, 2). - Decrement
nby one.ncounts the modes to remove, and steps 5 and 6 slice(end - n):end, which is a window of the originaldt.ncolumns. Sodt.n = 1removes the single largest component, which is the market mode. - Read the diagonal of
Xintos. When any entry ofsis not one,Xis a covariance matrix: replaceswith its square roots and convertXto a correlation matrix withStatsBase.cov2cor!. The test isany(!isone, s), so it is the value of the diagonal that decides, never the type ofX. - Eigendecompose
X, giving the ascending eigenvaluesvalsand the eigenvectorsvecs. - Take the trailing block of
vals, which holds thedt.nlargest eigenvalues. - Take the matching trailing columns of
vecs. - Subtract
vecs * vals * transpose(vecs)fromX, giving the remainder $\mathbf{C}$. - Rescale the remainder to unit diagonal with
StatsBase.cov2cor. - Repair the rescaled matrix with
posdef!, underdt.pdm. - When step 3 converted a covariance matrix, convert
Xback withStatsBase.cor2cov!. The standard deviations are the ones read in step 3, so the original diagonal returns exactly.
Fields
pdm: Optional positive definite matrix estimator.
n: Number of leading principal components to remove.
Constructors
Detone(; pdm::Option{<:AbstractPosdefEstimator} = Posdef(), n::Integer = 1,) -> DetoneKeywords correspond to the struct's fields.
Validation
n > 0.
Examples
julia> Detone(; n = 2)Detone pdm ┼ Posdef │ alg ┼ UnionAll: NearestCorrelationMatrix.Newton │ kwargs ┴ @NamedTuple{}: NamedTuple() n ┴ Int64: 2Related
References
PortfolioOptimisers.detone — Function
detone(dt::Option{<:AbstractDetoneEstimator}, X::MatNum) -> MatNumOut-of-place version of detone!.
Algorithm
- Copy
X. - Apply
detone!to the copy, and return it. The input is never modified.
Arguments
dt: Optional matrix detoning estimator.::Detone: The topnprincipal components are removed from a copy ofX.::Nothing: No-op, returnsXunchanged.
X: Covariance-like or correlation-like matrixassets × assets.
Returns
X::MatNum: A new matrix equal to the detoned version of the input.
Examples
julia> using StableRNGsjulia> rng = StableRNG(123456789);julia> X = rand(rng, 10, 5); X = X' * X;julia> Xd = detone(Detone(), X);julia> size(Xd)(5, 5)Related
References
PortfolioOptimisers.detone! — Function
detone!(dt::Option{<:AbstractDetoneEstimator}, X::MatNum) -> MatNumIn-place removal of the top n principal components (market modes) from a covariance or correlation matrix.
For matrices without unit diagonal, the function converts them into correlation matrices i.e. matrices with unit diagonal, applies the algorithm, and rescales them back.
Arguments
dt: Optional matrix detoning estimator.::Detone: The topnprincipal components are removed fromXin-place.::Nothing: No-op.
X: Covariance-like or correlation-like matrixassets × assets.
Validation
0 < dt.n <= size(X, 2).
Returns
X::MatNum: The input matrixXis modified in-place.
Examples
julia> using StableRNGsjulia> rng = StableRNG(123456789);julia> X = rand(rng, 10, 5);julia> X = X' * X5×5 Matrix{Float64}: 3.29494 2.0765 1.73334 2.01524 1.77493 2.0765 2.46967 1.39953 1.97242 2.07886 1.73334 1.39953 1.90712 1.17071 1.30459 2.01524 1.97242 1.17071 2.24818 1.87091 1.77493 2.07886 1.30459 1.87091 2.44414julia> detone!(Detone(), X)5×5 Matrix{Float64}: 3.29494 -1.14673 0.0868439 -0.502106 -1.71581 -1.14673 2.46967 -0.876289 -0.0864304 0.274663 0.0868439 -0.876289 1.90712 -1.18851 -0.750345 -0.502106 -0.0864304 -1.18851 2.24818 -0.0774753 -1.71581 0.274663 -0.750345 -0.0774753 2.44414Related
References
References
- [5]
- D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).
- [9]
- M. M. De Prado. Machine learning for asset managers (Cambridge University Press, 2020).