Creating Company-Specific LifeInsureR Implementations (using an RStudio Package Template)

Reinhold Kainhofer

Open Tools
reinhold@kainhofer.com

2023-10-27

## Warning: Paket 'knitr' wurde unter R Version 4.2.3 erstellt
## Warning: Paket 'kableExtra' wurde unter R Version 4.2.3 erstellt
## Warning: Paket 'dplyr' wurde unter R Version 4.2.3 erstellt
## Warning: Paket 'tibble' wurde unter R Version 4.2.3 erstellt
## Warning: Paket 'lubridate' wurde unter R Version 4.2.3 erstellt
## Warning: Paket 'pander' wurde unter R Version 4.2.3 erstellt

The LifeInsureR package provides a full-featured framework to model classical life insurance contracts (non-unit linked). This is typically sufficient to implement simple example calculations or validate a single contract or tariff by a single individual.

However, when working for a company (either from inside the company or as an external consultant), one typically wants the implementation to be nicely structured, easily available for the whole team and still have the chance to improve the implementation. This can be achieved by encapsulating the company-specific tariff implementations in an R package that provides and exports the individual products of the company.

The LifeInsureR package even provides an RStudio project template to create a new skeleton of a company-specific implementation to use as the foundation of such an implementation.

1 Creating an RStudio project from the template

The LifeInsureR package provides an RStudio project template that sets up a package for a company-specific tariff implementation. After installing the package, simply create a new RStudio project from the template:

Menu item “File -> New Project...”

Select “New Directory”

Use the “LifeInsureR Implementation” project typeName the directory and provide the company name

The resulting RStudio project will have the following file structure:

Package file structure generated by the project template

2 Implementing a full portfolio calculation

Once the individual products are implemented in the R/ directory, one typical application is to use them for batch-testing the contract administration or financial statement system.

To automate this, the template [MyCompany]RechnungGesamtbestand.R is provided in the project’s top-level directory. It’s purpose is to read in a set of contract data and calculate the corresponding reserves (e.g. to validate the official numbers in the financial statements).

2.1 Steps to implement batch-testing a whole (sub-)portfolio

  1. Implement the corresponding products in the files in the R/ subdirectory. Use the (LifeInsureR documentation)<using-the-lifeinsurer-package.html> for further details and guidance.
  2. Install the package (using the “Install” button in RStudio’s “Build” pane)
  3. Set up the mapping of the columns of the contract data source to the package’s arguments. The columns of the input data can be directly mapped to named arguments in LifeInsuranceContract$new(..) calls.
  4. If some columns need manual modifications (e.g. sex or frequencies expressed with other values than the package expects), update the VTmodify.* functions correspondingly.
  5. Update the column types in the readXXXCOMPANYXXXBestand(..) function. This helps preventing errors, as these columns are always cast to the required type.
  6. The calculate_contract(..) function might need to some adjustments / modifications, in particular when modified contracts, premiums waivers, additional tariffs / single-payment add-ons etc. are present.
  7. Depending on which columns / data are available in the company-provided contract data, the column modifications / calculations of other reserves, etc. at the end of the calculate_portfolio(…) function might need to be adjusted.
  8. Update the files and outfile variables to point to the input files (“Bestandsdaten”) and the output file name
  9. Call the calculate_portfolio function on the contract data set (potentially filtered to some subsets to prevent performance issues)

Typically, a call to calculate a portfolio and store the results in a dedicated (Excel) output file is:

results = NULL;
results = calculate_portfolio(bestandinfos.all,
    tarif = c("ProdName1", "ProdName2"), GV = c("123"), debug =TRUE)
openxlsx::write.xlsx(results, outfile("Prods-1-2"), 
    asTable = TRUE, overwrite = TRUE, sheetName = "Vergleichsrechnung")
openXL(outfile("Prods-1-2"))

2.2 General Overview of the batch-calculation procedure

  1. The contract data are read in from the filenames provided in the files list and stored in the data.frame called bestandinfos.all.

    1. Each file is read using the function readXXXCOMPANYXXXBestand.
    2. The readXXXCOMPANYXXXBestand function uses read_excel to read in the raw data, then ensures the defined columns have the proper data type.
    3. The columns are renamed according to the mapping in colMapping
    4. All contracts are sorted by Polizzennummer
    5. Additional modifications are done by the function VTmodify.general.
    6. Further custom modifications can be manually added either in readXXXCOMPANYXXXBestand or in VTmodify.general
  2. All contracts are calculated by a call to calculate_portfolio. The arguments tarif and GV can be used to restrict the calculation only to certain products and/or profit classes. Additionally, n_max can be used to calculate only the first n_max contracts. The calculate_portfolio function does its work with the following steps:

    1. The portfolio data is filted with the given tariff, GV, skip, n_max arguments
    2. Only the relevant columns of the portfolio data are taken, some sanity checks (sumInsured > 0, premiumFrequency >= 0) are applied.
    3. Grouping happens by column SliceID. This allows multiple portfolio data rows to be combined to one contract with several slices / sum increases, which are calculated as one contract (see section “10.3 Dynamic Increases” of the LifeInsureR vignette). If each slice / dynamic increase is supposed to be calculated individually and independent from the main contract / other increases, then the column mapped to the SliceID column needs to have a different value for each portfolio data row. If SliceID uses contract numbers, all dynamics, etc. belonging to the same contract number will be combined and calculated using $addDynamics
    4. Each contract (entries with distinct SliceID value) is calculated in a loop using the by_slice function, which calls the calculate_contract function for each contract.
  3. The calculate_contract function calculates one individual contract, with the individual columns of the portfolio data passed as named parameters to the function.

    1. A progress message is printed (if applicable)
    2. All slices are arranged by date, with the slice starting first assumed to be the main contract part.
    3. For the main contract, an instance of the LifeInsuranceContract (with the given tariff / product) is created and all values of the contract are automatically calculated by the package by default.
    4. All additional slices (e.g. dynamic increases) with the same SliceID are added using the $addDynamics method of the LifeInsuranceContract class. The slice start date and duration are adjusted correspondingly.
    5. The reserves are extracted from the contract and stored in the final data.frame z. If debug=TRUE, a column is added to the resulting data.frame containing the R code to reproduce with full contract.
    6. The calculate_portfolio combines the data.frames returned for each contract’s calculate_contract call into one large data frame, adds some derived columns and returns the data frame as result of the calculations.

2.3 Column Mapping

The following columns / named parameters are typically used by a LifeInsuranceTariff implementation or the concrete contract as a LifeInsuranceContract object. Most parameters are not mandatory. Additional arguments / columns are possible and will be preserved, even if they are not used by the contract.

Columns used for comparison with the calculated values: