Lower Tail Dependence Covariance: private API

PortfolioOptimisers.lower_tail_dependenceFunction
lower_tail_dependence(X::MatNum, alpha::Number = 0.05,
                      ex::FLoops.Transducers.Executor = FLoops.SequentialEx())

Compute the lower tail dependence matrix for a set of asset returns.

The lower tail dependence (LTD) between two assets quantifies the probability that both assets experience returns in their respective lower tails (i.e., joint drawdowns or adverse events), given a specified quantile level alpha. This function estimates the LTD matrix for all pairs of assets in the input matrix X, which is particularly useful for risk management and stress testing.

Mathematical definition

For a quantile level $\alpha \in (0,1)$ and $k = \lceil T \alpha \rceil$, let $\hat{q}_i$ denote the empirical $\alpha$-quantile of asset $i$ (the $k$-th order statistic). The lower tail dependence between assets $i$ and $j$ is estimated as:

\[\begin{align} \hat{\lambda}_{ij} &= \frac{1}{k} \sum_{t=1}^{T} \mathbf{1}\left[x_{ti} \leq \hat{q}_i \text{ and } x_{tj} \leq \hat{q}_j\right]\,. \end{align}\]

The resulting matrix is symmetric with entries clamped to $[\sqrt{\varepsilon},\, 1]$, where $\varepsilon$ is the machine epsilon of eltype(X). The floor is not zero. A pair whose tails never coincide is reported as 1.4901161193847656e-8 for a Float64 input, so a caller may divide by an entry or take its logarithm.

Where:

  • $\hat{\lambda}_{ij}$: Lower tail dependence estimate between assets $i$ and $j$.
  • $T$: Number of observations.
  • $\alpha$: Significance level (left tail probability), $\alpha \in (0, 1)$.
  • $k = \lceil T \alpha \rceil$: Number of observations in the lower tail.
  • $x_{ti}$: Return of asset $i$ at time $t$.
  • $\hat{q}_i$: Empirical $\alpha$-quantile of asset $i$.

Algorithm

  1. Set $k = \lceil T \alpha \rceil$, the number of observations the lower tail holds, and allocate the result as a matrix of zeros. The allocation is zeros and not undef, so a k of zero reaches the caller as zeros and never as uninitialised memory.
  2. When k is zero, return that zero matrix. # Validation states the two cases that reach this branch.
  3. Copy X, and partially sort each column of the copy about position k. Position k of the sorted column is the $k$-th order statistic, which is the empirical $\alpha$-quantile of that asset.
  4. Build the whole boolean mask, one column at a time: entry (t, j) is true when $x_{tj}$ is at or below the quantile of step 3. The mask is complete before any pair is counted, because the pair loop of step 5 reads a column of lower index than the one it writes.
  5. For each pair $(i, j)$ with $i \leq j$, count the observations whose two mask entries are both true, divide that count by k, and clamp the ratio to $[\sqrt{\varepsilon},\, 1]$. Write the clamped value into both rho[i, j] and rho[j, i]. The executor ex runs this loop.

Arguments

  • X: Data matrix of asset returns (observations × assets).
  • alpha: Quantile level for the lower tail.
  • ex: Parallel execution strategy.

Validation

  • ceil(Int, T * alpha) > 0. The count is zero in exactly two cases, and the function then returns a zero matrix rather than an estimate: alpha is exactly 0, or X carries no observation. No small positive alpha reaches this branch. ceil of any positive real is at least 1, so alpha = nextfloat(0.0) on a matrix of one or more observations still selects one observation. LowerTailDependenceCovariance rules the first case out at construction, because assert_unit_interval requires 0 < alpha < 1.

Returns

  • rho::Matrix{<:Number}: Symmetric matrix of lower tail dependence coefficients, where rho[i, j] is the estimated LTD between assets i and j. The diagonal is exactly 1, and every entry lies in $[\sqrt{\varepsilon},\, 1]$.

Related

source