Modeling Level Term Insurance

Share on:

Overview

The objective of this article is to provide a tutorial for how you can implement a level term insurance plan and perform illustration of a level term insurance policy using Rgogo framework.

Product Features

A standard level term insurance plan has the following key features:

  1. Death benefit is equal to face the amount of a policy. The benefit amount stays level througout the coverage period.

  2. There is no maturity benefit if the insured is alive at the end of the coverage period.

  3. There is no cash value if the policy is surrendered during the coverage period.

The coverage period of a level term insurance plan is defined either by a fixed number of years (for example, 20-year level term) or by expiry age (for example, level term to age 65).

Creating a Simple Level Term Plan

Rgogo provides a constructor function IPlan.LT that allows you to create a level term insurance plan easily. IPlan.LT function has many aurguments, each of which corresponds to a specific product feature that you can customize.

Before you try out any of the code snippets below, make sure that you have attached Rgogo package in your current R session:

1library(Rgogo)

Coverage Period

To create a simplest level term insurance plan with a fixed overage period regardless of issue age, set the value of the argument covYears equal to the number of years of the coverage period.

For example, to create a 20-year level term plan:

1IPlan.LT(covYears = 20)

It is possible to assign to covYears a value that is not a whole number. For example, if you set the argument value to be 1.5, the coverage period will be one year and six months. Similarly, covYears = 0.25 sets the coverage period to three months. Rgogo rounds the value to the nearest whole number of months, and the shortest possible coverage period is one month (i.e. covYears = 1/12).

To create a level term plan with coverage period that expires at a specified age, set the value of covToAge equal to the expiry age.

For example, to create a a level term to age 65:

1IPlan.LT(covToAge = 65L)

Note that in the above example I explicitly set the argument value to be an integer. If a non-integer value is provided, it will be coerced to an integer using as.integer function.

What happens if you set argument values for both covYears and covToAge? It depends on the issue age of the insured.

For example, a level term is defined as follows:

1IPlan.LT(
2   covYears = 20, 
3   covToAge = 65
4)

In this case, if the issue age is 30, the coverage period will end at age 50 (20 years). If the issue age is 50, the coverage period will end at age 65 (15 years). What IPlan.LT does is to calculate the lengths of the coverage period using both approaches ("20 years" and "to age 65"), and take the shorter one as the effective coverage period.

Premium Period

Most level term insurance products require premiums payable throughout the entire coverage period. If you create a level term plan by setting the value of covYears or covToAge only (like previous examples), IPlan.LT constructor automatically sets the premium period to be the same as the coverage period. If this is the product that you would like to have, you do not need to do anything further regarding the premium period.

However, if a product has a premium period shorter than its coverage period, you can customize the premium period by setting the argument value of premYears or premToAge.

premYears allows you to set a premium period as a fixed number of years regardless of the insured's issue age. For example, to create a 20-year level term with premium payable for 15 years:

1IPlan.LT(
2    covYears = 20, 
3    premYears = 15
4)

premToAge allows you to set a plan that premium is paid up at a certain age. For example, to create a level term plan that expires at age 65 and the premium is paid up at age 60:

1IPlan.LT(
2    covToAge = 65L,
3    premToAge = 60L,
4)

Simple Illustration

Now we know how to create a simple level term insurance product. Let's see what it can do for us. I am going to use Project method to illustrate the policy values of a level term insurance coverage. Illustration projects policy values during the entire coverage period without taking into account decrements.

The definition of Project method contains three arguments:

1Project(object, cov, resultContainer = list())

In order to illustrate a level term insurance coverage, argument object must be an instance of IPlan.LT class. In other words, it shall be an object created by IPlan.LT constructor.

Argument cov is a coverage object, which must be an instance of Cov class. Cov class is a class defined in Rgogo. An object of Cov class is like a coverage record pulled from an insurance company's administration system. It contains insurance details of an issued policy. To create a coverage object, use Cov, the constructor of Cov class.

The third argument resultContainer is a list. As the name suggests, it is used as a container for storing the illustration results. If you do not provide a result container, the method will assign an empty list to the argument.

Project method returns a list that contains the output generated by Project method.

