Skip to content

Commit

Permalink
added the first version of implementation of endowment
Browse files Browse the repository at this point in the history
  • Loading branch information
leeyuntien committed Feb 14, 2023
1 parent a440622 commit 5d452a9
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 9 deletions.
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

0 comments on commit 5d452a9

Please sign in to comment.