--- title: "Creating Company-Specific LifeInsureR Implementations (using an RStudio Package Template)" author: - name: Reinhold Kainhofer affiliation: Open Tools email: reinhold@kainhofer.com date: "`r Sys.Date()`" output: rmarkdown::html_vignette: toc: true toc_depth: 3 fig_width: 7 fig_height: 5 number_sections: true vignette: > %\VignetteIndexEntry{Creating Company-Specific LifeInsureR Implementations (using an RStudio Package Template)} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} editor_options: markdown: wrap: 72 --- ```{r setup, echo = FALSE, message=FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") library(knitr) library(kableExtra) library(LifeInsureR) library(dplyr) library(tibble) library(lubridate) library(pander) ``` 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. # 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\..."](images/01_RStudio_ProjectTemplate_new.png){width="61%"} ![Select "New Directory"](images/02_RStudio_ProjectTemplate_new.png){width="61%"} ![Use the "LifeInsureR Implementation" project type![Name the directory and provide the company name](images/04_RStudio_ProjectTemplate_projectSettings.png){width="61%"}](images/03_RStudio_ProjectTemplate_selectTemplate.png){width="61%"} The resulting RStudio project will have the following file structure: ![Package file structure generated by the project template](images/05_RStudio_ProjectTemplate_fileStructure.png) - The `DESCRIPTION` file provides the package name and its settings (author, explanation, dependencies, etc.). This is typically the first file to update. - The `NAMESPACE` file will be automatically handled by roxygen - The files in the `R/` subdirectory contain the tariff / product definitions, i.e. they implement the `LifeInsuranceTarif` objects for each product. The `*_General.R` file contains general definitions, like mortality tables, parameter / cost sets, surrender penalty functions, etc. and is typically sourced from each tariff's implementation file. - The files in the `tests/testthat/` directory are unit tests for the testthat package. Typically, you will use the example prescribed (by the regulator) in the official tariff definitions as test cases, so that the implementation will always correspond to the official documents. # 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). ## 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) 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: ```{r CalculatePortfolio,echo=TRUE,eval=FALSE} 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")) ``` ## 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.frame`s 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. ## 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. - `Polizzennummer` - `SliceID` - `balanceSheetDate` - `tarif` - `GV` - `i` - `sex` - `age` - `contractClosing` - `sliceDate` - `policyPeriod` - `premiumPeriod` - `premiumFrequency` - `annuityFrequency` - `sumInsured` Columns used for comparison with the calculated values: - `Bruttoprämie` - `Sparprämie` - `Risikoprämie` - `Kostenprämie` - `Bilanzreserve` - `Gewinnreserve` - `Prämienübertrag`