Actuarial Math Solutions with Python
Actuarial Math Solutions with Python
Terence Lim
1 Actuarial Python 5
1.1 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2 Interest Theory 9
2.1 Interest rates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4 Survival Models 21
4.1 Lifetime distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.2 Survival function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.3 Force of mortality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.4 Actuarial notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.5 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.6 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
6 Fractional Ages 33
6.1 Uniform distribution of deaths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
6.2 Constant force of mortality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
6.3 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
6.4 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
7 Insurance 39
7.1 Present value of life insurance r.v. 𝑍 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
i
7.2 Whole life insurance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
7.3 Term insurance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
7.4 Deferred insurance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
7.5 Endowment insurance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
7.6 Pure endowment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
7.7 Variances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
7.8 Varying insurance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
7.9 Probabilities and percentiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
7.10 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
7.11 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
8 Annuities 53
8.1 Present value of life annuity r.v. 𝑌 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
8.2 Whole life annuity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
8.3 Temporary annuity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
8.4 Deferred whole life annuity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
8.5 Certain and life annuity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
8.6 Life insurance twin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
8.7 Variances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
8.8 Immediate life annuity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
8.9 Varying life annuities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
8.10 Probabilities and percentiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
8.11 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
8.12 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
9 Premiums 63
9.1 Present value of loss at issue r.v. 0 𝐿 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
9.2 Equivalence principle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
9.3 Net premium . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
9.4 Gross premium . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
9.5 Portfolio Percentile Premium . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
9.6 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
9.7 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
10 Policy Values 71
10.1 Net policy value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
10.2 Gross policy value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
10.3 Variance of future loss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
10.4 Expense reserve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
10.5 Probabilities and Percentiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
10.6 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
10.7 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
11 Reserves 81
11.1 Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
11.2 Interim reserves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
11.3 Modified reserves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
11.4 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
11.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
12 Recursion 87
12.1 Chain rule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
12.2 Expected future lifetime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
12.3 Life insurance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
12.4 Life annuities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
ii
12.5 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
12.6 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
13 Life Table 97
13.1 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
13.2 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
14 SULT 103
14.1 Standard ultimate life table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
14.2 Pure endowment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
14.3 Term life insurance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
14.4 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
14.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
19 1/M’thly 139
19.1 Life Insurance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
19.2 Life Annuity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
19.3 Life Insurance Twin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
19.4 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
19.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
iii
20.5 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
20.6 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
iv
Solving Actuarial Math with Python
Quick Start
CONTENTS 1
Solving Actuarial Math with Python
Examples
Resources
2 CONTENTS
Solving Actuarial Math with Python
1. Jupyter notebook or run in Colab, to solve all sample SOA FAM-L exam questions.
2. User Guide, or download pdf
3. API reference
4. Github repo and issues
Sources
Contact
Github: [Link]
Enjoy!
Terence Lim PhD ASA CFA CPA CFP CAIA
CONTENTS 3
Solving Actuarial Math with Python
4 CONTENTS
CHAPTER
ONE
ACTUARIAL PYTHON
The actuarialmath package is written in and requires Python (currently: version 3.10). Though the comparable R
language possesses other desirable qualities, object-oriented programming is more straightforward in Python: since our
sequence of actuarial concepts logically build upon each other, they are naturally developed as a hieararchy of Python
classes with inherited methods and properties.
1.1 Installation
1.2 Overview
Each section of this document introduces a class, along with the actuarial concepts it implements, arranged logically in
three groups. To use the package, a suitable subclass should first be selected from the last group to load the given actuarial
assumptions. Then the appropriate computational methods can be called, which may be inherited from the other general
classes or make use of any shortcut formulas that can be obtained from the specific survival model assumed.
1. Implement general actuarial methods
• Basic interest theory and probability laws
• Survival functions, expected future lifetimes and fractional ages
• Insurance, annuity, premiums, policy values, and reserves calculations
2. Adjust results for
• Extra mortality risks
• 1/mthly payment frequency using UDD or Woolhouse approaches
3. Specify survival models and assumptions, and implement associated shortcut formulas
• Recursion inputs
• Life table, select life table, or standard ultimate life table
• Mortality laws, such as constant force of maturity, beta and uniform distributions, or Makeham’s and Gom-
pertz’s laws
5
Solving Actuarial Math with Python
1.3 License
MIT License
Copyright (c) 2022-2023 Terence Lim
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PAR-
TICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFT-
WARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1.4 Methods
The Actuarial base class provides some common helpful utility functions and definitions of constants, that are needed
by other classes in the package.
Constants:
VARIANCE : select variance as the statistical moment to calculate
Methods:
--------
add_term(t, n):
Add two terms, either term may be Whole Life
max_term(x, t, u):
Decrease term t if adding deferral period u to (x) exceeds maxage
1.5 Examples
The solve() method is called to impute the value of a parameter such that its function output value is equal to a
specified target value, by either returning the zero root (set argument mad = False, by default) or minimizing the
absolute difference (set mad = True). As a simple example, to solve for the median of the exponential cumulative
distribution:
0.6931471805599453
The add_term method adds two terms, while handling the case where either may not be a fixed term, i.e. they may be
whole life and indicated with the constant WHOLE. The max_term method trims the value of a term t, such that its sum
with age x and deferral period u is no larger than the maximum age
actuarial = Actuarial()
for a,b in [(3, [Link]), ([Link], -1), (3, 2), (3, -1)]:
print(f"{as_term(a)} + {as_term(b)} =", as_term(actuarial.add_term(a, b)))
print()
for t in [10, 50, [Link]]:
print(as_term(t), '->', actuarial.max_term(x=65, t=t, u=20))
3 + WHOLE_LIFE = WHOLE_LIFE
WHOLE_LIFE + -1 = WHOLE_LIFE
3 + 2 = 5
3 + -1 = 2
10 -> 10
50 -> 15
WHOLE_LIFE -> 15
1.5. Examples 7
Solving Actuarial Math with Python
TWO
INTEREST THEORY
Interest theory functions, that are in common actuarial and financial use, are reviewed. Interest rates are generally assumed
to be fixed and constant.
𝑣𝑡 = 𝑒−𝛿𝑡
= (1 − 𝑑)𝑡
= (1 + 𝑖)−𝑡
𝑑(𝑚) 𝑚𝑡
= (1 − )
𝑚
𝑖(𝑚) −𝑚𝑡
= (1 + )
𝑚
9
Solving Actuarial Math with Python
2
𝛿 ← 2𝛿
2
𝑖 ← 2𝑖 + 𝑖2
2
𝑑 ← 2𝑑 − 𝑑2
2
𝑣 ← 𝑣2
Annuity certain
Present value of an annuity certain that pays at a rate of $1 per year for 𝑛 years
1 − 𝑣𝑛
𝑎𝑛|
̈ =
𝑑
• Annuity certain due: pays $1 at the beginning of the year
1 − 𝑣𝑛
𝑎𝑛| = = 𝑎𝑛+1|
̈ −1
𝑖
• Immediate annuity certain: pays $1 at the end of the year
1 − 𝑣𝑛
𝑎𝑛| =
𝛿
• Continuous annuity certain: pays at a rate of $1 per year continuously.
2.2 Methods
The Interest class implements methods to convert between nominal, discount, continuously-compounded and 1/m’thy
rates of interest, and compute the value of an annuity certain.
class Interest - Store an assumed interest rate, and compute interest rate␣
↪functions
Args:
i : assumed annual interest rate
d : or annual discount rate
v : or annual discount factor
delta : or continuously compounded interest rate
v_t : or discount rate as a function of time
i_m : or m-thly interest rate
d_m : or m-thly discount rate
m : m'thly frequency, if i_m or d_m are specified
Methods:
--------
annuity(t, m, due):
Compute value of the annuity certain factor
2.3 Examples
The Interest class can be initialized with an assumed annual interest rate expressed in any one of the forms i, d,
v, delta, i_m, d_m (where the latter two forms also require the argument m to specify the number of times
compounded in a year). Then that rate can be retrieved in any other annual form as an attribute.
interest = Interest(i=0.05)
print(interest.d, interest.v, [Link], interest.i)
The mthly static method converts between annual-pay and m-thly pay interest rates in any form.
delta = 0.05
i = Interest(delta=delta).i # convert to annual interest rate
d = Interest(delta=delta).d # convert to annual discount rate
i_m = [Link](i=i, m=12) # convert to annual interest rate monthly-pay
d_m = [Link](d=d, m=12) # convert to annual discount rate monthly-pay
print('Continuously-compounded annual rate of interest:', delta)
print(' Convert to annual interest rate: ', i, [Link](i_m=i_m,␣
↪m=12))
The annuity class method computes the present value of an annuity that pays at the rate of $1 per year continuously
or discrete (due, immediate, or m’thly)
2.3. Examples 11
Solving Actuarial Math with Python
The double_force static method takes an interest rate in any form, and converts to the same form after doubling the
force of interest.
dependent on whether they are right-handed or left-handed. You are given the following:
• The initial membership is made up of 75% left-handed members (L) and 25% right-handed members (R)
• After the group initially forms, 35 new (L) and 15 new (R) join the group at the start of each subsequent year
• Members leave the group only at the end of each year
• 𝑞 𝐿 = 0.25 for all years
• 𝑞 𝑅 = 0.50 for all years Calculate the proportion of the Scissor Usage Support Group’s expected membership that
is left-handed at the start of the group’s6 th year, before any new members join for that year.
2.3. Examples 13
Solving Actuarial Math with Python
THREE
Life contingencies use probability models as well as interest rate functions. By modeling future lifetimes as random
variables, probabilities of survival or death can be calculated. This section reviews essential probability relationships and
the moments of common distributions.
3.1 Probability
The cumulative distribution function of a r.v. X is the probability 𝐹 (𝑥) = 𝑃 𝑟(𝑋 ≤ 𝑥). For a continuous r.v., 𝑓(𝑥) =
𝑑
𝑑𝑥 𝐹 (𝑥) is the probability density function. For a discrete r.v. 𝑝(𝑥) = 𝑃 𝑟(𝑋 = 𝑥) is the probability mass function.
∞
𝐸(𝑥) = ∫−∞ 𝑥𝑓(𝑥)𝑑𝑥 when continuous, or ∑𝑥 𝑥𝑓(𝑥) when discrete
• is the mean, or first moment of 𝑋.
𝑉 𝑎𝑟(𝑥) = 𝐸[(𝑋 − 𝐸[𝑥])2 ]
• is the variance, or second central moment of 𝑋, often denoted 𝜎2
Linearity of expectations
𝐸(𝑎 + 𝑏𝑋 + 𝑐𝑌 ) = 𝑎 + 𝑏𝐸(𝑋) + 𝑐𝐸(𝑌 )
• the mean of the sum of random variables is the sum of the means
Variances
𝑉 𝑎𝑟(𝑎𝑋 + 𝑏𝑌 ) = 𝑎2 𝑉 𝑎𝑟(𝑋) + 𝑏2 𝑉 𝑎𝑟(𝑌 ) + 2 𝑎 𝑏 𝐶𝑜𝑣(𝑋, 𝑌 )
• is the formula to compute the variance of the weighted sum of two random variables
𝐶𝑜𝑣(𝑋, 𝑌 ) = 𝐸[𝑋𝑌 ] − 𝐸[𝑋] ⋅ 𝐸[𝑌 ]
• is the formula to compute the covariance between two random variables
Bernoulli:
The Bernoulli is a discrete distribution that takes one of two values 𝑌 ∈ {𝑎, 𝑏} with probabilities (𝑝, 1 − 𝑝)
𝐸[𝑌 ] = 𝑎 𝑝 + 𝑏 (1 − 𝑝)
• mean of Bernoulli distribution
𝑉 𝑎𝑟[𝑌 ] = (𝑎 − 𝑏)2 𝑝 (1 − 𝑝)
• Bernoulli shortcut formula for the variance
Binomial:
A binomial r.v. measures the total number of successes from 𝑁 independent 0-1 Bernoulli r.v., where each has a proba-
bility of success 𝑝.
15
Solving Actuarial Math with Python
𝐸[𝑌 ] = 𝑁 𝑝
• mean of Binomial distribution
𝑉 𝑎𝑟[𝑌 ] = 𝑁 𝑝 (1 − 𝑝)
• variance of Binomial distribution
Mixture
A mixture distribution is a random variable 𝑌 whose distribution function can be expressed as a weighted average of
the distribution functions of 𝑛 random variables 𝑌1 , ⋯ , 𝑌𝑛 . A common case is when Y is a mixture of two Binomial
distributions (𝑝′ , 𝑁 ), where 𝑝′ ∈ (𝑝1 , 𝑝2 ) with respective probabilities (𝑝, 1 − 𝑝): a value is first drawn from a
Bernoulli random variable w.p. 𝑝, then depending on its value, a final value is then drawn from the selected one of the
two component Binomial distributions.
𝐸[𝑌 ] = 𝑝 𝑁 𝑝1 + (1 − 𝑝) 𝑁 𝑝2
• mean of mixture of two Binomial distributions
𝑉 𝑎𝑟[𝑌 ] = 𝐸[𝑌 2 ] − 𝐸[𝑌 ]2 = 𝐸[𝑉 𝑎𝑟(𝑌 | 𝑝′ ) + 𝐸(𝑌 | 𝑝′ )2 ] − 𝐸[𝑌 ]2
• variance of mixture of two Binomial distributions
Conditional Variance
𝑉 𝑎𝑟[𝑌 ] = 𝑉 𝑎𝑟(𝐸[𝑌 | 𝑝′ ]) + 𝐸[𝑉 𝑎𝑟(𝑌 | 𝑝′ )]
• alternative calculation of the variance of a mixture by using conditional variance formula, where the first term can
be easily calculated using the Bernoulli shortcut, since 𝐸[𝑌 | 𝑝′ ] is a Bernoulli random variable that takes a value
of either 𝐸[𝑌 | 𝑝1 ] or 𝐸[𝑌 | 𝑝2 ] with respective probabilities (𝑝, 1 − 𝑝)
Normal Approximation
A portfolio, 𝑌 , which is the sum of 𝑁 iid random variables each with mean 𝜇 and variance 𝜎2 , has a normal distribution
with
• mean 𝐸[𝑌 ] = 𝑁 𝜇 and
• variance 𝑉 𝑎𝑟[𝑌 ] = 𝑁 𝜎2
Percentiles
Percentiles are essentially an inverse function of the cumulative probability distribution. If 𝐹 (𝑦) is the cdf for 𝑌 , then
the p’th quantile (or 100𝑝’th percentile) is a number 𝑦𝑝 such that 𝐹 (𝑦𝑝 ) = 𝑃 𝑟(𝑌 ≤ 𝑦𝑝 ) = 𝑝.
𝑌𝑝 = 𝐸[𝑌 ] + 𝑧𝑝 √𝑉 𝑎𝑟[𝑌 ]
• value of 𝑌 at percentile 𝑝 when Y is normally distributed, where 𝑧𝑝 is the inverse standard normal function 𝑧𝑝 =
Φ−1 (𝑝)
3.3 Methods
The Life class implements methods for computing moments and probability distributions of random variables.
import math
from actuarialmath import Life
import describe
[Link](Life)
Methods:
--------
covariance(a, b, ab):
Covariance of two r.v.
bernoulli(p, a, b, variance):
Mean or variance of bernoulli r.v. with values {a, b}
binomial(p, N, variance):
Mean or variance of binomial r.v.
set_interest(interest):
Set interest rate, which can be given in any form
quantiles_frame(quantiles):
Display selected quantile values from Normal distribution table
3.4 Examples
The variance method computes the variance of 𝑎𝑋 + 𝑏𝑌 by plugging in the required inputs into the formula
𝑎2 𝑉 𝑎𝑟(𝑋) + 𝑏2 𝑉 𝑎𝑟(𝑌 ) + 2 𝑎 𝑏 𝐶𝑜𝑣(𝑋, 𝑌 ). Similarly, the covariance method computes the covariance of
𝑋 and 𝑌 by pluggin the required inputs into the formula 𝐸[𝑋𝑌 ] − 𝐸[𝑋] ⋅ 𝐸[𝑌 ]
133
3.3. Methods 17
Solving Actuarial Math with Python
The bernoulli and binomial methods compute the mean or variance (by setting the argument variance=True)
of the respective probability distributions
0.5 0.25
50.0 25.0
For a mixture of two binomial distribution, the mixture method computes the mean or variance, while the condi-
tional_variance method computes the variance using the conditional variance formula.
35.0 47.5
The sum of 𝑁 iid r.v. with identical mean and variance can be approximated by a normal distribution that is computed by
the portfolio_cdf method, or whose 100𝑝’th percentile is computed by the portfolio_percentile method.
0.7602499389065233
4.994313317536634
The set_interest method is called to initialize and store an Interest object with the assumed interest rate,
expressed in any annual or 𝑚-thly form, which is used in all subsequent life contingency computations.
life = Life().set_interest(i=0.05)
[Link]
0.04879016416943205
3.4. Examples 19
Solving Actuarial Math with Python
FOUR
SURVIVAL MODELS
The future lifetime of an individual is represented as a random variable. Under this framework, probabilities of death
or survival, as well as an important quantity known as the force of mortality, can be calculated. It is shown how these
quantitites are related, along with some actuarial notation.
Let (𝑥) denotes a life aged x, where 𝑥 ≥ 0, and 𝑇𝑥 is time-to-death, or future lifetime, of (𝑥). This means that 𝑥 + 𝑇𝑥
represents the age-at-death random variable for (𝑥).
𝑡
𝐹𝑥 (𝑡) = 𝑃 𝑟[𝑇𝑥 ≤ 𝑡] = ∫0 𝑓𝑥 (𝑡)𝑑𝑠
• probability that (𝑥) does not survive beyond age 𝑥 + 𝑡.
Lifetime density function
𝜕 𝑓0 (𝑥 + 𝑡)
𝑓𝑥 (𝑡) = 𝜕𝑡 𝐹𝑥 (𝑡) =
𝑆0 (𝑥)
• probability density function for the random variable 𝑇𝑥
In life insurance problems we may be interested in the probability of survival rather than death
𝑆𝑥 (𝑡) ≡ 𝑡 𝑝𝑥 = 𝑃 𝑟[𝑇𝑥 > 𝑡] = 1 − 𝐹𝑥 (𝑡)
• the probability that (x) survives for at least t years
𝑆0 (𝑥 + 𝑡)
𝑆𝑥 (𝑡) =
𝑆0 (𝑥)
• by assumption that 𝑃 𝑟[𝑇𝑥 ≤ 𝑡] = 𝑃 𝑟[𝑇0 ≤ 𝑥 + 𝑡|𝑇0 > 𝑥]
∞
𝑆𝑥 (𝑡) = ∫𝑡 𝑓𝑥 (𝑠)𝑑𝑠
𝑡 ∞
• since ∫0 𝑓𝑥 (𝑠))𝑑𝑠 + ∫𝑡 𝑓𝑥 (𝑠)𝑑𝑠 = 1
𝑙𝑥+𝑡
𝑆𝑥 (𝑡) =
𝑙𝑥
• a life table provides a concrete way to visualize the survival function: from a certain number of lives at a starting
age, we apply survival probabilities to compute the expected number of lives at any other age.
𝑆𝑥 (0) = 1, 𝑆𝑥 (∞) = 0, 𝑆𝑥′ (𝑡) ≤ 0
21
Solving Actuarial Math with Python
What actuaries call the force of mortality, denoted 𝜇𝑥 , is known as the hazard rate in survival analysis and the failure rate
in reliability theory.
𝜕
𝑓𝑥 (𝑡) − 𝜕 𝑙𝑥+𝑡 − 𝜕𝑡 𝑝
𝑡 𝑥 𝜕
𝜇𝑥+𝑡 = = 𝜕𝑡 = = ln 𝑝
𝑆𝑥 (𝑡) 𝑙𝑥+𝑡 𝑡 𝑝𝑥 𝜕𝑡 𝑡 𝑥
• the force of mortality actually completely determines the survival function
𝑓𝑥 (𝑡) = 𝑡 𝑝𝑥 𝜇𝑥+𝑡
• can be related to the lifetime density function for (𝑥)
𝑥+𝑡 𝑡
𝑆𝑥 (𝑡) = 𝑒− ∫𝑥 𝜇𝑠 𝑑𝑠
= 𝑒− ∫0 𝜇𝑥+𝑠 𝑑𝑠
• the survival probability, conditional on survival to the age represented by the lower bound, can be computed by
integrating the force of mortality up to the age represented by the upper bound.
𝜇𝑥 𝑑𝑥 ≈ 𝑃 𝑟[𝑇0 ≤ 𝑥 + 𝑑𝑥|𝑇0 > 𝑥]
• intuitively, multiplying the force of mortality by a small number 𝑑𝑥 can be interpreted as the probability that (𝑥)
dies before attaining age 𝑥 + 𝑑𝑥
∞
∫0 𝜇𝑥+𝑠 𝑑𝑠 = ∞
• since 𝑆𝑥 (∞) = 0
• is the expected number of surviving lives at age 𝑥 from 𝑙𝑥0 independent individuals aged 𝑥0 .
Mortality rate
𝑡 𝑞𝑥 = 1 − 𝑡 𝑝𝑥 ≡ 𝐹𝑥 (𝑡)
• probability that (𝑥) dies before age 𝑥 + 𝑡.
Deferred mortality rate
𝑢+𝑡
𝑢|𝑡 𝑞𝑥 = 𝑃 𝑟[𝑢 < 𝑇𝑥 ≤ 𝑢 + 𝑡] = ∫𝑢 𝑠 𝑝𝑥 𝜇𝑥+𝑠 𝑑𝑠
• probability that (𝑥) survives 𝑢 years, and then dies within the subsequent 𝑡 years.
𝑙𝑥+𝑢 − 𝑙𝑥+𝑢+𝑡
𝑢|𝑡 𝑞𝑥 =
𝑙𝑥
• can be related to the number of lives in the life table: total number of deaths of ages between 𝑥 + 𝑢 and 𝑥 + 𝑢 + 𝑡,
divided by number of lives aged 𝑥
• there are three useful formulas for computing deferred mortality: (1) deferred mortality (2) limited mortality, or
(3) limited survival probabilities.
4.5 Methods
The Survival class implements methods to compute and apply relationships between the various equivalent forms
of survival and mortality functions. The force of mortality function fully describes the lifetime distribution, just as the
survival function does.
import math
from actuarialmath import Survival
import describe
[Link](Survival)
class Survival - Set and derive basic survival and mortality functions
Methods:
--------
l_x(x, s):
Number of lives at integer age [x]+s: l_[x]+s
d_x(x, s):
Number of deaths at integer age [x]+s: d_[x]+s
p_x(x, s, t):
Probability that [x]+s lives another t years: : t_p_[x]+s
q_x(x, s, t, u):
Probability that [x]+s lives for u, but not t+u years: u|t_q_[x]+s
f_x(x, s, t):
Lifetime density function of [x]+s after t years: f_[x]+s(t)
mu_x(x, s, t):
Force of mortality of [x] at s+t years: mu_[x](s+t)
4.5. Methods 23
Solving Actuarial Math with Python
4.6 Examples
The set_survival class method is called to specify any survival model by either its survival probability (with the
argument S), force of mortality (mu), number of lives (l), or lifetime density (f) functions. Then the various survival and
mortality probabilities and function values can be retrieved using methods with names similar to their respective actuarial
notations: p_x, q_x, f_x, mu_x, d_x, l_x. In later sections, special mortality laws like the uniform and
exponential distributions, along with their available shortcut formulas, will be described and implemented.
100 0 50 1 1
0.9888888888888889 0.11111111111111105 1.0 0.011111111111111112
0.98 0.20000000000000007 1.0 0.02
0.9 1.0 1.0 0.1
Calculate the probability that (30) dies within the next 20 years
CAS41-F99:12
You are given the following survival function:
2
𝑆(𝑥) = 100(𝑘 − 𝑥2 ) 3
1
Find 𝑘, given that 𝜇50 = 48
print("CAS41-F99:12: k = 41")
def fun(k):
return Survival().set_survival(l=lambda x,s: 100*(k - (x+s)/2)**(2/3))\
.mu_x(50)
print([Link](fun, target=1/48, grid=50))
CAS41-F99:12: k = 41
41.00115767959991
4.6. Examples 25
Solving Actuarial Math with Python
FIVE
In many insurance applications we are interested in the first and second moments not only of the future lifetime of an
individual, but also of the individual’s curtate future lifetime, defined as the integer part of future lifetime. For some
lifetime distributions (as described in later sections), we are able to obtain the mean and variances of future lifetimes
directly without using numerical integrationn techniques.
𝐾𝑥 = ⌊𝑇𝑥 ⌋
• is the curtate future lifetime random variable, representing the number of completed whole future years by (x) prior
to death
∞ ∞
𝑒𝑥 = 𝐸[𝐾𝑥 ] = ∑𝑘=0 𝑘 𝑘| 𝑞𝑥 = ∑𝑘=1 𝑘 𝑝𝑥 𝑑𝑡
• Is the curtate expectation of life, or the mean curtate lifetime: it can be computed two ways intuitively similar to
the approach of complete expectations.
∞ ∞
𝐸[𝐾𝑥2 ] = ∑𝑘=0 𝑘2 𝑘| 𝑞𝑥 = ∑𝑘=1 (2𝑘 − 1) 𝑘 𝑞𝑥 𝑑𝑡
• second moment of curtate future lifetime
𝑉 𝑎𝑟[𝐾𝑥 ] = 𝐸[𝐾𝑥2 ] − (𝑒𝑥 )2
• variance of curtate future lifetime
27
Solving Actuarial Math with Python
We are sometimes interested in the future lifetime random variable subject to a cap of n years, which is represented by
the random variable 𝑚𝑖𝑛(𝑇𝑥 , 𝑛).
∘ 𝑛 𝑛
𝑒𝑥∶𝑛| = ∫0 𝑡 𝑡 𝑝𝑥 𝜇𝑥+𝑡 𝑑𝑠 + 𝑛 𝑛 𝑝𝑥 = ∫0 𝑡 𝑝𝑥 𝑑𝑡
• term complete expectation of life
𝑛−1 𝑛
𝑒𝑥∶𝑛| = ∑𝑘=0 𝑘 𝑘| 𝑞𝑥 + 𝑛 𝑛 𝑝𝑥 = ∑𝑘=1 𝑘 𝑝𝑥
• temporary curtate expectation of life, limited at 𝑛 years
5.4 Methods
The Lifetime class implements methods to compute curtate and complete expectations and second moments of future
lifetime
Methods:
--------
5.5 Examples
The e_x class method computes the mean, variance or second moment (by setting the value of the argument moment to
be 1, VARIANCE, or 2 respectively) of either the complete or curtate future lifetime (by setting the value of the argument
curtate to be False or True respectively).
9.999999999999998 99.99999999999997
9.508331944775032 99.91670831680462
Limited future lifetimes are specified by the argument t. The example below plots the expected limited curtate lifetimes
by varying age 𝑥 and lifetime limit 𝑡.
import math
import numpy as np
import [Link] as plt
ax = [Link](projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis', edgecolor=None)
ax.set_xlabel('Age (x)')
ax.set_ylabel('Lifetime Limit (t)')
ax.set_title('Expected Limited Curtate Lifetime ($e_{x:\overline{t}|}$)')
ax.view_init(15, -45)
5.5. Examples 29
Solving Actuarial Math with Python
(a) 0.1558791201558899
(b) 0.8586207034844889
(c) 0.13943444007155992
(d) 0.003636764417087519
(e) 53.28125000000001
(f) 45.833333325511866
(g) 45.17675143564247
5.5. Examples 31
Solving Actuarial Math with Python
SIX
FRACTIONAL AGES
Given values of 𝑙𝑥 at integer ages only, we need to make some assumption about the probability distribution for the
future lifetime random variable between integer ages, in order to calculate survival function values for non-integer ages
or durations. Such fractional age assumptions may be specified in terms of the force of mortality function (e.g. constant)
or the survival or mortality probabilities (e.g. uniform distribution of deaths).
This is the simplest assumption, which linearly interpolates the number of lives between integer ages.
𝑇𝑥 = 𝐾𝑥 + 𝑅𝑥
• The UDD assumptions define can be modeled by a new random variable 𝑅𝑥 ∼ 𝑈 (0, 1) which is independent of
curtate lifetime 𝐾𝑥 .
33
Solving Actuarial Math with Python
Under this assumption, the force of mortality is set to be constant between integer ages
𝜇𝑥+𝑟 = 𝜇𝑥 = − ln 𝑝𝑥 , for 0 ≤ 𝑟 < 1
• which leads to a step function for the force of mortality over successive years of age
𝑙𝑥+𝑟 = (𝑙𝑥 )1−𝑟 ⋅ (𝑙𝑥+1 )𝑟
• constant force of mortality is equivalent to exponential interpolation of lives between integer ages
𝑟 𝑝𝑥 = 𝑒−𝜇𝑥 𝑟 = (𝑝𝑥 )𝑟
1
• since 𝑝𝑥 = 𝑒− ∫0 𝜇𝑥+𝑢 𝑑𝑢
= 𝑒−𝜇𝑥
𝑟
𝑟 𝑝𝑥+𝑠 = 𝑒− ∫0 𝜇𝑥+𝑠+𝑢 𝑑𝑢
= (𝑝𝑥 )𝑟 , for 0 ≤ 𝑟 + 𝑠 < 1
• the probability of surviving for period of 𝑟 from age 𝑥 + 𝑠 is independent of 𝑠 under constant force of mortality
𝑓𝑥 (𝑟) = 𝑟 𝑝𝑥 𝜇𝑥+𝑟 = 𝑒−𝜇𝑥 𝑟 ⋅ 𝜇𝑥 , for 0 ≤ 𝑟 < 1
• lifetime density function at fractional age using constant force of mortality assumption
∘ 1 − 𝑒−𝜇𝑥 𝑟
𝑒𝑥∶𝑟| =
𝜇
• fractional year limited complete expectation
6.3 Methods
The Fractional class implements methods to compute survival and mortality functions between integer ages, assuming
either uniform distribution of deaths or constant force of mortality
Args:
udd : select UDD (True, default) or CFM (False) between integer ages
Methods:
--------
l_r(x, s, r):
Number of lives at fractional age: l_[x]+s+r
p_r(x, s, r, t):
Probability of survival from and through fractional age: t_p_[x]+s+r
q_r(x, s, r, t, u):
Deferred mortality rate within fractional ages: u|t_q_[x]+s+r
mu_r(x, s, r):
Force of mortality at fractional age: mu_[x]+s+r
f_r(x, s, r, t):
mortality function at fractional age: f_[x]+s+r (t)
E_r(x, s, r, t):
Pure endowment at fractional age: t_E_[x]+s+r
e_r(x, s, t):
Temporary expected future lifetime at fractional age: e_[x]+s:t
6.4 Examples
The Fractional class can be initialized to apply the uniform distribution or constant force of mortality assumptions
at fractional ages (by setting the udd argument to be either True or False respectively), while using any other survival
model for integer ages. Then survival function values at fractional ages can be computed by the methods l_r, p_r, q_r,
mu_r, f_r and E_r (for the number of lives, survival probability, mortality rate, force of mortality, lifetime density and
pure endowment functions respectively).
6.4. Examples 35
Solving Actuarial Math with Python
When using constant force of maturity as the fractional age assumption, the force of maturity may show discontinuous
jumps at integer ages
import numpy as np
import [Link] as plt
cfm = Fractional(udd=False).set_survival(l=lambda x,s: 100 - (x+s))
udd = Fractional(udd=True).set_survival(l=lambda x,s: 100 - (x+s))
x = [Link](0, 3, 0.01)
[Link](x, [cfm.mu_r(x=0, r=r) for r in x])
[Link](x, [udd.mu_r(x=0, r=r) for r in x])
[Link]([' Constant Force of Maturity', 'Uniform Distribution'])
[Link]('force of mortality $\mu$')
[Link]('Age')
[Link]('Fractional Age Assumptions and Force of Mortality')
The uniform distribution assumption for fractional ages linearly interpolates the number of lives between integer ages,
whereas the constant force of mortality asssumption uses exponential interpolation.
The complete future lifetime for fractional limited durations starting from integer ages is computed by the e_r class
method. Under the hood, when the duration is one year or less, then shortcuts are available for both the uniform distri-
bution and constant force of maturity cases; when the limited duration is more than one year, then the general one-year
recursion formula is repeatedly applied until the last period of length less than or equal to one year is reached.
import numpy as np
x = [0.5, 1., 1.5, 2.]
udd = Fractional(udd=False).set_survival(l=lambda x,s: 50 - (x+s), maxage=50)
cfm = Fractional(udd=True).set_survival(mu=lambda x,s: 0.03)
print('UDD: ', [round(udd.e_r(x=0, t=t), 4) for t in x])
print('CFM: ', [round(cfm.e_r(x=0, t=t), 4) for t in x])
The exact results under the assumption of uniform distribution of deaths are often used to approximate between curtate
and complete expectations, and can be computed by the e_approximate static method
6.4. Examples 37
Solving Actuarial Math with Python
print(Fractional.e_approximate(e_complete=15),
Fractional.e_approximate(e_complete=200, variance=True))
print(Fractional.e_approximate(e_curtate=15),
Fractional.e_approximate(e_curtate=200, variance=True))
14.5 199.91666666666666
15.5 200.08333333333334
SEVEN
INSURANCE
For a life insurance policy, the time at which the benefit will be paid is unknown until the policyholder actually dies and
the policy becomes a claim. In this section, formulas are developed for the valuation of traditional insurance benefits
(such as whole life, term and endowment insurance) and their second moments, as well as varying insurances.
Valuation functions for the present value of insurance benefits, denoted by 𝑍, are based on the continuous future lifetime
random variable, 𝑇𝑥 , or the curtate future lifetime random variable, 𝐾𝑥 . The interest rate is generally assumed to be
fixed and constant. Given a survival model and an interest rate we can derive the distribution of the present value random
variable for a life contingent benefit, and can therefore compute quantities such as the mean and variance of the present
value. Valuation functions are developed per dollar of sum assured, which can be multiplied by the actual sum insured for
different benefit amounts. The expected present value of insurance benefit, denoted and solved by 𝐸𝑃 𝑉 (𝑍), is sometimes
referred to as the actuarial value, actuarial present valuen (APV), or single net premium. Later sections will review special
mortality law assumptions where EPV’s can be computed without the need for integration.
39
Solving Actuarial Math with Python
A term life insurace for 𝑡 years pays the benefit if death occurs within 𝑡 years, 0 otherwise.
𝑍 = 0 when 𝑇𝑥 > 𝑡, else 𝑣𝑇𝑥
• death benefit is payable immediately only if the policy-holder dies within 𝑡 years
1 𝑡
𝐴𝑥∶𝑡| = ∫𝑠=0 𝑣𝑠 𝑠 𝑝𝑥 𝜇𝑥+𝑠 𝑑𝑠 = 𝐴𝑥 − 𝑡 𝐸𝑥 𝐴𝑥+𝑡
• continous term insurance as the difference of continous whole life and deferred continuous whole life
𝑍 = 0 when 𝐾𝑥 ≥ 𝑡, else 𝑣𝐾𝑥 +1
• death benefit is payable at the end of the year of death provided this occurs within 𝑡 years
𝑡−1
𝐴1𝑥∶𝑡| = ∑𝑘=0 𝑣𝑘+1 𝑘| 𝑞𝑥 = 𝐴𝑥 − 𝑡 𝐸𝑥 𝐴𝑥+𝑡
• annual term insurance as the difference of annual whole life and deferred annual whole life
Does not begin to offer death benefit cover until the end of a deferred period
𝑍 = 0 when 𝑇𝑥 < 𝑢, else 𝑣𝑇𝑥
• benefit is payable immediately on the death of (x) provided that (x) dies after the age of 𝑥 + 𝑢
𝑢| 𝐴𝑥∶𝑡| = 𝑢 𝐸𝑥 𝐴𝑥+𝑢∶𝑡|
• continuous deferred insurance as the EPV of whole life insurance starting at the end of deferred period.
𝑍 = 0 when 𝐾𝑥 < 𝑢, else 𝑣𝐾𝑥 +1
• annual deferred insurance where the death benefit is payable at the end of the year of death
𝑢| 𝐴𝑥∶𝑡| = 𝑢 𝐸𝑥 𝐴𝑥+𝑢∶𝑡|
• annual deferred insurance as the EPV of whole life insurance starting at the end of deferred period.
40 Chapter 7. Insurance
Solving Actuarial Math with Python
𝑛 𝐸𝑥 = 𝐴𝑥∶𝑛|1 = 𝑣𝑛 𝑛 𝑝𝑥
• because the pure endowment will be paid only at time 𝑡, assuming the life survives, there is only the discrete time
version
7.7 Variances
The variance of life insurance benefits is computed as the difference of the second moment 𝐸[𝑍 2 ] and square of the first
moment 𝐸[𝑍]2 . By the rule of momenets: 𝐸[𝑍 𝑛 ] at force of interest 𝛿 equals 𝐸[𝑍] at force of interest 𝑛𝛿 with the benefit
amount also raised to the 𝑛-th power.
∞
𝐸[𝑍 𝑛 ] = ∫𝑡=0 𝑏𝑡𝑛 𝑣𝑡𝑛 𝑡 𝑝𝑥 𝜇𝑥+𝑡 𝑑𝑡
• when calculating with moments, the benefit and discount factor are raised to the 𝑛-th power.
2 ∞
𝐴𝑥 = ∫𝑡=0 𝑣2𝑡 𝑡 𝑝𝑥 𝜇𝑥+𝑡 𝑑𝑡
• variance of continous endowment insurance may also be expressed on the sum of a term insurance and pure en-
1 1
dowment, which are mutually exclusive hence 𝐶𝑜𝑣(𝐴𝑥∶𝑡| , 𝑡 𝐸𝑥 ) = 𝐸[𝐴𝑥∶𝑡| ]𝐸[𝑡 𝐸𝑥 ]
𝑉 𝑎𝑟(𝐴𝑥∶𝑡| ) = 2 𝐴𝑥∶𝑡| − (𝐴𝑥∶𝑡| )2 = 𝑉 𝑎𝑟(𝐴1𝑥∶𝑡| ) + 𝑉 𝑎𝑟(𝑡 𝐸𝑥 ) − 𝐶𝑜𝑣(𝐴1𝑥∶𝑡| , 𝑡 𝐸𝑥 )
Increasing insurance:
Amount of death benefit increases arithmetically at a rate of $1 per year.
∞
(𝐼𝐴)𝑥 = ∫𝑡=0 𝑡 𝑣𝑡 𝑡 𝑝𝑥 𝜇𝑥+𝑡 𝑑𝑡
• increasing continuous whole life insurance
∞
(𝐼𝐴)𝑥 = ∑𝑘=0 (𝑘 + 1) 𝑣𝑘+1 𝑘| 𝑞𝑥
• increasing annual whole life insurance
𝑡
(𝐼𝐴)1𝑥∶𝑡| = ∫𝑠=0 𝑠 𝑣𝑠 𝑠 𝑝𝑥 𝜇𝑥+𝑠 𝑑𝑠
42 Chapter 7. Insurance
Solving Actuarial Math with Python
The larger 𝑇 , the lower the present value of the payment and the random variable 𝑍. For a level benefit whole life
insurance, the 100𝜋-th percentile of the actuarial present value is determined by the 100(1 − 𝜋)-th percentile of the
lifetime random variable.
Percentile of 𝑍:
𝑡𝜋 = 𝑆 −1 (𝜋)
• the 100(1 − 𝑝)-th percentile of the lifetime variable for WL insurance can be obtained by looking up the inverse
− ln 𝜋
of the survival function at probability 𝑝; e.g. if 𝑡 𝑝𝑥 = 𝑒−𝜇𝑡 , then 𝑡𝜋 =
𝜇
𝑍𝜋 = 𝑏𝜋 𝑣𝑡𝜋
• the PV of the insurance r.v. that pays benefit 𝑏𝑡𝜋 at time 𝑡𝜋
Probability of 𝑍:
To determine the probability that the present value of an annuity is greater than some percentile value 𝑃 𝑟(𝑍 ≥ 𝑧𝜋 ), it
is necessary to plot Z as a function of 𝑡, then identify those time periods where 𝑍 ≥ 𝑧𝜋 and compute the cumulative
probability of 𝑇 being in those time periods.
7.10 Methods
The Insurance class implements methods to compute the expected present value of life insurance
import math
import numpy as np
import [Link] as plt
from actuarialmath import Insurance
import describe
[Link](Insurance)
Methods:
--------
increasing_insurance(x, s, t, b, discrete):
Increasing life insurance: (IA)_x
decreasing_insurance(x, s, t, b, discrete):
Decreasing life insurance: (DA)_x
Z_x(x, s, t, discrete):
EPV of year t insurance death benefit for life aged [x]+s: b_x[s]+s(t)
Z_from_t(t, discrete):
PV of annual or continuous insurance payment Z(t) at t=T_x
Z_to_t(Z):
T_x s.t. PV of continuous WL insurance payment is Z
Z_to_prob(x, Z):
Probability that continuous WL insurance PV r.v. is no more than Z
7.11 Examples
Given a survival model, the A_x class method computes the EPV for arbitrary life insurance benefits, endowments,
discount functions, terms and moments using numerical integration or summation.
life = Insurance().set_interest(delta=0.06)\
.set_survival(mu=lambda *x: 0.04)
benefit = lambda x,t: [Link](0.02 * t)
A1 = life.A_x(0, benefit=benefit, discrete=False)
A2 = life.A_x(0, moment=2, benefit=benefit, discrete=False)
var = A2 - A1**2 # compute variance of this life insurance
print(var) # 0.0833
0.08333333333333331
But typically, the common life insurance EPV’s are computed by calling the class methods
whole_life_insurance, term_insurance, deferred_insurance, endowment_insurance,
increasing_insurance, decreasing insurance, or E_x (i.e. pure endowment). It is observed in the
example below that term insurance can be equivalently computed as the difference of endowment insurance and a pure
endowment, or as the difference of whole life and deferred insurance.
44 Chapter 7. Insurance
Solving Actuarial Math with Python
The Z_t class method returns the 100𝑝-th percentile of the survival function, then Z_from_t can be called to discount
the benefit paid at that time. The Z_from_prob class method performs both steps with one call. Z_plot plots the
insurance present value r.v. 𝑍 as a function of time 𝑡.
T: 60.274191128246535
p: 0.75 0.7500000000000304
Z: 0.05282410383956641 0.05282410383956641
7.11. Examples 45
Solving Actuarial Math with Python
By setting the dual argument to True, the survival probability of (x) can be simultaneously plotted to annotate, say, the
median survival lifetime.
S(K=68.0000): 0.5003864946350018
S(T=68.0097): 0.49999343308443683
46 Chapter 7. Insurance
Solving Actuarial Math with Python
7.11. Examples 47
Solving Actuarial Math with Python
𝑍 = 0, if 𝑇𝑥 ≤ 10
= 𝑣𝑇 , if 10 < 𝑇𝑥 ≤ 20
𝑇
= 2𝑣 , if 20 < 𝑇𝑥 ≤ 30
= 0, thereafter
48 Chapter 7. Insurance
Solving Actuarial Math with Python
fig, ax = [Link](3, 2) # plot shapes of the target and the five functions
ax = [Link]()
for i, b in enumerate([fun] + benefits):
life.Z_plot(0, benefit=b, ax=ax[i], color=f"C{i+1}", title='')
ax[i].legend(["(" + "abcde"[i-1] + ")" if i else "Z"])
7.11. Examples 49
Solving Actuarial Math with Python
50 Chapter 7. Insurance
Solving Actuarial Math with Python
• 𝐸[𝑍1 ] = 528
• 𝑉 𝑎𝑟(𝑍2 ) = 15, 000
• 𝐴𝑥∶𝑛|1 = 0.209
• 2 𝐴𝑥∶𝑛|1 = 0.136
Calculate 𝑉 𝑎𝑟(𝑍1 ).
7.11. Examples 51
Solving Actuarial Math with Python
52 Chapter 7. Insurance
CHAPTER
EIGHT
ANNUITIES
A life annuity is a regular sequence of payments as long as the annuitant is alive on the payment date.
Valuation functions for the present value of annuity benefits, denoted by Y, are based on the continuous future lifetime
random variable, 𝑇𝑥 , or the curtate future lifetime random variable, 𝐾𝑥 . The expected present value of annuity benefits
is denoted and solved by EPV(Y). A useful formula from interest theory is that for a 𝑇 -year continuous annuity-certain
1 − 𝑣𝑇 1 − 𝑣𝑇
and annual annuity-due – 𝑎𝑇 | = and 𝑎𝑇̈ | = – which are used to calculate the actuarial present value of
𝛿 𝑑
life annuities.
A whole life annuity pays a level benefit until the death of the annuitant
1 − 𝑣𝑇𝑥
𝑌 = 𝑎𝑇𝑥 | =
𝛿
• present value random variable for continuous life annuity that pays at a rate of 1 per year until the moment of death
𝑇𝑥
∞
𝑎𝑥 = 𝐸𝑃 𝑉 [𝑎𝑇𝑥 | ] = ∫𝑡=0 𝑣𝑡 𝑡 𝑝𝑥 𝑑𝑡
• expected present value of continuous whole life annuity
∘
𝑎𝑥 = 𝑒𝑥
• special case when interest rate is 0
1 − 𝑣𝐾𝑥 +1
𝑌 = 𝑎𝐾
̈ 𝑥 +1| =
𝑑
• present value random variable for annual life annuity due that pays 1 up to the beginning of the year of death
∞
̈ 𝑥 +1| ] = ∑𝑘=0 𝑣𝑘 𝑘 𝑝𝑥
𝑎𝑥̈ = 𝐸𝑃 𝑉 [𝑎𝐾
• expected present value of annual whole life annuity due
𝑎𝑥̈ = 1 + 𝑒𝑥
• special case when interest rate is 0
53
Solving Actuarial Math with Python
A 𝑡-year temporary life year annuity pays a level benefit until the earlier of death and year 𝑡
𝑌 = 𝑎𝑡| when or 𝑇𝑥 > 𝑡, else 𝑌 = 𝑎𝑇𝑥 |
• present value random variable for continuous temporary life annuity that pays at a rate of 1 until the moment of
death, or 𝑡 if earlier
𝑡
𝑎𝑥∶𝑡| = ∫𝑠=0 𝑣𝑠 𝑠 𝑝𝑥 𝑑𝑠 = 𝑎𝑥 − 𝑡 𝐸𝑥 𝑎𝑥+𝑡
• expected present value of continuous temporary life annuity
∘
𝑎𝑥∶𝑡| = 𝑒𝑥∶𝑡|
• when interest rate is 0
𝑌 = 𝑎𝑡|̈ when 𝐾𝑥 ≥ 𝑡, else 𝑌 = 𝑎𝐾
̈ 𝑥 +1|
• present value random variable for annual life annuity that pays 1 at the beginning of the year until the moment of
death, or 𝑡 if earlier.
𝑡−1
̈ = ∑𝑘=0 𝑣𝑘 𝑘| 𝑝𝑥 = 𝑎𝑥̈ − 𝑡 𝐸𝑥 𝑎𝑥+𝑡
𝑎𝑥∶𝑡| ̈
• expected present value of annual temporary life annuity due
𝑎𝑥∶𝑡|
̈ = 1 + 𝑒𝑥∶𝑡| − 𝑡 𝑝𝑥
• when interest rate is 0
A 𝑛-year deferred life annuity pays a level benefit starting at time 𝑛 and continuing until death.
𝑢| 𝑎𝑥 = 𝑎𝑥 − 𝑎𝑥+𝑢
• continuous deferred life annuity as the difference of whole life annuities
𝑢| 𝑎𝑥
̈ = 𝑎𝑥̈ − 𝑎𝑥+𝑢
̈
• annual deferred annuity due as the difference of annual whole life annuities due
A common feature of pension benefits is that the pension annuity is guaranteed to be paid for some period even if the
annuitant dies before the end of the period.
𝑌 = 𝑎𝑛| when 𝑇𝑥 ≤ 𝑛, else 𝑎𝑇𝑥 |
• present value random variable for continuous certain and life annuity
𝑎𝑥∶𝑛| = 𝑎𝑛| + 𝑛| 𝑎𝑥
• can be computed as a continuous temporary certain annuity plus a deferred continuous whole life annuity
𝑌 = 𝑎𝑛|
̈ when 𝐾𝑥 < 𝑛, else 𝑎𝐾
̈ 𝑥 +1|
• present value random variable for annual certain and life annuity due
𝑎𝑥∶𝑛|
̈ = 𝑎𝑛|
̈ + 𝑛| 𝑎𝑥
̈
54 Chapter 8. Annuities
Solving Actuarial Math with Python
• can be computed as an annual temporary certain annuity due, plus a deferred annual life annuity due
Whole and Temporary Life Annuities (and Whole Life and Endowment Insurance) ONLY:
1 − 𝐴𝑥
𝑎𝑥 =
𝛿
𝐴𝑥 = 1 − 𝛿 𝑎𝑥
• continuous whole life insurance twin for continuous whole life annuity
1 − 𝐴𝑥
𝑎𝑥̈ =
𝑑
𝐴𝑥 = 1 − 𝑑 𝑎𝑥̈
• annual whole life insurance twin for annual life annuity due
1 − 𝐴𝑥∶𝑡|
𝑎𝑥∶𝑡| =
𝛿
𝐴𝑥∶𝑡| = 1 − 𝛿 𝑎𝑥∶𝑡|
• continuous endowment insurance twin for continuous temporary life annuity
1 − 𝐴𝑥∶𝑡|
𝑎𝑥∶𝑡|
̈ =
𝑑
𝐴𝑥∶𝑡| = 1 − 𝑑 𝑎𝑥∶𝑡|
̈
• annual endowment insurance twin for annual temporary life annuity due
8.7 Variances
The second moment of a life annuity is not easy to compute directly. However, for whole life and temporary life annuities,
the variance can be calculated directly using the insurance twin equations.
Whole life annuity
2 1 − 2 𝐴𝑥
𝑎𝑥 =
2𝛿
2
𝐴𝑥 = 1 − (2𝛿) 2 𝑎𝑥
2
𝐴𝑥 − (𝐴𝑥 )2
𝑉 𝑎𝑟(𝑎𝑥 ) =
𝛿2
• from doubling the force of interest of continuous whole life insurance
2 1 − 2 𝐴𝑥
𝑎𝑥̈ =
2𝑑 − 𝑑2
2
𝐴𝑥 = 1 − (2𝑑 − 𝑑2 ) 2 𝑎𝑥̈
𝐴𝑥̈ − (𝐴𝑥 )2
2
𝑉 𝑎𝑟(𝑎𝑥̈ ) =
𝑑2
• from doubling the force of interest of annual whole life insurance
Benefit is paid at the end of the year, as long as the annuitant is alive, and can be related to the value of an annual life
annuity due:
𝑎𝑥 = 𝑎𝑥̈ − 1
• whole life annuities
𝑎𝑥∶𝑡| = 𝑎𝑥∶𝑡|
̈ − 1 + 𝑡 𝐸𝑥
• temporary life annuities
Increasing annuity:
The amount of the annuity payment increases arithmetically with time.
∞
(𝐼𝑎)𝑥 = ∫𝑡=0 𝑡 𝑣𝑡 𝑡 𝑝𝑥 𝑑𝑡
• EPV of continuously increasing whole life annuity
∞
(𝐼 𝑎)̈ 𝑥 = ∑𝑘=0 (𝑘 + 1) 𝑣𝑘+1 𝑘 𝑝𝑥
• EPV of annual increasing whole life annuity due
𝑡
(𝐼𝑎)𝑥∶𝑡| = ∫𝑠=0 𝑠 𝑣𝑠 𝑠 𝑝𝑥 𝑑𝑠
• EPV of continuously increasing temporary life annuity
𝑡−1
(𝐼 𝑎)̈ 𝑥∶𝑡| = ∑𝑘=0 (𝑘 + 1) 𝑣𝑘+1 𝑘 𝑝𝑥
• EPV of annual increasing temporary life annuity due
Decreasing annuity:
The amount of the annuity payment decreases arithmetically with time. There are no decreasing whole life annuities.
56 Chapter 8. Annuities
Solving Actuarial Math with Python
𝑡
(𝐷𝑎)𝑥∶𝑡| = ∫𝑠=0 (𝑡 − 𝑠) 𝑣𝑠 𝑠 𝑝𝑥 𝑑𝑠
• EPV of continuously decreasing temporary life annuity (not defined for whole life)
𝑡−1
(𝐷𝑎)̈ 𝑥∶𝑡| = ∑𝑘=0 (𝑡 − 𝑘) 𝑣𝑘+1 𝑘 𝑝𝑥
• EPV of annual decreasing temporary life annuity due (not defined for whole life)
Identity relationships:
(𝐷𝑎)𝑥∶𝑡| + (𝐼𝑎)𝑥∶𝑡| = 𝑡 𝑎𝑥∶𝑡|
• the sum of continuous 𝑡-year decreasing and increasing life annuities is 𝑡 times a 𝑡-year temporary annuity
(𝐷𝑎)̈ 𝑥∶𝑡| + (𝐼 𝑎)̈ 𝑥∶𝑡| = (𝑡 + 1) 𝑎𝑥∶𝑡|
̈
• the sum of 𝑡-year annual decreasing and increasing life annuities due is 𝑡 + 1 times a 𝑡-year temporary annuity
The value of annuity random variables always increases as a function of survival time. Hence calculating a percentile of
a life annuity simply requires calculating the percentile of 𝑇 , then calculating the value of a temporary annuity.
Percentile of 𝑌 :
𝑡𝜋 = 𝑆 −1 (1 − 𝜋)
• the 100𝑝-th percentile of the lifetime variable for whole life annuity can be obtained from looking up the inverse
of the survival function at probability 𝑝.
1 − 𝑣 𝑡𝜋
𝑌𝜋 = 𝑏 𝑎𝑡𝜋 | = 𝑏
𝛿
• the PV of the annuity r.v. that pays level benefit 𝑏 up to time 𝑡𝜋
Probability of 𝑌 :
Since annuity values monotonically increases with survaval time, to calculate he probability that the present value of an
annuity is greater than some value 𝑃 𝑟(𝑌 ≥ 𝑌𝜋 ), calculate the time 𝑡𝜋 for which 𝑎𝑡𝜋 | has the required value, then calculate
the probability that time of death 𝑇𝑥 is in that range.
8.11 Methods
The Annuity class implements methods to compute the expected present value of life annuities
Methods:
--------
immediate_annuity(x, s, t, b, variance):
Compute EPV of immediate life annuity
annuity_twin(A, discrete):
Returns annuity from its WL or Endowment Insurance twin"
deferred_annuity(x, s, u, t, b, discrete):
Deferred life annuity n|t_a_x = n+t_a_x - n_a_x
certain_life_annuity(x, s, u, t, b, discrete):
Certain and life annuity = certain + deferred
increasing_annuity(x, s, t, b, discrete):
Increasing annuity
decreasing_annuity(x, s, t, b, discrete):
Identity (Da)_x:n + (Ia)_x:n = (n+1) a_x:n temporary annuity
Y_x(x, s, t, discrete):
EPV of year t annuity benefit for life aged [x]+s: b_x[s]+s(t)
Y_from_t(t, discrete):
PV of insurance payment Y(t), given T_x
58 Chapter 8. Annuities
Solving Actuarial Math with Python
8.12 Examples
Given a survival model, the a_x class method computes the EPV for arbitrary life annuity benefits, discount functions, and
terms using numerical integration or summation. Common life annuity EPV’s are computed by calling the class methods
whole_life_annuity, temporary_annuity, deferred_annuity, certain_life_annuity,
increasing_insurance, decreasing insurance, or immediate_annuity. The examples below
confirm the numerical relationships of
• whole life immediate annuity with annuity-due
• whole life with temporary and deferred annuities
• certain-and-life annuity with deferred life annuity and annuity-certain
• temporary life annuity with increasing and decreasing life annuities
The Y_t class method returns the 100𝑝-th percentile of the survival function, then Y_from_t can be called to discount
the benefits up to that time. The Y_from_prob class method performs both steps with one call. `Y_plot plots the
annuity present value r.v. as a function of time.
8.12. Examples 59
Solving Actuarial Math with Python
T: 74.02351166727799
p: 0.75 0.7499999999997666
Y: 19.94237852945247 19.94237852945247
By setting the `dual`` argument to True, the survival probability of (x) can be simultaneously plotted to annotate, say, the
median survival lifetime.
60 Chapter 8. Annuities
Solving Actuarial Math with Python
S(K=69.0000): 0.45995640909219604
S(T=68.0097): 0.49999343308443683
8.12. Examples 61
Solving Actuarial Math with Python
62 Chapter 8. Annuities
CHAPTER
NINE
PREMIUMS
When insurance company agrees to pay some life contingent benefits, the policyholder agrees to pay premiums to the
insurance company to secure these benefits. The premiums will also need to reimburse the insurance company for the
expenses associated with the policy.
The calculation of the premium may not explicitly allow for the insurance company’s expenses, which we refer to as a
net premium (also called a risk premium or benefit premium). If the calculation does explicitly allow for expenses, the
premium is called a gross premium (also called expense-loaded premium).
Premiums for life insurance are payable in advance, with the first premium payable when the policy is purchased.
• Fully continuous insurance: both benefits and premiums are payable continuously
• Fully discrete insurance: benefits are paid at the end of the year, premiums are paid at the beginning of the year
• Semi-continuous insurance: benefits are paid at moment of death, premiums are paid at the beginning of the year
The loss at issue random variable is defined as the present value of the amount the insurance loses.
63
Solving Actuarial Math with Python
Under this principle, premiums are set such that the actuarial present value of the benefit premiums equals the actuarial
present value of the benefits, hence expected loss at issue is zero:
𝐸[0 𝐿] = 𝐸𝑃 𝑉0 (future benefits) − 𝐸𝑃 𝑉0 (future premiums) = 0
For net premiums, we take into consideration outgoing benefit payments only: expenses are not a part of the calculation.
The benefit may be a death benefit or a survival benefit or a combination. Under the equivalence principle, the net premium
is set such that the expected value of the future loss is zero at the start of the contract, 𝐸[𝐿0 ] = 0.
Whole life insurance:
𝐴
𝑃𝑥 = 𝑥
𝑎𝑥̈
• fully discrete whole life insurance net premium
𝐴𝑥
𝑃𝑥 =
𝑎𝑥
• fully continuous whole life insurance net premium
Term life insurance:
𝐴1𝑥∶𝑡|
1
𝑃𝑥∶𝑡| =
𝑎𝑥∶𝑡|
̈
• fully discrete term life net premium
1
1 𝐴𝑥∶𝑡|
𝑃 𝑥∶𝑡| =
𝑎𝑥∶𝑡|
• fully continuous term life net premium
Pure endowment:
𝐸
𝑃𝑥∶𝑡|1 = 𝑡 𝑥
𝑎𝑥∶𝑡|
̈
• fully discrete pure endowment net premium
1 𝑡 𝐸𝑥
𝑃 𝑥∶𝑡| =
𝑎𝑥∶𝑡|
• fully continuous pure endowment net premium
Endowment insurance:
𝐴𝑥∶𝑡|
𝑃𝑥∶𝑡| =
𝑎𝑥∶𝑡|
̈
• fully discrete endowment insurance net premium
𝐴𝑥∶𝑡|
𝑃 𝑥∶𝑡| =
𝑎𝑥∶𝑡|
• fully continuous endowment insurance net premium
64 Chapter 9. Premiums
Solving Actuarial Math with Python
1 𝑑𝐴𝑥∶𝑛|
𝑃 𝑥∶𝑡| = 𝑏 ( − 𝛿) = 𝑏 ( )
𝑎𝑥∶𝑛| 1 − 𝐴𝑥∶𝑛|
• fully continuous endowment insurance shortcut
When we calculate a gross premium for an insurance policy or an annuity, we take account of the expenses the insurer
incurs. There are three main types of expense associated with policies – initial expenses, renewal expenses and termination
or claim expenses.
Expenses:
𝑒𝑖 = initial_per_policy + initial_per_premium × gross_premium
• initial expenses at the beginning of year 1 when a policy is issued, which may be both proportional to premiums or
may be ‘per policy’, meaning that the amount is fixed for all policies, and is not related to the size of the contract.
𝑒𝑟 = renewal_per_policy + renewal_per_premium × gross_premium
• renewal expenses in the beginning of each year 2+, may be both per policy or percent of premium.
𝐸 = settlement expense
• is paid along with death benefit (𝑏); hence total claim cost upon death is:
claim cost = 𝑏 + 𝐸 = death benefit + settlement expense.
Return of premiums paid without interest upon death:
𝑡−1
𝐸𝑃 𝑉0 (return of premiums paid) = ∑𝑘=0 𝑃 (𝑘 + 1) 𝑣𝑘+1 𝑘| 𝑞𝑥 = 𝑃 ⋅ (𝐼𝐴)1𝑥∶𝑡|
• an additional benefit in some insurance policies, whose EPV can be calculated using an increasing insurance factor
Equivalence principle:
If gross premiums are set under equivalence principle, then expected gross future loss at issue equals zero:
𝐸[0 𝐿𝑔 ] = 𝐸𝑃 𝑉0 (future benefits) + 𝐸𝑃 𝑉0 (future expenses) − 𝐸𝑃 𝑉0 (future premiums) = 0
The portfolio percentile premium principle is an alternative to the equivalence premium principle. Using the mean and
variance of the future loss random variable, the portfolio percentile premium principle can be used to determine a pre-
mium. We assume a large portfolio of 𝑁 identical and independent policies. The present value of the total future loss 𝐿
of the portfolio can be approximated by a normal distribution over the sum of the individual losses 𝐿𝑖
𝐿 = 𝐿 1 + 𝐿2 + ⋯ + 𝐿 𝑁
𝐸[𝐿] = 𝑁 𝐸[𝐿]
𝑉 𝑎𝑟[𝐿] = 𝑁 𝑉 𝑎𝑟[𝐿]
Note 𝐸[𝐿] and 𝑉 𝑎𝑟[𝐿] are functions of the unspecified premium 𝑃 . A probability percentile 𝑞 (say, 95% confidence) and
a threshold 𝐿∗ (say, 0) are chosen, then 𝑃 is solved for implicitly from the following equation, such that the probability
of 𝐿 not exceeding 𝐿∗ is 𝑞
⎡ 𝐿 − 𝐸[𝐿] 𝐿∗ − 𝐸[𝐿] ⎤ ∗
⎛ 𝐿 − 𝐸[𝐿] ⎞
𝑃 𝑟[𝐿 < 𝐿∗ ] = 𝑃 𝑟 ⎢ < ⎥ = Φ⎜
⎜ ⎟
⎟=𝑞
√
⎣ 𝑉 𝑎𝑟[𝐿] √ 𝑉 𝑎𝑟[𝐿] ⎦ √
⎝ 𝑉 𝑎𝑟[𝐿] ⎠
9.6 Methods
The Premiums class implements methods for computing net and gross premiums under the equivalence principle
import numpy as np
from actuarialmath import Premiums
import describe
[Link](Premiums)
Methods:
--------
insurance_equivalence(premium, b, discrete):
Compute whole life or endowment insurance factor, given net premium
annuity_equivalence(premium, b, discrete):
Compute whole life or temporary annuity factor, given net premium
premium_equivalence(A, a, b, discrete):
Compute premium from whole life or endowment insurance and annuity factors
66 Chapter 9. Premiums
Solving Actuarial Math with Python
9.7 Examples
When net premiums are set by the equivalence principle, then the three class methods insurance_equivalence,
annuity_equivalence or premium_equivalence can be called to compute the insurance, annuity or net
premium respectively given any one of the other value, for whole life and endowment insurances. For other general life
insurance or annuity benefits, net_premium computes the net premium under EPP given any term (t), deferral period
(u), endowment benefit amount (endowment), other initial expected costs (initial_cost), or refund of premium
without interest at death (return_premium).
life = Premiums().set_interest(delta=0.06)\
.set_survival(mu=lambda x,s: 0.04)
P = life.net_premium(x=0, discrete=False)
A = life.whole_life_insurance(x=0, discrete=False)
a = life.whole_life_annuity(x=0, discrete=False)
print('Insurance:', A, life.insurance_equivalence(premium=P, discrete=False))
print('Annuity:', a, life.annuity_equivalence(premium=P, discrete=False))
print('Net Premium:', P,
life.premium_equivalence(A=A, discrete=False),
life.premium_equivalence(a=a, discrete=False))
The gross_premium class method computes the level premium under the equivalence principles given expense and
claim amounts (initial and renewal expenses per policy or per $1 premium, settlement expense, death benefit, and endow-
ment) and corresponding actuarial present value factors (A for insurance benefits, a for premium annuity, IA for refund
of premium without interest death benefit, and E for endowment benefit).
SOA Question 6.2
For a fully discrete 10-year term life insurance policy on (x), you are given:
• Death benefits are 100,000 plus the return of all gross premiums paid without interest
• Expenses are 50% of the first year’s gross premium, 5% of renewal gross premiums and 200 per policy expenses
each year
• Expenses are payable at the beginning of the year
• 𝐴1𝑥∶10| = 0.17094
• (𝐼𝐴)1𝑥∶10| = 0.96728
• 𝑎𝑥∶10|
̈ = 6.8865
Calculate the gross premium using the equivalence principle.
9.7. Examples 67
Solving Actuarial Math with Python
Taxes 4% 0 4% 0
Sales Commission 35% 0 2% 0
Policy Maintenance 0% 250 0% 50
68 Chapter 9. Premiums
Solving Actuarial Math with Python
𝑥 𝑝𝑥
75 0.90
76 0.88
77 0.85
• 𝑖 = 0.04
Calculate the annual net premium.
9.7. Examples 69
Solving Actuarial Math with Python
• 𝑖 = 0.03
• 𝜇𝑥 (𝑡) = 0.02𝑡, 𝑡≥0
• 0 𝐿 is the aggregate loss at issue random variable for these pure endowments.
Using the normal approximation without continuity correction, calculate 𝑃 𝑟(0 𝐿) > 50, 000).
70 Chapter 9. Premiums
CHAPTER
TEN
POLICY VALUES
Policy value at time 𝑡 is the expected present value, at time 𝑡, of the future loss random variable, where 𝑃 is the premium
for 𝑏 units of insurance, not necessarily the benefit premium determined from the equivalence principle.
Net future loss at issue
For net future loss at time 𝑡 = 0, we consider benefit payments and net premiums only.
𝑃 𝐾𝑥 +1 𝑃
0𝐿 = 𝑏 𝑣𝐾𝑥 +1 − 𝑃 𝑎𝐾
̈ 𝑥 +1| = (𝑏 + )𝑣 −
𝑑 𝑑
• net future loss at issue of fully discrete whole life insurance
𝑃 𝑇𝑥 𝑃
0𝐿 = 𝑏 𝑣𝑇𝑥 − 𝑃 𝑎𝑇𝑥 | = (𝑏 + ) 𝑣 − (continuous)
𝛿 𝛿
• net future loss at issue of fully continuous whole life insurance
Gross future loss at issue
For gross future loss at time 𝑡 = 0, expenses are included along with benefits payments and gross premiums.
𝐺 − 𝑒𝑟 𝐾𝑥 +1 𝐺 − 𝑒𝑟
0𝐿 = (𝑏 + 𝐸 + )𝑣 − + (𝑒𝑖 − 𝑒𝑟 )
𝑑 𝑑
• gross future loss at issue of fully discrete whole life insurance
The amount needed at time 𝑡 to cover the shortfall of expected future benefits greater than the EPV of future net premiums
after time 𝑡 is called the policy value for the policy at time 𝑡, denoted 𝑡 𝑉 ,
0𝑉 =0
• net policy value at issue is 0 because of the equivalence principle
Term insurance:
𝑛𝑉 =0
• net policy value at year 𝑛 is 0 for a n-year term insurance
Endowment insurance:
𝑛𝑉 = endowment benefit
• net policy value at year 𝑛 is equal to the endowment benefit for a n-year endowment insurance
71
Solving Actuarial Math with Python
Gross premium policy values explicitly allow for expenses and for the full gross premium.
𝑔
𝑡𝑉 = 𝐸[𝑡 𝐿𝑔 ] = 𝐸𝑃 𝑉𝑡 (future benefits) + 𝐸𝑃 𝑉𝑡 (future expenses) − 𝐸𝑃 𝑉𝑡 (future premiums)
• gross policy value at time 𝑡 is the expected net future loss of benefits and expenses less premiums after time 𝑡
These formulas apply to whole life and endowment insurance only. In these cases, the function of the random variable
𝑣𝑇𝑥 is the same for both the insurance and annuity of premiums which simplifies the formulas for variance of future loss.
Net future loss
𝑃 2 2
𝑉 𝑎𝑟[𝑡 𝐿] = (𝑏 + ) [ 𝐴𝑥+𝑡 − (𝐴𝑥+𝑡 )2 ]
𝑑
• fully discrete whole life insurance
𝑃 2 2
𝑉 𝑎𝑟[𝑡 𝐿] = (𝑏 + ) [ 𝐴𝑥+𝑡 − (𝐴𝑥+𝑡 )2 ]
𝛿
• fully continuous whole life insurance
𝑃 2 2
𝑉 𝑎𝑟[𝑡 𝐿] = (𝑏 + ) [ 𝐴𝑥+𝑡∶𝑛−𝑡| − (𝐴𝑥+𝑡∶𝑛−𝑡| )2 ]
𝑑
• fully discrete endowment insurance
𝑃 2 2
𝑉 𝑎𝑟[𝑡 𝐿] = (𝑏 + ) [ 𝐴𝑥+𝑡∶𝑛−𝑡| − (𝐴𝑥+𝑡∶𝑛−𝑡| )2 ]
𝛿
• fully continuous endowment insurance
𝑃𝑒 = 𝑃𝑔 − 𝑃𝑛
• expense premium (sometimes, expense loading) is defined as the difference of gross premium and net premium: if
expenses are weighted to the start of the contract, as is normally the case, then 𝑃 𝑒 will be greater than the renewal
expense as it must fund both the renewal and initial expenses.
𝑒
𝑡𝑉 = 𝑡 𝑉 𝑔 − 𝑡 𝑉 = 𝐸𝑃 𝑉𝑡 (future expenses) − 𝐸𝑃 𝑉𝑡 (future expense loadings)
• expense reserves, defined as the difference between gross reserves and net reserves, also equals the expected present
value of future expenses less the expected present value of future expense loadings (or expense premiums)
Generally:
• 𝑡𝑉 𝑒 < 0
• 𝑡𝑉 > 𝑡𝑉 𝑔 > 0 > 𝑡𝑉 𝑒
For whole life, term and endowment insurance, the present value of benefits decreases with time, while the present value
of the premium annuity increases
Percentile of 0 𝐿:
Since loss at issue is a monotonically decreasing function of lifetime, the 100𝑝-th percentile of the loss at issue r.v. is
determined from the 100𝑝-th percentile of the survival function.
Probability of 0 𝐿:
To calculate the probability that the present value of loss at issue is greater than some value 𝑃 𝑟(0 𝐿 ≥ 𝐿𝜋 ), calculate the
time 𝑡𝜋 for which 0 𝐿 = 𝑏 𝑣𝑡𝜋 − 𝑃 𝑎𝑡𝜋 | has the required value, then calculate the probability that time of death 𝑇𝑥 is in
that range.
10.6 Methods
The PolicyValues class implements methods for computing net and gross future losses, and policy values (expected
present values). The Contract class is used store and retrieve policy contract terms, such as benefit amounts and the
various types of expenses.
class PolicyValues - Compute net and gross future losses and policy values
Methods:
--------
gross_policy_value(x, s, t, n, contract):
Gross policy values for insurance: t_V = E[L_t]
gross_policy_variance(x, s, t, n, contract):
Variance of gross policy value for WL and Endowment Insurance only
gross_future_loss(A, a, contract):
Shortcut for gross policy value with WL or Endowment Insurance factors
L_from_t(t, contract):
PV of Loss L(t) at time of death t = T_x
L_to_t(L, contract):
Time of death T_x s.t. PV future loss is no more than L
L_to_prob(x, L, contract):
Probability such that PV of future loss r.v. is no more than L"
[Link](Contract)
Args:
premium : level premium amount
benefit : insurance death benefit amount
settlement_policy : settlement expense per policy
endowment : endowment benefit amount
initial_policy : first year total expense per policy
initial_premium : first year total premium per $ of gross premium
renewal_policy : renewal expense per policy
renewal_premium : renewal premium per $ of gross premium
discrete : annuity due (True) or continuous (False)
T : term of insurance
discrete : annuity due (True) or continuous (False)
Methods:
--------
set_contract(terms):
Update any existing policy contract terms
premium_terms():
Dict of terms required for calculating gross premiums
renewal_profit():
Renewal dollar profit (premium less renewal expenses)
initial_cost():
Total initial cost (excludes renewal expenses)
claims_cost():
Total claims cost (death benefit + settlement expense)
renewals(t):
Returns contract object with initial terms set to renewal terms
10.6. Methods 75
Solving Actuarial Math with Python
10.7 Examples
The Contract helper class can be initialized to store expenses, benefit amounts and values of other contract features.
Its set_contract method is called to update individual values, its renewals method returns a new Contract object
with its initial expenses changed to be the same as renewal expenses, and other “getter” methods derive various summary
values.
contract = Contract(benefit=1000,
settlement_policy=20,
initial_policy=10,
initial_premium=0.75,
renewal_policy=2,
renewal_premium=0.1)
print(contract.premium_terms)
print([Link]().premium_terms)
contract.set_contract(premium=35.38618830746352)
print(contract.initial_cost)
print(contract.claims_cost)
print(contract.renewal_profit)
31.00102239985129
1020
29.84756947671717
The gross_policy_value method in the PolicyValues class compute policy values at issue or future year 𝑡
for general insurances and premiums. For whole life and endowment insurances only, gross_policy_variance
computes the variance of the future gross loss PV random variable. Internally, these use the gross_future_loss
and gross_variance_loss helper methods, which are also available to be called directly if the actuarial insurance
factors are already provided as inputs.
When net premiums are given by the equivalence principle, net_policy_value computes the net policy values at
issue or a future year 𝑡. For whole life and endowment insurances only, net_policy_variance compute the variance
of the future net loss PV random variable. Internally, these use the net_future_loss and net_variance_loss
helper methods, which are also available to be called directly if the actuarial insurance factors are already provided as
inputs.
print(life.gross_policy_value(x=20, contract=contract),
life.gross_policy_variance(x=20, contract=contract))
0.0 12.88972064810054
3734.4039865925088 4534.593306902999
-514.7435740643272 9155.101027638082
The L_from_t method computes the present value of the future loss when death occurs at time 𝑡, while L_from_prob
returns the 100𝑝-th percentile value of 𝐿. The inverse computations are performed by L_to_t and L_to_prob
methods respectively. L_plot plots the present value of future loss r.v. 𝐿 as a function of time 𝑡. The example below
determines the breakeven lifetime, when the present value of future loss L(t) = 0, and annotates in a plot with survival
probabilities.
L: -4.905848505210031e-10 -4.905742478911179e-10
10.7. Examples 77
Solving Actuarial Math with Python
For a fully continuous whole life insurance of 1 on (x), you are given:
• L is the present value of the loss at issue random variable if the premium rate is determined by the equivalence
principle
• L^* is the present value of the loss at issue random variable if the premium rate is 0.06
• 𝛿 = 0.07
• 𝐴𝑥 = 0.30
• 𝑉 𝑎𝑟(𝐿) = 0.18
Calculate 𝑉 𝑎𝑟(𝐿∗ ).
• 𝛿 = 0.06
Calculate the variance of the present value of future loss at 𝑡 for Policy B.
Year 1 Years 2+
10.7. Examples 79
Solving Actuarial Math with Python
ELEVEN
RESERVES
The term reserves is sometimes used in place of policy values. AMLCR uses policy value to mean the expected value of
the future loss random variable, and restricts reserve to mean the actual capital held in respect of a policy, which may be
greater than or less than the policy value.
11.1 Recursion
The following recursive formulae relating 𝑡 𝑉 to 𝑡+1 𝑉 for policy values can be derived for policies with discrete cash flows.
Gross reserves
(𝑡 𝑉 𝑔 + 𝐺 − 𝑒)(1 + 𝑖) = 𝑞𝑥+𝑡 (𝑏 + 𝐸) + 𝑝𝑥+𝑡 𝑡+1 𝑉 𝑔
• recursion for gross reserves
Expense reserves
(𝑡 𝑉 𝑒 + 𝑃 𝑒 − 𝑒)(1 + 𝑖) = 𝑞𝑥+𝑡 𝐸 + 𝑝𝑥+𝑡 𝑡+1 𝑉 𝑒
• recursion for expense reserves
Net reserves
(𝑡 𝑉 + 𝑃 )(1 + 𝑖) = 𝑞𝑥+𝑡 𝑏 + 𝑝𝑥+𝑡 𝑡+1 𝑉
• recursion for net reserves
Refund of benefit reserve
(𝑡 𝑉 + 𝑃 )(1 + 𝑖) = 𝑞𝑥+𝑡 (𝑏 + 𝑡+1 𝑉 ) + 𝑝𝑥+𝑡 𝑡+1 𝑉
• if reserves are also refunded after death, then the end-of-year value reserves should be added to death benefits in
the reserves recursion formula
Recursive formulae for interim reserves 𝑡+𝑟 𝑉 where 0 ≤ 𝑟 ≤ 1 can be similarly obtained. These compute the reserve
for fully discrete annual-premium insurances in the middle of the year.
(𝑡 𝑉 + 𝑃 )(1 + 𝑖)𝑟 = 𝑟 𝑞𝑥+𝑡 𝑏 𝑣1−𝑟 + 𝑟 𝑝𝑥+𝑡 𝑡+𝑟 𝑉
• forward recursion for interim net reserves
81
Solving Actuarial Math with Python
Because acquisition expenses are large relative to the renewal and claims expenses, accounting with level net premiums
typically results in large negative values for expense reserves (called deferred acquisition costs or DAC) particularly at
issue. Modified premium reserves are computed without expenses, and modifies the net premium method to assume a
lower initial net premium that allow implicitly for the DAC.
Full Preliminary Term
FPT is the most common method for modifying net premium policy value. It treats the insurance policy as one-year term
insurance combined with a policy as if it were issued one year later.
𝛼 = 𝐴1𝑥∶1| = 𝑣 𝑞𝑥
11.4 Methods
The Reserves class implements methods to solve reserves by recursion, and compute interim and modified reserves.
Methods:
--------
r_V_backward(x, s, r, benefit):
Backward recursion for interim reserves
FPT_premium(x, s, n, b, first):
Initial or renewal Full Preliminary Term premiums
reserves_frame():
Returns reserves table as a DataFrame
11.5 Examples
The FPT_premium and FPT_policy_value methods compute the Full Preliminary Term premiums and policy
values for whole life, temporary and endowment insurances.
import math
def S(x, s, t): # define a survival function
return [Link](-.00022*t - (.0000027*1.124**(x+s)*(1.124**t-1))/[Link](1.124))
life = Reserves().set_survival(S=S, minage=20, maxage=130)\
.set_interest(i=0.05)
P = life.net_premium(x=20, b=1000)
print('Level Net Premium:', P)
print('FPT premiums: ',
life.FPT_premium(x=20, b=1000, first=True),
life.FPT_premium(x=20, b=1000, first=False))
print('FPT policy values:',
[round(life.FPT_policy_value(x=20, b=1000, t=t), 4) for t in range(5)])
The set_reserves method is called to load given values of reserves into an internal table, and then
fill_reserves method can be called to iteratively fill in missing values. Internally, this calls the t_V method
which attempts both the t_V_forward and t_V_backward methods that implement the recursion formula forwards
and backwards respectively. Interim reserves recursion formulas for fractional durations are similarly implemented in the
r_V_forward and r_V_backward methods. Values of reserves over time can be plotted with V_plot or displayed
with the reserves_frame methods.
SOA Question 7.31
For a fully discrete 3-year endowment insurance of 1000 on (x), you are given:
• Expenses, payable at the beginning of the year, are:
11.5. Examples 83
Solving Actuarial Math with Python
11.5. Examples 85
Solving Actuarial Math with Python
TWELVE
RECURSION
Using annual values provided we can calculate other values at other ages and durations by applying recursion formulas
and other actuarial identities.
𝑡+𝑛 𝑝𝑥 = 𝑛 𝑝𝑥 ⋅ 𝑡 𝑝𝑥+𝑛
• survival probability chain rule
𝑡+𝑛 𝐸𝑥 = 𝑛 𝐸𝑥 ⋅ 𝑡 𝐸𝑥+𝑛
• pure endowment chain rule
For both complete and curtate future lifetime, we can decompose the formula for the life expectancy of (𝑥) into a
temporary life expectancy, plus the probability of surviving till the end of that term times the remaining life expectancy.
𝑒𝑥 = 𝑒𝑥∶𝑚| + 𝑚 𝑝𝑥 𝑒𝑥+𝑚
• recursion formula for curtate expectation of lifetime
∘ ∘ ∘
𝑒𝑥 = 𝑒𝑥∶𝑚| + 𝑚 𝑝𝑥 𝑒𝑥+𝑚
• recursion formula for complete expectation of lifetime
𝑒𝑥∶𝑚+𝑛| = 𝑒𝑥∶𝑚| + 𝑚 𝑝𝑥 𝑒𝑥∶𝑛|
• recursion formula for limited curtate expectation of lifetime
∘ ∘ ∘
𝑒𝑥∶𝑚+𝑛| = 𝑒𝑥∶𝑚| + 𝑚 𝑝𝑥 𝑒𝑥+𝑚∶𝑛|
• recursion formula for limited complete expectation of lifetime
𝑒𝑥 = 𝑝𝑥 (1 + 𝑒𝑥+1 )
• special case of a one-year recursion formula for curtate expectaion of lifetime
∘ ∘ ∘
𝑒𝑥 = 𝑒𝑥∶1| + 𝑝𝑥 𝑒𝑥+1
• special case of a one-year recursion formula for complete expectation of lifetime
𝑒𝑥∶1| = 𝑝𝑥
• shortcut for one-year limited curtate expectaion of lifetime
87
Solving Actuarial Math with Python
𝐴𝑥 − 𝑣 𝑞 𝑥
𝐴𝑥 = 𝑣 𝑞𝑥 + 𝑣 𝑝𝑥 𝐴𝑥+1 ⇒ 𝐴𝑥+1 =
𝑣 𝑝𝑥
• whole life insurance recursion
𝐴1𝑥∶𝑡| = 𝑣 𝑞𝑥 + 𝑣 𝑝𝑥 𝐴1𝑥+1∶𝑡−1|
𝑎𝑥̈ − 1
𝑎𝑥̈ = 1 + 𝑣 𝑝𝑥 𝑎𝑥+1
̈ ⇒ 𝑎𝑥+1
̈ =
𝑣 𝑝𝑥
• whole life annuity recursion
𝑎𝑥∶𝑡|
̈ = 1 + 𝑣 𝑝𝑥 𝑎𝑥+1∶𝑡−1|
̈
• temporary annuity recursion
𝑎𝑥∶1|
̈ =1
• shortcut for one-year temporary annuity
12.5 Methods
The Recursion class implements methods to apply recursive, shortcut and actuarial formulas, and traces the steps taken
to find the solution.
Caveats:
1. Not all possible recursion rules and actuarial equations have (yet) been implemented in the present version of the
package.
2. You may set the recursion depth to a larger limit than the default of 3 (with the keyword argument depth when
initializing a Recursion class object).
3. But generally, the current implementation may be fragile if the solution is not available within a relatively shallow
search.
Notes:
• If a colab or jupyter notebook is auto-detected, the steps are displayed in latex format; else as raw text.
• These display options can be changed by calling the blog_options static method
Args:
depth : maximum depth of recursions (default is 3)
verbose : whether to echo recursion steps (True, default)
Notes:
7 types of function values can be loaded for recursion computations:
Methods:
--------
set_q(val, x, s, t, u):
Set mortality rate u|t_q_[x+s] to given value
set_p(val, x, s, t):
Set survival probability t_p_[x+s] to given value
12.5. Methods 89
Solving Actuarial Math with Python
set_IA(val, x, s, t, b, discrete):
Set increasing insurance IA_[x+s]:t to given value
set_DA(val, x, s, t, b, discrete):
Set decreasing insurance DA_[x+s]:t to given value
blog_options(latex, notebook):
Static method to change display options for tracing the recursion steps
12.6 Examples
The given values of functions are set by calling the respective “setter” methods set_q, set_p, set_e, set_E,
set_A, set_IA, set_DA, or set_a. Then derived function values, at other ages or durations, can be computed
– the successful recursion steps taken are displayed if the verbose flag is initiated to True (which is the default value).
AMLCR2 Exercise 2.6
Given 𝑃𝑥 = 0.99 , 𝑃𝑥+1 = 0.985, 3 𝑃𝑥+1 = 0.95, 𝑞𝑥+3 = 0.02,
Calculate (a) 𝑃𝑥+3 , (b) 2 𝑃𝑥 , (c) 2 𝑃𝑥+1 , (d) 3 𝑃𝑥 , (e) 1|2 𝑞𝑥 .
print(life.p_x(x=x+3), 0.98)
print(life.p_x(x=x, t=2), 0.97515)
print(life.p_x(x=x+1, t=2), 0.96939)
print(life.p_x(x=x, t=3), 0.95969)
print(life.q_x(x=x, t=2, u=1), 0.03031)
Survival 𝑝𝑥+3 ∶=
𝑝𝑥+3 = 1 − 𝑞𝑥+3 complement of mortality
0.98 0.98
Survival 2 𝑝𝑥 ∶=
2 𝑝𝑥 = 3 𝑝𝑥 / 𝑝𝑥+2 survival chain rule
𝑝𝑥+2 = 2 𝑝𝑥+1 / 𝑝𝑥+1 survival chain rule
2 𝑝𝑥+1 = 3 𝑝𝑥+1 / 𝑝𝑥+3 survival chain rule
3 𝑝𝑥 = 4 𝑝𝑥 / 𝑝𝑥+3 survival chain rule
4 𝑝𝑥 = 3 𝑝𝑥+1 ∗ 𝑝𝑥 survival chain rule
𝑝𝑥+3 = 1 − 𝑞𝑥+3 complement of mortality
0.9751500000000001 0.97515
Survival 2 𝑝𝑥+1 ∶=
2 𝑝𝑥+1 = 3 𝑝𝑥 / 𝑝𝑥 survival chain rule
3 𝑝𝑥 = 4 𝑝𝑥 / 𝑝𝑥+3 survival chain rule
4 𝑝𝑥 = 3 𝑝𝑥+1 ∗ 𝑝𝑥 survival chain rule
𝑝𝑥+3 = 1 − 𝑞𝑥+3 complement of mortality
0.9693877551020409 0.96939
Survival 3 𝑝𝑥 ∶=
3 𝑝𝑥 = 4 𝑝𝑥 / 𝑝𝑥+3 survival chain rule
4 𝑝𝑥 = 3 𝑝𝑥+1 ∗ 𝑝𝑥 survival chain rule
𝑝𝑥+3 = 1 − 𝑞𝑥+3 complement of mortality
0.9596938775510204 0.95969
Mortality 1|2 𝑞𝑥 ∶=
1|2 𝑞𝑥 = 𝑝𝑥 − 3 𝑝𝑥 complement survival
3 𝑝𝑥 = 4 𝑝𝑥 / 𝑝𝑥+3 survival chain rule
4 𝑝𝑥 = 3 𝑝𝑥+1 ∗ 𝑝𝑥 survival chain rule
𝑝𝑥+3 = 1 − 𝑞𝑥+3 complement of mortality
0.030306122448979567 0.03031
12.6. Examples 91
Solving Actuarial Math with Python
7.2735849056603765 0.8000943396226414
116.51945397474269
Survival 𝑝𝑥+2 ∶=
𝑝𝑥+2 = 𝐸𝑥+2 /𝑣 one-year pure endowment
𝐸𝑥+2 = 𝐴𝑥+2∶1| − 𝐴1𝑥+2∶1| endowment insurance minus term
𝐴1𝑥+2∶1| = [ 𝐴1𝑥+1∶2| /𝑣 − 𝑞𝑥+1 ∗ 𝑏]/ 𝑝𝑥+1 forward recursion
𝑝𝑥+1 = [ 𝑎𝑥+1∶2|
̈ − 1]/[𝑣 ∗ 𝑎𝑥+2∶1|
̈ ] annuity recursion
𝑎𝑥+1∶2|
̈ = [ 𝑎𝑥∶3|
̈ − 1]/ 𝐸𝑥 forward recursion
𝐴1𝑥+1∶2| = [ 𝐴1𝑥∶3| /𝑣 − 𝑞𝑥 ∗ 𝑏]/ 𝑝𝑥 forward recursion
𝐸𝑥 = 𝑝𝑥 ∗ 𝑣 pure endowment
0.9097382950525702
Pure Endowment 5 𝐸𝑥 ∶=
5
5 𝐸𝑥 = 5 𝑝𝑥 ∗ 𝑣 pure endowment
Pure Endowment 5 𝐸𝑥 ∶=
5
5 𝐸𝑥 = 5 𝑝𝑥 ∗ 𝑣 pure endowment
Term Insurance 𝐴1𝑥+5∶3| ∶=
𝐴1𝑥+5∶3| = 𝐴𝑥+5∶3| − 3 𝐸𝑥+5 endowment insurance - pure
3 𝐸𝑥+5 = 𝐸𝑥+5 ∗ 2 𝐸𝑥+6 pure endowment chain rule
2 𝐸𝑥+6 = 𝐸𝑥+6 ∗ 𝐸𝑥+7 pure endowment chain rule
𝐸𝑥+7 = 𝑝𝑥+7 ∗ 𝑣 pure endowment
𝐸𝑥+6 = 𝑝𝑥+6 ∗ 𝑣 pure endowment
𝐸𝑥+5 = 𝑝𝑥+5 ∗ 𝑣 pure endowment
𝑝𝑥+7 = 1 − 𝑞𝑥+7 complement of mortality
𝐴1𝑥+5∶3| = 𝑣 ∗ [ 𝑞𝑥+5 ∗ 𝑏 + 𝑝𝑥+5 ∗ 𝐴1𝑥+6∶2| ] backward recursion
𝐴1𝑥+6∶2| = 𝑣 ∗ [ 𝑞𝑥+6 ∗ 𝑏 + 𝑝𝑥+6 ∗ 𝐴1𝑥+7∶1| ] backward recursion
𝑝𝑥+6 = 1 − 𝑞𝑥+6 complement of mortality
𝑝𝑥+5 = 1 − 𝑞𝑥+5 complement of mortality
12.6. Examples 93
Solving Actuarial Math with Python
3195.118917658744
life1 = Recursion().set_interest(i=0.08)\
.set_q(life.q_x(x, t=1) * 1.5, x=x, t=1)\
.set_q(life.q_x(x+1, t=1) * 1.5, x=x+1, t=1)
contract = Contract(premium=P * 2, benefit=100000, endowment=30000)
L = life1.gross_policy_value(x, t=0, n=2, contract=contract)
print(L)
-30107.42633581115
12.6. Examples 95
Solving Actuarial Math with Python
Lifetime 𝑒𝑥+41 ∶=
𝑒𝑥+41 = [ 𝑒𝑥+40 − 𝑒𝑥+40∶1| ]/ 𝑝𝑥+40 forward recursion
𝑒𝑥+40 = 𝑒𝑥+40∶20| + 20 𝑝𝑥+40 ∗ 𝑒𝑥+60 backward recursion
20 𝑝𝑥+40 = 1 − 20 𝑞𝑥+40 complement of mortality
𝑒𝑥+40∶1| = 𝑝𝑥+40 1-year curtate shortcut
𝑝𝑥+40 = 1 − 𝑞𝑥+40 complement of mortality
37.11434302908726
THIRTEEN
LIFE TABLE
A life table, from some initial age 𝑥0 to a maximum age 𝜔, represents a survival model with probabilities 𝑡 𝑝𝑥 . A life table
is typically tabulated for the number of lives 𝑙𝑥 at integer ages 𝑥 only – fractional age assumptions would be needed to
calculate survival probabilities for non-integer ages and durations.
𝑙𝑥0 +𝑡 = 𝑙𝑥0 𝑡 𝑝𝑥0 .
• 𝑙𝑥0 is an arbitrary positive number of lives at starting age 𝑥0 called the radix
𝑑𝑥 = 𝑙𝑥 − 𝑙𝑥+1
• it is usual for a life table to also show the values of 𝑑𝑥 , the expected of deaths in the year of age 𝑥 to 𝑥 + 1.
𝑑𝑥
𝑞𝑥 =
𝑙𝑥
• the mortality rate can then be derived, which is the probability that a life aged 𝑥 dies within one year.
Recursion formulas from earlier sections allow the recursive construction of a table of other life contingent values, such
as life expectancy, insurance, and annuities, at all ages.
13.1 Methods
The LifeTable class specifies a given life table to be the survival model. It inherits all the general methods for com-
puting life contingency risks, and overrides those methods where values can be looked up or calculated from life table
entries.
class LifeTable - Calculate life table, and iteratively fill in missing values
Args:
udd : assume UDD or constant force of mortality for fractional ages
verbose : whether to echo update steps
Notes:
4 types of columns can be loaded and calculated in the life table:
97
Solving Actuarial Math with Python
Methods:
--------
fill_table(radix):
Iteratively fill in missing table cells (does not check consistency)
frame():
Return life table columns and values in a DataFrame
__getitem__(col):
Returns a column of the life table
13.2 Examples
The set_table method is called to load a life table, by age, with given values of number of lives, number of deaths,
mortality rate, and/or survival probability: if the fill flag is set to True (by default), fill_table is automatically
called to fill in any missing values using recursion and identify formulas. All other computational methods can then be
called in the usual manner, which will use the survival model provided by the life table.
AMLCR2 Exercise 3.2
You are given the following life table extract.
Age,x 𝑙𝑥
52 89948
53 89089
54 88176
55 87208
56 86181
57 85093
58 83940
59 82719
60 81429
Calculate
• 0.2 𝑞52.4 assuming UDD (fractional age assumption),
• 0.2 𝑞52.4 assuming constant force of mortality (fractional age assumption),
• 5.7 𝑝52.4 assuming UDD,
• 5.7 𝑝52.4 assuming constant force of mortality,
• 3.2|2.5 𝑞52.4 assuming UDD, and
• 3.2|2.5 𝑞52.4 assuming constant force of mortality.
13.2. Examples 99
Solving Actuarial Math with Python
• 𝑖 = 0.05
• The death benefit in the first year is 100,000
• Both the benefits and premiums increase by 1% in the second year
Calculate the annual net premium in the first year.
𝑥 60 61 62 63 64 65 66 67
𝑙𝑥 99,999 88,888 77,777 66,666 55,555 44,444 33,333 22,222
𝑎= 3.4|2.5 𝑞60 assuming a uniform distribution of deaths over each year of age
𝑏= 3.4|2.5 𝑞60 assuming a constant force of mortality over each year of age
Calculate 100, 000(𝑎 − 𝑏)
x 𝑙𝑥 𝑑𝑥 𝑝𝑥 𝑞𝑥
95 − − − 0.40
96 − − 0.20 −
97 − 72 − 1.00
FOURTEEN
SULT
This tabulates single net premiums and basic functions (whole life and endowment insurances, whole life and temporary
annuities and pure endowments) for several time periods at integer ages between 20 and 100 years. According to the
SOA’s “Excel Workbook for FAM-L Tables”, this table was developed from the following assumptions:
• constant interest rate 𝑖 = 0.05
• radix of 100000 initial lives aged 20
• incorporates Makeham’s Law as its survival model with 𝐴 = 0.00022, 𝐵 = 0.0000027, 𝑐 = 1.124
Pure endowment functions can be calculated from numbers of lives survived and compounded interest rates.
𝑙𝑥+𝑡
𝑡 𝐸𝑥 = 𝑣𝑡
𝑙𝑥
2 𝑙𝑥+𝑡
𝑡 𝐸𝑥 = 𝑣2𝑡 = 𝑣𝑡 𝑡 𝐸𝑥
𝑙𝑥
Term life insurance functions can be calculated from whole life insurance and pure endowment table columns.
𝐴1𝑥∶𝑡| = 𝐴𝑥 − 𝑡 𝐸𝑥 𝐴𝑥+𝑡 = 𝐴𝑥∶𝑡| − 𝑡 𝐸𝑥
2
𝐴1𝑥∶𝑡| = 2 𝐴𝑥 − 2𝑡 𝐸𝑥 2 𝐴𝑥+𝑡 = 2 𝐴𝑥 − 𝑣𝑡 𝑡 𝐸𝑥 2 𝐴𝑥+𝑡
103
Solving Actuarial Math with Python
14.4 Methods
The SULT class implements an instance of a LifeTable, called the standard ultimate life table, which is based on Make-
ham’s Law with parameters specified in SOA’s “Excel Workbook for FAM-L Tables”
import math
from actuarialmath import SULT
import describe
[Link](SULT)
Args:
i : interest rate
radix : initial number of lives
minage : minimum age
maxage : maximum age
S : survival function, default is Makeham with SOA FAM-L parameters
Examples:
>>> sult = SULT()
>>> a = sult.temporary_annuity(70, t=10)
>>> A = sult.deferred_annuity(70, u=10)
>>> P = sult.gross_premium(a=a, A=A, benefit=100000, initial_premium=0.75,
>>> renewal_premium=0.05)
Methods:
--------
frame(minage, maxage):
Derive FAM-L exam table columns of SULT as a DataFrame
__getitem__(col):
Returns a column of the sult table
14.5 Examples
Calculate 𝑓.
Taxes 5% 0 5% 0
Commissions 30% 0 10% 0
Maintenance 0% 8 0% 4
• i = 0.05
• 𝑎30∶5|
̈ = 4.5431
Calculate the annual gross premium using the equivalence principle.
• Expenses of 100 are payable at the end of each year including the year of death
• Mortality follows the Standard Ultimate Life Table
• i = 0.05
Calculate the annual gross premium using the equivalence principle.
import pandas as pd
print("Standard Ultimate Life Table at i=0.05")
pd.set_option('display.max_rows', None)
[Link]()
FIFTEEN
A newly selected policyholder is in the best health condition possible, compared to the general population with the same
age. The life table can be expanded to tabulate the select period when selection has an effect on mortality. Since this
selection process wears off after a few years, the ultimate part of the table can be then be used when select age is assumed
to no longer have an effect on mortality.
• Future survival probabilities depend on the individual’s current age and on the age at which the individual joined
the group (i.e. was selected). Current age is written [𝑥] + 𝑠, where 𝑥 is the selected age and 𝑠 is the number of
years after selection.
• If an individual joined the group more than 𝑑 years ago (called the select period), future survival probabilities (called
the ultimate mortality) depend only on current age. The initial selection effect is assumed to have worn off after 𝑑
years. Current age can be written as 𝑥 + 𝑠 after the select period 𝑠 ≥ 𝑑
Select life tables reflect duration as well as age during the select period. A select and ultimate mortality table is shown
in tabular form by listing agex vertically and the selection durations horizontally. If the select period is 𝑛, there are 𝑛
columns followed by a column with ultimate mortality. To find the mortality at a duration after a selection age, we read
across the row corresponding to that selection age then continue down the last column.
Notation for select survival models:
• defines the life table within the select period, by working backwards from the value of 𝑙𝑥+𝑑 in the ultimate part of
the table which only depends on current age.
With a select period 𝑑 and for 𝑠 ≥ 𝑑 (i.e. durations beyond the select period) the values of 𝑝[𝑥−𝑠]+𝑠 , 𝑞[𝑥−𝑠]+𝑠 , 𝑙[𝑥−𝑠]+𝑠
depend only on current age 𝑥 and not on 𝑠. So for 𝑠 ≥ 𝑑, these terms are all equal to and can be written simply as
𝑝𝑥 , 𝑞𝑥 , 𝑙𝑥 respectively.
115
Solving Actuarial Math with Python
15.2 Methods
The SelectLife class specifies a given select life table to be the survival model. It inherits all the general methods for
computing life contingency risks, and overrides those methods where values can be looked up or calculated from select
life table entries.
class SelectLife - Calculate select life table, and iteratively fill in missing␣
↪values
Args:
periods : number of select period years
verbose : whether to echo update steps
Notes:
6 types of columns can be loaded and calculated in the select table:
Methods:
--------
set_table(fill, l, d, q, A, a, e):
Update from table, every age has row for all select durations
fill_table(radix):
Fills in missing table values (does not check for consistency)
__getitem__(table):
Returns values from a select and ultimate table
frame(table):
Returns select and ultimate table values as a DataFrame
l_x(x, s):
Returns number of lives aged [x]+s computed from select table
p_x(x, s, t):
t_p_[x]+s by chain rule: prod(1_p_[x]+s+y) for y in range(t)
q_x(x, s, t, u):
t|u_q_[x]+s = [x]+s survives u years, does not survive next t
e_x(x, s, t, curtate):
Returns expected life time computed from select table
(continues on next page)
15.3 Examples
The set_table method is called to load a life table, by age and duration, with given values of number of lives, number
of deaths, mortality rate, insurance, annuity and/or life expectancy. The set_select method can also be called instead
to update values of the same functions but for a particular duration 𝑠 only. If the fill flag is set to True, fill_table
is automatically called to fill in any missing values using recursion and identify formulas. All other computational methods
can then be called in the usual manner, which will use the survival model provided by the select life table.
SOA Question 3.2:
You are given:
• The following extract from a mortality table with a one-year select period:
𝑥 𝑞𝑥
50 0.045
51 0.050
52 0.055
53 0.060
• 𝑖 = 0.04
• The death benefit is payable at the end of the year of death
Calculate the actuarial present value of this insurance.
𝑒64 = 5.10
Calculate 𝑒[61] .
l_[x]+s: 0 1 2 3
Age
21 100000.000000 99880.000000 99730.180000 99560.638694
22 99834.996495 99710.202749 99555.651935 99381.429544
23 99665.273502 99535.708646 99376.451512 99197.573900
SIXTEEN
MORTALITY LAWS
Another approach to defining a survival model is to fit a parametric function, which may have convenient properties
that simplify computations. When using these special mortality laws for lifetime distribution, shortcut formulas may be
available without the need for numerical integration.
If deaths are uniformly distributed then insurance benefits are paid out in a level manner. A convenient property of a
uniform distribution on [0, 𝜃] is that it has a midrange mean and median of 𝜃/2
𝑙𝑥 ∼ 𝜔 − 𝑥
𝜔 − (𝑥 + 𝑡)
𝑡 𝑝𝑥 =
𝜔−𝑥
• number of lives, and survival function, are linearly declining with age
1
𝜇𝑥+𝑡 = 𝑓𝑥 (𝑡) =
𝜔−𝑥−𝑡
• the force of mortality and lifetime density function are identical
∘ 𝜔−𝑥
𝑒𝑥 =
2
• expected future lifetime is the mean of a uniform distribution
∘ 𝑛
𝑒𝑥∶𝑛| = 𝑛 𝑝𝑥 𝑛 + 𝑛 𝑞𝑥
2
• expected limited lifetime is the weighted sum of the mean lifetime of deaths (for those who died) and the limit 𝑛
(for those who survived)
$Var(T_x) = \dfrac{(\omega - x)^2}{12}
• variance of complete future lifetime is the variance of a uniform distribution
𝜔 − (𝑥 + 𝑛)
𝑛 𝐸𝑥 = 𝑣𝑛
𝜔−𝑥
• pure endowment shortcut by substituting in formula for the survival function
𝑎𝜔−𝑥|
̄
𝐴𝑥̄ =
𝜔−𝑥
1
• it is not necessary to carry out integration since whole life insurance is equivalent to an annuity-certain that pays
𝜔
per year. To calculate the second moment of insurance, simply double the force of interest in the annuity-certain
in the shortcut formula.
123
Solving Actuarial Math with Python
𝑎𝑛|
̄
̄
𝐴1𝑥∶𝑛| =
𝜔−𝑥
1
• term insurance is an annuity-certain that pays per year for the term of the insurance.
𝜔
This is a generalized version of the uniform distribution with two parameters 𝛼, 𝜔. The uniform is a special case of the
Beta distribution with 𝛼 = 1. However, unlike the uniform, its density function is longer constant.
𝑙𝑥 ∼ (𝜔 − 𝑥)𝛼
𝛼(𝜔 − 𝑥 − 𝑡)𝛼−1
𝑓𝑥 (𝑡) =
(𝜔 − 𝑥)𝛼
𝛼
𝜇𝑥 =
𝜔−𝑥
𝜔 − (𝑥 + 𝑡) 𝛼
𝑡 𝑝𝑥 = ( )
𝜔−𝑥
∘ 𝜔−𝑥
𝑒𝑥 =
𝛼+1
𝛼(𝜔 − 𝑥)2
𝑉 𝑎𝑟(𝑇𝑥 ) =
(1 + 𝛼)2 (2 + 𝛼)
Gompertz proposed this law based on observations that the logarithm of the force of mortality appeared to fit well to a
straight line for ages above 20.
𝜇𝑥 = 𝐵𝑐𝑥
𝐵𝑐𝑥
(𝑐𝑡 −1)
𝑡 𝑝𝑥 =𝑒 ln 𝑐
Makeham improved Gompertz’s law by adding a third parameters 𝐴 ≥ −𝐵. This includes represents a constant element
in the force of mortality that does not depend on age.
𝜇𝑥 = 𝐴 + 𝐵𝑐𝑥
𝐵𝑐𝑥
(𝑐𝑡 −1)−𝐴𝑡
𝑡 𝑝𝑥 =𝑒 ln 𝑐
16.5 Methods
The MortalityLaws class, and Beta, Uniform, Makeham and Gompertz subclasses, specify continuous func-
tions for survival distributions. These classes inherit all the general methods for computing life contingency risks, and
override those methods with shortcut formulas that may be available when assuming their respective mortality laws.
Methods:
--------
l_r(x, s, r):
Fractional lives given special mortality law: l_[x]+s+r
p_r(x, s, r, t):
Fractional age survival probability given special mortality law
q_r(x, s, r, t, u):
Fractional age deferred mortality given special mortality law
mu_r(x, s, r):
Fractional age force of mortality given special mortality law
f_r(x, s, r, t):
fractional age lifetime density given special mortality law
e_r(x, s, r, t):
Fractional age future lifetime given special mortality law
class Uniform - Shortcuts with uniform distribution of deaths aka DeMoivre's Law
Args:
omega : maximum age
Examples:
>>> print(Uniform(95).e_x(30, t=40, curtate=False)) # 27.692
class Beta - Shortcuts with beta distribution of deaths (is Uniform when alpha = 1)
Args:
omega : maximum age
alpha : alpha paramter of beta distribution
radix : assumed starting number of lives for survival function
Examples:
(continues on next page)
Args:
B, c : parameters of Gompertz distribution
Examples:
>>> print(Gompertz(B=0.00027, c=1.1).f_x(50, t=10)) # 0.04839
class Makeham - Includes element in force of mortality that does not depend on age
Args:
A, B, c : parameters of Makeham distribution
Examples:
>>> print(Makeham(A=0.00022, B=2.7e-6, c=1.124).mu_x(60) * 0.9803) # 0.00316
16.6 Examples
The Uniform class is initialized with the omega parameter to specify the range of the uniform distribution of deaths
assumption. All the inherited general computation methods can be accessed; where shortcut formulas are available, then
those specific methods are overriden.
print('Uniform')
uniform = Uniform(80).set_interest(delta=0.04)
print(uniform.whole_life_annuity(20)) # 15.53
print(uniform.temporary_annuity(20, t=5)) # 4.35
print(Uniform(161).p_x(70, t=1)) # 0.98901
print(Uniform(95).e_x(30, t=40, curtate=False)) # 27.692
print()
uniform = Uniform(omega=80).set_interest(delta=0.04)
print(uniform.E_x(20, t=5)) # .7505
print(uniform.whole_life_insurance(20, discrete=False)) # .3789
print(uniform.term_insurance(20, t=5, discrete=False)) # .0755
print(uniform.endowment_insurance(20, t=5, discrete=False)) # .8260
print(uniform.deferred_insurance(20, u=5, discrete=False)) # .3033
Uniform
16.03290804858584
4.47503070125663
0.989010989010989
32.30769230769231
0.7505031903214833
0.378867519462745
0.07552885288417432
0.8260320432056576
0.30333866657857067
The Beta class is initialized with omega and alpha parameters of the beta distribution of deaths assumption. All
the inherited general computation methods can be accessed; where shortcut formulas are available, then those specific
methods are overriden.
0.007188905547861446
50.0
13.333333333333332
The Gompertz or Makeham classes are initialized with the respective parameters of the Gompertz and Makeham Laws
assumptions. All the inherited general computation methods can be accessed.
869.3908338193208
0.048389180223511644
0.0031580641631654026
SEVENTEEN
If force of mortality is constant, then future lifetime is exponentially distributed. The exponential distribution is easy to
work with, and has the memoryless property that survival probability is independent of age (which is clearly an unrealistic
assumption for human mortality).
𝑡 𝑝𝑥 = 𝑒−𝜇𝑡
𝑡 𝑞𝑥 = 1 − 𝑒−𝜇𝑡
• survival functions do not depend on age x; given any desired probability of survival (or death), the time-of-death
threshold can also be easily derived by inverting this simple shortcut formula
∘ 1
𝑒𝑥 =
𝜇
• expected future lifetime is the mean of an exponential distribution, and does not depend on age x
∘ 1
𝑒𝑥∶𝑛| = (1 − 𝑒−𝜇𝑛 )
𝜇
• temporary life expectancy is expected future life time, minus the probability of surviving 𝑛 years times the expected
remaining lifetime of those survivors
1
𝑉 𝑎𝑟(𝑇𝑥 ) =
𝜇2
• variance of future lifetime is the variance of an exponential distribution
𝑛 𝐸𝑥 = 𝑒−(𝜇+𝛿)𝑛
• shortcut for pure endowment after substituting in the formula for survival function
129
Solving Actuarial Math with Python
1
𝑎𝑥̄ =
𝜇+𝛿
• whole life annuity, does not depend on age 𝑥
1
𝑎𝑥∶𝑡|
̄ = (1 − 𝑒−𝜇𝑡 )
𝜇+𝛿
• temporary life annuity as difference of whole life and deferred whole life annuity shortcut formulas
17.6 Methods
The ConstantForce class specifies a constant force of mortality for the survival model. It inherits all the general
methods for computing life contingency risks, and overrides those methods with shortcut formulas that are available when
assuming an exponential distribution for future lifetime.
Args:
mu : constant value of force of mortality
udd : assume UDD (True) or CFM (False, default) between integer ages
(continues on next page)
Examples:
>>> life = ConstantForce(mu=0.01).set_interest(delta=0.05)
>>> life.term_insurance(35, t=35, discrete=False) + life.E_x(35, t=35)*0.
↪51791
Methods:
--------
17.7 Examples
To use the constant force of mortality assumption, the ConstantForce subclass can be initialized with the value of
mu; then shortcut formulas for computing life contingencies, where available, would be called instead of general inherited
methods.
life = ConstantForce(mu=0.01).set_interest(delta=0.05)
life.term_insurance(35, t=35, discrete=False), life.E_x(35, t=35)
(0.14625726195783623, 0.12245642825298157)
Premium Rate 3P P
Benefit 1,000,000 500,000
EIGHTEEN
EXTRA RISK
If the underwriter determines that an individual should be offered insurance but at above standard rates, there are different
ways in which we can model the extra mortality risk in a premium calculation.
(𝑥) ← (𝑥 + 𝑘)
• add years to age, referred to as age rating: the insurer may compensate for extra risk by treating the individual as
being older, for example, an impaired life aged 40 might be asked to pay the same premium paid by a non- impaired
life aged 45.
𝑞𝑥 ← 𝑞𝑥 ⋅ 𝑘
• multiply mortality rate by a constant, which assumes that lives are subject to mortality rates that are higher than the
standard lives’ mortality rates.
A linear transformation of 𝜇𝑥 affects survival probability in the following ways: Recall that 𝜇 is exponentiated to get 𝑝,
hence adding a constant to 𝜇 requires multiplying 𝑡 𝑝𝑥 by the constant exponentiated, while multiplying 𝜇 by a factor
requires raising 𝑡 𝑝𝑥 to that power
𝜇𝑥+𝑡 ← 𝜇𝑥+𝑡 + 𝑘 ⇒ 𝑡 𝑝𝑥 ← 𝑡 𝑝𝑥 𝑒−𝑘𝑡
• add constant to force of mortality, when the extra risk is largely independent of age
𝜇𝑥+𝑡 ← 𝜇𝑥+𝑡 ⋅ 𝑘 ⇒ 𝑡 𝑝𝑥 ← (𝑡 𝑝𝑥 )𝑘
• multiply force of mortality by constant
135
Solving Actuarial Math with Python
18.4 Methods
The ExtraRisk class implements methods to adjust the survival or mortality function by extra risks.
Args:
life : contains original survival and mortality rates
extra : amount of extra risk to adjust
risk : adjust by {"ADD_FORCE", "MULTIPLY_FORCE", "ADD_AGE", "MULTIPLY_RATE"}
Methods:
--------
q_x(x, s):
Return q_[x]+s after adding age rating or multipliying mortality rate
p_x(x, s):
Return p_[x]+s after adding or multiplying force of mortality
__getitem__(col):
Returns survival function values adjusted by extra risk
18.5 Examples
The ExtraRisk class is initialized with its life argument set to an instance of a survival model, with amount of ex-
tra risk and type of risk (either “ADD_FORCE”, “MULTIPLY_FORCE”, “ADD_AGE” or “MULTIPLY_RATE”)
specified. The q_x or p_x methods can then be called to return the adjusted mortality rate or survival probability
respectively, or an entire column of values by age can be retrieved as the item “q” or “p”.
SOA Question 5.5
For an annuity-due that pays 100 at the beginning of each year that (45) is alive, you are given:
• Mortality for standard lives follows the Standard Ultimate Life Table
• The force of mortality for standard lives age 45 + t is represented as 𝜇𝑆𝑈𝐿𝑇
45+𝑡
• The force of mortality for substandard lives age 45 + t, 𝜇𝑆45+𝑡 , is defined as:
𝜇𝑆45+𝑡 = 𝜇𝑆𝑈𝐿𝑇
45+𝑡 + 0.05, 0≤𝑡<1
= 𝜇𝑆𝑈𝐿𝑇
45+𝑡 , 𝑡≥1
• 𝑖 = 0.05
Calculate the actuarial present value of this annuity for a substandard life age 45.
Other examples
life = SULT()
extra = ExtraRisk(life=life, extra=2, risk="MULTIPLY_FORCE")
print(life.p_x(45), extra.p_x(45))
0.9992288829941123 0.9984583606096613
NINETEEN
1/M’THLY
A 1/m-thly annuity pays 1/𝑚 every 𝑚-th of a year, while a 1/m-thly insurance pays $1 of benefits at the end of the
(𝑚)
1/𝑚-th year of death. A new lifetime random variable 𝐾𝑥 is introduced to value benefits which depend on the number
of complete periods of length 1/𝑚 years lived by a life (x).
(𝑚) 1
𝐾𝑥 = ⌊𝑚𝑇𝑥 ⌋
𝑚
• 1/mthly curtate future lifetime random variable, where 𝑚 > 1 is an integer, is the future lifetime of (x) in years
1
rounded to the lower 𝑚 th of a year.
(𝑚) 1
𝑃 𝑟[𝐾𝑥 = 𝑘] = 𝑃 𝑟[𝑘 ≤ 𝑇𝑥 ≤ 𝑘 + ]= 1 𝑞
𝑘| 𝑚 𝑥 = 𝑘 𝑝𝑥 − 1 𝑝
𝑘+ 𝑚 𝑥
𝑚
(𝑚)
• the probability function for 𝐾𝑥 can be derived from the associated probabilities for 𝑇𝑥 .
• death benefit is payable at the end of the 1/m-th year of death , provided this occurs within 𝑡 years.
(𝑚) 𝑚𝑡−1 𝑘+1
𝐴1𝑥∶𝑡| = ∑𝑘=0 𝑣 𝑚 𝑘 1 𝑞𝑥
𝑚|𝑚
139
Solving Actuarial Math with Python
(𝑚) 1 𝑘
∞
𝑎𝑥̈ = ∑𝑘=0 𝑣 𝑚 𝑘 𝑝𝑥
𝑚 𝑚
Whole and Temporary Life Annuities (and Whole Life and Endowment Insurance) ONLY:
(𝑚)
(𝑚) (𝑚) (𝑚) 1 − 𝐴𝑥
𝐴𝑥 = 1 − 𝑑(𝑚) 𝑎𝑥̈ ⟺ 𝑎𝑥̈ =
𝑑(𝑚)
• 1/m’thly whole life annuity due
(𝑚)
1 − 𝐴𝑥∶𝑡|
(𝑚) (𝑚) (𝑚) (𝑚)
𝐴𝑥∶𝑡| =1−𝑑 𝑎𝑥∶𝑡|
̈ ⟺ 𝑎𝑥∶𝑡|
̈ =
𝑑(𝑚)
• 1/m’thly temporary annuity due and endowment insurance
19.4 Methods
Ths Mthly class implements methods to compute life insurance and annuity values with 1/mthly benefits.
Args:
m : number of payments per year
life : original survival and life contingent functions
Methods:
--------
v_m(k):
Compute discount rate compounded over k m'thly periods
term_insurance(x, s, t, b, moment):
Term life insurance: A_x:t^1
deferred_insurance(x, s, n, b, t, moment):
Deferred insurance n|_A_x:t^1 = discounted whole life
immediate_annuity(x, s, t, b):
Immediate m'thly annuity
insurance_twin(a):
Return insurance twin of m'thly annuity
annuity_twin(A):
Return value of annuity twin of m'thly insurance
whole_life_annuity(x, s, b, variance):
Whole life m'thly annuity: a_x
temporary_annuity(x, s, t, b, variance):
Temporary m'thly life annuity: a_x:t
deferred_annuity(x, s, u, t, b):
Deferred m'thly life annuity due n|t_a_x = n+t_a_x - n_a_x
immediate_annuity(x, s, t, b):
Immediate m'thly annuity
19.5 Examples
The Mthly class is initialized with its life argument set to an instance of a survival model with fractional age assump-
tion, and the number of periods 𝑚 in a year. The v_m, p_m and q_m methods can then be called to compute the discount
factor, survival probability and mortality rate for m-thly ages and durations. The Z_m methods returns a table of the PV
of the insurance random variable and deferred mortality rate by m-thly time of death. Actuarial present values, at integer
ages, of insurance and annuities that pay every m-th of a year, can be computed by calling their respective methods in the
usual manner.
SOA Question 6.4
For whole life annuities-due of 15 per month on each of 200 lives age 62 with independent future lifetimes, you are given:
• 𝑖 = 0.06
(12) (12)
• 𝐴62 = 0.2105 and 2 𝐴62 = 0.4075
• 𝜋 is the single premium to be paid by each of the 200 lives
• 𝑆 is the present value random variable at time 0 of total payments made to the 200 lives
Using the normal approximation, calculate 𝜋 such that 𝑃 𝑟(200𝜋 > 𝑆) = 0.90.
TWENTY
UDD M’THLY
With the UDD fractional age assumption, we can work with annual insurance and annuity factors 𝐴𝑥 and 𝑎𝑥̈ , then adjust
(𝑚) (𝑚)
for a more appropriate frequency 𝐴𝑥 and 𝑎𝑥̈ using the following relationships.
Under UDD, continuous life insurance can also be related to annual life insurance factors
𝑖
𝐴𝑥 = 𝐴𝑥
𝛿
• whole life insurance
2 𝑖2 − 2𝑖 2
𝐴𝑥 = 𝐴𝑥
2𝛿
• doubling the force of interest for whole life insurance
145
Solving Actuarial Math with Python
1 𝑖
𝐴𝑥∶𝑡| = 𝐴1𝑥∶𝑡|
𝛿
• term life insurance
𝑖
𝐴𝑥∶𝑡| = 𝐴1𝑥∶𝑡| + 𝑡 𝐸𝑥
𝛿
• endowment insurance
𝑖
𝑢| 𝐴𝑥 = 𝑢 𝐸𝑥 𝐴𝑥+𝑢
𝛿
• deferred life insurance
Under UDD, values of 1/mthly life annuities can be adjusted from annual life annuity factors using interest rate functions
𝛼(𝑚) and 𝛽(𝑚). This can be shown by subtituting in annuity twins into the insurance relationships above.
𝑖𝑑
𝛼(𝑚) =
𝑖(𝑚) 𝑑(𝑚)
𝑖 − 𝑖(𝑚)
𝛽(𝑚) =
𝑖(𝑚) 𝑑(𝑚)
20.5 Methods
The UDD class implements an instance of Mthly, which assumes uniform distribution of deaths (UDD) between integer
ages, to compute life insurance and annuities with 1/mthly benefits with shortcut formulas.
Args:
m : number of payments per year
(continues on next page)
Methods:
--------
alpha(m, i):
Derive 1/mthly UDD interest rate beta function value
beta(m, i):
Derive 1/mthly UDD interest rate alpha function value
interest_frame(i):
Display 1/mthly UDD interest function values
20.6 Examples
TWENTYONE
WOOLHOUSE M’THLY
Woolhouse’s formula is a method of approximating 1/mthly life annuities from annual factors that does not depend on
a fractional age assumption. It is based on the Euler-Maclaurin series expansion for the integral of a function. Life
insurances may then be computed from twin relationships.
(𝑚) 𝑚 − 1 𝑚2 − 1
𝑎𝑥̈ ≈ 𝑎𝑥̈ − − (𝜇𝑥 + 𝛿)
2𝑚 12𝑚2
• 1/m’thly whole life annuity using the three-term Woolhouse approximation. The third term is often omitted in
practice, which leads to poor approximations in some cases.
(𝑚) (𝑚) (𝑚) 𝑚−1 𝑚2 − 1
𝑎𝑥∶𝑡|
̈ ≈ 𝑎𝑥̈ − 𝑡 𝐸𝑥 𝑎𝑥+𝑡
̈ ̈ −
= 𝑎𝑥∶𝑡| (1 − 𝑡 𝐸𝑥 ) − (𝜇𝑥 + 𝛿 − 𝑡 𝐸𝑥 (𝜇𝑥+𝑡 + 𝛿))
2𝑚 12𝑚2
• 1/m’thly temporary life annuity from the difference of whole life Woolhouse approximations
1 1
𝑎𝑥 ≈ 𝑎𝑥̈ − − (𝜇𝑥 + 𝛿)
2 12
• continuous life annuity with Woolhouse approximation when we let 𝑚 → ∞.
1
𝜇𝑥 ≈ − (ln 𝑝𝑥−1 + ln 𝑝𝑥 )
2
• if the force of mortality 𝜇 is not provided for the third Woolhouse term, it can be approximated from survival
probabilities at integer ages.
21.2 Methods
Ths Woolhouse class implements an instance of Mthly, which uses the Woolhouse assumption with either two or three
terms, to compute m-thly pay annuities with Woolhouse approximation formulas, from which 1/m-thly life insurance are
computed from their twin formulas.
Args:
(continues on next page)
151
Solving Actuarial Math with Python
Methods:
--------
mu_x(x, s):
Computes mu_x or calls approximate_mu for third term
21.3 Examples
12.141666666666666
5260.0 -4152.028174603174 1107.9718253968258
17.37671459632958
17376.71459632958
TWENTYTWO
155
Solving Actuarial Math with Python
import time
class IsClose:
"""Helper class for testing and reporting if two values are close"""
def __init__(self, rel_tol : float = 0.01, score : bool = False,
verbose: bool = False):
[Link] = [Link] = 0
[Link] = score # whether to count INCORRECTs instead of assert
[Link] = verbose # whether to run silently
[Link] = [] # to keep list of messages for INCORRECT
[Link] = rel_tol
[Link] = [Link]()
Args:
solution (str | numeric) : gold label
answer (str | numeric) : computed answer
question (str) : label to associate with this test
rel_tol (float) : relative tolerance to be considered close
"""
if isinstance(solution, str):
isclose = (solution == answer)
else:
isclose = [Link](solution, answer, rel_tol=rel_tol or [Link])
[Link] += 1
[Link] += isclose
msg = f"{question} {solution}: {answer}"
if [Link]:
print("-----", msg, "[OK]" if isclose else "[INCORRECT]", "-----")
if not [Link]:
assert isclose, msg
if not isclose:
[Link](msg)
return isclose
def __str__(self):
"""Display cumulative score and errors"""
return f"Elapsed: {[Link]()-[Link]:.1f} secs\n" \
+ f"Passed: {[Link]}/{[Link]}\n" + "\n".join([Link])
isclose = IsClose(0.01, score=False, verbose=True)
22.1 1 Tables
life = Lifetime()
def mu_from_l(omega): # first solve for omega, given mu_65 = 1/180
return life.set_survival(l=lambda x,s: (1 - (x+s)/omega)**0.25).mu_x(65)
omega = int([Link](mu_from_l, target=1/180, grid=100))
e = life.set_survival(l=lambda x,s:(1 - (x + s)/omega)**.25, maxage=omega)\
.e_x(106) # then solve expected lifetime from omega
isclose(2.5, e, question="Q2.1")
True
Calculate the standard deviation of the number of survivors at the end of year 2.
hints:
• calculate survival probabilities for the two scenarios
• apply conditional variance formula (or mixed distribution)
True
B, c = 0.00027, 1.1
S = lambda x,s,t: [Link](-B * c**(x+s) * (c**t - 1)/[Link](c))
life = Survival().set_survival(S=S)
f = life.f_x(x=50, t=10)
isclose(0.0483, f, question="Q2.3")
True
True
Lifetime 𝑒𝑥+41 ∶=
𝑒𝑥+41 = [ 𝑒𝑥+40 − 𝑒𝑥+40∶1| ]/ 𝑝𝑥+40 forward recursion
𝑒𝑥+40 = 𝑒𝑥+40∶20| + 20 𝑝𝑥+40 ∗ 𝑒𝑥+60 backward recursion
20 𝑝𝑥+40 = 1 − 20 𝑞𝑥+40 complement of mortality
𝑒𝑥+40∶1| = 𝑝𝑥+40 1-year curtate shortcut
𝑝𝑥+40 = 1 − 𝑞𝑥+40 complement of mortality
True
True
True
def fun(mu): # Solve first for mu, given ratio of start and end proportions
male = Survival().set_survival(mu=lambda x,s: 1.5 * mu)
female = Survival().set_survival(mu=lambda x,s: mu)
return (75 * female.p_x(0, t=20)) / (25 * male.p_x(0, t=20))
mu = [Link](fun, target=85/15, grid=0.5)
p = Survival().set_survival(mu=lambda x,s: mu).p_x(0, t=1)
isclose(0.94, p, question="Q2.8")
True
True
e_curtate = Fractional.e_approximate(e_complete=15)
life = SelectLife(udd=True).set_table(l={65: [1000, None,],
66: [955, None]},
e={65: [e_curtate, None]},
d={65: [40, None,],
66: [45, None]})
e = life.e_r(x=66)
isclose(14.7, e, question="Q3.2")
True
True
sult = SULT()
mean = sult.p_x(25, t=95-25)
var = [Link](mean, variance=True)
pct = sult.portfolio_percentile(N=4000, mean=mean, variance=var, prob=0.1)
isclose(815, pct, question="Q3.4")
True
𝑥 60 61 62 63 64 65 66 67
𝑙𝑥 99,999 88,888 77,777 66,666 55,555 44,444 33,333 22,222
𝑎= 3.4|2.5 𝑞60 assuming a uniform distribution of deaths over each year of age
𝑏= 3.4|2.5 𝑞60 assuming a constant force of mortality over each year of age
Calculate 100, 000(𝑎 − 𝑏)
hints:
• compute mortality rates by interpolating lives between integer ages, with UDD and constant force of mortality
assumptions
True
𝑒64 = 5.10
Calculate 𝑒[61] .
hints:
• apply recursion formulas for curtate expectation
True
True
sult = SULT()
p1 = sult.p_x(35, t=40)
p2 = sult.p_x(45, t=40)
mean = [Link](p1) * 1000 + [Link](p2) * 1000
var = ([Link](p1, variance=True) * 1000
+ [Link](p2, variance=True) * 1000)
pct = sult.portfolio_percentile(mean=mean, variance=var, prob=.95)
isclose(1505, pct, question="Q3.8")
True
sult = SULT()
p1 = sult.p_x(20, t=25)
p2 = sult.p_x(45, t=25)
mean = [Link](p1) * 2000 + [Link](p2) * 2000
var = ([Link](p1, variance=True) * 2000
+ [Link](p2, variance=True) * 2000)
pct = sult.portfolio_percentile(mean=mean, variance=var, prob=.99)
isclose(3850, pct, question="Q3.9")
True
interest = Interest(v=0.75)
L = 35*[Link](t=4, due=False) + 75*interest.v_t(t=5)
interest = Interest(v=0.5)
R = 15*[Link](t=4, due=False) + 25*interest.v_t(t=5)
isclose(0.86, L / (L + R), question="Q3.10")
True
True
True
True
x 𝑙𝑥 𝑑𝑥 𝑝𝑥 𝑞𝑥
95 − − − 0.40
96 − − 0.20 −
97 − 72 − 1.00
True
x 𝐴𝑥 20 𝐸𝑥
40 0.36987 0.51276
60 0.62567 0.17878
6. 𝐸[𝑍 2 ] = 0.24954
Calculate the standard deviation of Z.
hints:
• solve EPV as sum of term and deferred insurance
• compute variance as difference of second moment and first moment squared
life = Recursion().set_interest(i=0.03)
life.set_A(0.36987, x=40).set_A(0.62567, x=60)
life.set_E(0.51276, x=40, t=20).set_E(0.17878, x=60, t=20)
Z2 = 0.24954
A = (2 * life.term_insurance(40, t=20) + life.deferred_insurance(40, u=20))
std = [Link](life.insurance_variance(A2=Z2, A1=A))
isclose(0.27212, std, question="Q4.1")
True
True
life = Recursion(verbose=True).set_interest(i=0.05)\
.set_q(0.01, x=60)\
.set_A(0.86545, x=60, t=3, endowment=1)
q = life.q_x(x=61)
A = Recursion(verbose=True).set_interest(i=0.045)\
.set_q(0.01, x=60)\
.set_q(q, x=61)\
.endowment_insurance(60, t=3)
isclose(0.878, A, question="Q4.3")
Mortality 𝑞𝑥+61 ∶=
𝑞𝑥+61 = 1 − 𝑝𝑥+61 complement survival
𝑝𝑥+61 = [𝑣 − 𝐴𝑥+61∶2| ]/[𝑣 ∗ [1 − 𝐴𝑥+62∶1| ]] insurance recursion
𝐴1𝑥+61∶2| = [ 𝐴1𝑥+60∶3| /𝑣 − 𝑞𝑥+60 ∗ 𝑏]/ 𝑝𝑥+60 forward recursion
𝑝𝑥+60 = 1 − 𝑞𝑥+60 complement of mortality
True
x = 40
life = Insurance().set_survival(f=lambda *x: 0.025, maxage=x+40)\
.set_interest(v_t=lambda t: (1 + .2*t)**(-2))
def benefit(x,t): return 1 + .2 * t
A1 = life.A_x(x, benefit=benefit, discrete=False)
A2 = life.A_x(x, moment=2, benefit=benefit, discrete=False)
var = A2 - A1**2
isclose(0.036, var, question="Q4.4")
True
sult = SULT(udd=True).set_interest(delta=0.05)
Z = 100000 * sult.Z_from_prob(45, prob=0.95, discrete=False)
isclose(35200, Z, question="Q4.5")
True
sult = SULT()
life = LifeTable().set_interest(i=0.05)\
.set_table(q={70+k: .95**k * sult.q_x(70+k) for k in range(3)})
A = life.term_insurance(70, t=3, b=1000)
isclose(29.85, A, question="Q4.6")
True
def fun(i):
life = Recursion(verbose=False).set_interest(i=i)\
.set_p(0.57, x=0, t=25)
return 0.1*life.E_x(0, t=25) - life.E_x(0, t=25, moment=[Link])
i = [Link](fun, target=0, grid=[0.058, 0.066])
isclose(0.06, i, question="Q4.7")
True
True
2. 𝐴135∶15| = 0.25
3. 𝐴35 = 0.32
Calculate 𝐴50 .
hints:
• solve 15 𝐸35 from endowment insurance minus term insurance
• solve implicitly from whole life as term plus deferred insurance
True
True
7. 2 𝐴𝑥∶𝑛|1 = 0.136
Calculate 𝑉 𝑎𝑟(𝑍1 ).
hints:
• compute endowment insurance = term insurance + pure endowment
• apply formula of variance as the difference of second moment and first moment squared.
True
True
2. 𝑖 = 0.04
True
sult = SULT()
p = sult.p_x(60, t=85-60)
mean = [Link](p)
var = [Link](p, variance=True)
F = sult.portfolio_percentile(mean=mean, variance=var, prob=.86, N=400)
F *= 5000 * [Link].v_t(85-60)
isclose(390000, F, question="Q4.14")
True
True
𝑥 𝑞𝑥
50 0.045
51 0.050
52 0.055
53 0.060
hints:
• compute EPV of future benefits with adjusted mortality rates
True
sult = SULT()
median = sult.Z_t(48, prob=0.5, discrete=False)
def benefit(x,t): return 5000 if t < median else 10000
A = sult.A_x(48, benefit=benefit)
isclose(1130, A, question="Q4.17")
True
True
life = SULT()
q = ExtraRisk(life=life, extra=0.8, risk="MULTIPLY_RATE")['q']
select = SelectLife(periods=1).set_select(s=0, age_selected=True, q=q)\
.set_select(s=1, age_selected=False, q=life['q'])\
.set_interest(i=.05)\
.fill_table()
A = 100000 * select.whole_life_insurance(80, s=0)
isclose(59050, A, question="Q4.19")
True
22.5 5 Annuities
life = ConstantForce(mu=0.01).set_interest(delta=0.06)
EY = life.certain_life_annuity(0, u=10, discrete=False)
p = life.p_x(0, t=life.Y_to_t(EY))
isclose(0.705, p, question="Q5.1") # 0.705
True
4. i = 0.05
Calculate 𝑎𝑥∶𝑛| .
hints:
• compute term life as difference of whole life and deferred insurance
• compute twin annuity-due, and adjust to an immediate annuity.
x, n = 0, 10
a = Recursion().set_interest(i=0.05)\
.set_A(0.3, x)\
.set_A(0.4, x+n)\
.set_E(0.35, x, t=n)\
.immediate_annuity(x, t=n)
isclose(9.64, a, question="Q5.2")
True
t = 10.5
E = t * SULT().E_r(40, t=t)
isclose(6.239, E, question="Q5.3")
True
life = ConstantForce(mu=0.02).set_interest(delta=0.01)
u = life.e_x(40, curtate=False)
P = 10000 / life.certain_life_annuity(40, u=u, discrete=False)
isclose(213.7, P, question="Q5.4") # 213.7
True
3. The force of mortality for substandard lives age 45 + t, 𝜇𝑆45+𝑡 , is defined as:
𝜇𝑆45+𝑡 = 𝜇𝑆𝑈𝐿𝑇
45+𝑡 + 0.05, 0≤𝑡<1
= 𝜇𝑆𝑈𝐿𝑇
45+𝑡 , 𝑡≥1
4. 𝑖 = 0.05
Calculate the actuarial present value of this annuity for a substandard life age 45.
hints:
• adjust mortality rate for the extra risk
• compute annuity by backward recursion.
True
life = Annuity().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)
isclose(1200, fund, question="Q5.6")
True
hints:
• compute endowment insurance from relationships of whole life, temporary and deferred insurances.
• compute temporary annuity from insurance twin
• apply Woolhouse approximation
life = Recursion().set_interest(i=0.04)\
.set_A(0.188, x=35)\
.set_A(0.498, x=65)\
.set_p(0.883, x=35, t=30)
mthly = Woolhouse(m=2, life=life, three_term=False)
a = 1000 * mthly.temporary_annuity(35, t=30)
isclose(17376.7, a, question="Q5.7")
True
• find survival probability of lifetime s.t. sum of annual payments exceeds EPV
sult = SULT()
a = sult.certain_life_annuity(55, u=5)
p = sult.p_x(55, t=[Link](a))
isclose(0.92118, p, question="Q5.8")
True
True
• solve net premium such that EPV annuity = EPV insurance + IA factor for returns of premiums without interest
P = SULT().set_interest(i=0.03)\
.net_premium(80, t=2, b=1000, return_premium=True)
isclose(35.36, P, question="Q6.1")
True
(vi) 𝑎1𝑥∶10|
̈ = 6.8865
life = Premiums()
A, IA, a = 0.17094, 0.96728, 6.8865
P = life.gross_premium(a=a, A=A, IA=IA, benefit=100000,
initial_premium=0.5, renewal_premium=.05,
renewal_policy=200, initial_policy=200)
isclose(3604, P, question="Q6.2")
True
Calculate the probability that Y is less than the actuarial accumulated value of S’s premiums.
hints:
• solve lifetime 𝑡 such that PV annuity certain = PV whole life annuity at age 65
• calculate mortality rate through the year before curtate lifetime
life = SULT()
t = life.Y_to_t(life.whole_life_annuity(65))
q = 1 - life.p_x(65, t=[Link](t) - 1)
isclose(0.39, q, question="Q6.3")
True
True
life = SULT()
P = life.net_premium(30, b=1000)
def gain(k):
return life.Y_x(30, t=k) * P - life.Z_x(30, t=k) * 1000
(continues on next page)
isclose(33, k, question="Q6.5")
True
life = SULT()
P = life.net_premium(62, b=10000)
contract = Contract(premium=1.03*P,
renewal_policy=5,
initial_policy=5,
initial_premium=0.05,
benefit=10000)
L = life.gross_policy_value(62, contract=contract)
var = life.gross_policy_variance(62, contract=contract)
prob = life.portfolio_cdf(mean=L, variance=var, value=40000, N=600)
isclose(.79, prob, question="Q6.6")
True
life = SULT()
a = life.temporary_annuity(40, t=20)
A = life.E_x(40, t=20)
IA = a - [Link](t=20) * life.p_x(40, t=20)
G = life.gross_premium(a=a, A=A, IA=IA, benefit=100000)
isclose(2880, G, question="Q6.7")
True
life = SULT()
initial_cost = (50 + 10 * life.deferred_annuity(60, u=1, t=9)
+ 5 * life.deferred_annuity(60, u=10, t=10))
P = life.net_premium(60, initial_cost=initial_cost)
isclose(9.5, P, question="Q6.8")
True
life = SULT()
a = life.temporary_annuity(50, t=10)
A = life.term_insurance(50, t=20)
initial_cost = 25 * life.deferred_annuity(50, u=10, t=10)
P = life.gross_premium(a=a, A=A, benefit=100000,
initial_premium=0.42, renewal_premium=0.12,
initial_policy=75 + initial_cost, renewal_policy=25)
isclose(647, P, question="Q6.9")
True
x = 0
life = Recursion(depth=5).set_interest(i=0.06)\
.set_p(0.975, x=x)\
.set_a(152.85/56.05, x=x, t=3)\
.set_A(152.85, x=x, t=3, b=1000)
p = life.p_x(x=x+2)
isclose(0.91, p, question="Q6.10")
Survival 𝑝𝑥+2 ∶=
𝑝𝑥+2 = 𝐸𝑥+2 /𝑣 one-year pure endowment
𝐸𝑥+2 = 𝐴𝑥+2∶1| − 𝐴1𝑥+2∶1| endowment insurance minus term
𝐴1𝑥+2∶1| = [ 𝐴1𝑥+1∶2| /𝑣 − 𝑞𝑥+1 ∗ 𝑏]/ 𝑝𝑥+1 forward recursion
𝑝𝑥+1 = [ 𝑎𝑥+1∶2|
̈ − 1]/[𝑣 ∗ 𝑎𝑥+2∶1|
̈ ] annuity recursion
𝑎𝑥+1∶2|
̈ = [ 𝑎𝑥∶3|
̈ − 1]/ 𝐸𝑥 forward recursion
𝐴1𝑥+1∶2| = [ 𝐴1𝑥∶3| /𝑣 − 𝑞𝑥 ∗ 𝑏]/ 𝑝𝑥 forward recursion
𝐸𝑥 = 𝑝𝑥 ∗ 𝑣 pure endowment
True
life = Recursion().set_interest(i=0.04)
A = life.set_A(0.39788, 51)\
.set_q(0.0048, 50)\
.whole_life_insurance(50)
P = life.gross_premium(A=A, a=life.annuity_twin(A=A))
A = life.set_q(0.048, 50).whole_life_insurance(50)
loss = A - life.annuity_twin(A) * P
isclose(0.041, loss, question="Q6.11")
True
Year 1 Years 2+
life = PolicyValues().set_interest(i=0.06)
a = 12
A = life.insurance_twin(a)
contract = Contract(benefit=1000, settlement_policy=20,
initial_policy=10, initial_premium=0.75,
renewal_policy=2, renewal_premium=0.1)
[Link] = life.gross_premium(A=A, a=a, **contract.premium_terms)
L = life.gross_variance_loss(A1=A, A2=0.14, contract=contract)
isclose(88900, L, question="Q6.12")
True
Calculate 𝐸[0 𝐿] .
life = SULT().set_interest(i=0.05)
A = life.whole_life_insurance(45)
contract = Contract(benefit=10000, initial_premium=.8, renewal_premium=.1)
def fun(P): # Solve for premium, given Loss(t=0) = 4953
return life.L_from_t(t=10.5, contract=contract.set_contract(premium=P))
contract.set_contract(premium=[Link](fun, target=4953, grid=100))
L = life.gross_policy_value(45, contract=contract)
life.L_plot(x=45, T=10.5, contract=contract)
isclose(-400, L, question="Q6.13")
True
life = SULT().set_interest(i=0.05)
a = life.temporary_annuity(40, t=10) + 0.5*life.deferred_annuity(40, u=10, t=10)
A = life.whole_life_insurance(40)
P = life.gross_premium(a=a, A=A, benefit=100000)
isclose(1150, P, question="Q6.14")
True
True
Taxes 4% 0 4% 0
Sales Commission 35% 0 2% 0
Policy Maintenance 0% 250 0% 50
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)
isclose(2410, p, question="Q6.16")
True
x = 0
life = ConstantForce(mu=0.1).set_interest(i=0.08)
A = life.endowment_insurance(x, t=2, b=100000, endowment=30000)
a = life.temporary_annuity(x, t=2)
P = life.gross_premium(a=a, A=A)
life1 = Recursion().set_interest(i=0.08)\
.set_q(life.q_x(x, t=1) * 1.5, x=x, t=1)\
.set_q(life.q_x(x+1, t=1) * 1.5, x=x+1, t=1)
contract = Contract(premium=P*2, benefit=100000, endowment=30000)
L = life1.gross_policy_value(x, t=0, n=2, contract=contract)
isclose(-30000, L, question="Q6.17")
Pure Endowment 2 𝐸𝑥 ∶=
2
2 𝐸𝑥 = 2 𝑝𝑥 ∗ 𝑣 pure endowment
2 𝑝𝑥 = 𝑝𝑥+1 ∗ 𝑝𝑥 survival chain rule
𝑝𝑥 = 1 − 𝑞 𝑥 complement of mortality
𝑝𝑥+1 = 1 − 𝑞𝑥+1 complement of mortality
True
life = SULT().set_interest(i=0.05)
def fun(P):
A = (life.term_insurance(40, t=20, b=P)
+ life.deferred_annuity(40, u=20, b=30000))
return life.gross_premium(a=1, A=A) - P
P = [Link](fun, target=0, grid=[162000, 168800])
isclose(166400, P, question="Q6.18")
True
life = SULT()
contract = Contract(initial_policy=.2, renewal_policy=.01)
a = life.whole_life_annuity(50)
A = life.whole_life_insurance(50)
[Link] = life.gross_premium(A=A, a=a, **contract.premium_terms)
L = life.gross_policy_variance(50, contract=contract)
isclose(0.033, L, question="Q6.19")
True
𝑥 𝑝𝑥
75 0.90
76 0.88
77 0.85
3. 𝑖 = 0.04
Calculate the annual net premium.
True
life = Recursion(verbose=False).set_interest(d=0.04)
life.set_A(0.7, x=75, t=15, endowment=1)
life.set_E(0.11, x=75, t=15)
def fun(P):
return (P * life.temporary_annuity(75, t=15) -
life.endowment_insurance(75, t=15, b=1000, endowment=15*float(P)))
P = [Link](fun, target=0, grid=(80, 120))
isclose(100, P, question="Q6.21")
True
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)
P = life.gross_premium(A=A, a=a, benefit=100000) / 12
isclose(102, P, question="Q6.22")
True
x = 0
life = Recursion().set_a(15.3926, x=x)\
.set_a(10.1329, x=x, t=15)\
.set_a(14.0145, x=x, t=30)
def fun(P):
per_policy = 30 + (30 * life.whole_life_annuity(x))
per_premium = (0.6 + 0.1*life.temporary_annuity(x, t=15)
+ 0.1*life.temporary_annuity(x, t=30))
a = life.temporary_annuity(x, t=30)
return (P * a) - (per_policy + per_premium * P)
P = [Link](fun, target=0, grid=[30.3, 49.5])
isclose(44.7, P, question="Q6.23")
True
life = PolicyValues().set_interest(delta=0.07)
x, A1 = 0, 0.30 # Policy for first insurance
P = life.premium_equivalence(A=A1, discrete=False) # Need its premium
contract = Contract(premium=P, discrete=False)
def fun(A2): # Solve for A2, given Var(Loss)
return life.gross_variance_loss(A1=A1, A2=A2, contract=contract)
A2 = [Link](fun, target=0.18, grid=0.18)
True
life = SULT()
woolhouse = Woolhouse(m=12, life=life)
benefits = woolhouse.deferred_annuity(55, u=10, b=1000 * 12)
expenses = life.whole_life_annuity(55, b=300)
payments = life.temporary_annuity(55, t=10)
def fun(P):
return life.gross_future_loss(A=benefits + expenses, a=payments,
contract=Contract(premium=P))
P = [Link](fun, target=-800, grid=[12110, 12550])
isclose(12330, P, question="Q6.25")
True
life = SULT().set_interest(i=0.05)
def fun(P):
return P - life.net_premium(90, b=1000, initial_cost=P)
P = [Link](fun, target=0, grid=[150, 190])
isclose(180, P, question="Q6.26")
True
Premium Rate 3P P
Benefit 1,000,000 500,000
life = ConstantForce(mu=0.03).set_interest(delta=0.06)
x = 0
payments = (3 * life.temporary_annuity(x, t=20, discrete=False)
+ life.deferred_annuity(x, u=20, discrete=False))
benefits = (1000000 * life.term_insurance(x, t=20, discrete=False)
+ 500000 * life.deferred_insurance(x, u=20, discrete=False))
P = benefits / payments
isclose(10310, P, question="Q6.27")
True
life = SULT().set_interest(i=0.05)
a = life.temporary_annuity(40, t=5)
A = life.whole_life_insurance(40)
P = life.gross_premium(a=a, A=A, benefit=1000,
initial_policy=10, renewal_premium=.05,
renewal_policy=5, initial_premium=.2)
isclose(36, P, question="Q6.28")
True
life = Premiums().set_interest(i=0.035)
def fun(a):
return life.gross_premium(A=life.insurance_twin(a=a), a=a,
initial_policy=200, initial_premium=.5,
renewal_policy=50, renewal_premium=.1,
benefit=100000)
a = [Link](fun, target=1770, grid=[20, 22])
isclose(20.5, a, question="Q6.29")
True
life = PolicyValues().set_interest(i=0.04)
contract = Contract(premium=2.338,
benefit=100,
initial_premium=.1,
renewal_premium=0.05)
var = life.gross_variance_loss(A1=life.insurance_twin(16.50),
A2=0.17, contract=contract)
isclose(900, var, question="Q6.30")
True
life = ConstantForce(mu=0.01).set_interest(delta=0.05)
A = (life.term_insurance(35, t=35, discrete=False)
+ life.E_x(35, t=35)*0.51791) # A_35
P = life.premium_equivalence(A=A, b=100000, discrete=False)
isclose(1330, P, question="Q6.31")
True
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)
P = life.gross_premium(a=payments, A=benefits, benefit=100000)/12
isclose(550, P, question="Q6.32")
True
True
life = SULT()
def fun(benefit):
A = life.whole_life_insurance(61)
a = life.whole_life_annuity(61)
return life.gross_premium(A=A, a=a, benefit=benefit,
initial_premium=0.15, renewal_premium=0.03)
b = [Link](fun, target=500, grid=[23300, 23700])
isclose(23300, b, question="Q6.34")
True
sult = SULT()
A = sult.whole_life_insurance(35, b=100000)
a = sult.whole_life_annuity(35)
P = sult.gross_premium(a=a, A=A, initial_premium=.19, renewal_premium=.04)
isclose(530, P, question="Q6.35")
True
life = ConstantForce(mu=0.04).set_interest(delta=0.08)
a = life.temporary_annuity(50, t=20, discrete=False)
A = life.term_insurance(50, t=20, discrete=False)
def fun(R):
return life.gross_premium(a=a, A=A, initial_premium=R/4500,
renewal_premium=R/4500, benefit=100000)
R = [Link](fun, target=4500, grid=[400, 800])
isclose(500, R, question="Q6.36")
True
sult = SULT()
benefits = sult.whole_life_insurance(35, b=50000 + 100)
expenses = sult.immediate_annuity(35, b=100)
a = sult.temporary_annuity(35, t=10)
P = (benefits + expenses) / a
isclose(820, P, question="Q6.37")
True
6. 𝐴𝑥∶𝑛| = 0.192
Calculate the annual net premium for this insurance.
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)
True
sult = SULT()
P40 = sult.premium_equivalence(sult.whole_life_insurance(40), b=1000)
P80 = sult.premium_equivalence(sult.whole_life_insurance(80), b=1000)
p40 = sult.p_x(40, t=10)
p80 = sult.p_x(80, t=10)
P = (P40 * p40 + P80 * p80) / (p80 + p40)
isclose(29, P, question="Q6.39")
True
1. The death benefit is 1000(1.03)𝑘 for death in policy year k, for 𝑘 = 1, 2, 3...
2. 𝑞𝑥 = 0.05
3. 𝑖 = 0.06
4. 𝑎𝑥+1
̈ = 7.00
5. The annual net premium for this insurance at issue age x is 110
Calculate the annual net premium for this insurance at issue age 𝑥 + 1.
True
x = 0
life = LifeTable().set_interest(i=0.05).set_table(q={x:.01, x+1:.02})
a = 1 + life.E_x(x, t=1) * 1.01
A = life.deferred_insurance(x, u=0, t=1) + 1.01*life.deferred_insurance(x, u=1, t=1)
P = 100000 * A / a
isclose(1417, P, question="Q6.41")
True
x = 0
life = ConstantForce(mu=0.06).set_interest(delta=0.06)
contract = Contract(discrete=True, premium=315.8,
T=3, endowment=1000, benefit=1000)
L = [life.L_from_t(t, contract=contract) for t in range(3)] # L(t)
Q = [life.q_x(x, u=u, t=1) for u in range(3)] # prob(die in year t)
Q[-1] = 1 - sum(Q[:-1]) # follows SOA Solution: incorrectly treats endowment!
p = sum([q for (q, l) in zip (Q, L) if l > 0])
isclose(0.113, p, question="Q6.42")
True
Taxes 5% 0 5% 0
Commissions 30% 0 10% 0
Maintenance 0% 8 0% 4
3. i = 0.05
4. 𝑎30∶5|
̈ = 4.5431
Calculate the annual gross premium using the equivalence principle.
• although 10-year term, premiums only paid first first years: separately calculate the EPV of per-policy maintenance
expenses in years 6-10 and treat as additional initial expense
sult = SULT()
a = sult.temporary_annuity(30, t=5)
A = sult.term_insurance(30, t=10)
other_expenses = 4 * sult.deferred_annuity(30, u=5, t=5)
P = sult.gross_premium(a=a, A=A, benefit=200000, initial_premium=0.35,
initial_policy=8 + other_expenses, renewal_policy=4,
renewal_premium=0.15)
isclose(170, P, question="Q6.43")
True
life = Recursion().set_interest(i=0.05)\
.set_IA(0.15, x=50, t=10)\
.set_a(17, x=50)\
.set_a(15, x=60)\
.set_E(0.6, x=50, t=10)
A = life.deferred_insurance(50, u=10)
IA = life.increasing_insurance(50, t=10)
a = life.temporary_annuity(50, t=10)
P = life.gross_premium(a=a, A=A, IA=IA, benefit=100)
isclose(2.2, P, question="Q6.44")
True
life = SULT(udd=True)
contract = Contract(benefit=100000, premium=560, discrete=False)
L = life.L_from_prob(x=35, prob=0.75, contract=contract)
life.L_plot(x=35, contract=contract,
T=life.L_to_t(L=L, contract=contract))
isclose(690, L, question="Q6.45")
True
life = Recursion().set_interest(i=0.05)\
.set_IA(0.51213, x=55, t=10)\
.set_a(12.2758, x=55)\
.set_a(7.4575, x=55, t=10)
A = life.deferred_annuity(55, u=10)
IA = life.increasing_insurance(55, t=10)
a = life.temporary_annuity(55, t=10)
P = life.gross_premium(a=a, A=A, IA=IA, benefit=300)
isclose(208, P, question="Q6.46")
True
sult = SULT()
a = sult.temporary_annuity(70, t=10)
A = sult.deferred_annuity(70, u=10)
P = sult.gross_premium(a=a, A=A, benefit=100000, initial_premium=0.75,
renewal_premium=0.05)
isclose(66400, P, question="Q6.47")
True
x = 0
life = Recursion(depth=5).set_interest(i=0.06)\
.set_p(.95, x=x, t=5)\
.set_q(.02, x=x+5)\
.set_q(.03, x=x+6)\
.set_q(.04, x=x+7)
a = 1 + life.E_x(x, t=5)
A = life.deferred_insurance(x, u=5, t=3)
P = life.gross_premium(A=A, a=a, benefit=100000)
isclose(3195, P, question="Q6.48")
Pure Endowment 5 𝐸𝑥 ∶=
5
5 𝐸𝑥 = 5 𝑝𝑥 ∗ 𝑣 pure endowment
Pure Endowment 5 𝐸𝑥 ∶=
5
5 𝐸𝑥 = 5 𝑝𝑥 ∗ 𝑣 pure endowment
True
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) / 12
isclose(86, P, question="Q6.49")
True
life = SULT()
P = life.premium_equivalence(a=life.whole_life_annuity(35), b=1000)
a = life.deferred_annuity(35, u=1, t=1)
A = life.term_insurance(35, t=1, b=1000)
cash = (A - a * P) * 10000 / [Link].v
isclose(-47000, cash, question="Q6.50")
True
True
sult = SULT()
a = sult.temporary_annuity(45, t=10)
other_cost = 10 * sult.deferred_annuity(45, u=10)
P = sult.gross_premium(a=a, A=0, benefit=0, # set face value H = 0
initial_premium=1.05, renewal_premium=0.05,
initial_policy=100 + other_cost, renewal_policy=20)
isclose(50.8, P, question="Q6.52")
True
x = 0
life = LifeTable().set_interest(i=0.08).set_table(q={x:.1, x+1:.1, x+2:.1})
A = life.term_insurance(x, t=3)
P = life.gross_premium(a=1, A=A, benefit=2000, initial_premium=0.35)
isclose(720, P, question="Q6.53")
True
life = SULT()
std = [Link](life.net_policy_variance(45, b=200000))
isclose(25440, std, question="Q6.54")
True
life = SULT()
x, n, t = 40, 20, 10
A = (life.whole_life_insurance(x+t, b=50000)
+ life.deferred_insurance(x+t, u=n-t, b=50000))
a = life.temporary_annuity(x+t, t=n-t, b=875)
L = life.gross_future_loss(A=A, a=a)
isclose(11150, L, question="Q7.1")
True
x = 0
life = Recursion(verbose=False).set_interest(i=.1)\
.set_q(0.15, x=x)\
.set_q(0.165, x=x+1)\
.set_reserves(T=2, endowment=2000)
True
True
hints:
• split benefits into two policies
life = SULT()
P = life.gross_premium(a=life.whole_life_annuity(40),
A=life.whole_life_insurance(40),
initial_policy=100, renewal_policy=10,
benefit=1000)
P += life.gross_premium(a=life.whole_life_annuity(40),
A=life.deferred_insurance(40, u=11),
benefit=4000) # for deferred portion
contract = Contract(benefit=1000, premium=1.02*P,
renewal_policy=10, initial_policy=100)
V = life.gross_policy_value(x=40, t=1, contract=contract)
contract = Contract(benefit=4000, premium=0)
A = life.deferred_insurance(41, u=10)
V += life.gross_future_loss(A=A, a=0, contract=contract) # for deferred portion
isclose(-74, V, question="Q7.4")
True
x = 0
life = Recursion(udd=True).set_interest(i=0.03)\
.set_q(0.04561, x=x+4)\
.set_reserves(T=3, V={4: 1405.08})
V = life.r_V_forward(x, s=4, r=0.5, benefit=10000, premium=647.46)
isclose(1900, V, question="Q7.5")
True
life = SULT()
P = life.net_premium(45, b=2000)
contract = Contract(benefit=2000, initial_premium=.25, renewal_premium=.05,
initial_policy=2*1.5 + 30, renewal_policy=2*.5 + 10)
(continues on next page)
True
x = 0
life = Recursion().set_interest(i=0.05).set_A(0.4, x=x+10)
a = Woolhouse(m=12, life=life).whole_life_annuity(x+10)
contract = Contract(premium=0, benefit=10000, renewal_policy=100)
V = life.gross_future_loss(A=0.4, contract=[Link]())
contract = Contract(premium=30*12, renewal_premium=0.05)
V += life.gross_future_loss(a=a, contract=[Link]())
isclose(1110, V, question="Q7.7")
True
sult = SULT()
x = 70
q = {x: [sult.q_x(x+k)*(.7 + .1*k) for k in range(3)] + [sult.q_x(x+3)]}
life = Recursion().set_interest(i=.05)\
.set_q(sult.q_x(70)*.7, x=x)\
(continues on next page)
Survival 𝑝𝑥+70 ∶=
𝑝𝑥+70 = 1 − 𝑞𝑥+70 complement of mortality
True
sult = SULT(udd=True)
x, n, t = 45, 20, 10
a = UDD(m=12, life=sult).temporary_annuity(x=x+10, t=n-t)
A = UDD(m=0, life=sult).endowment_insurance(x=x+10, t=n-t)
contract = Contract(premium=253*12, endowment=100000, benefit=100000)
V = sult.gross_future_loss(A=A, a=a, contract=contract)
isclose(38100, V, question="Q7.9")
True
life = SULT()
G = 977.6
P = life.net_premium(45, b=100000)
contract = Contract(benefit=0, premium=G-P, renewal_policy=.02*G + 50)
V = life.gross_policy_value(45, t=5, contract=contract)
isclose(-970, V, question="Q7.10")
True
True
benefit = lambda k: 26 - k
x = 44
life = Recursion().set_interest(i=0.04)\
.set_q(0.15, x=55)\
.set_reserves(T=25, endowment=1, V={11: 5.})
def fun(P): # solve for net premium, from final year recursion
(continues on next page)
Survival 𝑝𝑥+55 ∶=
𝑝𝑥+55 = 1 − 𝑞𝑥+55 complement of mortality
True
life = SULT()
V = life.FPT_policy_value(40, t=10, n=30, endowment=1000, b=1000)
isclose(180, V, question="Q7.13")
True
x = 45
life = Recursion(verbose=False).set_interest(i=0.05)\
.set_q(0.009, x=50)\
.set_reserves(T=10, V={5: 5500})
def fun(P): # solve for net premium,
return life.t_V(x=x, t=6, premium=P*0.96 - 50, benefit=lambda t: 100000+200)
P = [Link](fun, target=7100, grid=[2200, 2400])
isclose(2200, P, question="Q7.14")
True
x = 0
V = Recursion(udd=True).set_interest(i=0.05)\
.set_q(0.1, x=x+15)\
.set_reserves(T=3, V={16: 49.78})\
.r_V_backward(x, s=15, r=0.6, benefit=100)
isclose(50.91, V, question="Q7.15")
True
life = SelectLife().set_interest(v=.95)\
.set_table(A={86: [683/1000]},
q={80+k: [.01*(k+1)] for k in range(6)})
x, t, n = 80, 3, 5
A = life.whole_life_insurance(x+t)
a = life.temporary_annuity(x+t, t=n-t)
V = life.gross_future_loss(A=A, a=a, contract=Contract(benefit=1000, premium=130))
isclose(380, V, question="Q7.16")
True
x = 0
life = Recursion().set_interest(v=[Link](0.90703))\
.set_q(0.02067, x=x+10)\
.set_A(0.52536, x=x+11)\
.set_A(0.30783, x=x+11, moment=2)
A1 = life.whole_life_insurance(x+10)
A2 = life.whole_life_insurance(x+10, moment=2)
ratio = (life.insurance_variance(A2=A2, A1=A1)
/ life.insurance_variance(A2=0.30783, A1=0.52536))
isclose(1.018, ratio, question="Q7.17")
True
x = 10
life = Recursion(verbose=False).set_interest(i=0.04).set_q(0.009, x=x)
def fun(a):
return life.set_a(a, x=x).net_policy_value(x, t=1)
a = [Link](fun, target=0.012, grid=[17.1, 19.1])
isclose(17.1, a, question="Q7.18")
True
life = SULT()
contract = Contract(benefit=100000,
initial_policy=300,
initial_premium=.5,
(continues on next page)
True
life = SULT()
S = life.FPT_policy_value(35, t=1, b=1000) # is 0 for FPT at t=0,1
contract = Contract(benefit=1000,
initial_premium=.3,
initial_policy=300,
renewal_premium=.04,
renewal_policy=30)
G = life.gross_premium(A=life.whole_life_insurance(35), **contract.premium_terms)
R = life.gross_policy_value(35, t=1, contract=contract.set_contract(premium=G))
isclose(-277.23, R - S, question="Q7.20")
True
life = SULT()
x, t, u = 55, 9, 10
P = life.gross_premium(IA=0.14743,
a=life.temporary_annuity(x, t=u),
(continues on next page)
True
life = PolicyValues().set_interest(i=0.06)
contract = Contract(benefit=8, premium=1.250)
def fun(A2):
return life.gross_variance_loss(A1=0, A2=A2, contract=contract)
A2 = [Link](fun, target=20.55, grid=20.55/8**2)
contract = Contract(benefit=12, premium=1.875)
var = life.gross_variance_loss(A1=0, A2=A2, contract=contract)
isclose(46.2, var, question="Q7.22")
True
True
1. The annual gross premium, calculated using the equivalence principle, is 11,800
2. Mortality follows the Standard Ultimate Life Table
3. i = 0.05
Calculate the expense loading, P for this policy.
life = SULT()
P = life.premium_equivalence(A=life.whole_life_insurance(50), b=1000000)
isclose(680, 11800 - P, question="Q7.24")
True
life = SelectLife().set_interest(i=.04)\
.set_table(A={55: [.23, .24, .25],
56: [.25, .26, .27],
57: [.27, .28, .29],
58: [.20, .30, .31]})
V = life.FPT_policy_value(55, t=3, b=100000)
isclose(3950, V, question="Q7.25")
True
x = 0
life = Recursion(verbose=False).set_interest(i=.05)\
.set_p(0.85, x=x)\
.set_p(0.85, x=x+1)\
.set_reserves(T=2, endowment=50000)
def benefit(k): return k * 25000
def fun(P): # solve P s.t. V is equal backwards and forwards
policy = dict(t=1, premium=P, benefit=benefit, reserve_benefit=True)
return life.t_V_backward(x, **policy) - life.t_V_forward(x, **policy)
P = [Link](fun, target=0, grid=[27650, 28730])
isclose(28540, P, question="Q7.26")
True
x = 0
life = Recursion(verbose=False).set_interest(i=0.03)\
.set_q(0.008, x=x)\
.set_reserves(V={0: 0})
def fun(G): # Solve gross premium from expense reserves equation
return life.t_V(x=x, t=1, premium=G - 187, benefit=lambda t: 0,
per_policy=10 + 0.25*G)
G = [Link](fun, target=-38.70, grid=[200, 252])
isclose(213, G, question="Q7.27")
True
life = SULT()
PW = life.net_premium(65, b=1000) # 20_V=0 => P+W is net premium for A_65
P = life.net_premium(45, t=20, b=1000) # => P is net premium for A_45:20
isclose(24.3, PW - P, question="Q7.28")
True
x = 0
life = Recursion(verbose=False).set_interest(i=0.04)\
.set_a(14.8, x=x)\
.set_a(11.4, x=x+10)
def fun(B):
return life.net_policy_value(x, t=10, b=B)
B = [Link](fun, target=2290, grid=2290*10) # Solve benefit B given net 10_V
contract = Contract(initial_policy=30, renewal_policy=5, benefit=B)
G = life.gross_premium(a=life.whole_life_annuity(x), **contract.premium_terms)
V = life.gross_policy_value(x, t=10, contract=contract.set_contract(premium=G))
isclose(2270, V, question="Q7.29")
True
True
x = 0
life = Reserves().set_reserves(T=3)
G = 368.05
def fun(P): # solve net premium expense reserve equation
return life.t_V(x, t=2, premium=G-P, benefit=lambda t:0, per_policy=5+0.08*G)
P = [Link](fun, target=-23.64, grid=[.29, .31]) / 1000
isclose(0.310, P, question="Q7.31")
True
• 𝛿 = 0.06
Calculate the variance of the present value of future loss at 𝑡 for Policy B.
life = PolicyValues().set_interest(i=0.06)
contract = Contract(benefit=1, premium=0.1)
def fun(A2):
return life.gross_variance_loss(A1=0, A2=A2, contract=contract)
A2 = [Link](fun, target=0.455, grid=0.455)
contract = Contract(benefit=2, premium=0.16)
var = life.gross_variance_loss(A1=0, A2=A2, contract=contract)
isclose(1.39, var, question="Q7.32")
True
Final Score
2023-08-08 14:57:24.992543
Elapsed: 5.9 secs
Passed: 136/136