Example:

  • Plan: 10-year level term.
  • Date of issue: January 1, 2020.
  • Insured: male, issue age 30.
  • Face amount: $500,000.
  • Premium: payable monthly with monthly premium $100.

You can copy and paste the following codes and run them in your local RStudio:

 1library(Rgogo)
 2
 3# To create a 10-year level term plan
 4plan <- IPlan.LT(covYears = 10)
 5
 6# To create a coverage 
 7cov <- Cov(
 8    issDate = as.Date("2020-01-01"),    # issue date
 9    issAge = 30L,      # issue age
10    riskClass = "M",   # male.  The value is not important in this example.
11    faceAmt = 500000,  # face amount
12    premMode = 12L,    # 1L: annual; 2L: semiannual; 4L: quarterly; 12L:monthly
13    modPrem = 100      # modal premium
14)
15
16# To illustrate policy values and store the result in a list named rslt
17rslt <- Project(plan, cov, list())

Let's find out the content of the returned result container. If you are using RStudio, you can click on the variable name rslt in the Environment Pane.

Inspect result container returned by Project method.

Project method created a list Proj and stored it in the returned result container rslt. Proj is also a list, consisting of three items: Timeline, Prem and Ben.Dth.

To view the details of Proj, execute the following command in R console:

1View(data.frame(rslt$Proj))

All items in list Proj (namely Timeline, Prem, Ben.Dth) are vectors. They have the same length, which is equal to 120. The length of theses vectors depends on number of policy months in the coverage. In this example, since the plan is a 10-year level term, there are one hundred and twenty policy months during the coverage period.

  • Timeline is a character vector, representing the policy month of the coverage in the format of "yyyy-mm". In this example, the first policy month is January 2020, and the last policy month is December 2029.

  • Prem is a numeric vector. It is the projected premium stream in each policy month during the entire coverage period.

  • Ben.Dth is also a numeric vector. It is the projected death benefit in each policy month during the coverage period.

Feel free to do experiments by changing the argument values of IPlan.LT and Cov constructors to create different plans and coverages and inspect the details of rslt$Proj.

Setting Sales Compensation

We have just seen how to create a simple level term plan and perform an illustration for a coverage. Now let's add more complexity by incorporating sales compensation into the product features.

IPlan.LT class allows you to model two types of sales compensation:

  1. Commission. Commission is a percentage of premium payable to a sales agent.

  2. Override. Override is a percentage of commission or premium that is payable to the manager of a sales agent.

Commission Schedule

Commission schedule can be specified by setting the values of argument commSchd in IPlan.LT constructor.

Value of commSchd, if assigned, must be a numeric vector that represents the rate of commission in each policy year during the period in which commissions are payable. The amount of commission is then calculated as:

Commission amount = commission rate x premium amount

For example, the commission schedule of a level term plan is as follows:

Policy Year Commission Rate (% of Premium)
1 40%
2 10%
3 - 5 5%
After 5 years no commission

The R script to implement a 20-year level term with the above commission schedule is:

1IPlan.LT(
2   covYears = 20,
3   commSchd = c(0.4, 0.1, 0.05, 0.05, 0.05)
4)

If you do not supply commission schdule in the constructor, IPlan.LT assumes no commission.

Override Schedule

There are two types of override schedule that you can set in IPlan.LT constructor:

  1. Argument ovrdOnCommSchd allows you to set the schedule of override that is calculated as a percentage of commission.

  2. Argument ovrdOnPremSchd allows you to set the schedule of override that is calculated as a percentage of premium.

The value to be assigned to ovrdOnCommSchd and ovrdOnPremSchd must be a numeric vector containing the override rate in each policy year that the override is payable.

Let's continue with the previous example. Assume that, in addition to the above commission schedule, the product also pays a manager override equal to 50% of first-year commission and no override in renewal years:

1IPlan.LT(
2   covYears = 20,
3   commSchd = c(0.4, 0.1, 0.05, 0.05, 0.05),
4   ovrdOnCommSchd = 0.5
5)

Alternatively, assume that the override is 10% of premium in the first and the second policy years:

1IPlan.LT(
2   covYears = 20,
3   commSchd = c(0.4, 0.1, 0.05, 0.05, 0.05),
4   ovrdOnPremSchd = c(0.1, 0.1)
5)

