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 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 () reverses the order of the distribution, so the -th largest loss becomes the -th smallest (most negative) value. The definitions translate as:
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 flips to .
In code, three things change: the sort direction, which end of the sorted array is the tail, and a final negation when reporting magnitudes.
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.