Introduction

These study notes are based on the Exam 9 syllabus reading Managing Interest Rate Risk: ALM, Franchise Value, and Strategy by William H. Panning. The paper describes how to quantify the value associated with future policy renewals, and the sensitivity of this value to changes in interest rates. This paper corresponds to learning objective B4 on the syllabus.

Franchise Value

Franchise value is the present value of cash flows associated with future policy renewals. Terminology and notation used in the paper is as follows:

The above quantities are related by the equation for the value of cash flows as of the end of the year: \[ P - L - E + y(S + P - E) = kS \] If all quantities except the premium are known, we can solve for the premium required to achieve a return-on-surplus target: \[ P = \frac{(k-y)S + L}{1+y} + E \] The example in the paper uses the following parameters:

surplus = 50
losses = 75
expenses = 25
interest.rate = 0.05
required.return = 0.15
retention = 0.90

In this case, we can calculate the required premium as follows:

required.premium = expenses + (surplus * (required.return - interest.rate) + losses) / (1 + interest.rate)
print(paste0("The required premium is ", required.premium))
## [1] "The required premium is 101.190476190476"

The current economic value of the firm, denoted by \(C\), is the value of items on its balance sheet as of the beginning of the year; it is the present value of surplus and business already written: \[ C = P - E + S - \frac{L}{1+y} = \frac{(1+k)S}{1+y} \] Note that because premium is set in order to achieve an expected return, this formula has a natural interpretation: the surplus grows by \(k\) in the course of the year, and dividing by \(1+y\) gives the present value. Continuing the example, the current economic value is:

current.economic.value = required.premium - expenses + surplus - losses / (1 + interest.rate)
print(paste0("The current economic value is ", current.economic.value))
## [1] "The current economic value is 54.7619047619048"

The franchise value is denote by \(F\), and is equal to the present value of future renewals. The combined effect of retention and discounting on future premium, expenses, and losses is given by a factor \[ d = \frac{cr}{1+y} \] The present value of future premiums is equal to \[ P \sum_{i\geq 1} d^i = \frac{dP}{1-d} \] Similarly, the present value of future expenses is \[ \frac{dE}{1-d} \] Since losses are paid at the end of the year, then they must be discounted by one additional year, giving a present value of \[ \frac{dL}{(1-d)(1+y)} \] Therefore, the franchise value is \[ F = \frac{d}{1-d}\left[P - E - \frac{L}{1+y}\right] \] Given the relationship between premium and surplus, this can also be written as \[ F = \frac{d}{1-d} \frac{(k-y)S}{1+y} \] The quantity \(d / (1-d)\) can be simplified by multiplying the numerator and denominator by \(1+y\): \[ \frac{d}{1-d} = \frac{cr}{1 + y - cr} \] This gives the following expression for franchise value: \[ F = \frac{cr(k-y)S}{(1+y-cr)(1+y)} \] Note that this is equal to the “retention factor” \(d /(1-d)\) multiplied by the present value of one year’s dollar-value return on surplus, in excess of the risk-free rate.

Continuing the example above, assuming a retention rate of 90%, the franchise value can be calculated as follows:

retention.rate = 0.9
d = retention.rate / (1 + interest.rate)
franchise.value = d / (1 - d) * (required.premium - expenses - losses / (1 + interest.rate))
print(paste0("The franchise value is ", franchise.value))
## [1] "The franchise value is 28.5714285714285"

The total economic value is the sum of the franchise value and the current economic value; this is the (theoretical) market value of the firm if it is publicly traded. In this example:

total.economic.value = franchise.value + current.economic.value
franchise.value.pct = 100 * franchise.value / total.economic.value
print(paste0("The total economic value is ", total.economic.value, " with franchise value representing ", franchise.value.pct, " of the total economic value."))
## [1] "The total economic value is 83.3333333333333 with franchise value representing 34.2857142857143 of the total economic value."

The market-to-book ratio can be calculated by dividing the total economic value by the current economic value:

market.to.book = total.economic.value / current.economic.value
print(paste0("The market-to-book ratio is ", market.to.book))
## [1] "The market-to-book ratio is 1.52173913043478"

To illustrate the sensitivity of franchise value to the input parameters, write a function to allow us to quickly calculate the franchise value and current economic value over a range of inputs.