Next, let's consider the following example and illustrate the policy values of a level term:

Example:

  • Plan: 10-year level term.
  • Date of issue: January 1, 2020.
  • Insured: male, issue age 30.
  • Face amount: $500,000.
  • Premium: payable monthly with monthly premium $100.
  • Commission rate is 30% in the first policy year, 20% in the second year, and 10% from the third to the fifth policy years.
  • Manager override is 50% of first-year commission.

The R scripts to implement the above example are as follows:

 1library(Rgogo)
 2
 3# To create a 10-year level term plan:
 4plan <- IPlan.LT(
 5    covYears = 10,
 6    commSchd = c(0.3, 0.2, 0.1, 0.1, 0.1),
 7    ovrdOnCommSchd = 0.5
 8)
 9
10# To create a coverage:
11cov <- Cov(
12    issDate = as.Date("2020-01-01"),    # issue date
13    issAge = 30L,      # issue age
14    riskClass = "M",   # male
15    faceAmt = 500000,  # face amount
16    premMode = 12L,    # 1L: annual; 2L: semiannual; 4L: quarterly; 12L:monthly
17    modPrem = 100      # modal premium
18)
19
20# To illustrate policy values and store the result in a list named rslt:
21rslt <- Project(plan, cov, list())
22
23# To view illustration result:
24View(data.frame(rslt$Proj))

This time, there are two new numeric vectors added to the project result rslt$Proj in addition to Timeline, Prem and Ben.Dth:

  • Comm contains the amount of commission in each policy month.

  • Comm.Ovrd contains the amount of override in each policy month.

Calculating Premium

When we projected policy values in previous examples, we assumed that a coverage object contained modal premium information. This is usually the case when a modeler performs valuation of policy liabilities of in-force policies in which the premium information comes from a policy administration system. However, it is possible that a coverage does not have premium information. Instead, an actuarial model needs to calculate the premium of a coverage based on a plan's premium rate table and an insured's risk profile and face amount of a coverage. In this case, the plan object must contain logic for determining coverage premiums.

In IPlan.LT class, there are four properties related to premiums: premium table, modal factors, policy fee and premium tax rate. You can set these properties by setting appropriate arguments of IPlan.LT constructor.

Premium Table

A premium table in Rgogo framework is an object that extends (or inherits) ITable virtual class. I will explain the detalis of table objects in a separate post. For now, let's focus on how to set the premium table of a plan.

In IPlan.LT constructor, you can set the premium table of a level term plan using argument premTable. The value assigned to premTable must be a string scalar or a named vector of string. The string assigned to premTable represents the name of the premium table object.

To illustrate this, let's assume a 10-year level term product as follows:

Example: 10-Year Level Term

  • Issue ages: the minimum issue age is 20, and the maximum issue age is 50.
  • Premium rates: the same premium rates apply to both male and female.
  • Commission rates are 30% in the first policy year, 20% in the second year, and 10% from the third to the fifth policy years.
  • Manager override is 50% of first-year commission.

In addition, we want to illustrate the policy values for the following coverage:

  • Date of issue: January 1, 2020.
  • Insured: male, issue age 30.
  • Face amount: $500,000.
  • Premium mode: monthly.

The codes to implement the above plan is as follows:

 1library(Rgogo)
 2
 3# To create a 10-year level term plan object.
 4plan <- IPlan.LT(
 5    covYears = 10,
 6    premTable = "Prem.T10",
 7    commSchd = c(0.3, 0.2, 0.1, 0.1, 0.1),
 8    ovrdOnCommSchd = 0.5
 9)
10
11# To create an annual premium rate table object:
12Prem.T10 <- Table.IA(
13   minAge = 20L,
14   maxAge = 50L,
15   tValue = c(
16      0.92, 0.93, 0.94, 0.96, 0.99, 1.03, 1.07, 1.11, 1.17, 1.23, 1.29, 
17      1.37, 1.45, 1.55, 1.65, 1.77, 1.91, 2.06, 2.23, 2.42, 2.62, 
18      2.85, 3.1, 3.39, 3.69, 4.03, 4.4, 4.81, 5.26, 5.76, 6.3
19   ),
20   tBase = 1000
21)
22
23# To create a coverage:
24cov <- Cov(
25    issDate = as.Date("2020-01-01"),    
26    issAge = 30L,      
27    riskClass = "M",   
28    faceAmt = 500000,  
29    premMode = 12L      
30)
31
32# To illustrate policy values and store the result in a list named rslt:
33rslt <- Project(plan, cov, list())
34
35# To view illustration result:
36View(data.frame(rslt$Proj))
37

