From 1e0cc03162f36af9de97df975f3c92c8262f40cb Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer <reinhold@kainhofer.com> Date: Fri, 26 Nov 2021 23:47:41 +0100 Subject: [PATCH] Allow (premium|benefit)FrequencyLoading to be a function or a fixed value; set premiumFrequencyOrder to -1 if loading is given The (premium|benefit)FrequencyLoading parameters are evaluated using a new function evaluateFrequencyLoading, which handles functions, named lists, and fixed values. Also, set premiumFrequencyOrder automatically to -1 (i.e. no adjustment due to multiple payments per yer) when a loading is given. --- R/InsuranceParameters.R | 8 ++++---- R/InsuranceTarif.R | 43 +++++++++++++++++++++++++++++++---------- man/InsuranceTarif.Rd | 30 ++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 14 deletions(-) diff --git a/R/InsuranceParameters.R b/R/InsuranceParameters.R index ba76034..79a4937 100644 --- a/R/InsuranceParameters.R +++ b/R/InsuranceParameters.R @@ -548,8 +548,8 @@ InsuranceContract.ParameterDefaults = list( surrenderValueCalculation = NULL, # By default no surrender penalties premiumWaiverValueCalculation = NULL, # By default, surrender value will be used - premiumFrequencyOrder = 0, # Order of the approximation for payments within the year (unless an extra frequency loading is used => then leave this at 0) - benefitFrequencyOrder = 0 + premiumFrequencyOrder = function(params, ...) { if (is.null(params$Loadings$premiumFrequencyLoading)) 0 else -1}, # Order of the approximation for payments within the year (unless an extra frequency loading is used => then leave this at 0) + benefitFrequencyOrder = function(params, ...) { if (is.null(params$Loadings$benefitFrequencyLoading)) 0 else -1} ), Costs = initializeCosts(), minCosts = NULL, # Base costs, which cannot be waived @@ -565,8 +565,8 @@ InsuranceContract.ParameterDefaults = list( premiumRebate = 0, # gross premium reduction for large premiums, % of gross premium # TODO partnerRebate = 0, # Partner rabate on premium (including loading and other rebates) if more than one similar contract is concluded extraChargeGrossPremium = 0, # extra charges on gross premium (smoker, leisure activities, BMI too high, etc.) - benefitFrequencyLoading = list("1" = 0.0, "2" = 0.0, "4" = 0.0, "12" = 0.0), # TODO: Properly implement this as a function - premiumFrequencyLoading = list("1" = 0.0, "2" = 0.0, "4" = 0.0, "12" = 0.0), # TODO: Properly implement this as a function + benefitFrequencyLoading = NULL, # TODO: Properly implement this as a function + premiumFrequencyLoading = NULL, # TODO: Properly implement this as a function alphaRefundPeriod = 5 # How long acquisition costs should be refunded in case of surrender ), Features = list( # Special cases for the calculations diff --git a/R/InsuranceTarif.R b/R/InsuranceTarif.R index a3e5362..ec977de 100644 --- a/R/InsuranceTarif.R +++ b/R/InsuranceTarif.R @@ -665,12 +665,12 @@ InsuranceTarif = R6Class( i = params$ActuarialBases$i; v = 1/(1 + i); - benefitFreqCorr = correctionPaymentFrequency(i = i, - m = params$ContractData$benefitFrequency, - order = params$ActuarialBases$benefitFrequencyOrder); - premiumFreqCorr = correctionPaymentFrequency(i = i, - m = params$ContractData$premiumFrequency, - order = params$ActuarialBases$premiumFrequencyOrder); + benefitFreqCorr = correctionPaymentFrequency( + i = i, m = params$ContractData$benefitFrequency, + order = valueOrFunction(params$ActuarialBases$benefitFrequencyOrder, params = params, values = values)); + premiumFreqCorr = correctionPaymentFrequency( + i = i, m = params$ContractData$premiumFrequency, + order = valueOrFunction(params$ActuarialBases$premiumFrequencyOrder, params = params, values = values)); pvRefund = calculatePVDeath(px, qx, values$cashFlows$death_GrossPremium, v = v); pvRefundPast = calculatePVDeath( @@ -1011,14 +1011,14 @@ InsuranceTarif = R6Class( values$premiums[["unitcost"]] = premium.unitcosts; - frequencyLoading = valueOrFunction(loadings$premiumFrequencyLoading, params = params, values = values); + frequencyLoading = self$evaluateFrequencyLoading(loadings$premiumFrequencyLoading, params$ContractData$premiumFrequency, params = params, values = values) premiumBeforeTax = (values$premiums[["unit.gross"]]*(1 + noMedicalExam.relative + extraChargeGrossPremium) + noMedicalExam - sumRebate - extraRebate) * sumInsured * (1 - advanceProfitParticipation); if (params$Features$unitcostsInGross) { premiumBeforeTax = premiumBeforeTax + premium.unitcosts; } premiumBeforeTax = premiumBeforeTax * (1 - premiumRebate - advanceProfitParticipationUnitCosts - partnerRebate); # TODO / FIXME: Add a check that frequencyLoading has an entry for the premiumFrequency -> Otherwise do not add any loading (currently NULL is returned, basically setting all premiums to NULL) - premiumBeforeTax.y = premiumBeforeTax * (1 + frequencyLoading[[toString(params$ContractData$premiumFrequency)]]); + premiumBeforeTax.y = premiumBeforeTax * (1 + frequencyLoading); premiumBeforeTax = premiumBeforeTax.y / params$ContractData$premiumFrequency; values$premiums[["written_yearly"]] = premiumBeforeTax.y * (1 + tax) values$premiums[["written_beforetax"]] = premiumBeforeTax; @@ -1352,9 +1352,9 @@ InsuranceTarif = R6Class( afterRebates = afterProfit + rebate.premium + rebate.partner; # premium frequency loading - frequencyLoading = valueOrFunction(params$Loadings$premiumFrequencyLoading, params = params, values = values); + frequencyLoading = self$evaluateFrequencyLoading(loadings$premiumFrequencyLoading, params$ContractData$premiumFrequency, params = params, values = values) - afterFrequency = afterRebates * (1 + frequencyLoading[[toString(params$ContractData$premiumFrequency)]]); + afterFrequency = afterRebates * (1 + frequencyLoading); charge.frequency = afterFrequency - afterRebates; # insurance tax @@ -1481,6 +1481,29 @@ InsuranceTarif = R6Class( apply(values, 2, pv) }, + #' @description Calculate the premium frequency loading, i.e. the surcharge + #' on the premium for those cases where the premium is not paid yearly. + #' Return values can be either a numeric value or a named list with all + #' possible premium frequencies as keys. + #' @param loading The premiumFrequencyLoading parameter of the Contract or Tariff to be evaluated + #' @param frequency The premiumFrequency parameter of the contract + evaluateFrequencyLoading = function(loading, frequency, params, values) { + frequencyLoading = valueOrFunction(loading, frequency = frequency, params = params, values = values); + if (is.null(frequencyLoading)) { + 0 + } else if (is.list(frequencyLoading)) { + if (as.character(frequency) %in% names(frequencyLoading)) { + frequencyLoading[[as.character(frequency)]] + } else { + warning("Unable to handle premium frequency ", frequency, " with the given loading ", frequencyLoading); + } + } else if (is.numeric(frequencyLoading)) { + frequencyLoading + } else { + warning("premiumFrequencyLoading must be a number or a named list, given: ", frequencyLoading); + 0 + } + }, diff --git a/man/InsuranceTarif.Rd b/man/InsuranceTarif.Rd index 4dbdb55..07f8179 100644 --- a/man/InsuranceTarif.Rd +++ b/man/InsuranceTarif.Rd @@ -165,6 +165,7 @@ all fields.} \item \href{#method-premiumDecomposition}{\code{InsuranceTarif$premiumDecomposition()}} \item \href{#method-calculateFutureSums}{\code{InsuranceTarif$calculateFutureSums()}} \item \href{#method-calculatePresentValues}{\code{InsuranceTarif$calculatePresentValues()}} +\item \href{#method-evaluateFrequencyLoading}{\code{InsuranceTarif$evaluateFrequencyLoading()}} \item \href{#method-clone}{\code{InsuranceTarif$clone()}} } } @@ -1097,6 +1098,35 @@ scheme and the contract)} } } \if{html}{\out{<hr>}} +\if{html}{\out{<a id="method-evaluateFrequencyLoading"></a>}} +\if{latex}{\out{\hypertarget{method-evaluateFrequencyLoading}{}}} +\subsection{Method \code{evaluateFrequencyLoading()}}{ +Calculate the premium frequency loading, i.e. the surcharge +on the premium for those cases where the premium is not paid yearly. +Return values can be either a numeric value or a named list with all +possible premium frequencies as keys. +\subsection{Usage}{ +\if{html}{\out{<div class="r">}}\preformatted{InsuranceTarif$evaluateFrequencyLoading(loading, frequency, params, values)}\if{html}{\out{</div>}} +} + +\subsection{Arguments}{ +\if{html}{\out{<div class="arguments">}} +\describe{ +\item{\code{loading}}{The premiumFrequencyLoading parameter of the Contract or Tariff to be evaluated} + +\item{\code{frequency}}{The premiumFrequency parameter of the contract} + +\item{\code{params}}{Contract-specific, full set of parameters of the contract +(merged parameters of the defaults, the tariff, the profit participation +scheme and the contract)} + +\item{\code{values}}{Contract values calculated so far (in the \code{contract$Values} +list) then this method is called by the contract object} +} +\if{html}{\out{</div>}} +} +} +\if{html}{\out{<hr>}} \if{html}{\out{<a id="method-clone"></a>}} \if{latex}{\out{\hypertarget{method-clone}{}}} \subsection{Method \code{clone()}}{ -- GitLab