Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added the first version of implementation of endowment #74

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 53 additions & 9 deletions src/LifeContingencies.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ using Yields
const mt = MortalityTables

export LifeContingency,
Insurance, AnnuityDue, AnnuityImmediate,
Insurance, AnnuityDue, AnnuityImmediate, Endowment,
APV,
SingleLife, Frasier, JointLife,
LastSurvivor,
Expand All @@ -36,7 +36,6 @@ export LifeContingency,
# 'actuarial objects' that combine multiple forms of decrements (lapse, interest, death, etc)
abstract type Life end


"""
struct SingleLife
mortality
Expand Down Expand Up @@ -292,13 +291,22 @@ struct Term{L,Y} <: Insurance
term::Int
end

struct Endowment{L,Y} <: Insurance
life::L
int::Y
term::Int
maturity::Int
end

"""
Insurance(lc::LifeContingency, term)
Insurance(life,interest, term)
Insurance(life, interest, term)
Insurance(lc::LifeContingency)
Insurance(life,interest)
Insurance(life, interest)
Insurance(lc::LifeContingency, term, maturity)
Insurance(life, interest, term, maturity)

Life insurance with a term period of `term`. If `term` is `nothing`, then whole life insurance.
Life insurance with a term period of `term`. If `maturity` is nothing, then term life insurance. If `term` is `nothing`, then whole life insurance.

Issue age is based on the `issue_age` in the LifeContingency `lc`.

Expand All @@ -312,14 +320,18 @@ ins = Insurance(
)
```
"""
Insurance(lc::LifeContingency, term) = Insurance(lc.life, lc.int, term)
Insurance(lc::LifeContingency, term::Int, maturity::Int) = Insurance(lc.life, lc.int, term, maturity)
Insurance(lc::LifeContingency, term::Int) = Insurance(lc.life, lc.int, term)
Insurance(lc::LifeContingency) = Insurance(lc.life, lc.int)

function Insurance(life, int, term::Int)
function Insurance(life::Life, int, term::Int, maturity::Int)
return Endowment(life, int, term, maturity)
end
function Insurance(life::Life, int, term::Int)
term < 1 && return ZeroBenefit(life, int)
return Term(life, int, term)
end
function Insurance(life, int)
function Insurance(life::Life, int)
return WholeLife(life, int)
end

Expand Down Expand Up @@ -489,6 +501,10 @@ function benefit(ins::I) where {I<:Insurance}
return 1.0
end

function benefit(ins::I) where {I<:Endowment}
return (1.0, ins.maturity)
end

function benefit(ins::ZeroBenefit)
return 0.0
end
Expand All @@ -511,6 +527,18 @@ function probability(ins::I) where {I<:Insurance}
end
end

function probability(ins::I) where {I<:Endowment}
m = ins.life.mortality
issage = ins.life.issue_age
return Iterators.map(timepoints(ins)) do t
if t == lastindex(timepoints(ins))
(survival(m, issage + t - 1) * decrement(m, issage + t - 1, issage + t), survival(m, issage + t))
else
(survival(m, issage + t - 1) * decrement(m, issage + t - 1, issage + t), 0)
end
end
end

function probability(ins::ZeroBenefit)
return Iterators.repeated(1.0, length(timepoints(ins)))
end
Expand Down Expand Up @@ -542,6 +570,10 @@ function cashflows(ins::I) where {I<:Insurance}
return Iterators.map(p -> p * b, probability(ins))
end

function cashflows(ins::I) where {I<:Endowment}
b = benefit(ins)
return Iterators.map(p -> p[1] * b[1] + p[2] * b[2], probability(ins))
end

"""
timepoints(Insurance)
Expand All @@ -558,6 +590,10 @@ function timepoints(ins::Term)::UnitRange{Int64}
return 1:min(omega(ins.life), ins.term)
end

function timepoints(ins::Endowment)::UnitRange{Int64}
return 1:min(omega(ins.life), ins.term)
end

function timepoints(ins::ZeroBenefit)
return Iterators.repeated(0.0, 1)
end
Expand Down Expand Up @@ -654,7 +690,7 @@ present_value(ins,10) / survival(ins,10)
```
"""
function ActuaryUtilities.present_value(ins::T,time) where {T<:Insurance}
ts =timepoints(ins)
ts = timepoints(ins)
times = (t - time for t in ts if t > time)
cfs = (cf for (cf,t) in zip(cashflows(ins),ts) if t > time)
yield = ins.int
Expand All @@ -676,6 +712,8 @@ end

premium_net(lc::LifeContingency, to_time) = A(lc, to_time) / ä(lc, to_time)

premium_net(lc::LifeContingency, to_time, maturity) = A(lc, to_time, maturity) / ä(lc, to_time)

"""
reserve_premium_net(lc::LifeContingency,time)

Expand All @@ -687,6 +725,12 @@ function reserve_premium_net(lc::LifeContingency, time)
return (PVFB - PVFP) / APV(lc, time)
end

function reserve_premium_net(lc::LifeContingency, time, term, maturity)
PVFB = present_value(Insurance(lc, term, maturity)) - present_value(Insurance(lc, term, maturity), time)
PVFP = premium_net(lc, term ,maturity) * (ä(lc, term) - ä(lc, term - time))
return max(0.0, (PVFB - PVFP) / APV(lc, time))
end

"""
APV(lc::LifeContingency,to_time)

Expand Down
3 changes: 3 additions & 0 deletions test/single_life.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,12 @@
@test present_value(Insurance(ins),0) ≈ 0.1107844934319970
@test present_value(Insurance(ins),90) / survival(Insurance(ins),90) ≈ 1 / 1.05
@test present_value(AnnuityDue(ins)) ≈ 18.6735256379281000
@test present_value(Insurance(ins, 20, 1000)) ≈ 366.52476153552664
@test premium_net(ins) ≈ 0.0059327036350854
@test premium_net(ins, 20, 1000) ≈ 28.20263753
@test reserve_premium_net(ins, 1) ≈ 0.0059012862412992
@test reserve_premium_net(ins, 2) ≈ 0.0119711961204193
@test reserve_premium_net(ins, 2, 20, 1000) ≈ 0

qs = t.select[30][30:55]
@test present_value(Insurance(ins, 26)) ≈ sum(qs .* [1; cumprod(1 .- qs[1:25])] .* [1.05^-t for t = 1:26])
Expand Down