diff --git a/.Rbuildignore b/.Rbuildignore index 0a40bd4bbcd84e4841a42d1fd9e8317fdd611b59..75d241a094abdeaa98d4b239ebaaf524d8997613 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -5,5 +5,6 @@ ^R/Companies ^Auswertungen ^BEISPIEL_Contract.xlsx - +^Examples +.Rbuildignore$ diff --git a/DESCRIPTION b/DESCRIPTION index 377bba09ba67620ed718078dad924b558329a26d..bbd7461fcb997c0ffcac5069e11083fa38761c55 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: LifeInsuranceContracts Type: Package -Version: 0.0.4 -Date: 2022-09-10 +Version: 0.0.5 +Date: 2023-10-18 Title: Framework for Traditional Life Insurance Contracts Description: R6 classes to model traditional life insurance contracts like annuities, whole life insurances or endowments. Such life @@ -53,7 +53,8 @@ Suggests: knitr, magrittr, tibble, - testthat + testthat, + fs VignetteBuilder: knitr Roxygen: list(markdown = TRUE) URL: https://gitlab.open-tools.net/R/r-life-insurance-contracts diff --git a/R/HelperFunctions.R b/R/HelperFunctions.R index 8c84b0eac893491fb146456121248cddaefa3821..9184fbaa187b13df519bebf371d47a3699ddc4f5 100644 --- a/R/HelperFunctions.R +++ b/R/HelperFunctions.R @@ -295,19 +295,34 @@ mergeValues3D = function(starting, ending, t) { } } -# PVfactory (R6Class for present values with arbitrary dimensions) #### +#' PVfactory (R6Class for present values with arbitrary dimensions) +#' +#' provides functions to calculate present values for survival, death, dread +#' disease, invalidity and guaranteed benefits in various dimensions +#' @param qx the vector of mortality / death probabilities +#' @param m the number of yearly payments +#' @param mCorrection the list of alpha/beta to correct present values for multiple payments per year +#' @param v the discount factor (1 / (1+i)) +#' @param advance the payment cashflows in advance +#' @param arrears the payment cashflows in arrears +#' @param start the time index, where the PV calculation should be based +#' @param benefits the vector of death / invalidity / disease benefits +#' @param ... other parameters (currently not used, for future use) #' @export +#' PVfactory = R6Class( "PVfactory", ######################### PUBLIC METHODS ################################# # public = list( + #' @description Initialize the present value factory with defaults for qx, interest and multiple payments per year initialize = function(qx, m = 1, mCorrection = list(alpha = 1, beta = 0), v = 1) { private$qx = qx; private$m = m; private$mCorrection = mCorrection; private$v = v; }, + #' @description Present values of guaranteed benefits (paid in advance or arrears, possible multiple times per year) guaranteed = function(advance = NULL, arrears = NULL, start = 0, ..., m = private$m, mCorrection = private$mCorrection, v = private$v) { # General Note: Since the CF vectors can have an arbitrary number of # dimensions, we cannot directly access them via advance[1,..]. Rather, @@ -373,6 +388,7 @@ PVfactory = R6Class( VL(Qres, list(1:l)) }, + #' @description Present values of survival benefits (paid in advance or arrears, possible multiple times per year) survival = function(advance = NULL, arrears = NULL, start = 0, ..., m = private$m, mCorrection = private$mCorrection, v = private$v) { # General Note: Since the CF vectors can have an arbitrary number of # dimensions, we cannot directly access them via advance[1,..]. Rather, @@ -439,6 +455,7 @@ PVfactory = R6Class( VL(Qres, list(1:l)) }, + #' @description Present values of death benefits death = function(benefits, start = 0, ..., v = private$v) { # General Note: Since the CF vectors can have an arbitrary number of # dimensions, we cannot directly access them via advance[1,..]. Rather, @@ -497,6 +514,7 @@ PVfactory = R6Class( } VL(Qres, list(1:l)) }, + #' @description Present values of disease benefits disease = function(benefits, start = 0, ..., v = private$v) { # General Note: Since the CF vectors can have an arbitrary number of # dimensions, we cannot directly access them via advance[1,..]. Rather, @@ -560,6 +578,7 @@ PVfactory = R6Class( # payments (present value conditional on active, but payments only when # dead => need to write the Thiele difference equations as a pair of # recursive equations rather than a single recursive formula...) + #' @description Present values of guaranteed benefits after death (paid in advance or arrears, possible multiple times per year) afterDeath = function(advance = NULL, arrears = NULL, start = 0, ..., m = private$m, mCorrection = private$mCorrection, v = private$v) { # General Note: Since the CF vectors can have an arbitrary number of # dimensions, we cannot directly access them via advance[1,..]. Rather, diff --git a/R/InsuranceContract.R b/R/InsuranceContract.R index 33e1e650065a5b3a51ec80cb65b868d9c48ea7ac..65680abc9b86bb45c821a72d9c7cd58372c0b3c8 100644 --- a/R/InsuranceContract.R +++ b/R/InsuranceContract.R @@ -1156,6 +1156,21 @@ InsuranceContract = R6Class( #### # self$Parameters$Costs = private$evaluateCosts() + + ##### # + # SUM INSURED: By default, sum insured is required and the premium + # is calculated from the SI. However, it is also possible (not yet + # fully implemented) to prescribe the premium and/or initial capital + # to determine the SI. If neither is given, a warning should be printed + # and a default of sumInsured=10000 should be applied + if (is.null(self$Parameters$ContractData$sumInsured) && + is.null(self$Parameters$ContractData$premium) && + (is.null(self$Parameters$ContractData$initialCapital) || + self$Parameters$ContractData$initialCapital == 0)) { + self$Parameters$ContractData$sumInsured = 10000 + warning("InsuranceContract: Neither sumInsured nor premium nor initialCapital given! => Assuming default sum insured 10.000. Tariff: ", self$tarif$name) + } + invisible(self) }, diff --git a/R/InsuranceTarif.R b/R/InsuranceTarif.R index 65858741d44c18ab98737fc47d7be9620031ed10..aa5de30957e4a36e57cf3c4fd38c3aa86b3da33e 100644 --- a/R/InsuranceTarif.R +++ b/R/InsuranceTarif.R @@ -944,7 +944,11 @@ InsuranceTarif = R6Class( ); coeff[["Premium"]][["benefits"]][["premiums"]] = 1; - coeff[["SumInsured"]][["benefits"]][["additional_capital"]] = -1 / params$ContractData$sumInsured; + if (!is.null(params$ContractData$sumInsured)) { + coeff[["SumInsured"]][["benefits"]][["additional_capital"]] = -1 / params$ContractData$sumInsured; + } else { + coeff[["SumInsured"]][["benefits"]][["additional_capital"]] = 0; + } # Costs proportional to NetPremium introduce a non-linearity, as the NP is not available when the gross premium is calculated # => the corresponding costs PV is included in the coefficient! @@ -980,8 +984,11 @@ InsuranceTarif = R6Class( # TODO: How to handle beta costs proportional to Sum Insured coeff[["Premium"]] [["costs"]][affected, "SumPremiums", ] = -values$unitPremiumSum; coeff[["Premium"]] [["costs"]][affected, "GrossPremium",] = -1; - coeff[["SumInsured"]][["costs"]][affected, "Constant", ] = 1 / params$ContractData$sumInsured; - + if (!is.null(params$ContractData$sumInsured)) { + coeff[["SumInsured"]][["costs"]][affected, "Constant", ] = 1 / params$ContractData$sumInsured; + } else { + coeff[["SumInsured"]][["costs"]][affected, "Constant", ] = 0; + } } else if (type == "Zillmer") { # TODO: Include costs with basis NetPremium and fixed costs! affected = c("Zillmer") diff --git a/R/ProfitParticipation.R b/R/ProfitParticipation.R index 2c4d53014281e64fa9e33c1480a70a5b9b2057fb..1bf0964668009a59f0244ef293270d9180864cc6 100644 --- a/R/ProfitParticipation.R +++ b/R/ProfitParticipation.R @@ -16,7 +16,7 @@ NULL #' @export filterProfitRates = function(rates, classes) { rates %>% - dplyr::filter(profitClass %in% classes) + dplyr::filter(.data$profitClass %in% classes) } diff --git a/R/exportInsuranceContract_xlsx.R b/R/exportInsuranceContract_xlsx.R index 47a54d576d98860fc212cf11d9f6d365b890461a..19c49f2dc01191b3b91a921fc1456d3528a03d40 100644 --- a/R/exportInsuranceContract_xlsx.R +++ b/R/exportInsuranceContract_xlsx.R @@ -283,10 +283,10 @@ getContractBlockPremiums = function(contract) { #' @param costValues Cost definition data structure costValuesAsDF = function(costValues) { as.data.frame.table(costValues, responseName = "Value", stringsAsFactors = TRUE) %>% - mutate(Var4 = recode(Var4, "Erl." = "")) %>% - arrange(Var4, Var2, Var3, Var1) %>% + mutate(Var4 = recode(.data$Var4, "Erl." = "")) %>% + arrange(.data$Var4, .data$Var2, .data$Var3, .data$Var1) %>% unite("costtype", "Var2", "Var3", "Var4", sep = " ") %>% - pivot_wider(names_from = costtype, values_from = Value) %>% + pivot_wider(names_from = .data$costtype, values_from = .data$Value) %>% mutate(Var1 = NULL) } @@ -350,10 +350,10 @@ exportContractDataTable = function(wb, sheet, contract, ccol = 1, crow = 1, styl tmp = contractValues %>% mutate(`Initial Capital` = additional_capital) %>% select( - Vertragsteil = ID, Beginn = `Start of Contract`, Tarif = Tariff, `Sum insured`, - `Initial Capital`, - `Mortality table`, i, Age, `Policy duration`, `Premium period`, - `Deferral period`, `Guaranteed payments`) + Vertragsteil = .data$ID, Beginn = .data$`Start of Contract`, Tarif = .data$Tariff, .data$`Sum insured`, + .data$`Initial Capital`, + .data$`Mortality table`, .data$i, .data$Age, .data$`Policy duration`, .data$`Premium period`, + .data$`Deferral period`, .data$`Guaranteed payments`) writeValuesTable(wb, sheet, values = setInsuranceValuesLabels(tmp), caption = "Basisdaten der Vertragsteile", crow = crow, ccol = 1, tableName = "BlocksBasicData", styles = styles) @@ -365,7 +365,7 @@ exportContractDataTable = function(wb, sheet, contract, ccol = 1, crow = 1, styl # Unit Premiums #### tmp = contractPremiums %>% - select(Vertragsteil = ID, unit.net, unit.Zillmer, unit.gross) + select(Vertragsteil = .data$ID, .data$unit.net, .data$unit.Zillmer, .data$unit.gross) writeValuesTable(wb, sheet, values = setInsuranceValuesLabels(tmp), caption = "Pr\u00e4miens\u00e4tze (auf VS 1)", crow = crow, ccol = 1, tableName = "UnitPremiums", styles = styles, valueStyle = styles$unitpremiums) @@ -373,7 +373,7 @@ exportContractDataTable = function(wb, sheet, contract, ccol = 1, crow = 1, styl # Yearly Premiums #### tmp = contractPremiums %>% - select(Vertragsteil = ID, net, Zillmer, gross, written_yearly) + select(Vertragsteil = .data$ID, .data$net, .data$Zillmer, .data$gross, .data$written_yearly) writeValuesTable(wb, sheet, values = setInsuranceValuesLabels(tmp), caption = "Jahrespr\u00e4mien", crow = crow, ccol = 1, tableName = "YearlyPremiums", styles = styles, valueStyle = styles$currency0) @@ -381,7 +381,7 @@ exportContractDataTable = function(wb, sheet, contract, ccol = 1, crow = 1, styl # Written Premiums #### tmp = contractPremiums %>% - select(Vertragsteil = ID, written, unitcost, written_beforetax, tax) + select(Vertragsteil = .data$ID, .data$written, .data$unitcost, .data$written_beforetax, .data$tax) writeValuesTable(wb, sheet, values = setInsuranceValuesLabels(tmp), caption = "Pr\u00e4mien (pro Zahlungsweise)", crow = crow, ccol = 1, tableName = "WrittenPremiums", styles = styles, valueStyle = styles$currency0) diff --git a/inst/rstudio/templates/project/LifeInsuranceContracts/XXXCOMPANYXXXRechnungGesamtbestand.R b/inst/rstudio/templates/project/LifeInsuranceContracts/XXXCOMPANYXXXRechnungGesamtbestand.R index 8414a92ad39908d89d22d2128dc8002943a70618..25b7095d3ce86c7b25cdd4bbf6b96df6871cc021 100644 --- a/inst/rstudio/templates/project/LifeInsuranceContracts/XXXCOMPANYXXXRechnungGesamtbestand.R +++ b/inst/rstudio/templates/project/LifeInsuranceContracts/XXXCOMPANYXXXRechnungGesamtbestand.R @@ -14,7 +14,7 @@ # expressed with other values than the package expects), update the # VTmodify.* functions below correspondingly. # 5. Update the column types in the readXXXCOMPANYXXXBestand(..) function. This helps -# preventing errors, as these columnt are always cast to the required type. +# 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. @@ -40,7 +40,7 @@ # # # General Overview of the calculation procedure -# 1. The contract data are read in from the filenames provided in the `files` +# 1. The contract data are read in from the file names provided in the `files` # list and stored in the data.frame called `bestandinfos.all`. # a. Each file is read using the function `readXXXCOMPANYXXXBestand`. # b. The `readXXXCOMPANYXXXBestand` function uses read_excel to read in the raw data, @@ -54,10 +54,10 @@ # 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: -# a. The portfolio data is filted with the given tariff, GV, skip, n_max arguments +# a. The portfolio data is filtered with the given tariff, GV, skip, n_max arguments # b. Only the relevant columns of the portfolio data are taken, some # sanity checks (sumInsured > 0, premiumFrequency >= 0) are applied. -# c. Grouping happend by column SliceID. This allows humtiple portfolio +# c. 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 LifeInsuranceContracts vignette). If each diff --git a/man/PVfactory.Rd b/man/PVfactory.Rd new file mode 100644 index 0000000000000000000000000000000000000000..8b0306caeb85535084ba52a71c1bfc1ebbf84a65 --- /dev/null +++ b/man/PVfactory.Rd @@ -0,0 +1,224 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/HelperFunctions.R +\name{PVfactory} +\alias{PVfactory} +\title{PVfactory (R6Class for present values with arbitrary dimensions)} +\description{ +PVfactory (R6Class for present values with arbitrary dimensions) + +PVfactory (R6Class for present values with arbitrary dimensions) +} +\details{ +provides functions to calculate present values for survival, death, dread +disease, invalidity and guaranteed benefits in various dimensions +} +\section{Methods}{ +\subsection{Public methods}{ +\itemize{ +\item \href{#method-PVfactory-new}{\code{PVfactory$new()}} +\item \href{#method-PVfactory-guaranteed}{\code{PVfactory$guaranteed()}} +\item \href{#method-PVfactory-survival}{\code{PVfactory$survival()}} +\item \href{#method-PVfactory-death}{\code{PVfactory$death()}} +\item \href{#method-PVfactory-disease}{\code{PVfactory$disease()}} +\item \href{#method-PVfactory-afterDeath}{\code{PVfactory$afterDeath()}} +\item \href{#method-PVfactory-clone}{\code{PVfactory$clone()}} +} +} +\if{html}{\out{<hr>}} +\if{html}{\out{<a id="method-PVfactory-new"></a>}} +\if{latex}{\out{\hypertarget{method-PVfactory-new}{}}} +\subsection{Method \code{new()}}{ +Initialize the present value factory with defaults for qx, interest and multiple payments per year +\subsection{Usage}{ +\if{html}{\out{<div class="r">}}\preformatted{PVfactory$new(qx, m = 1, mCorrection = list(alpha = 1, beta = 0), v = 1)}\if{html}{\out{</div>}} +} + +\subsection{Arguments}{ +\if{html}{\out{<div class="arguments">}} +\describe{ +\item{\code{qx}}{the vector of mortality / death probabilities} + +\item{\code{m}}{the number of yearly payments} + +\item{\code{mCorrection}}{the list of alpha/beta to correct present values for multiple payments per year} + +\item{\code{v}}{the discount factor (1 / (1+i))} +} +\if{html}{\out{</div>}} +} +} +\if{html}{\out{<hr>}} +\if{html}{\out{<a id="method-PVfactory-guaranteed"></a>}} +\if{latex}{\out{\hypertarget{method-PVfactory-guaranteed}{}}} +\subsection{Method \code{guaranteed()}}{ +Present values of guaranteed benefits (paid in advance or arrears, possible multiple times per year) +\subsection{Usage}{ +\if{html}{\out{<div class="r">}}\preformatted{PVfactory$guaranteed( + advance = NULL, + arrears = NULL, + start = 0, + ..., + m = private$m, + mCorrection = private$mCorrection, + v = private$v +)}\if{html}{\out{</div>}} +} + +\subsection{Arguments}{ +\if{html}{\out{<div class="arguments">}} +\describe{ +\item{\code{advance}}{the payment cashflows in advance} + +\item{\code{arrears}}{the payment cashflows in arrears} + +\item{\code{start}}{the time index, where the PV calculation should be based} + +\item{\code{...}}{other parameters (currently not used, for future use)} + +\item{\code{m}}{the number of yearly payments} + +\item{\code{mCorrection}}{the list of alpha/beta to correct present values for multiple payments per year} + +\item{\code{v}}{the discount factor (1 / (1+i))} +} +\if{html}{\out{</div>}} +} +} +\if{html}{\out{<hr>}} +\if{html}{\out{<a id="method-PVfactory-survival"></a>}} +\if{latex}{\out{\hypertarget{method-PVfactory-survival}{}}} +\subsection{Method \code{survival()}}{ +Present values of survival benefits (paid in advance or arrears, possible multiple times per year) +\subsection{Usage}{ +\if{html}{\out{<div class="r">}}\preformatted{PVfactory$survival( + advance = NULL, + arrears = NULL, + start = 0, + ..., + m = private$m, + mCorrection = private$mCorrection, + v = private$v +)}\if{html}{\out{</div>}} +} + +\subsection{Arguments}{ +\if{html}{\out{<div class="arguments">}} +\describe{ +\item{\code{advance}}{the payment cashflows in advance} + +\item{\code{arrears}}{the payment cashflows in arrears} + +\item{\code{start}}{the time index, where the PV calculation should be based} + +\item{\code{...}}{other parameters (currently not used, for future use)} + +\item{\code{m}}{the number of yearly payments} + +\item{\code{mCorrection}}{the list of alpha/beta to correct present values for multiple payments per year} + +\item{\code{v}}{the discount factor (1 / (1+i))} +} +\if{html}{\out{</div>}} +} +} +\if{html}{\out{<hr>}} +\if{html}{\out{<a id="method-PVfactory-death"></a>}} +\if{latex}{\out{\hypertarget{method-PVfactory-death}{}}} +\subsection{Method \code{death()}}{ +Present values of death benefits +\subsection{Usage}{ +\if{html}{\out{<div class="r">}}\preformatted{PVfactory$death(benefits, start = 0, ..., v = private$v)}\if{html}{\out{</div>}} +} + +\subsection{Arguments}{ +\if{html}{\out{<div class="arguments">}} +\describe{ +\item{\code{benefits}}{the vector of death / invalidity / disease benefits} + +\item{\code{start}}{the time index, where the PV calculation should be based} + +\item{\code{...}}{other parameters (currently not used, for future use)} + +\item{\code{v}}{the discount factor (1 / (1+i))} +} +\if{html}{\out{</div>}} +} +} +\if{html}{\out{<hr>}} +\if{html}{\out{<a id="method-PVfactory-disease"></a>}} +\if{latex}{\out{\hypertarget{method-PVfactory-disease}{}}} +\subsection{Method \code{disease()}}{ +Present values of disease benefits +\subsection{Usage}{ +\if{html}{\out{<div class="r">}}\preformatted{PVfactory$disease(benefits, start = 0, ..., v = private$v)}\if{html}{\out{</div>}} +} + +\subsection{Arguments}{ +\if{html}{\out{<div class="arguments">}} +\describe{ +\item{\code{benefits}}{the vector of death / invalidity / disease benefits} + +\item{\code{start}}{the time index, where the PV calculation should be based} + +\item{\code{...}}{other parameters (currently not used, for future use)} + +\item{\code{v}}{the discount factor (1 / (1+i))} +} +\if{html}{\out{</div>}} +} +} +\if{html}{\out{<hr>}} +\if{html}{\out{<a id="method-PVfactory-afterDeath"></a>}} +\if{latex}{\out{\hypertarget{method-PVfactory-afterDeath}{}}} +\subsection{Method \code{afterDeath()}}{ +Present values of guaranteed benefits after death (paid in advance or arrears, possible multiple times per year) +\subsection{Usage}{ +\if{html}{\out{<div class="r">}}\preformatted{PVfactory$afterDeath( + advance = NULL, + arrears = NULL, + start = 0, + ..., + m = private$m, + mCorrection = private$mCorrection, + v = private$v +)}\if{html}{\out{</div>}} +} + +\subsection{Arguments}{ +\if{html}{\out{<div class="arguments">}} +\describe{ +\item{\code{advance}}{the payment cashflows in advance} + +\item{\code{arrears}}{the payment cashflows in arrears} + +\item{\code{start}}{the time index, where the PV calculation should be based} + +\item{\code{...}}{other parameters (currently not used, for future use)} + +\item{\code{m}}{the number of yearly payments} + +\item{\code{mCorrection}}{the list of alpha/beta to correct present values for multiple payments per year} + +\item{\code{v}}{the discount factor (1 / (1+i))} +} +\if{html}{\out{</div>}} +} +} +\if{html}{\out{<hr>}} +\if{html}{\out{<a id="method-PVfactory-clone"></a>}} +\if{latex}{\out{\hypertarget{method-PVfactory-clone}{}}} +\subsection{Method \code{clone()}}{ +The objects of this class are cloneable with this method. +\subsection{Usage}{ +\if{html}{\out{<div class="r">}}\preformatted{PVfactory$clone(deep = FALSE)}\if{html}{\out{</div>}} +} + +\subsection{Arguments}{ +\if{html}{\out{<div class="arguments">}} +\describe{ +\item{\code{deep}}{Whether to make a deep clone.} +} +\if{html}{\out{</div>}} +} +} +} diff --git a/vignettes/creating-company-specific-implementations-as-package.Rmd b/vignettes/creating-company-specific-implementations-as-package.Rmd index 50dd350b2c20ec58fcf18513c898c4924ef5dab7..e7982e81adbf94f880b1be058468dec15639c391 100644 --- a/vignettes/creating-company-specific-implementations-as-package.Rmd +++ b/vignettes/creating-company-specific-implementations-as-package.Rmd @@ -4,7 +4,6 @@ author: - name: Reinhold Kainhofer affiliation: Open Tools email: reinhold@kainhofer.com - date: "`r Sys.Date()`" output: rmarkdown::html_vignette: @@ -14,12 +13,14 @@ output: fig_height: 5 number_sections: true vignette: > - %\VignetteIndexEntry{Using the LifeInsuranceContracts Package} - %\VignetteEngine{knitr::rmarkdown} + %\VignetteIndexEntry{Creating Company-Specific LifeInsuranceContracts 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) @@ -33,14 +34,255 @@ options(scipen=5) library(pander) ``` -The LifeInsuranceContracts 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. +The LifeInsuranceContracts 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 LifeInsuranceContracts 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 `LifeInsuranceContracts` 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: + +{width="61%"} + +{width="61%"} + +{width="61%"}](images/03_RStudio_ProjectTemplate_selectTemplate.png){width="61%"} + +The resulting RStudio project will have the following file structure: + + + +- 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 LifeInsuranceContracts documentation available + at + <https://cran.r-project.org/web/packages/LifeInsuranceContracts/vignettes/using-the-lifeinsurancecontracts-package.html> + +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 + LifeInsuranceContracts 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` -However, when working for a company (eith 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. +- `Bilanzreserve` -The LifeInsuranceContracts 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. +- `Gewinnreserve` -Once, +- `Prämienübertrag` diff --git a/vignettes/images/01_RStudio_ProjectTemplate_new.png b/vignettes/images/01_RStudio_ProjectTemplate_new.png new file mode 100644 index 0000000000000000000000000000000000000000..1d17a679958ada8fe63ecd10f2f0d3008794fcd1 Binary files /dev/null and b/vignettes/images/01_RStudio_ProjectTemplate_new.png differ diff --git a/vignettes/images/02_RStudio_ProjectTemplate_new.png b/vignettes/images/02_RStudio_ProjectTemplate_new.png new file mode 100644 index 0000000000000000000000000000000000000000..ba76420ef8ac75202e1e189b056754c41de7ed4e Binary files /dev/null and b/vignettes/images/02_RStudio_ProjectTemplate_new.png differ diff --git a/vignettes/images/03_RStudio_ProjectTemplate_selectTemplate.png b/vignettes/images/03_RStudio_ProjectTemplate_selectTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..4ce31e7ade7b082a66247a3709d8b5701980b259 Binary files /dev/null and b/vignettes/images/03_RStudio_ProjectTemplate_selectTemplate.png differ diff --git a/vignettes/images/04_RStudio_ProjectTemplate_projectSettings.png b/vignettes/images/04_RStudio_ProjectTemplate_projectSettings.png new file mode 100644 index 0000000000000000000000000000000000000000..9ac947cb71b2a56be2e0dacfba83c1647d205dd1 Binary files /dev/null and b/vignettes/images/04_RStudio_ProjectTemplate_projectSettings.png differ diff --git a/vignettes/images/05_RStudio_ProjectTemplate_fileStructure.png b/vignettes/images/05_RStudio_ProjectTemplate_fileStructure.png new file mode 100644 index 0000000000000000000000000000000000000000..96de59c51dcda3532156b5026f0b382aa11becaa Binary files /dev/null and b/vignettes/images/05_RStudio_ProjectTemplate_fileStructure.png differ