In the above example, we created a 10-year level term plan object. The name of its premium table is Prem.T10. In order to make the object working, we also need to create an actual premium table object with this name. To do so, I utilized a constructor function Table.IA provided by Rgogo framework. A brief explanation is as follows:

  • Table.IA function above is a constructor to create an object of Table.IA class. Table.IA class implements an issue age table ("IA" stands for issue age).
  • minAge and maxAge arguments set the minimum and the maximum issue ages of the rates that are available in the table.
  • tValue argument is used to set the actual rates.
  • tBase argument value is set to 1000, which means that the table contains rates per 1000 face amount.

The coverage object does not contain modal premium information. Therefore, the premium amount is calculated and projected using the premium rate table. What the level term plan object has done is to use the insured's issue age to look up the premium table and calculate the annual premium for the sum insured. However, since the premium mode of the coverage is monthly, the annual premium is divided by 12 in the projection to arrive at the monthly premium.

You can try different issue ages, premium modes and face amounts to see how the projected premium changes. Also note that if you do supply modal premium information in the coverage object (by setting argument modPrem in Cov constructor), the premium calculation logic will be automatically ignored. Instead, the modal premium set in the coverage object will be used in the projection.

Premium Table with Multiple Risk Classes

A product usually has different premium rate tables for different risk classes. For example, a product can have one set of premium rates for male and another set of rates for female. It is also common for a product to have different premium tables for genders, smoking statuses and even face amount bands. This can be easily set up by assigning a named vector of string to premTable argument. In the named vector, the name of each element represents a risk class, and the value of the element is the name of the premium table of the corresponding risk class.

For example, the following 10-year level term plan has two premium tables: Prem.T10.M contains premium rates for male, and Prem.T10.F contains premium rates for female:

 1plan <- IPlan.LT(
 2    covYears = 10,
 3    premTable = c(
 4       M = "Prem.T10.M",
 5       F = "Prem.T10.F"
 6    ),
 7    commSchd = c(0.3, 0.3, 0.1, 0.1, 0.1),
 8    ovrdOnCommSchd = 0.5
 9)
10
11# You also need to create two premium table objects to make the above working.

Below is a more complicated case which involves four risk classes, namely male non-smoker, male smoker, female non-smoker and female smoker:

 1plan <- IPlan.LT(
 2   covYears = 10,
 3   premTable = c(
 4      MN = "Prem.T10.MN", 
 5      MS = "Prem.T10.MS",
 6      FN = "Prem.T10.FN",
 7      FS = "Prem.T10.FS"
 8   ),
 9    commSchd = c(0.3, 0.3, 0.1, 0.1, 0.1),
10    ovrdOnCommSchd = 0.5
11)
12
13# You also need to create four premium table objects to make the above working.
14

You can have as many risk classes as you want.

So far, we have assumed that a level term premium table contains "annual" premium rates. If a coverage has a premium mode other than annual, the projection converts annual premium to modal premium by applying the following conversion rules:

  • Semi-annual premium = one half of annual premium
  • Quarterly premium = one fourth of annual premium
  • Monthly premium = one twelfth annual premium

Alternatively, we can say that the default modal factors are 0.5 for semi-annual, 0.25 for quarterly and 1/12 for monthly.

Insurance companies apply modal factors to adjust for the difference in interest and risk exposure due to different premium modes. Therefore, the actual modal factors used are likely different from the above. To set modal factors for a product, you can assign values to argument modFactor of a plan constructor.

For example, the above 10-year level term plan has modal factors 1 for annual, 0.51 for semi-annual, 0.26 for quarterly and 0.09 for monthly:

 1plan <- IPlan.LT(
 2    covYears = 10,
 3    premTable = c(
 4       M = "Prem.T10.M",
 5       F = "Prem.T10.F"
 6    ),
 7    modFactor = c("1" = 1, "2" = 0.51, "4" = 0.26, "12" = 0.09),
 8    commSchd = c(0.3, 0.3, 0.1, 0.1, 0.1),
 9    ovrdOnCommSchd = 0.5
10)
11