required.premium = function(losses, expenses, interest.rate, surplus, required.return) {
  return(((required.return - interest.rate) * surplus + losses) / (1 + interest.rate) + expenses)
}
required.premium(losses, expenses, interest.rate, surplus, required.return)
## [1] 101.1905
current.economic.value = function(losses, expenses, interest.rate, surplus, required.return) {
  premium = required.premium(losses, expenses, interest.rate, surplus, required.return)
  return(premium - expenses + surplus - losses /(1 + interest.rate))
}
current.economic.value(losses, expenses, interest.rate, surplus, required.return)
## [1] 54.7619
franchise.value = function(losses, expenses, interest.rate, surplus, required.return, retention.rate) {
  premium = required.premium(losses, expenses, interest.rate, surplus, required.return)
  d = retention.rate / (1 + interest.rate)
  return((premium - expenses - losses / (1 + interest.rate)) * d / (1 - d))
}
franchise.value(losses, expenses, interest.rate, surplus, required.return, retention.rate)
## [1] 28.57143

Calculate the values for a range of interest rates between 1% and 10%, and a range of retention values between 0% and 100%, keeping all other parameters fixed:

franchise.sensitivity = merge(data.frame(interest_rate = 10:100 * 0.001), data.frame(retention_rate = 0:100 * 0.01))
franchise.sensitivity$franchise_value = apply(franchise.sensitivity, 1, function(y){franchise.value(losses, expenses, y['interest_rate'], surplus, required.return, y['retention_rate'])})
franchise.sensitivity$current_economic_value = apply(franchise.sensitivity, 1, function(y){current.economic.value(losses, expenses, y['interest_rate'], surplus, required.return)})
franchise.sensitivity = franchise.sensitivity %>% mutate(total_economic_value = current_economic_value + franchise_value, franchise_pct = franchise_value / total_economic_value, market_to_book = total_economic_value / current_economic_value)
datatable(franchise.sensitivity, filter = "top")

As in Panning’s paper, we can visualize the sensitivity of the franchise value and market-to-book ratio when the interest rate is fixed at 5%:

ggplot(data = franchise.sensitivity %>% filter(interest_rate == 0.05), aes(x = retention_rate, y = franchise_pct)) + geom_line(colour = "red") + labs(title = "Impact of Retention Rate on Franchise Value", subtitle = "Interest Rate fixed at 5%", x = "Retention Rate", y = "Franchise Value as Percentage of Total Value")

ggplot(data = franchise.sensitivity %>% filter(interest_rate == 0.05), aes(x = retention_rate, y = market_to_book)) + geom_line(colour = "red") + labs(title = "Impact of Retention Rate on Market-to-Book Ratio", subtitle = "Interest Rate fixed at 5%", x = "Retention Rate", y = "Market-to-Book Ratio")

We can also visualize the sensitivity to both parameters at the same time, using a contour plot. (Filter out some of the smaller values of franchise_pct to get a clearer gradient.)

ggplot(data = franchise.sensitivity %>% filter(franchise_pct >= 0.15), aes(x = interest_rate, y = retention_rate, fill = franchise_pct)) + geom_raster() + scale_fill_gradientn(colours = terrain.colors(10)) + geom_contour(aes(z = franchise_pct))

ggplot(data = franchise.sensitivity %>% filter(market_to_book <= 4 & market_to_book >= 1.25), aes(x = interest_rate, y = retention_rate, fill = market_to_book)) + geom_raster() + scale_fill_gradientn(colours = terrain.colors(10)) + geom_contour(aes(z = market_to_book))

Interest Rate Sensitivity

The paper analyzes interest rate sensitivity along with the additional assumption that the company will vary its future pricing based on the interest rate, specifically, that for some constants \(a\) and \(b\), the required return on surplus is \[ k = a + by \] Typical strategies include a fixed rate of return (\(b=0\)) or a risk-free rate plus a risk premium (\(b=1\)). These are behavioural parameters that should be selected based on what the firm is actually expected to do. With this assumption, the franchise value becomes \[ F = \frac{cr(a + (b-1)y) S}{(1 + y - cr)(1+y)} \]

The modified duration of franchise value is given by \[ D = -\frac{1}{F}\frac{dF}{dy} \] A simple way to calculate this derivative is to notice that, for any functions \(f\) and \(g\), \[ \frac{g}{f}\frac{d}{dx}\left(\frac{f}{g}\right) = \frac{g}{f}\left(\frac{f'}{g} - \frac{fg'}{g^2}\right) = \frac{f'}{f} - \frac{g'}{g} \] Applying to the franchise value, we have \[ D = \frac{2 + 2y - cr}{(1 + y - cr)(1+y)} - \frac{cr(b-1)S}{cr(a + (b-1)y)S} = \frac{2 + 2y - cr}{(1 + y - cr)(1+y)} - \frac{b-1}{a + (b-1)y} \] Note that this form has a convenient interpretation when the return on surplus is set as a risk free rate plus a premium (\(b=1\)), since the second term becomes zero in that case. Panning gives the equivalent formula \[ D = \frac{a - b + 1}{(1+y)(a + (b-1)y)} + \frac{1}{1+y-cr} \] The R function to implement this formula is:

franchise.duration = function(a, b, interest.rate, retention.rate) {
  duration = (a - b + 1) / ((1 + interest.rate) * (a + (b-1)*interest.rate)) + 1 / (1 + interest.rate - retention.rate)
  return(duration)
}
print(paste0("With a constant rate of return strategy, the duration of franchise value is ", franchise.duration(a = 0.15, b = 0, interest.rate, retention.rate)))
## [1] "With a constant rate of return strategy, the duration of franchise value is 17.6190476190476"

Managing Interest Rate Sensitivity

Management Through Investment Strategy

The duration of total economic value will be the present-value weighted average of the franchise value and the current economic value. The modified duration of current economic value is: \[ D_C = \frac{1+y}{(1+k)S} \frac{(1+k)S}{(1+y)^2} = \frac{1}{1+y} \] Panning assumes a fixed duration of 1 for the current economic value, which is an approximation to the above. The following function provides the flexibility to calculate both the exact value and the approximation used in the reading.

TEV.duration = function(a, b, interest.rate, retention.rate, losses, expenses, surplus, required.return, exact = TRUE) {
  CEV.duration = ifelse(exact, 1 / (1 + interest.rate), 1)
  CEV = current.economic.value(losses, expenses, interest.rate, surplus, required.return)
  FV = franchise.value(losses, expenses, interest.rate, surplus, required.return, retention.rate)
  FV.duration = franchise.duration(a, b, interest.rate, retention.rate)
  return((CEV.duration * CEV + FV.duration * FV) / (FV + CEV))
}
print(paste0("Using the approximation in the reading, the duration of total economic value is ", TEV.duration(0.15, 0, interest.rate, retention.rate, losses, expenses, surplus, required.return, exact = FALSE)))
## [1] "Using the approximation in the reading, the duration of total economic value is 6.69795918367347"
print(paste0("Using the exact value for duration of current economic value, the duration of total economic value is ", TEV.duration(0.15, 0, interest.rate, retention.rate, losses, expenses, surplus, required.return, exact = TRUE)))
## [1] "Using the exact value for duration of current economic value, the duration of total economic value is 6.66666666666666"

Panning considers the possibility that the duration of total economic value could be reduced by reducing the duration of the assets through changing the composition of its investment portfolio, or through the purchase of derivative securities. However, even if the duration of the assets can be reduced to zero, this has a limited effect. When the duration of assets is zero, only the liabilities have an impact on the duration of current economic value. Using the fact that the duration of the losses is \[ \frac{L}{(1+y)^2}\frac{1+y}{L} = \frac{1}{1+y} \] the example in the reading becomes:

CEV.duration.zero.asset = (- losses / (1 + interest.rate)) / current.economic.value(losses, expenses, interest.rate, surplus, required.return)
print(paste0("With zero-duration assets, the duration of current economic vaule is ", CEV.duration.zero.asset))
## [1] "With zero-duration assets, the duration of current economic vaule is -1.30434782608696"

Therefore, the best that can be achieved through management of asset liability is:

TEV.duration.zero.asset = (current.economic.value(losses, expenses, interest.rate, surplus, required.return) * CEV.duration.zero.asset + franchise.value(losses, expenses, interest.rate, surplus, required.return, retention.rate)  * franchise.duration(0.15, 0, interest.rate, retention.rate)) / (current.economic.value(losses, expenses, interest.rate, surplus, required.return) + franchise.value(losses, expenses, interest.rate, surplus, required.return, retention.rate))
print(paste0("With zero-duration assets, the duration of total economic value is ", TEV.duration.zero.asset))
## [1] "With zero-duration assets, the duration of total economic value is 5.18367346938775"

An additional barrier to this approach is that it lacks transparency to regulators who may only see the accounting numbers of the firm, and who may view these actions as increasing risk rather than reducing it.

Management Through Pricing Strategy

Changing the pricing strategy has a bigger impact on duration than managing the duration of assets. Calculate the duration of total economic value for a range of values of \(a\) and \(b\), keeping other parameters fixed:

duration.sensitivity.data = merge(data.frame(a = 50:200/1000),data.frame(b = 0:200 / 100))
duration.sensitivity.data$TEV_duration = apply(duration.sensitivity.data, 1, function(y){TEV.duration(a = y['a'], b = y['b'], interest.rate, retention.rate, losses, expenses, surplus, required.return, exact = FALSE)})
duration.sensitivity.data$FEV_duration = apply(duration.sensitivity.data, 1, function(y){franchise.duration(a = y['a'], b = y['b'], interest.rate, retention.rate)})
datatable(duration.sensitivity.data, filter = "top")
## Warning in instance$preRenderHook(instance): It seems your data is too
## big for client-side DataTables. You may consider server-side processing:
## http://rstudio.github.io/DT/server.html

