Source code for actuarialmath.udd

"""UDD - 1/mthly insurance and annuities with uniform distribution of deaths

MIT License. Copyright 2022-2023 Terence Lim
"""
import pandas as pd
from actuarialmath import Interest
from actuarialmath import Annuity
from actuarialmath import Mthly

[docs]class UDD(Mthly): """1/mthly shortcuts with UDD assumption Args: m : number of payments per year life : original fractional survival and mortality functions """ def __init__(self, m: int, life: Annuity, **kwargs): super().__init__(m=m, life=life, **kwargs) self.alpha_m = self.alpha(m=m, i=self.life.interest.i) self.beta_m = self.beta(m=m, i=self.life.interest.i)
[docs] @staticmethod def alpha(m: int, i: float) -> float: """Compute 1/mthly UDD interest rate beta function value Args: m : number of payments per year i : annual interest rate """ d = i / (1 + i) i_m = Interest.mthly(m=m, i=i) i_d = Interest.mthly(m=m, d=d) return abs(i*d / ( i_m * i_d))
[docs] @staticmethod def beta(m: int, i: float) -> float: """Compute 1/mthly UDD interest rate alpha function value Args: m : number of payments per year i : annual interest rate """ d = i / (1 + i) i_m = Interest.mthly(m=m, i=i) i_d = Interest.mthly(m=m, d=d) return abs((i - i_m) / (i_m * i_d))
[docs] def whole_life_insurance(self, x: int, s: int = 0, moment: int = 1, b: int = 1) -> float: """1/mthly UDD Whole life insurance: A_x Args: x : age of selection s : years after selection b : amount of benefit moment : compute first or second moment Examples: >>> UDD(m=0, life=SULT(udd=True)).whole_life_insurance(x) """ assert moment in [1, 2, Annuity.VARIANCE] if moment == Annuity.VARIANCE: return ((self.whole_life_insurance(x, s=s, moment=2) - self.whole_life_insurance(x, s=s)**2) * b**2) A = self.life.whole_life_insurance(x, s=s, moment=moment) i = self.life.interest.i if moment == 2: i = self.interest.double_force(i) i_m = self.life.interest.mthly(m=self.m, i=i) return A * i / i_m
[docs] def endowment_insurance(self, x: int, s: int = 0, t: int = 1, b: int = 1, endowment: int = -1, moment: int = 1) -> float: """1/mthly UDD Endowment insurance = term insurance + pure endowment Args: x : year of selection s : years after selection t : term of insurance in years b : amount of benefit endowment : amount of endowment moment : return first or second moment """ assert moment in [1, 2, Annuity.VARIANCE] if moment == Annuity.VARIANCE: return (self.endowment_insurance(x, s=s, t=t, endowment=endowment, b=b, moment=2) - self.endowment_insurance(x, s=s, t=t, endowment=endowment, b=b)**2) E = self.E_x(x, s=s, t=t, moment=moment) A = self.term_insurance(x, s=s, t=t, b=b, moment=moment) return A + E * (b if endowment < 0 else endowment)**moment
[docs] def term_insurance(self, x: int, s: int = 0, t: int = 1, b: int = 1, moment: int = 1) -> float: """1/mthly UDD Term insurance: A_x:t Args: x : year of selection s : years after selection t : term of insurance in years b : amount of benefit moment : return first or second moment Examples: >>> UDD(m=12, life=SULT(udd=True)).term_insurance(45) """ assert moment in [1, 2, Annuity.VARIANCE] if moment == Annuity.VARIANCE: return (self.term_insurance(x, s=s, t=t, b=b, moment=2) - self.term_insurance(x, s=s, t=t, b=b)**2) A = self.life.term_insurance(x, s=s, t=t, b=b, moment=moment) i = self.life.interest.i if moment == 2: i = self.interest.double_force(i) i_m = self.life.interest.mthly(m=self.m, i=i) return A * (i / i_m)
[docs] def whole_life_annuity(self, x: int, s: int = 0, b: int = 1, variance: bool = False) -> float: """1/mthly UDD Whole life annuity: a_x Args: x : year of selection s : years after selection b : amount of benefit variance : return first moment (False) or variance (True) Examples: >>> UDD(m=12, life=SULT(udd=True)).whole_life_annuity(x) """ if variance: # short cut for variance of whole life A1 = self.whole_life_insurance(x, s=s, moment=1) A2 = self.whole_life_insurance(x, s=s, moment=2) return (b**2 * (A2 - A1**2) / self.d**2) a = self.life.whole_life_annuity(x, s=s) return b * (a * self.alpha_m - self.beta_m)
[docs] def temporary_annuity(self, x: int, s: int = 0, t: int = Annuity.WHOLE, b: int = 1, variance: bool = False) -> float: """1/mthly UDD Temporary life annuity: a_x:t Args: x : year of selection s : years after selection t : term of annuity in years b : amount of benefit variance : return first moment (False) or variance (True) Examples: >>> UDD(m=12, life=SULT(udd=True)).temporary_annuity(45, t=20) """ if variance: # short cut for variance of temporary life annuity A1 = self.temporary_insurance(x, s=s, t=t) A2 = self.temporary_insurance(x, s=s, t=t, moment=2) return (b**2 * (A2 - A1**2) / self.d**2) # difference of whole life on (x) and deferred whole life on (x+t) if t < 0: return self.whole_life_annuity(x, b=b, s=s) # UDD a = self.life.temporary_annuity(x, s=s, t=t) return b * (a*self.alpha_m - self.beta_m*(1 - self.E_x(x, s=s, t=t)))
[docs] def deferred_annuity(self, x: int, s: int = 0, u: int = 0, t: int = Annuity.WHOLE, b: int = 1, variance: bool = False) -> float: """1/mthly UDD Deferred life annuity n|t_a_x = n+t_a_x - n_a_x Args: x : year of selection s : years after selection u : years of deferral t : term of annuity in years b : amount of benefit """ if self.max_term(x+s, n) < n: return 0. if variance: # short cut for variance of temporary life annuity A1 = self.endowment_insurance(x, s=s, t=t) A2 = self.endowment_insurance(x, s=s, t=t, moment=2) return (b**2 * (A2 - A1**2) / self.d**2) a = self.life.deferred_annuity(x, s=s, u=u, t=t) return (a * self.alpha_m - self.beta_m * self.E_x(x, s=s, t=u)) * b
[docs] @staticmethod def interest_frame(i: float = 0.05): """Return 1/mthly UDD interest function values in a DataFrame Args: i : annual interest rate """ interest = Interest(i=i) out = pd.DataFrame(columns=["i(m)", "d(m)", "i/i(m)", "d/d(m)", "alpha(m)", "beta(m)"], index=[1, 2, 4, 12, 0], dtype=float) for m in out.index: i_m = Interest.mthly(m=m, i=interest.i) d_m = Interest.mthly(m=m, d=interest.d) out.loc[m] = [i_m, d_m, interest.i/i_m, interest.d/d_m, UDD.alpha(m=m, i=interest.i), UDD.beta(m=m, i=interest.i)] return out.round(5)
if __name__ == "__main__": from actuarialmath.sult import SULT from actuarialmath.policyvalues import Contract print("SOA Question 7.9: (A) 38100") sult = SULT(udd=True) x, n, t = 45, 20, 10 a = UDD(m=12, life=sult).temporary_annuity(x+10, t=n-10) print(a) A = UDD(m=0, life=sult).endowment_insurance(x+10, t=n-10) print(A) print(A*100000 - a*12*253) contract = Contract(premium=253*12, endowment=100000, benefit=100000) print(sult.gross_future_loss(A=A, a=a, contract=contract)) print() print("SOA Question 6.49: (C) 86") sult = SULT(udd=True) a = UDD(m=12, life=sult).temporary_annuity(40, t=20) A = sult.whole_life_insurance(40, discrete=False) P = sult.gross_premium(a=a, A=A, benefit=100000, initial_policy=200, renewal_premium=0.04, initial_premium=0.04) print(P/12) print() from actuarialmath.recursion import Recursion print("SOA Question 6.38: (B) 11.3") x, n = 0, 10 life = Recursion().set_interest(i=0.05)\ .set_A(0.192, x=x, t=n, endowment=1, discrete=False)\ .set_E(0.172, x=x, t=n) a = life.temporary_annuity(x, t=n, discrete=False) print(a) def fun(a): # solve for discrete annuity, given continuous life = Recursion().set_interest(i=0.05)\ .set_a(a, x=x, t=n)\ .set_E(0.172, x=x, t=n) return UDD(m=0, life=life).temporary_annuity(x, t=n) a = life.solve(fun, target=a, grid=a) # discrete annuity P = life.gross_premium(a=a, A=0.192, benefit=1000) print(P) print() print("SOA Question 6.32: (C) 550") x = 0 life = Recursion().set_interest(i=0.05).set_a(9.19, x=x) benefits = UDD(m=0, life=life).whole_life_insurance(x) payments = UDD(m=12, life=life).whole_life_annuity(x) print(benefits, payments) print(life.gross_premium(a=payments, A=benefits, benefit=100000)/12) print() print("SOA Question 6.22: (C) 102") life = SULT(udd=True) a = UDD(m=12, life=life).temporary_annuity(45, t=20) A = UDD(m=0, life=life).whole_life_insurance(45) print(life.gross_premium(A=A, a=a, benefit=100000)/12) print() print("Interest Functions at i=0.05") print("----------------------------") print(UDD.interest_frame(i=0.05)) print()