Introduction

These study notes are based on the Exam 9 syllabus reading An Application of Game Theory: Property Catastrophe Risk Load by Donald F. Mango. This reading presents methods for distributing a risk load among correlated risks, such as property catastrophe risks. This paper corresponds to learning objective D7 on the syllabus. The method assumes that we have access to the output of a catastrophe model, both with and without the account whose risk load is being calculated. Such an output typically includes the probability of an event occurring, and the modelled loss amount for that event.

easypackages::packages("dplyr", "ggplot2", "DT")

The concepts are illustrated using the following example from the reading, for two accounts \(X\) and \(Y\). It is important to interpret this output not as a probabibility distribution for the losses, but a list of binomial probabilities for events that are not mutually exclusive. For example, there is a 0.0002 probability that events 1 and 2 both occur:

mango.example = read.csv("./Data/mango_example.csv")
datatable(mango.example)

Let \(N_i\) be a binomial random variable that is equal to 1 if event \(i\) occurs, and 0 otherwise. Let \(x_i\) and \(y_i\) denote the losses under lines X and Y in event \(i\). Then the total loss to account X is given by the random variable \[ X = \sum_i x_i N_i \] and the loss to account Y is \[ Y = \sum_i y_i N_i \]

From this, we obtain \[ E[X] = \sum_{i} p_i x_i \] and \[ \mathrm{Var}(X) = \sum_{i} x_i^2 p_i(1-p_i) \] The variance formula comes from viewing this as a sum of binomial random variables rather than a single discrete loss distribution. For the covariance, we have \[ \mathrm{Cov}(X,Y) = \sum_{i,j} x_i y_j \mathrm{Cov}(N_i, N_j) = \sum_{i} x_i y_i p_i (1 - p_i) \] since events \(N_i\) and \(N_j\) are independent. Note that this formulation implies that the covariance of the two lines is always non-negative.

expected.X = sum(mango.example$probability * mango.example$loss_X)
variance.X = sum(mango.example$loss_X^2 * mango.example$probability * (1 - mango.example$probability))
expected.Y = sum(mango.example$probability * mango.example$loss_Y)
variance.Y = sum(mango.example$loss_Y^2 * mango.example$probability * (1 - mango.example$probability))
covariance.XY = sum(mango.example$loss_X * mango.example$loss_Y * mango.example$probability * (1 - mango.example$probability))
print(paste0("The expected value and variance for account X are ", expected.X, " and ", variance.X,". The expected value and variance for account Y are ", expected.Y, " and ", variance.Y, ". The covariance between the two is ", covariance.XY, "."))
## [1] "The expected value and variance for account X are 1290 and 19619900. The expected value and variance for account Y are 179 and 377959. The covariance between the two is 1450550."

The usual formula for the variance of a combined portfolio applies; \[ \mathrm{Var}(X + Y) = \mathrm{Var}(X) + \mathrm{Var}(Y) + 2 \mathrm{Cov}(X,Y) \]

variance.XY = variance.X + variance.Y + 2 * covariance.XY
print(paste0("The variance of the combined portfolio is ", variance.XY))
## [1] "The variance of the combined portfolio is 22898959"

Marginal Methods

