diff --git a/Formulas_Reference/.gitignore b/Formulas_Reference/.gitignore index 57c51da76456a015a5fd12ae5e07414e86019849..5a1c25fd024551483b808dbf4e0b36a328951687 100644 --- a/Formulas_Reference/.gitignore +++ b/Formulas_Reference/.gitignore @@ -4,3 +4,4 @@ *.synctex.gz *.backup *.kilepr +Bernkopf Max diff --git a/R/HelperFunctions.R b/R/HelperFunctions.R index 7ebdffd7688e54903311449862a0d414aacacab5..be97ab16c1aaec86b19f08f2cc0d9e504c4c1074 100644 --- a/R/HelperFunctions.R +++ b/R/HelperFunctions.R @@ -56,11 +56,13 @@ calculatePVDeath = function(q, benefits, ..., v=1) { } -correctionPaymentsPerYear = function(m = 1, i = self$i, order = 0) { +correctionPaymentFrequency = function(m = 1, i = self$i, order = 0) { # 0th-order approximation alpha=1; - beta=(m-1)/(2*m); - + beta=0; + # negative orders mean that NO correction is done, e.g. because other means of + # correction are used like an explicit premium frequency loading on the premium. + if (order >=0 ) beta = beta + (m-1)/(2*m); # For higher orders, simply add one term after the other! if (order >= 1) beta = beta + (m^2-1)/(6*m^2)*i; # order 1.5 has a special term that should NOT be used for higher-order approximations! @@ -90,3 +92,10 @@ pad0 = function(v, l, value=0) { } } +valueOrFunction = function(val, ...) { + if (is.function(val)) { + val(...) + } else { + val + } +} diff --git a/R/InsuranceTarif.R b/R/InsuranceTarif.R index a2112230b56fc6f95a88ce578ab04fb41f548ae9..1ff3bfca125bbd781cf870cd69f3d0cca266eb2f 100644 --- a/R/InsuranceTarif.R +++ b/R/InsuranceTarif.R @@ -1,4 +1,4 @@ -library(R6); +library(R6) library(lifecontingencies) library(objectProperties) library(foreach) @@ -42,27 +42,39 @@ InsuranceTarif = R6Class( i = 0, # guaranteed interest rate v = 1, # discount factor tariffType = TariffTypeEnum("wholelife"), # possible values: annuity, wholelife, endowment, pureendowment - benefitPaymentsPerYearOrder = 0, + premiumFrequencyOrder = 0, + benefitFrequencyOrder = 0, widowFactor = 0, premiumRefund = 0, - premiumRefundSpread = 0, + premiumRefundLoading = 0, # Mindesttodesfallrisiko soll damit erreicht werden, z.B. 105% der einbezahlten Prämien advanceBonus = 0, sumRebate = 0, costs = list(), - paymentsPerYearSurcharge = list("1" = 0.0, "2" = 0.01, "4" = 0.015, "12" = 0.02), - surcharges = list("tax" = 0.04, "unitcosts" = 0, "security" = 0, "noMedicalExam" = 0), - - - initialize = function(name = NA, mortalityTable = NA, i = NA, type = "wholelife", ..., paymentsPerYearOrder = 0, costs) { + benefitFrequencyLoading = list("1" = 0.0, "2" = 0.01, "4" = 0.015, "12" = 0.02), # TODO: Properly implement this + premiumFrequencyLoading = list("1" = 0.0, "2" = 0.01, "4" = 0.015, "12" = 0.02), # TODO: Implement this + loadings = list( # Loadings can also be function(sumInsured, premiums) # TODO: Add other possible arguments + "tax" = 0.04, # insurance tax, factor on each premium paid + "unitcosts" = 0, # annual unit cost for each policy (Stückkosten), absolute value + "security" = 0, # Additional security loading on all benefit payments, factor on all benefits + "noMedicalExam" = 0, # Loading when no medicial exam is done, % of SumInsured + "sumRebate" = 0, # gross premium reduction for large premiums, % of SumInsured + "premiumRebate" = 0, # gross premium reduction for large premiums, % of gross premium # TODO + "advanceProfitParticipation" = 0, # Profit participation in advance, % of the premium + "ongoingAlphaGrossPremium" = 0 # Acquisition cost that increase the gross premium + ), + + + initialize = function(name = NA, mortalityTable = NA, i = NA, type = "wholelife", ..., premiumFrequencyOrder = 0, benefitFrequencyOrder = 0, costs) { if (!missing(name)) self$name = name; if (!missing(mortalityTable)) self$mortalityTable = mortalityTable; if (!missing(i)) self$i = i; if (!missing(type)) self$tariffType = type; self$costs = if (!missing(costs)) costs else initializeCosts(); - if (!missing(paymentsPerYearOrder)) self$paymentsPerYearOrder = paymentsPerYearOrder; + if (!missing(benefitFrequencyOrder)) self$benefitFrequencyOrder = benefitFrequencyOrder; + if (!missing(premiumFrequencyOrder)) self$premiumFrequencyOrder = premiumFrequencyOrder; self$v = 1/(1+self$i); @@ -174,8 +186,10 @@ InsuranceTarif = R6Class( for (i in 1:premiumPeriod) { cf[i,,] = cf[i,,] + self$costs[,,"PremiumPeriod"]; } - for (i in (premiumPeriod+1):policyPeriod) { - cf[i,,] = cf[i,,] + self$costs[,,"PremiumFree"]; + if (premiumPeriod<policyPeriod) { + for (i in (premiumPeriod+1):policyPeriod) { + cf[i,,] = cf[i,,] + self$costs[,,"PremiumFree"]; + } } for (i in 1:policyPeriod) { cf[i,,] = cf[i,,] + self$costs[,,"PolicyPeriod"]; @@ -183,18 +197,19 @@ InsuranceTarif = R6Class( cf }, - presentValueCashFlows = function(cashflows, age, ..., benefitPaymentsPerYear = 1, maxAge = getOmega(self$mortalityTable)) { + presentValueCashFlows = function(cashflows, age, ..., premiumFrequency = 1, benefitFrequency = 1, maxAge = getOmega(self$mortalityTable)) { len = length(cashflows$premiums_advance); qq = self$getTransitionProbabilities (age, ...); q = pad0(qq$q, len); ages = pad0(qq$age, len); - correctionPaymentsPerYear = correctionPaymentsPerYear(m = benefitPaymentsPerYear, i = self$i, order = self$benefitPaymentsPerYearOrder); + benefitFrequencyCorrection = correctionPaymentFrequency(m = benefitFrequency, i = self$i, order = self$benefitFrequencyOrder); + premiumFrequencyCorrection = correctionPaymentFrequency(m = premiumFrequency, i = self$i, order = self$premiumFrequencyOrder); - pv = as.matrix(data.frame( + pv = as.matrix(data.frame( # TODO: Find a better way to combine the vectors into a matrix with given row/column names! age = ages, - premiums = calculatePVSurvival (q, cashflows$premiums_advance, cashflows$premiums_arrears, v=self$v), - guaranteed = calculatePVSurvival (q*0, cashflows$guaranteed_advance, cashflows$guaranteed_arrears, m=benefitPaymentsPerYear, mCorrection=correctionPaymentsPerYear, v=self$v), - survival = calculatePVSurvival (q, cashflows$survival_advance, cashflows$survival_arrears, m=benefitPaymentsPerYear, mCorrection=correctionPaymentsPerYear, v=self$v), + premiums = calculatePVSurvival (q, cashflows$premiums_advance, cashflows$premiums_arrears, m=premiumFrequency, mCorrection=premiumFrequencyCorrection, v=self$v), + guaranteed = calculatePVSurvival (q*0, cashflows$guaranteed_advance, cashflows$guaranteed_arrears, m=benefitFrequency, mCorrection=benefitFrequencyCorrection, v=self$v), + survival = calculatePVSurvival (q, cashflows$survival_advance, cashflows$survival_arrears, m=benefitFrequency, mCorrection=benefitFrequencyCorrection, v=self$v), death_SumInsured = calculatePVDeath (q, cashflows$death_SumInsured, v=self$v), death_GrossPremium = calculatePVDeath (q, cashflows$death_GrossPremium, v=self$v), death_PremiumFree = calculatePVDeath (q, cashflows$death_PremiumFree, v=self$v), @@ -215,9 +230,9 @@ InsuranceTarif = R6Class( getPremiumCoefficients = function(type="gross", coeffBenefits, coeffCosts, ..., premiumSum = 0, - premiums = list("gross"=0,"net"=0, "Zillmer"=0)) { - securityLoading = self$surcharges$security; - refundAddon = self$premiumRefundSpread; + premiums = c("unit.gross"=0)) { + securityLoading = self$loadings$security; + refundAddon = self$premiumRefundLoading; coefficients = list( "SumInsured" = list("benefits" = coeffBenefits*0, "costs" = coeffCosts*0), @@ -233,7 +248,7 @@ InsuranceTarif = R6Class( if (type == "gross") { coefficients[["Premium"]][["benefits"]][["death_GrossPremium"]] = -(1+refundAddon) * (1+securityLoading); } else if (type=="net" || type=="Zillmer") { - coefficients[["SumInsured"]][["benefits"]][["death_GrossPremium"]] = premiums$gross * (1+refundAddon) * (1+securityLoading); + coefficients[["SumInsured"]][["benefits"]][["death_GrossPremium"]] = premiums[["unit.gross"]] * (1+refundAddon) * (1+securityLoading); } @@ -257,36 +272,56 @@ InsuranceTarif = R6Class( coefficients[["SumInsured"]][["costs"]]["beta", "SumInsured"] = 1; coefficients[["SumInsured"]][["costs"]]["gamma", "SumInsured"] = 1; - coefficients[["SumInsured"]][["costs"]]["Zillmer","SumPremiums"] = premiumSum * premiums$gross; - coefficients[["SumInsured"]][["costs"]]["beta", "SumPremiums"] = premiumSum * premiums$gross; - coefficients[["SumInsured"]][["costs"]]["gamma", "SumPremiums"] = premiumSum * premiums$gross; + coefficients[["SumInsured"]][["costs"]]["Zillmer","SumPremiums"] = premiumSum * premiums[["unit.gross"]]; + coefficients[["SumInsured"]][["costs"]]["beta", "SumPremiums"] = premiumSum * premiums[["unit.gross"]]; + coefficients[["SumInsured"]][["costs"]]["gamma", "SumPremiums"] = premiumSum * premiums[["unit.gross"]]; - coefficients[["SumInsured"]][["costs"]]["Zillmer","GrossPremium"] = premiums$gross; - coefficients[["SumInsured"]][["costs"]]["beta", "GrossPremium"] = premiums$gross; - coefficients[["SumInsured"]][["costs"]]["gamma", "GrossPremium"] = premiums$gross; + coefficients[["SumInsured"]][["costs"]]["Zillmer","GrossPremium"] = premiums[["unit.gross"]]; + coefficients[["SumInsured"]][["costs"]]["beta", "GrossPremium"] = premiums[["unit.gross"]]; + coefficients[["SumInsured"]][["costs"]]["gamma", "GrossPremium"] = premiums[["unit.gross"]]; } coefficients - } - , + }, - premiumCalculation = function(pvBenefits, pvCosts, costs=self$costs, premiumSum=0) { - premiums = c("net" = 0, "gross"= 0, "Zillmer" = 0); + premiumCalculation = function(pvBenefits, pvCosts, costs=self$costs, premiumSum=0, sumInsured=1, premiumFrequency = 1) { + premiums = c("unit.net" = 0, "unit.gross"= 0, "unit.Zillmer" = 0, "net" = 0, "gross" = 0, "Zillmer" = 0, "written" = 0); + # net, gross and Zillmer premiums are calculated from the present values using the coefficients on each present value as described in the formulas document coeff=self$getPremiumCoefficients("gross", pvBenefits["0",]*0, pvCosts["0",,]*0, premiums=premiums, premiumSum=premiumSum) enumerator = sum(coeff[["SumInsured"]][["benefits"]] * pvBenefits["0",]) + sum(coeff[["SumInsured"]][["costs"]] * pvCosts["0",,]); denominator = sum(coeff[["Premium" ]][["benefits"]] * pvBenefits["0",]) + sum(coeff[["Premium" ]][["costs"]] * pvCosts["0",,]); - premiums[["gross"]] = enumerator/denominator; + ongoingAlphaGrossPremium = self$loadings$ongoingAlphaGrossPremium; + premiums[["unit.gross"]] = enumerator/denominator * (1 + ongoingAlphaGrossPremium); + premiums[["gross"]] = premiums[["unit.gross"]] * sumInsured; coeff=self$getPremiumCoefficients("net", pvBenefits["0",]*0, pvCosts["0",,]*0, premiums=premiums, premiumSum=premiumSum) enumerator = sum(coeff[["SumInsured"]][["benefits"]] * pvBenefits["0",]) + sum(coeff[["SumInsured"]][["costs"]] * pvCosts["0",,]); denominator = sum(coeff[["Premium" ]][["benefits"]] * pvBenefits["0",]) + sum(coeff[["Premium" ]][["costs"]] * pvCosts["0",,]); - premiums[["net"]] = enumerator/denominator; premiums + premiums[["unit.net"]] = enumerator/denominator; premiums + premiums[["net"]] = premiums[["unit.net"]] * sumInsured; coeff=self$getPremiumCoefficients("Zillmer", pvBenefits["0",]*0, pvCosts["0",,]*0, premiums=premiums, premiumSum=premiumSum) enumerator = sum(coeff[["SumInsured"]][["benefits"]] * pvBenefits["0",]) + sum(coeff[["SumInsured"]][["costs"]] * pvCosts["0",,]); denominator = sum(coeff[["Premium" ]][["benefits"]] * pvBenefits["0",]) + sum(coeff[["Premium" ]][["costs"]] * pvCosts["0",,]); - premiums[["Zillmer"]] = enumerator/denominator; + premiums[["unit.Zillmer"]] = enumerator/denominator; + premiums[["Zillmer"]] = premiums[["unit.Zillmer"]] * sumInsured; + + # The written premium is the gross premium with additional loadings, rebates, unit costs and taxes + tax = valueOrFunction(self$loadings$tax, sumInsured=sumInsured, premiums=premiums); + unitCosts = valueOrFunction(self$loadings$unitcosts, sumInsured=sumInsured, premiums=premiums); + noMedicalExam = valueOrFunction(self$loadings$noMedicalExam,sumInsured=sumInsured, premiums=premiums); + sumRebate = valueOrFunction(self$loadings$sumRebate, sumInsured=sumInsured, premiums=premiums); + premiumRebate = valueOrFunction(self$loadings$premiumRebate,sumInsured=sumInsured, premiums=premiums); # TODO: How shall this be included in the premium calculation? + advanceProfitParticipation = valueOrFunction(self$loadings$advanceProfitParticipation,sumInsured=sumInsured, premiums=premiums); + + frequencyLoading = valueOrFunction(self$premiumFrequencyLoading, sumInsured=sumInsured, premiums=premiums); + + premiumBeforeTax = (premiums[["unit.gross"]] + noMedicalExam - sumRebate)*sumInsured * (1-advanceProfitParticipation) + unitCosts; + premiumBeforeTax = premiumBeforeTax * (1+frequencyLoading[[toString(premiumFrequency)]]) / premiumFrequency; + premiums[["written_beforetax"]] = premiumBeforeTax; + premiums[["tax"]] = premiumBeforeTax * tax; + premiums[["written"]] = premiumBeforeTax * (1 + tax); premiums }, @@ -298,6 +333,7 @@ InsuranceTarif = R6Class( ) ); + costs = initializeCosts(); # costs["alpha", "SumInsured",] = c(1,2,5); # costs["beta", "SumPremiums",] = c(3,2,1); @@ -327,7 +363,12 @@ cfc = TestTarif$getCashFlowsCosts(YOB = 1980, age = 30, premiumPaymentPeriod = 5 pv = TestTarif$presentValueCashFlows(cf, age = 30, YOB=1980); pv pvc = TestTarif$presentValueCashFlowsCosts(cfc, age=30, YOB=1980); pvc -premiums=TestTarif$premiumCalculation(pv, pvc, premiumSum=15); as.array(premiums)*1000 +premiums=TestTarif$premiumCalculation(pv, pvc, premiumSum=15); premiums + + + +premiums*1000 +as.array(premiums)*1000 c("net"=1, "gross"=23, "Zillmer"=44) str(as.matrix( premiums)) @@ -350,193 +391,81 @@ InsuranceContract = R6Class( "InsuranceContract", public = list( tarif = NA, + + #### Contract settings + sumInsured = 1, YOB = NA, age = NA, policyPeriod = Inf, - premiumPaymentPeriod = 1, + premiumPeriod = 1, deferral = 0, guaranteed = 0, premiumPayments = PaymentTimeEnum("in advance"), benefitPayments = PaymentTimeEnum("in advance"), - premiumPaymentsPerYear = 1, - benefitPaymentsPerYear = 1, # Only for annuities! - + premiumFrequency = 1, + benefitFrequency = 1, # Only for annuities! + #### Caching values for this contract, initialized/calculated when the object is created transitionProbabilities = NA, + + cashFlowsBasic = NA, cashFlows = NA, - # cashFlowsState = list(c(0), c(0)), - # cashFlowsTransition = list(matrix(c(0,1,0,1), 2, 2, byrow=TRUE)), - - initialize = function(tarif, age, policyPeriod = inf, ..., deferral = 0, YOB = 1975) { - if (!missing(tarif)) self$tarif = tarif; - if (!missing(age)) self$age = age; - if (!missing(YOB)) self$YOB = YOB; - if (!missing(deferral)) self$deferral = deferral; - if (!missing(policyPeriod)) self$policyPeriod = policyPeriod; + cashFlowsCosts = NA, + premiumSum = 0, + + presentValues = NA, + presentValuesCosts = NA, + + premiums = NA, + + + #### The code: + + initialize = function(tarif, age, policyPeriod, premiumPeriod = policyPeriod, sumInsured = 1, ..., deferral = 0, YOB = 1975) { + self$tarif = tarif; + self$age = age; + self$policyPeriod = policyPeriod; + self$premiumPeriod = premiumPeriod; + self$sumInsured = sumInsured; + if (!missing(deferral)) self$deferral = deferral; + if (!missing(YOB)) self$YOB = YOB; + self$determineTransitionProbabilities(); self$determineCashFlows(); + self$calculatePresentValues(); + self$calculatePremiums(); }, determineTransitionProbabilities = function() { - self$transitionProbabilities = self$tarif$getTransitionProbabilities(self$age, self$YOB); + self$transitionProbabilities = self$tarif$getTransitionProbabilities(YOB = self$YOB, age = self$age); + self$transitionProbabilities }, determineCashFlows = function() { - self$cashFlowsState = self$tarif$getCashFlowsState(age = self$age, YOB = self$YOB, policyPeriod = self$policyPeriod, deferral = self$deferral, maxAge = self$age + length(self$transitionProbabilities)); - self$cashFlowsTransition = self$tarif$getCashFlowsState(age = self$age, YOB = self$YOB, policyPeriod = self$policyPeriod, deferral = self$deferral, maxAge = self$age + length(self$transitionProbabilities)); - - } - + self$cashFlowsBasic = self$tarif$getBasicCashFlows(YOB = self$YOB, age = self$age, policyPeriod = self$policyPeriod, premiumPeriod = self$premiumPeriod); + self$cashFlows = self$tarif$getCashFlows(age = self$age, premiumPayments = self$premiumPayments, policyPeriod = self$policyPeriod, premiumPaymentPeriod = self$premiumPeriod, basicCashFlows = self$cashFlowsBasic); + self$premiumSum = sum(self$cashFlows$premiums_advance + self$cashFlows$premiums_arrears); + self$cashFlowsCosts = self$tarif$getCashFlowsCosts(YOB = self$YOB, age = self$age, premiumPaymentPeriod = self$premiumPeriod, policyPeriod = self$policyPeriod); + list("benefits"= self$cashFlows, "costs"=self$cashFlowCosts, "premiumSum" = self$premiumSum) + }, + calculatePresentValues = function() { + self$presentValues = self$tarif$presentValueCashFlows(self$cashFlows, age = self$age, YOB = self$YOB); + self$presentValuesCosts = self$tarif$presentValueCashFlowsCosts(self$cashFlowsCosts, age = self$age, YOB = self$YOB); + list("benefits" = self$presentValues, "costs" = self$presentValuesCosts) + }, + calculatePremiums = function() { + self$premiums = self$tarif$premiumCalculation(self$presentValues, self$presentValuesCosts, premiumSum = self$premiumSum, sumInsured = self$sumInsured); + self$premiums + }, + dummy=NA ) ); -setGeneric("setYOB", function(scale, ...) standardGeneric("setYOB")); -setMethod("setYOB", "InsuranceScale", - function (scale, ..., YOB=1975) { - scale@YOB=YOB; - scale - } -) - -setGeneric("getTransitionProbabilities", function(scale, ...) standardGeneric("getTransitionProbabilities")); -setMethod("getTransitionProbabilities", "InsuranceScale", - function (scale, ...) { - q = deathProbabilities(scale@mortalityTable, scale@YOB); - p = 1-q; - len = length(p); - df=data.frame(p, q, rep(0,len), rep(1,len), row.names=ages(scale@mortalityTable, scale@YOB)) - } -) - - -setGeneric("calculateTransitionProbabilities", function(scale, ...) standardGeneric("calculateTransitionProbabilities")); -setMethod("calculateTransitionProbabilities", "InsuranceScale", - function (scale, ...) { - scale@transitionProbabilities = getTransitionProbabilities(scale, ...); - scale - } -) - -TestTarif = InsuranceScale(name="Testtarif", YOB=1980, age=30) -#TestTarif = setYOB(TestTarif, YOB=1980) - -getTransitionProbabilities(TestTarif) - -t=AVOe2005R.unisex -t@ages -t@deathProbs -qqq -qqq["1",] - -mort=deathProbabilities(AVOe2005R.male, YOB=1977); mort - -mort=deathProbabilities(AVOe2005R.unisex, YOB=1977); mort -q=mort -p=1-mort; p -len=length(p); len -qqq=data.frame(q=t@deathProbs, row.names=t@ages); qqq - -df=data.frame("A-A"=p, "A-t"=q, "t-A"=rep(0, len), "t-t"=rep(1, len), row.names=t@ages) -df - - - -# createCostStructure=function(age=35,contractLength=inf, -# alphaVS, -# alphaBP, -# -# -# CostStructure=setClass( -# "CostStructure", -# -# ) - - -calcUnterjährigkeit = function(m=1,i=0, order=0) { - alpha=1; - beta=(m-1)/(2*m); - if (order>=1) { beta = beta + (m^2-1)/(6*m^2)*i; } - if (order == 1.5) { beta = beta + (1-m^2)/(12*m^2)*i^2; } - if (order >= 2) { beta = beta + (1-m^2)/(24*m^2)*i^2; - alpha= alpha+ (m^2-1)/(12*m^2)*i^2; } - list(alpha=alpha, beta=beta); -} - -setGeneric("createContractCashflows", function(object) standardGeneric("createContractCashflows")) - -setGeneric("calculate", function(object) standardGeneric("calculate")); -setMethod("calculate", "InsuranceContract", - function (object) { - # 0) Prepare helper values - # 0a: Unterjährigkeit - m = object@unterjährigkeit; - object@cache.uj=calcUnterjährigkeit(m=m, i=object@interest, order=object@unterjährigkeitsapproximation); - - - # 1) Generate mortality table - if (!is.null(object@contractLength) && is.finite(object@contractLength)) { - ages = (object@age):(object@contractLength); - } else { - ages = (object@age):150; - } - qx = deathProbabilities(object@mortalityTable, YOB=object@YOB)[ages+1]; - pxn = cumprod(1-qx); - object@probabilities = data.frame(ages=ages,qx=qx, pxn=pxn) - if (!is.null(object@YOB2)) { - ages2 = ages - object@age + object@age2; - qx2 = deathProbabilities(object@mortalityTable2, YOB=object@YOB2)[ages2+1]; - pxn2 = cumprod(1-qx2); - pxyn = pxn * pxn2; - object@probabilities = data.frame(object@probabilities, ages2=ages2, q2=qx2, pxn2=pxn2, pxyn=pxyn); - } - - - # 2) Properly set up the payment and cost cash flow data frames - # 3) Calculate all NPVs of the payment and the cost cash flows (recursively) - # 3a: life-long annuities for person 2 (and person 1+2), in case the death benefit is a life-long widow's annuity - # 4) Set up the coefficients of the NPVs for the premium calculation - # 5) Calculate the gross premium - # 6) Calculate the net premium and the Zillmer premium - - # 7) Calculate all reserves (benefits and costs) - - # 8) Calculate Spar- und Risikoprämie from the net premium and the reserves - - # 9) Calculate VS after Prämienfreistellung - # 9a: Calculate all reserves after Prämienfreistellung - - # 10) Calculate the Bilanz reserves - - # 11) Calculate the Rückkaufswert - # max(object@ages,na.rm=TRUE); - object - }) - - -beispielvertrag = InsuranceContract( - name="Beispielvertrag", tarif="Beispieltarif", - desc="Beispiel zum Testen des Codes", - YOB=1948, YOB2=1948+65-62, - age=65, age2=62, - # contractLength=30, - mortalityTable=AVOe2005R.unisex, mortalityTable2=AVOe2005R.unisex, - interest=0.0125, - - unterjährigkeit=12, unterjährigkeitsapproximation=1.5, - - # deathPayments=list(), - # survivalPayments=list(), - # costCashflows=data.frame(), - # cashflows=data.frame() -); -beispielvertrag=calculate(beispielvertrag) -beispielvertrag -# data.frame(x=0:121, qx=deathProbabilities(AVOe2005R.unisex, YOB=1948)) diff --git a/R/ValuationTables.R b/R/ValuationTables.R index fec78749592d004b45b4ccfa57957a93b78c222e..bd77f3eda939373edb8dc68b9ea0d8f688598d32 100644 --- a/R/ValuationTables.R +++ b/R/ValuationTables.R @@ -3,26 +3,26 @@ library(ggplot2); # (virtual) base class for valuation tables, contains only the name / ID valuationTable=setClass( - "valuationTable", - slots=list(name="character", baseYear="numeric"), - prototype=list(name="Actuarial Valuation Table", baseYear=2000), + "valuationTable", + slots=list(name="character", baseYear="numeric"), + prototype=list(name="Actuarial Valuation Table", baseYear=2000), contains="VIRTUAL" ); -# A period life table, giving death probabilities for each age, up to +# A period life table, giving death probabilities for each age, up to # maximum age omega. Optionally apply selection factors to the probabilities valuationTable.period=setClass( - "valuationTable.period", + "valuationTable.period", slots=list(ages="numeric", deathProbs="numeric"), prototype=list(ages=eval(0:120), deathProbs=rep(1,120)), contains="valuationTable" ); -# A cohort life table, obtained by age-shifting from a given base table (PODs +# A cohort life table, obtained by age-shifting from a given base table (PODs # for a base YOB) valuationTable.ageShift=setClass( - "valuationTable.ageShift", + "valuationTable.ageShift", slots=list(ageShifts="data.frame"), prototype=list(ageShifts=data.frame(YOB=c(), shifts=c())), contains="valuationTable.period" @@ -34,25 +34,25 @@ valuationTable.ageShift=setClass( # The dampingFunction can be used to modify the cumulative years (e.g. G(tau+x) instead of tau+x) # If trend2 is given, the G(tau+x) gives the weight of the first trend, 1-G(tau+x) the weight of the second trend valuationTable.trendProjection=setClass( - "valuationTable.trendProjection", + "valuationTable.trendProjection", slots=list(baseYear="numeric", trend="numeric", dampingFunction="function", trend2="numeric"), prototype=list(baseYear=1980, trend=rep(0,120), dampingFunction=identity, trend2=0), contains="valuationTable.period" ); -# A cohort life table, obtained by an improvment factor projection +# A cohort life table, obtained by an improvment factor projection # from a given base table (PODs for a given observation year). valuationTable.improvementFactors=setClass( - "valuationTable.improvementFactors", + "valuationTable.improvementFactors", slots=list(baseYear="numeric", improvement="numeric"), prototype=list(baseYear=2012, improvement=rep(0,120)), contains="valuationTable.period" ); -# A cohort life table described by actual observations (data frame of PODs +# A cohort life table described by actual observations (data frame of PODs # per year and age) valuationTable.observed=setClass( - "valuationTable.observed", + "valuationTable.observed", slots=list(data="data.frame"), prototype=list(data=data.frame()), contains="valuationTable" @@ -62,15 +62,15 @@ valuationTable.observed=setClass( # applies only to certain observation years (e.g. for the past use the observed # PODs, and project them to the future with the trend projection) valuationTable.joined=setClass( - "valuationTable.joined", + "valuationTable.joined", slots=list( - table1="valuationTable", yearRange1="numeric", + table1="valuationTable", yearRange1="numeric", table2="valuationTable", yearRange2="numeric"), contains="valuationTable" ); # A cohort life table obtained by mixing two life tables with the given weights valuationTable.mixed=setClass( - "valuationTable.mixed", + "valuationTable.mixed", slots=c(table1="valuationTable", table2="valuationTable", weight1="numeric", weight2="numeric"), prototype=list(weight1=1/2, weight2=1/2), contains="valuationTable" @@ -212,9 +212,9 @@ setGeneric("getPeriodTable", function(object, Period, ...) standardGeneric("getP setMethod("getPeriodTable","valuationTable", function (object, Period, ...) { valuationTable.period( - name = paste(object@name, ", Period ", Period), + name = paste(object@name, ", Period ", Period), baseYear = Period, - ages = ages(object), + ages = ages(object), deathProbs = periodDeathProbabilities(object, Period=Period) ) }) @@ -225,12 +225,19 @@ setMethod("getCohortTable","valuationTable", valuationTable.period( name = paste(object@name, ", YOB ", YOB), baseYear = YOB, - ages=ages(object), + ages=ages(object), deathProbs=deathProbabilities(object, YOB=YOB) ); }) +setGeneric("undampenTrend", function (object) standardGeneric("undampenTrend")); +setMethod("undampenTrend", "valuationTable.trendProjection", + function (object) { + object@dampingFunction=identity; + object + }); + makeQxDataFrame = function(..., YOB=1972, Period=NA) { data=list(...); @@ -242,7 +249,7 @@ makeQxDataFrame = function(..., YOB=1972, Period=NA) { cat("Period: ", Period,"\n"); data = lapply(data, function(t) cbind(x=t@ages, y=periodDeathProbabilities(t, Period=Period))) } - + list.names = names(data) lns <- sapply(data, nrow) data <- as.data.frame(do.call("rbind", data)) @@ -254,7 +261,7 @@ plotValuationTables = function(data, ..., title = "", legend.position=c(0.9,0.1) if (!is.data.frame(data)) { data = makeQxDataFrame(data, ...); } - + pl = ggplot(data, aes(x = x, y = y, colour = data$group)) + theme_bw() + theme( @@ -280,7 +287,7 @@ plotValuationTables = function(data, ..., title = "", legend.position=c(0.9,0.1) breaks = function (limits) seq(max(min(limits),0),max(limits),5), minor_breaks = function (limits) seq(max(round(min(limits)),0),round(max(limits)),1), #labels = scales::trans_format('log10', scales::math_format(10^.x)) - + ) + annotation_logticks(sides="lr") + xlab("Alter") + labs(colour="Sterbetafel"); diff --git a/R/ValuationTables_Austria_Annuities.R b/R/ValuationTables_Austria_Annuities.R index c4385c7af8351fd3d888ecf593e214a44177f685..a52c4f0e70028c99eba00b808f3f6879cf6d9e28 100644 --- a/R/ValuationTables_Austria_Annuities.R +++ b/R/ValuationTables_Austria_Annuities.R @@ -4,6 +4,7 @@ # rm(frame_files) # setwd(dirname(PATH)) +setwd("R") library("gdata") @@ -13,8 +14,8 @@ library("gdata") ############################################################################### rr67.data=read.xls( - "Tafeln/AVOe_R.xls", - sheet="OeVM59-61 RR67", skip=1, #row.names=1, + "Tables/AVOe_R.xls", + sheet="OeVM59-61 RR67", skip=1, #row.names=1, col.names=c("age","qx")); rr67=valuationTable.period( @@ -28,8 +29,8 @@ rr67=valuationTable.period( ############################################################################### eromf.data=read.xls( - "Tafeln/AVOe_R.xls", - sheet="EROM-F Basistafeln", skip=2, #row.names=1, + "Tables/AVOe_R.xls", + sheet="EROM-F Basistafeln", skip=2, #row.names=1, col.names=c("age", "EROM85", "EROF85", "EROMG1950", "EROFG1950","","","") ); @@ -55,8 +56,8 @@ EROF.G1950.female=valuationTable.period( ); eromf.data.av=read.xls( - "Tafeln/AVOe_R.xls", - sheet="EROM-F G AV", skip=1, row.names=1, + "Tables/AVOe_R.xls", + sheet="EROM-F G AV", skip=1, row.names=1, col.names=c("YOB", "shiftM", "shiftF") ); @@ -81,8 +82,8 @@ EROF.G1950.female.av=valuationTable.ageShift( ############################################################################### AVOe1996R.exakt.data=read.xls( - "Tafeln/AVOe_R.xls", - sheet="AVOe 1996R exakt", skip=2, #row.names=1, + "Tables/AVOe_R.xls", + sheet="AVOe 1996R exakt", skip=2, #row.names=1, col.names=c("age", "q1991M", "trendM.long", "trendM.short", "factorMG", "factorM", "", @@ -106,7 +107,7 @@ AVOe1996R.trend.switching=function(year) { AVÖ1996R.male=valuationTable.trendProjection( name="AVÖ 1996R male", ages=AVOe1996R.exakt.data$age, baseYear=1991, - deathProbs=AVOe1996R.exakt.data$q1991M*AVOe1996R.exakt.data$factorM, + deathProbs=AVOe1996R.exakt.data$q1991M*AVOe1996R.exakt.data$factorM, trend=AVOe1996R.exakt.data$trendM.long, trend2=AVOe1996R.exakt.data$trendM.short, dampingFunction=AVOe1996R.trend.switching @@ -114,7 +115,7 @@ AVÖ1996R.male=valuationTable.trendProjection( AVÖ1996R.female=valuationTable.trendProjection( name="AVÖ 1996R female", ages=AVOe1996R.exakt.data$age, baseYear=1991, - deathProbs=AVOe1996R.exakt.data$q1991F*AVOe1996R.exakt.data$factorF, + deathProbs=AVOe1996R.exakt.data$q1991F*AVOe1996R.exakt.data$factorF, trend=AVOe1996R.exakt.data$trendF.long, trend2=AVOe1996R.exakt.data$trendF.short, dampingFunction=AVOe1996R.trend.switching @@ -122,7 +123,7 @@ AVÖ1996R.female=valuationTable.trendProjection( AVÖ1996R.male.group=valuationTable.trendProjection( name="AVÖ 1996R male, group", ages=AVOe1996R.exakt.data$age, baseYear=1991, - deathProbs=AVOe1996R.exakt.data$q1991M*AVOe1996R.exakt.data$factorMG, + deathProbs=AVOe1996R.exakt.data$q1991M*AVOe1996R.exakt.data$factorMG, trend=AVOe1996R.exakt.data$trendM.long, trend2=AVOe1996R.exakt.data$trendM.short, dampingFunction=AVOe1996R.trend.switching @@ -130,7 +131,7 @@ AVÖ1996R.male.group=valuationTable.trendProjection( AVÖ1996R.female.group=valuationTable.trendProjection( name="AVÖ 1996R female, group", ages=AVOe1996R.exakt.data$age, baseYear=1991, - deathProbs=AVOe1996R.exakt.data$q1991F*AVOe1996R.exakt.data$factorFG, + deathProbs=AVOe1996R.exakt.data$q1991F*AVOe1996R.exakt.data$factorFG, trend=AVOe1996R.exakt.data$trendF.long, trend2=AVOe1996R.exakt.data$trendF.short, dampingFunction=AVOe1996R.trend.switching @@ -144,16 +145,16 @@ AVÖ1996R.female.group=valuationTable.trendProjection( ############################################################################### AVOe2005R.exakt.data=read.xls( - "Tafeln/AVOe_R.xls", - sheet="AVOe 2005R", skip=3, #row.names=1, + "Tables/AVOe_R.xls", + sheet="AVOe 2005R", skip=3, #row.names=1, header=FALSE, - col.names=c("age", - "q2001M","q2001MG", "trendM", - "q2001F", "q2001FG", "trendF", - "", - "q2001M.2Ord", "2001MG.2Ord", "trendM.2Ord", - "q2001F.2Ord", "q2001FG.2Ord", "trendF.2Ord", - "", + col.names=c("age", + "q2001M","q2001MG", "trendM", + "q2001F", "q2001FG", "trendF", + "", + "q2001M.2Ord", "2001MG.2Ord", "trendM.2Ord", + "q2001F.2Ord", "q2001FG.2Ord", "trendF.2Ord", + "", "q2001U", "q2001UG", "trendU", rep("", 10)) ); @@ -182,19 +183,28 @@ AVOe2005R.male.group =AVOe2005R_gen("AVÖ 2005R male group (exact), loaded", AVOe2005R.female.group=AVOe2005R_gen("AVÖ 2005R female group (exact), loaded", "q2001FG", "trendF"); AVOe2005R.unisex.group=AVOe2005R_gen("AVÖ 2005R unisex group (exact), loaded", "q2001UG", "trendU"); +AVOe2005R.male.nodamping = undampenTrend(AVOe2005R.male); +AVOe2005R.female.nodamping = undampenTrend(AVOe2005R.female); +AVOe2005R.unisex.nodamping = undampenTrend(AVOe2005R.unisex); +AVOe2005R.male.nodamping.unloaded = undampenTrend(AVOe2005R.male.unloaded); +AVOe2005R.female.nodamping.unloaded = undampenTrend(AVOe2005R.female.unloaded); +AVOe2005R.male.nodamping.group = undampenTrend(AVOe2005R.male.group); +AVOe2005R.female.nodamping.group = undampenTrend(AVOe2005R.female.group); +AVOe2005R.unisex.nodamping.group = undampenTrend(AVOe2005R.unisex.group); + ############################################################################### #AVÖ 2005R with age-shifting (Male, Female, unisex), 1st-order only ############################################################################### AVOe2005R.av.base=read.xls( - "Tafeln/AVOe_R.xls", + "Tables/AVOe_R.xls", sheet="AVOe 2005R AV Basistafel", skip=1, # row.names=1, col.names=c("age", "q1965M", "q1965MG", "q1965F", "q1965FG", "q1972U", "q1972UG") ); AVOe2005R.av.verschiebung=read.xls( - "Tafeln/AVOe_R.xls", + "Tables/AVOe_R.xls", sheet="AVOe 2005R AV Verschiebung",skip=2,row.names=1, col.names=c("YOB", "shiftM", "shiftMG", "shiftF", "shiftFG", "shiftU", "shiftUG") )