Find the duration corresponding to the original fixed-return pricing strategy

duration.sensitivity.data %>% filter(a == 0.15 & b == 0)
##      a b TEV_duration FEV_duration
## 1 0.15 0     6.697959     17.61905

Compare to a selection for \(a\) and \(b\) which corresponds to a risk premium of 10% over the risk-free rate (corresponding to the same return as above when the interest rate is 5%):

duration.sensitivity.data %>% filter(a == 0.10 & b == 1)
##     a b TEV_duration FEV_duration
## 1 0.1 1     3.269388     7.619048

Visualize the above using a contour graph, together with a red line indicating the combinations of \(a\) and \(b\) which correspond to a rate of return of 15% when the risk-free rate is 5%. Remove combinations corresponding to extreme durations to facilitate visualization.

duration.sensitivity.data = duration.sensitivity.data %>% mutate(fixed_b = (required.return - a) / interest.rate)
ggplot(data = duration.sensitivity.data %>% filter(TEV_duration <= 10), aes(x = a, y = b, fill = TEV_duration)) + geom_raster() + scale_fill_gradientn(colours = terrain.colors(10)) + geom_contour(aes(z = TEV_duration)) + geom_line(aes(y = fixed_b), colour = "red") + ylim(0, 2)
## Warning: Removed 10050 rows containing missing values (geom_path).

One approach is to select \(a\) and \(b\) such that a target return on equity is achieved given current interest rates, and that the duration of total economic value is zero. This can be done through either constrained optimization, or a numerical search (as done below)

head(duration.sensitivity.data %>% filter(b == round((required.return - a) / interest.rate, digits = 2)) %>% arrange(TEV_duration), 20)
##        a    b TEV_duration FEV_duration fixed_b
## 1  0.050 2.00  -0.15918367  -2.38095238    2.00
## 2  0.051 1.98  -0.09061224  -2.18095238    1.98
## 3  0.052 1.96  -0.02204082  -1.98095238    1.96
## 4  0.053 1.94   0.04653061  -1.78095238    1.94
## 5  0.054 1.92   0.11510204  -1.58095238    1.92
## 6  0.055 1.90   0.18367347  -1.38095238    1.90
## 7  0.056 1.88   0.25224490  -1.18095238    1.88
## 8  0.057 1.86   0.32081633  -0.98095238    1.86
## 9  0.058 1.84   0.38938776  -0.78095238    1.84
## 10 0.059 1.82   0.45795918  -0.58095238    1.82
## 11 0.060 1.80   0.52653061  -0.38095238    1.80
## 12 0.061 1.78   0.59510204  -0.18095238    1.78
## 13 0.062 1.76   0.66367347   0.01904762    1.76
## 14 0.063 1.74   0.73224490   0.21904762    1.74
## 15 0.064 1.72   0.80081633   0.41904762    1.72
## 16 0.065 1.70   0.86938776   0.61904762    1.70
## 17 0.066 1.68   0.93795918   0.81904762    1.68
## 18 0.067 1.66   1.00653061   1.01904762    1.66
## 19 0.068 1.64   1.07510204   1.21904762    1.64
## 20 0.069 1.62   1.14367347   1.41904762    1.62

(Note that there appears to be minor error in the reading: the values selected, \(a = 0.062\) and \(b = 1.762\), appear to correspond to a franchise value duration of zero, not a total economic value duration of 0).

To illustrate the impact of the different strategies, calculate franchise value across a range of interest rates. In the following:

  • Strategy 1 is a fixed rate of return

  • Strategy 2 is a 10% premium over the risk free rate

  • Strategy 3 is the selections of \(a\) and \(b\) that set duration of franchise value to 0

duration.strategies = data.frame(strategy_name = c("Strategy 1", "Strategy 2", "Strategy 3"), a = c(0.15, 0.10, 0.062), b = c(0, 1, 1.763))
strategy.comparison = merge(duration.strategies, data.frame(interest_rate = 0:150/1000))
strategy.comparison$franchise_value = apply(strategy.comparison %>% select(interest_rate, a, b), 1, function(y) {franchise.value(losses, expenses, y['interest_rate'], surplus, y['a'] + y['b'] * y['interest_rate'], retention.rate)})
ggplot(data = strategy.comparison, aes(x = interest_rate, y = franchise_value, col = strategy_name)) + geom_line()

The above illustrates that the “risk-free plus premium” strategy has mitigated interest rate risk somewhat, but the optimal strategy has essentially eliminated interest rate risk. Note that this is likely not feasible in practice due to the assumption of a constant retention rate, and in reality the retention rate will be affected by changes in premium.