The marginal surplus method is based on an assumption that surplus is set as some multiple \(z\) of the portfolio standard deviation of the portfolio, less the return on the portfolio. The multiplier \(z\) can be selected as a given percentile of the standard normal distribution, e.g. representing the “number of standard devaiations” corresponding to a given percentile. In this scenario, the risk load for a new account is based on a multiple of the change in standard deviation of the portfolio when the account is added. To understand why, let \(V_i\), \(S_i\), and \(R_i\) denote the surplus, standard deviation, and return of the portfolio, with subscript 0 indicating the original portfolio, and subscript 1 denoting the portfolio after a new account is added. The risk load for this new account will be the increase in return it provides: \[ r = R_1 - R_0 \] Per the surplus setting procedure described above, \[ V_i = z S_i - R_i \] Therefore, \[ r = z(S_1 - S_0) - (V_1 - V_0) \] Let \(y\) be the required rate of return on the marginal surplus \(V_1 - V_0\). Then \(r\) must satisfy \(r = y(V_1 - V_0)\), so \[ r = z(S_1 - S_0) - \frac{r}{y} \] Solving for \(r\), \[ r = \frac{zy}{1+y}(S_1 - S_0) \] Therefore, when a new account \(n\) is added to an existing portfolio \(L\), the risk load is \[ r = \frac{zy}{1+y}\left(\sqrt{\mathrm{Var}(L + n)} - \sqrt{\mathrm{Var}(L)}\right) \]

The marginal variance method is similar, except that the risk load is a fixed multiple \(\lambda\) of the increase in portfolio variance resulting from the addition of a new account \(n\) to an existing portfolio \(L\). Given the covariance formula above, the risk load is \[ r = \lambda( \mathrm{Var}(n) + 2 \mathrm{Cov}(L,n)) \]

Build-up

The build-up risk loads are calculated by assembling the portfolio one risk at a time. In the example, we assume that risk \(X\) is added to the portfolio first, and \(Y\) is added to the portfolio second.

To apply the marginal surplus method, assume the following parameters for \(z\) and \(y\):

z = 2.0
y = 0.2

When risk \(X\) is added, the incremental standard deviation is just its stand-alone standard deviation:

ms.bu.X.1 = sqrt(variance.X) * z * y / (1 + y)
print(paste0("The risk load for X is ", ms.bu.X.1))
## [1] "The risk load for X is 1476.47854332154"

When adding risk \(Y\), we caluclate the difference between the standard deviation of both risks together, and risk \(X\) alone:

ms.bu.Y.2 = (sqrt(variance.XY) - sqrt(variance.X)) * z * y / (1 + y)
print(paste0("The risk load for Y is ", ms.bu.Y.2))
## [1] "The risk load for Y is 118.616681979378"

To apply the marginal variance method, Mango recommends selecting a multiplier \(\lambda\) in the following manner to enable fair comparisons to the marginal surplus methods: \[ \lambda = \frac{zy}{(1+y) \sqrt{\mathrm{Var}(L)}} \] This ensures that both methods give the same value for the risk load for the entire portfolio. However, this is not an effective way to select \(\lambda\) for buildup scenarios in practice, since it requires knowledge of the entire portfolio.

When account \(X\) is added, it is the only account in the portfolio, so its risk load is based on its standalone variance:

lambda = z * y / (1 + y) / sqrt(variance.XY)
mv.bu.X.1 = lambda * variance.X
print(paste0("The risk load for X is ", mv.bu.X.1))
## [1] "The risk load for X is 1366.68259945273"

When account \(Y\) is added, we need to account for its covariance with X:

mv.bu.Y.2 = lambda * (variance.Y + 2 * covariance.XY)
print(paste0("The risk load for Y is ", mv.bu.Y.2))
## [1] "The risk load for Y is 228.412625848188"

Note that by construction, for both methods, during buildup the sum of risk loads of individual risks will equal the risk load of the aggregate portfolio.

Renewal

The renewal risk loads are calculated by comparing the total portfolio, with the portfolio minus the account of interest. It is essentially the last step of a build-up approach in which the account of interest is the last one being added.

Applying the marginal surplus method, the renewal risk load for account X is:

ms.rn.X = z * y / (1 + y) * (sqrt(variance.XY) - sqrt(variance.Y))
print(paste0("The renewal risk load for X is ", ms.rn.X, " compared to a build-up load of ", ms.bu.X.1))
## [1] "The renewal risk load for X is 1390.16732470062 compared to a build-up load of 1476.47854332154"

For account Y, the result is the same since it was the last one added during the initial buildup:

ms.rn.Y = z * y / (1 + y) * (sqrt(variance.XY) - sqrt(variance.X))
print(paste0("The renewal risk load for Y is ", ms.rn.Y, " compared to a build-up load of ", ms.bu.Y.2))
## [1] "The renewal risk load for Y is 118.616681979378 compared to a build-up load of 118.616681979378"

Aplying the marginal variance method, the renewal risk load for account X is:

mv.rn.X = lambda * (variance.X + 2 * covariance.XY)
print(paste0("The renewal risk load for X is ", mv.rn.X," compared to a build-up load of ", mv.bu.X.1))
## [1] "The renewal risk load for X is 1568.76736488335 compared to a build-up load of 1366.68259945273"

For account Y:

mv.rn.Y = lambda * (variance.Y + 2 * covariance.XY)
print(paste0("The renewal risk load for Y is ", mv.rn.Y, " compared to a build-up load of ", mv.bu.Y.2))
## [1] "The renewal risk load for Y is 228.412625848188 compared to a build-up load of 228.412625848188"

Properties of the Risk Loads

Sub-Additivity and Super-Additivity

A risk load \(\rho\) is said to be sub-additive if \[ \rho(X) + \rho(Y) \leq \rho(X+Y) \] and superadditive if \[ \rho(X) + \rho(Y) \geq \rho(X+Y) \] A risk load is renewal additive if the sum of renewal risk loads of the individual accounts equals the risk load of the aggregate portfolio: \[ \sum_{1\leq i \leq n} \rho(X_i) = \rho\left(\sum_{1\leq i \leq n} X_i\right) \]

The previous example illustrates that the marginal surplus method is subadditive:

print(paste0("The sum of marginal surplus renewal risk loads is ", ms.rn.X + ms.rn.Y, " compared to an aggregate portfolio risk load of ", z * y / (1 + y) * sqrt(variance.XY)))
## [1] "The sum of marginal surplus renewal risk loads is 1508.78400668 compared to an aggregate portfolio risk load of 1595.09522530092"

On the other hand, the marginal variance method is superadditive:

print(paste0("The sum of marginal variance renewal risk loads is ", mv.rn.X + mv.rn.Y, " compared to an aggregate portfolio risk load of ", lambda * variance.XY))
## [1] "The sum of marginal variance renewal risk loads is 1797.17999073154 compared to an aggregate portfolio risk load of 1595.09522530092"

These observations are true in general:

  • The marginal surplus method will be sub-additive due to the subadditivity of the square root function.

  • The marginal variance method will be superadditive because the covariance of each pair of risks gets “double counted” when each of the individual risk loads is calculated (and we are assuming covariance is non-negative).

Order Dependence

Note that, by construction, both the marginal surplus and marginal variance methods are additive at buildup. However, this requires that we specify a given entry order for the accounts. The marginal risk loads are order dependent because they differ based on the order of entry; in particular, it is possible that an account could be declined or rejected based on the order in which accounts are written.

To illustrate this idea, repeat the marginal surplus and marginal variance methods at buildup, but this time, assume that account Y enters first.

ms.bu.Y.1 = z * y / (1 + y) * (sqrt(variance.Y))
print(paste0("The risk load for Y when it enters first is ", ms.bu.Y.1, " compared to ", ms.bu.Y.2, " when it enters second."))
## [1] "The risk load for Y when it enters first is 204.927900600295 compared to 118.616681979378 when it enters second."
ms.bu.X.2 = z * y / (1 + y) * (sqrt(variance.XY) - sqrt(variance.Y))
print(paste0("The risk load for X when it enters second is ", ms.bu.X.2, " compared to ", ms.bu.X.1, " when it enters first."))
## [1] "The risk load for X when it enters second is 1390.16732470062 compared to 1476.47854332154 when it enters first."

The problem occurs with the marginal variance method as well:

mv.bu.Y.1 = lambda * variance.Y
print(paste0("The risk load for Y when it enters first is ", mv.bu.Y.1, " compared to ", mv.bu.Y.2, "  when it enters second."))
## [1] "The risk load for Y when it enters first is 26.3278604175635 compared to 228.412625848188  when it enters second."
mv.bu.X.2 = lambda * (variance.X + 2 * covariance.XY)
print(paste0("The risk load for X when it enters second is ", mv.bu.X.2, " compared to ", mv.bu.X.1, " when it enters first."))
## [1] "The risk load for X when it enters second is 1568.76736488335 compared to 1366.68259945273 when it enters first."

Approaches from Game Theory

The problems of renewal additivity and order dependence can be addressed by incorporating ideas from game theory.

General Concepts

The context of the game theoretic approach is that we assume there is a set of players who can form coalitions (groups) A characteristic function \(\nu\) assigns a real number \(\nu(S)\) to each group \(S\). If it is sub-additive, then \[ \nu(S) + \nu(T) < \nu(S \cup T) \] In other words, merging colations reduces the overall characteristic function. If it is super-additive, then \[ \nu(S) + \nu(T) > \nu(S \cup T) \] Note that from a game theoretic perspective, the choice of variance or standard deviation is material, since variance is superadditive and standard deviation is subadditive. The question that arises is how to allocate benefits / costs to coalition participants: each parcticipant should receive an allocation somewhere between their stand-alone value and their marginal impact on the charcateristic function.

Desirable proporties of an allocation method include:

  1. Additivity: the sum of allocations to each player must equal the total amount to be allocated.

  2. Stability: there must not be incentives for a subgroup to split from the main group, i.e. no individual or subgroup is better off on its own.

The core of the game is the range of allocations that satisfy these properties.

Shapley Value

The Shapley value is the average of the marginal impacts taken over all possible permutations of the entry order of the accounts. When marginal variance is used, the result for adding account \(n\) to a portfolio \(L\) simplifies to \[ \mathrm{Var}(n) + \mathrm{Cov}(n,L) \] This is essentially the marginal variance approach, except that there is no factor of two applied to the covariance term, so the double-counting effect does not occur.

Appling the Shapley value to the earlier example, at buildup we have the following (assuming that X enters the portfolio first):

shap.bu.X.1 = lambda * variance.X
print(paste0("The risk load for account X is ", shap.bu.X.1))
## [1] "The risk load for account X is 1366.68259945273"
shap.bu.Y.2 = lambda * (variance.Y + covariance.XY)
print(paste0("The risk load for account Y is ", shap.bu.Y.2, " for a total risk load of ", shap.bu.X.1 + shap.bu.Y.2))
## [1] "The risk load for account Y is 127.370243132876 for a total risk load of 1494.0528425856"

At renewal, the Shapley value risk loadings are:

shap.rn.X = lambda * (variance.X + covariance.XY)
print(paste0("The renewal risk load for account X is ", shap.rn.X, " compared to a buildup risk load of ", shap.bu.X.1))
## [1] "The renewal risk load for account X is 1467.72498216804 compared to a buildup risk load of 1366.68259945273"

The difference between the renewal and buildup risk loads is referred to as a deferred risk load; in essence, this is the part the covariance that “should” have been allocated to \(X\), except that it was not known at the time \(X\) was written.

print(paste0("The deferred risk load for account X is ", shap.rn.X - shap.bu.X.1))
## [1] "The deferred risk load for account X is 101.042382715312"
shap.rn.Y = lambda * (variance.Y + covariance.XY)
print(paste0("The renewal risk load for account Y is ", shap.rn.Y, " compared to a buildup risk load of ", shap.bu.Y.2, ". This gives a total risk load of ", shap.rn.X + shap.rn.Y))
## [1] "The renewal risk load for account Y is 127.370243132876 compared to a buildup risk load of 127.370243132876. This gives a total risk load of 1595.09522530092"

