Source code for actuarialmath.premiums

"""Premiums - Computes net and gross premiums, with the equivalence principle

MIT License. Copyright (c) 2022-2023 Terence Lim
"""
from actuarialmath import Annuity

[docs]class Premiums(Annuity): """Compute et and gross premiums under equivalence principle""" # # Net level premiums for special insurances #
[docs] def net_premium(self, x: int, s: int = 0, t: int = Annuity.WHOLE, u: int = 0, n: int = 0, b: int = 1, endowment: int = 0, discrete: bool | None = True, return_premium: bool = False, annuity: bool = False, initial_cost: float = 0.) -> float: """Net level premium for special n-pay, u-deferred t-year term insurance Args: x : age initially insured s : years after selection u : years of deferral n : number of years of premiums paid t : year of death b : benefit amount endowment : endowment amount initial_cost : EPV of any other expenses or benefits, if any return_premium : if premiums without interest refunded at death annuity : whether benefit is insurance (False) or annuity discrete : whether annuity due (True) or continuous (False) Examples: >>> life = Premiums().set_interest(delta=0.06)\ >>> .set_survival(mu=lambda x,s: 0.04) >>> life.net_premium(x=0) """ if annuity: A = self.deferred_annuity(x, s=s, b=b, t=t, u=u, discrete=discrete or discrete is None) else: A = self.deferred_insurance(x, s=s, b=b, t=t, u=u, discrete=discrete or discrete is None) if endowment: A += self.E_x(x, s=s, t=t+u) * endowment if n == 0: # if n not specified n = u or t # then set to defer period if any, else same term t discrete = discrete or discrete is None # discrete or semi-discrete a = self.temporary_annuity(x, s=s, t=n, discrete=discrete) if return_premium: # death benefit include premiums returned w/o interest a -= self.increasing_insurance(x, s=s, t=n, discrete=discrete) return (A + initial_cost) / a
# # Equivalence principle for WL, Endowment Insurance, and their Annuity twins #
[docs] def insurance_equivalence(self, premium: float, b: int = 1, discrete: bool = True) -> float: """Compute whole life or endowment insurance factor, given net premium Args: premium : level net premium amount b : benefit amount discrete : discrete/annuity due (True) or continuous (False) Returns: Insurance factor value, given net premium under equivalence principle Examples: >>> life = Premiums().set_interest(d=0.05) >>> life.insurance_equivalence(premium=2143, b=100000) """ d = self.interest.d if discrete else self.interest.delta return premium / (d*b + premium) # from P = b[dA/(1-A)]
[docs] def annuity_equivalence(self, premium: float, b: int = 1, discrete: bool = True) -> float: """Compute whole life or temporary annuity factor, given net premium Args: premium : level net premium amount b : benefit amount discrete : discrete/annuity due (True) or continuous (False) Returns: Annuity factor value, given net premium under equivalence principle Examples: >>> life = Premiums().set_interest(d=0.05) >>> a = life.annuity_equivalence(premium=2143, b=100000) """ d = self.interest.d if discrete else self.interest.delta return b / (d*b + premium) # from P = b * (1/a - d)
[docs] def premium_equivalence(self, A: float | None = None, a: float | None = None, b: int = 1, discrete: bool = True) -> float: """Compute premium from whole life or endowment insurance and annuity factors Args: A : insurance factor a : annuity factor b : insurance benefit amount discrete : annuity due (True) or continuous (False) Returns: Net premium under equivalence principle """ interest = self.interest.d if discrete else self.interest.delta if a is None: # Annuity not given => use shortcut for insurance return b * interest * A / (1 - A) elif A is None: # Insurance not given => use shortcut for annuity return b * (1/a - interest) else: # Both (special) insurance and annuity factors given return b * A / a
# # Gross premiums for special insurances #
[docs] def gross_premium(self, a: float | None = None, A: float | None = None, IA: float = 0, discrete: bool = True, benefit: float = 1, E: float = 0, endowment: int = 0, settlement_policy: float = 0., initial_policy: float = 0., initial_premium: float = 0., renewal_policy: float = 0., renewal_premium: float = 0.) -> float: """Gross premium by equivalence principle Args: A : insurance factor a : annuity factor IA : increasing insurance factor, to return premiums w/o interest E : pure endowment factor for endowment benefit benefit : insurance benefit amount endowment : endowment benefit amount settlement_policy : settlement expense per policy initial_policy : initial expense per policy renewal_policy : renewal expense per policy initial_premium : initial premium per $ of gross premium renewal_premium : renewal premium per $ of gross premium discrete : annuity due (True) or continuous (False) Examples: >>> Premiums().gross_premium(a=0.17094, >>> A=6.8865, >>> IA= 0.96728, >>> benefit=100000, >>> initial_premium=0.5, >>> renewal_premium=.05, >>> renewal_policy=200, >>> initial_policy=200) """ if a is None: # assume WL or Endowment Insurance for twin a = self.annuity_twin(A, discrete=discrete) elif A is None: # assume WL or Endowment Insurance for twin A = self.insurance_twin(a, discrete=discrete) assert endowment == 0 or E > 0 # missing pure endowment factor if needed per_premium = renewal_premium * a + (initial_premium - renewal_premium) per_policy = renewal_policy * a + (initial_policy - renewal_policy) return (((A*(benefit + settlement_policy) + per_policy) + (E*endowment)) / (a - per_premium - IA)) # IA returns premium w/o interest
if __name__ == "__main__": import numpy as np print("SOA Question 6.29 (B) 20.5") life = Premiums().set_interest(i=0.035) def fun(a): return life.gross_premium(A=life.insurance_twin(a=a), a=a, benefit=100000, initial_policy=200, initial_premium=.5, renewal_policy=50, renewal_premium=.1) print(life.solve(fun, target=1770, grid=[20, 22])) print() print("SOA Question 6.2: (E) 3604") life = Premiums() A, IA, a = 0.17094, 0.96728, 6.8865 print(life.gross_premium(a=a, A=A, IA=IA, benefit=100000, initial_premium=0.5, renewal_premium=.05, renewal_policy=200, initial_policy=200)) print() print("SOA Question 6.16: (A) 2408.6") life = Premiums().set_interest(d=0.05) A = life.insurance_equivalence(premium=2143, b=100000) a = life.annuity_equivalence(premium=2143, b=100000) p = life.gross_premium(A=A, a=a, benefit=100000, settlement_policy=0, initial_policy=250, initial_premium=0.04 + 0.35, renewal_policy=50, renewal_premium=0.04 + 0.02) print(A, a, p) print() print("SOA Question 6.20: (B) 459") l = lambda x,s: dict(zip([75, 76, 77, 78], np.cumprod([1, .9, .88, .85]))).get(x+s, 0) life = Premiums().set_interest(i=0.04).set_survival(l=l) a = life.temporary_annuity(75, t=3) IA = life.increasing_insurance(75, t=2) A = life.deferred_insurance(75, u=2, t=1) print(life.solve(lambda P: P*IA + A*10000 - P*a, target=0, grid=100)) print() print("Other usage") life = Premiums().set_interest(delta=0.06)\ .set_survival(mu=lambda x,s: 0.04) print(life.net_premium(0)) print("SOA Question 5.6: (D) 1200") life = Premiums().set_interest(i=0.05) var = life.annuity_variance(A2=0.22, A1=0.45) mean = life.annuity_twin(A=0.45) fund = life.portfolio_percentile(mean, var, prob=.95, N=100) print(fund)