The value assigned to modFactor must be a named numeric vector. The name attribute represents the allowed premium modes (i.e. frequency of premium payment), and the value of an element is the modal factor applicable to the mode specified by its name.

Policy Fee

An insurance company may charge a flat fee called policy fee in addition to the coverage premium calculated from premium table. Policy fee is fixed regardless of face amount. The purpose of policy fee is usually to cover an insurance company's policy administrative expense.

Argument polFee of a plan constructor allows you to set policy fee of a plan. The value assigned to polFee must be a numeric scalar or a named numeric vector.

If you set polFee argument value to be a scalar, the plan object will apply the same modal factors specified by argument modFactor to calculate modal policy fee.

For example, in a 10-year level term plan below, the annual policy fee is $100. The modal policy fees are then $51, $26 and $9 for semi-annual, quarterly and monthly modes respectively.

 1plan <- IPlan.LT(
 2   covYears = 10,
 3   premTable = c(
 4      M = "Prem.T10.M",
 5      F = "Prem.T10.F"
 6   ),
 7   modFactor = c("1" = 1, "2" = 0.51, "4" = 0.26, "12" = 0.09),
 8   polFee = 100,
 9   commSchd = c(0.3, 0.3, 0.1, 0.1, 0.1),
10   ovrdOnCommSchd = 0.5
11)

If the modal factors specified by modFactor argument do not apply to policy fee, you can specify policy fee under each premium mode explicitly by assigning polFee argument with a named vector in which the names represent the modes (i.e. "1", "2", "4" or "12") and the element values represent the amount modal policy fees.

In the example below, the modal policy fees of the 10-year level term plan are $100, $50, $25 and $8.33 for annual, semi-annual, quarterly and monthly modes respectively.

 1plan <- IPlan.LT(
 2   covYears = 10,
 3   premTable = c(
 4      M = "Prem.T10.M",
 5      F = "Prem.T10.F"
 6   ),
 7   modFactor = c("1" = 1, "2" = 0.51, "4" = 0.26, "12" = 0.09),
 8   polFee = c("1" = 100, "2" = 50, "4" = 25, "12" = 8.33),
 9   commSchd = c(0.3, 0.3, 0.1, 0.1, 0.1),
10   ovrdOnCommSchd = 0.5
11)

Premium Tax Rate

In some countries, insurance premium is subject to premium tax. You can model premium tax as a percentage of premium by setting the value of plan constructor argument premTaxRate equal to the premium tax rate.

For example, if the above 10-year level term plan premium is subject to a 5% premium tax:

 1plan <- IPlan.LT(
 2   covYears = 10,
 3   premTable = c(
 4      M = "Prem.T10.M",
 5      F = "Prem.T10.F"
 6   ),
 7   modFactor = c("1" = 1, "2" = 0.51, "4" = 0.26, "12" = 0.09),
 8   polFee = c("1" = 100, "2" = 50, "4" = 25, "12" = 8.33),
 9   premTaxRate = 0.05,
10   commSchd = c(0.3, 0.3, 0.1, 0.1, 0.1),
11   ovrdOnCommSchd = 0.5
12)

Setting premium tax rate does not change the premium calculation logic. In other words, the calculated premium amount of a coverage will be the same regardless whether you set the premium tax rate or not. Instead, premium tax is treated like an expense of an insurance company, and thus causes a cash outflow.

Setting Other Product Features

IPlan.LT constructor also provides additional arguments that allow you to set other product properties. They include the following:

Reinsruance

Argument rein allows you to set the reinsurance arrangement of a product if it is reinsured. I will have a separate post to show how you can create a reinsurance treaty and set the reinsurance arrangement for a product.

Product Description

Argument descrip allows you to add additional information regarding to product. descrip argument accepts any string value. There is no rule as to what you should provide for product description. I find it useful to set it to be the name of the product or any descriptive information that helps other users of the model to understand what the product really is.

Product Identifier

Argument id allows you to set the identifier of a product object. We have not set the identifier of a product object in the examples so far. However, it will be important when we create a complext model. I will explain this in a separate post.