When marginal surplus is used, there is no simple formula and the permutation formulation must be applied directly. In the case of a two-account portfolio, this is just the average of the two buildup marginal surplus values calculated earlier, one with X entering first and one with Y entering first. The renewal risk loads in this case are:

shap.ms.rn.X = (ms.bu.X.1 + ms.bu.X.2) / 2
shap.ms.rn.Y = (ms.bu.Y.1 + ms.bu.Y.2) / 2
print(paste0("The risk load for account X is ", shap.ms.rn.X, " and the risk load for account Y is ", shap.ms.rn.Y, ". This gives a total risk load of ", shap.ms.rn.X + shap.ms.rn.Y))
## [1] "The risk load for account X is 1433.32293401108 and the risk load for account Y is 161.772291289837. This gives a total risk load of 1595.09522530092"

Proporties of the Shapley vaule include:

  • Compared to the marginal variance method, the Shapley method gives a lower risk load to Y at build-up, and a lower aggregate risk load overall. However, it’s actually the risk load for X that is understated, because its contribution to covariance isn’t known at the time it is written.

  • At renewal the measure is additive: the sum of the risk loads equals the risk load for the portfolio as a whole. This is true for both the marginal variance and marginal surplus versions.

  • Because of the differences between renewal and buildup, Mango recommends using a marginal method for new accounts, and a renewal additive method for renewals.

Covariance Share

The covariance share method generalizes the Shapley method with marginal variance: instead of splitting the covariance equally between the accounts, the contribution to covariance for each event is split in proportion to each account’s expected losses during that event: \[ \text{CovShare}^X = 2 \sum_i \frac{x_i}{x_i + y_i} x_i y_i p_i(1 - p_i) \] and \[ \text{CovShare}^Y = 2 \sum_i \frac{y_i}{x_i + y_i} x_i y_i p_i(1 - p_i) \] Note that \[ \text{CovShare}^X + \text{CovShare}^Y = 2 \mathrm{Cov}(X, Y) \] so the overall covariance contribution is preserved.

Calculate the covariance share for each account:

cov.share = mango.example %>% mutate(covshare_X = loss_X / (loss_X + loss_Y) * loss_X * loss_Y * probability * (1 - probability), covshare_Y = loss_Y / (loss_X + loss_Y) * loss_X * loss_Y * probability * (1 - probability))
covshare.X = 2 * sum(cov.share$covshare_X)
covshare.Y = 2 * sum(cov.share$covshare_Y)

During buildup, the risk loads are as follows:

cs.bu.X.1 = lambda * variance.X
cs.bu.Y.2 = lambda * (variance.Y + covshare.Y)
print(paste0("The buildup risk load for X is ", cs.bu.X.1, " and the risk load for Y is ", cs.bu.Y.2, ", for a total risk load of ", cs.bu.X.1 + cs.bu.Y.2))
## [1] "The buildup risk load for X is 1366.68259945273 and the risk load for Y is 66.2208961212957, for a total risk load of 1432.90349557402"

The renewal risk loads are:

cs.rn.X = lambda * (variance.X + covshare.X)
cs.rn.Y = lambda * (variance.Y + covshare.Y)
print(paste0("The renewal risk load for X is ", cs.rn.X, " and the renewal risk load for Y is ", cs.rn.Y, " for a total risk load of ", cs.rn.X + cs.rn.Y))
## [1] "The renewal risk load for X is 1528.87432917962 and the renewal risk load for Y is 66.2208961212957 for a total risk load of 1595.09522530092"
print(paste0("The deferred risk load for account X is ", cs.rn.X - cs.bu.X.1))
## [1] "The deferred risk load for account X is 162.191729726892"

The covariance share method has properties similar to the Shapley method: it is renewal additive, but at buildup it understates the total risk load since the share of covariance for accounts written earlier is not known at the time they are written.