From 47fd0f5e53275780bcdbe0f4af813f953f867278 Mon Sep 17 00:00:00 2001
From: Reinhold Kainhofer <reinhold@kainhofer.com>
Date: Tue, 24 Aug 2021 16:55:54 +0200
Subject: [PATCH] Add freqCharge helper function; Don't use lubridate, but seq
 to get yearly recurrences; Typo fixes

---
 R/HelperFunctions.R     | 58 ++++++++++++++++++++++++++++++++++-------
 R/InsuranceParameters.R |  2 +-
 R/InsuranceTarif.R      |  8 +++---
 3 files changed, 55 insertions(+), 13 deletions(-)

diff --git a/R/HelperFunctions.R b/R/HelperFunctions.R
index ccc0fba..b8e92c3 100644
--- a/R/HelperFunctions.R
+++ b/R/HelperFunctions.R
@@ -170,18 +170,58 @@ deathBenefit.linearDecreasing = function(len, params, values) {
 #'
 #' @export
 deathBenefit.annuityDecreasing = function(interest) {
-    function(len, params, values) {
-        protectionPeriod = params$ContractData$policyPeriod - params$ContractData$deferralPeriod;
-        vk = 1/(1 + interest);
-        if (interest == 0) {
-            sumInsured = (protectionPeriod:0) / protectionPeriod
-        } else {
-            sumInsured = (vk ^ (protectionPeriod:0) - 1) / (vk ^ protectionPeriod - 1)
-        }
-        pad0(sumInsured, l = len)
+  function(len, params, values) {
+    protectionPeriod = params$ContractData$policyPeriod - params$ContractData$deferralPeriod;
+    vk = 1/(1 + interest);
+    if (interest == 0) {
+      sumInsured = (protectionPeriod:0) / protectionPeriod
+    } else {
+      sumInsured = (vk ^ (protectionPeriod:0) - 1) / (vk ^ protectionPeriod - 1)
     }
+    pad0(sumInsured, l = len)
+  }
 }
 
+#' Defines a frequency charge (surcharge for monthly/quarterly/semiannual) premium payments #'
+#' Tariffs are typically calculated with yearly premium installments. When
+#' premiums are paid more often then one a year (in advance), the insurance
+#' receives part of the premium later (or not at all in case of death), so a
+#' surcharge for premium payment frequencies higher than yearly is applied to
+#' the  premium, typically in the form of a percentage of the premium.
+#'
+#' This function generates the internal data structure to define surcharges for
+#' monthly, quarterly and semiannual premium payments. The given surcharges can
+#' be either given as percentage points (e.g. 1.5 means 1.5% = 0.015) or as
+#' fractions of 1 (i.e. 0.015 also means 1.5% surcharge). The heuristics applied
+#' to distinguish percentage points and fractions is that all values larger than 0.1
+#' are understood as percentage points and values 0.1 and lower are understood
+#' as fractions of 1.
+#' As a consequence, a frequency charge of 10% or more MUST be given as percentage points.
+#'
+#' Currently, the frequency charges are internally represented as a named list,
+#' \code{list("1" = 0, "2" = 0.01, "4" = 0.02, "12" = 0.03)}, but that might
+#' change in the future, so it is advised to use this function rather than
+#' explicitly using the named list in your code.
+#'
+#' @param monthly Surcharge for monthly premium payments
+#' @param quarterly Surcharge for quarterly premium payments
+#' @param semiannually Surcharge for semi-annual premium payments
+#' @param yearly Surcharge for yearly premium payments (optiona, default is no surcharge)
+#'
+#' @export
+freqCharge = function(monthly = 0, quarterly = 0, semiannually = 0, yearly = 0) {
+  # Apply the heuristics to allow percentage points given
+  if (monthly > 0.1) monthly = monthly / 100;
+  if (quarterly > 0.1) quarterly = quarterly / 100;
+  if (semiannually > 0.1) semiannually = semiannually / 100;
+  if (yearly > 0.1) yearly = yearly / 100;
+
+  # internal representation for now is a named list:
+  list("1" = yearly, "2" = semiannually, "4" = quarterly, "12" = monthly)
+}
+
+
+
 mergeValues = function(starting, ending, t) {
   # if either starting or ending is missing, always use the other, irrespective of t:
   if (missing(ending) || is.null(ending)) {
diff --git a/R/InsuranceParameters.R b/R/InsuranceParameters.R
index 82f3035..804ece4 100644
--- a/R/InsuranceParameters.R
+++ b/R/InsuranceParameters.R
@@ -248,7 +248,7 @@ InsuranceContract.Values = list(
 #'               is aligned with $t=blockStart$ of the parent block.}
 #'     \item{\code{$premiumPayments}}{Whether premiums are paid in advance
 #'               (default) or arrears. Value is of type [PaymentTimeEnum]
-#'               with possible values "in advance" and 'in arrears"}
+#'               with possible values "in advance" and "in arrears"}
 #'     \item{\code{$benefitPayments}}{Whether recurring benefits (e.g. annuities)
 #'               are paid in advance (default) or arrears. Value is of type
 #'               [PaymentTimeEnum] with possible values "in advance" and
diff --git a/R/InsuranceTarif.R b/R/InsuranceTarif.R
index ba54351..6dfc930 100644
--- a/R/InsuranceTarif.R
+++ b/R/InsuranceTarif.R
@@ -1078,7 +1078,7 @@ InsuranceTarif = R6Class(
     },
 
     #' @description Calculate the (linear) interpolation factors for the balance
-    #' sheet reserve (Dec. 31) between the yearly contract clowing dates
+    #' sheet reserve (Dec. 31) between the yearly contract closing dates
     #' @details Not to be called directly, but implicitly by the [InsuranceContract] object.
     #' @param method The method for the balance sheet interpolation (30/360, act/act, act/360, act/365 or a function)
     #' @param years how many years to calculate (for some usances, the factor
@@ -1090,8 +1090,10 @@ InsuranceTarif = R6Class(
         balanceDate = balanceDate + years(1);
       }
 
-      contractDates = params$ContractData$contractClosing + years(1:years);
-      balanceDates = balanceDate + years(1:years - 1);
+      # contractDates = params$ContractData$contractClosing + years(1:years);
+      # balanceDates = balanceDate + years(1:years - 1);
+      contractDates = seq(params$ContractData$contractClosing, length.out = years, by = "year")
+      balanceDates = seq(balanceDate, length.out = years, by = "year")
 
       if (is.function(method)) {
         baf = method(params = params, contractDates = contractDates, balanceDates = balanceDates)
-- 
GitLab