Skip to content
Property-cat reinsurance analytics for software engineers

Analytics Toolkit · Section 4.7

If your losses are negative

What changes in the metric definitions and their code when loss data is stored as negative numbers: the sort direction, the end of the array that holds the tail, and a final negation.

A detour, for readers whose loss data arrives negative; on the loss-positive convention the metric pages are complete and this page changes nothing. Every definition and implementation in this chapter assumes the loss-positive sign convention established in the trial worldview: the worst trials are the largest values, the exceedance probability (EP) curve sorts descending, and the tail is the top kk ranks. Many organisations, particularly those whose loss data flows out of accounting or profit and loss (P&L) systems, store losses as negative values instead. Every metric in this chapter survives the switch unchanged in magnitude, but every mechanical step mirrors.

Negating each trial loss (−Lj-L_j) reverses the order of the distribution, so the rr-th largest loss becomes the rr-th smallest (most negative) value. The definitions translate as:

VaRα=L(k)=−(k-th smallest of −L1,…,−LN)\text{VaR}_\alpha = L_{(k)} = -\big(k\text{-th smallest of } -L_1, \ldots, -L_N\big) TVaRα=1k∑r=1kL(r)=−(mean of the k smallest of −L1,…,−LN)\text{TVaR}_\alpha = \frac{1}{k} \sum_{r=1}^{k} L_{(r)} = -\Big(\text{mean of the } k \text{ smallest of } -L_1, \ldots, -L_N\Big)

In quantile language: the 1-in-100 loss is the 99th percentile of the loss-positive distribution but the 1st percentile of the loss-negative one. The tail moves from the right end of the distribution to the left, and every confidence level α\alpha flips to 1−α1 - \alpha.

In code, three things change: the sort direction, which end of the sorted array is the tail, and a final negation when reporting magnitudes.

caldera_re/sign_convention.py Python
import numpy as np

from caldera_re.aep import aep
from caldera_re.data import cedents
from caldera_re.utils import ep_index


if __name__ == '__main__':
    curve = aep(cedents.suncoast)
    losses = curve['loss'].to_numpy()  # loss-positive (this site)
    x = -losses                        # same trials, loss-negative view

    alpha = 0.90
    N = len(losses)
    k = ep_index(N, alpha)

    # Loss-positive: sort descending, the tail is the top k
    tail_pos = np.sort(losses)[::-1][:k]
    var_pos = tail_pos[-1]
    tvar_pos = tail_pos.mean()

    # Loss-negative: sort ascending, the tail is the FIRST k
    # (most negative = worst); negate to report magnitudes
    tail_neg = np.sort(x)[:k]
    var_neg = -tail_neg[-1]
    tvar_neg = -tail_neg.mean()

    print(f"k = {k} tail trials at alpha = {alpha:.0%}\n")
    print(f"{'':12}{'loss-positive':>15}{'loss-negative':>15}")
    print(f"{'VaR(90%)':12}{var_pos:>14.1f}M{var_neg:>14.1f}M")
    print(f"{'TVaR(90%)':12}{tvar_pos:>14.1f}M{tvar_neg:>14.1f}M")
    print("\nSame magnitudes -- but sort direction, tail end,")
    print("and a final negation all had to flip.")

The magnitudes are the same on either sign convention, and every page of the Financial Modelling chapter assumes the loss-positive one.