Summary

We use IPlan.LT constructor to create a level term plan object. I summarize this post by list the full usage of the constructor:

 1IPlan.LT(
 2   covYears = NA, 
 3   covToAge = NA, 
 4   premYears = NA, 
 5   premToAge = NA,
 6   premTable = character(0L), 
 7   modFactor = c("1" = 1, "2" = 0.5, "4" = 0.25, "12" = 1/12),
 8   polFee = numeric(0), 
 9   premTaxRate = numeric(0L),
10   commSchd = numeric(0L), 
11   ovrdOnPremSchd = numeric(0L), 
12   ovrdOnCommSchd = numeric(0L),
13   rein = character(0L), 
14   descrip = character(0L)
15   id = character(0L), 
16)

The arguments of the above consturctor represent product features that you can customize when creating a level term plan object:

Argument Description
covYears Number of years of coverage period.
covToAge Coverage expiry age
premYears Number of years of premium period.
premToAge Final attained age that the premium is payable.
premTable Premium table.
modFactor Modal factors.
polFee Policy fee.
premTaxRate Premium tax rate.
commSchd Schedule of commission rates.
ovrdOnCommSchd Schedule of override rates based on commission.
ovrdOnPremSchd Schedule of override rates based on premium.
rein Reinsurance.
descrip Description of plan.
id Plan object identifier.

I conclude this tutorial by providing another full sample codes for the implementation of a level term plan with the following features. You can simply copy the codes to RStudio and run them. Feel free to change any of the plan or coverage settings and inspect how the changes affect the illustration result.

 1library(Rgogo)
 2
 3# To create a level term to age 65:
 4plan <- IPlan.LT(
 5   covToAge = 65L,
 6   premTable = c(
 7      M = "Prem.T65.M",
 8      F = "Prem.T65.F"
 9   ),
10   modFactor = c("1" = 1, "2" = 0.5182, "4" = 0.2626, "12" = 0.0909),
11   polFee = 100,
12   premTax = 0.05,
13   commSchd = c(0.25, 0.10, rep(0.05, length.out = 6)),
14   ovrdOnComm = c(0.5, 0.5),
15   descrip = "Level Term to Age 65"
16)
17
18# To create a premium table for male rates:
19Prem.T65.M <- Table.IA(
20   minAge = 20L,
21   maxAge = 50L,
22   tValue = c(
23      1.76, 1.8, 1.84, 1.89, 1.94, 2.01, 2.08, 2.16, 2.25, 2.34, 2.43, 
24      2.54, 2.65, 2.76, 2.89, 3.02, 3.16, 3.31, 3.47, 3.64, 3.82, 
25      4.01, 4.2, 4.4, 4.6, 4.8, 5, 5.2, 5.39, 5.56, 5.72
26   ),
27   tBase = 1000
28)
29
30# To create a premium table for female rates:
31Prem.T65.F <- Table.IA(
32   minAge = 20L,
33   maxAge = 50L,
34   tValue = c(
35      1.13, 1.17, 1.22, 1.27, 1.32, 1.38, 1.45, 1.51, 1.58, 1.65, 1.73, 
36      1.8, 1.89, 1.97, 2.06, 2.16, 2.26, 2.36, 2.47, 2.57, 2.68, 
37      2.79, 2.91, 3.02, 3.14, 3.25, 3.37, 3.49, 3.6, 3.71, 3.81
38   ),
39   tBase = 1000
40)
41
42# To illustrate policy values of a coverage: male issue age 40
43cov1 <- Cov(
44    issDate = as.Date("2020-01-01"),    
45    issAge = 40L,      
46    riskClass = "M",   
47    faceAmt = 100000,  
48    premMode = 1L      
49)
50rslt1 <- Project(plan, cov1, list())
51View(data.frame(rslt1$Proj))
52
53# To inspect the illustration results:
54cov2 <- Cov(
55    issDate = as.Date("2020-01-01"),    
56    issAge = 45L,      
57    riskClass = "F",   
58    faceAmt = 200000,  
59    premMode = 4L      
60)
61rslt2 <- Project(plan, cov2, list())
62View(data.frame(rslt2$Proj))
63