diff --git a/Project.toml b/Project.toml index 4eace86..2379f14 100644 --- a/Project.toml +++ b/Project.toml @@ -1,20 +1,17 @@ name = "LifeContingencies" uuid = "c8f0d631-89cd-4a1f-93d0-7542c3692561" authors = ["Alec Loudenback"] -version = "2.3.1" +version = "2.4.0" [deps] -ActuaryUtilities = "bdd23359-8b1c-4f88-b89b-d11982a786f4" -Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +FinanceCore = "b9b1ffdd-6612-4b69-8227-7663be06e089" MortalityTables = "4780e19d-04b9-53dc-86c2-9e9aa59b5a12" Transducers = "28d57a85-8fef-5791-bfe6-a80928e7c999" -Yields = "d7e99b2f-e7f3-4d9e-9f01-2338fc023ad3" [compat] -ActuaryUtilities = "^2,^3" -MortalityTables = "^0.12, ^1, ^2" +MortalityTables = "^1, ^2" Transducers = "^0.4" -Yields = "^2,^3" +FinanceCore = "2" julia = "^1.6" [extras] diff --git a/README.md b/README.md index a04187d..e2c7d28 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Calculate various items for a 30-year-old male nonsmoker using 2015 VBT base tab using LifeContingencies using MortalityTables -using Yields +using FinanceModels import LifeContingencies: V, ä # pull the shortform notation into scope # load mortality rates from MortalityTables.jl @@ -50,7 +50,7 @@ life = SingleLife( # The life underlying the risk mortality = vbt2001.select[issue_age], # -- Mortality rates ) -yield = Yields.Constant(0.05) # Using a flat 5% interest rate +yield = FinanceModels.Yield.Constant(0.05) # Using a flat 5% interest rate lc = LifeContingency(life, yield) # LifeContingency joins the risk with interest @@ -157,7 +157,7 @@ tables = [ ] issue_ages = 30:90 -int = Yields.Constant(0.05) +int = FinanceModels.Yield.Constant(0.05) whole_life_costs = map(tables) do t map(issue_ages) do ia @@ -190,7 +190,7 @@ l2 = SingleLife(mortality = m2.ultimate, issue_age = 37) jl = JointLife(lives=(l1, l2), contingency=LastSurvivor(), joint_assumption=Frasier()) -Insurance(jl,Yields.Constant(0.05)) # whole life insurance +Insurance(jl,FinanceModels.Yield.Constant(0.05)) # whole life insurance ... # similar functions as shown in the first example above ``` diff --git a/src/LifeContingencies.jl b/src/LifeContingencies.jl index fb8aa55..df65615 100644 --- a/src/LifeContingencies.jl +++ b/src/LifeContingencies.jl @@ -1,10 +1,9 @@ module LifeContingencies -using ActuaryUtilities using MortalityTables using Transducers -using Dates -using Yields +using FinanceCore +import FinanceCore: present_value, discount const mt = MortalityTables @@ -69,11 +68,11 @@ struct SingleLife{M,D} <: Life fractional_assump::D end -function SingleLife(; mortality, issue_age = nothing, alive = true, fractional_assump = mt.Uniform()) +function SingleLife(; mortality, issue_age=nothing, alive=true, fractional_assump=mt.Uniform()) return SingleLife(mortality; issue_age, alive, fractional_assump) end -function SingleLife(mortality; issue_age = nothing, alive = true, fractional_assump = mt.Uniform()) +function SingleLife(mortality; issue_age=nothing, alive=true, fractional_assump=mt.Uniform()) if isnothing(issue_age) issue_age = firstindex(mortality) end @@ -231,7 +230,7 @@ end ``l_x`` is a retrospective actuarial commutation function which is the survival up to a certain point in time. By default, will have a unitary basis (ie `1.0`), but you can specify `basis` keyword argument to use something different (e.g. `1000` is common in the literature.) """ -function l(lc::LifeContingency, to_time; basis = 1.0) +function l(lc::LifeContingency, to_time; basis=1.0) return survival(lc.life, to_time) * basis end @@ -307,7 +306,7 @@ Issue age is based on the `issue_age` in the LifeContingency `lc`. ``` ins = Insurance( SingleLife(mortality = UltimateMortality([0.5,0.5]),issue_age = 0), - Yields.Constant(0.05), + FinanceModels.Yield.Constant(0.05), 1 # 1 year term ) ``` @@ -370,12 +369,12 @@ Annuity due with the benefit period starting at `start_time` and ending after `n ``` ins = AnnuityDue( SingleLife(mortality = UltimateMortality([0.5,0.5]),issue_age = 0), - Yields.Constant(0.05), + FinanceModels.Yield.Constant(0.05), 1, # term of policy ) ``` """ -function AnnuityDue(life, int, term; certain = nothing, start_time = 0, frequency = 1) +function AnnuityDue(life, int, term; certain=nothing, start_time=0, frequency=1) term < 1 && return ZeroBenefit(life, int) if isnothing(certain) Annuity(life, int, Due(), TermAnnuity(term), start_time, frequency) @@ -384,7 +383,7 @@ function AnnuityDue(life, int, term; certain = nothing, start_time = 0, frequenc end end -function AnnuityDue(life, int; certain = nothing, start_time = 0, frequency = 1) +function AnnuityDue(life, int; certain=nothing, start_time=0, frequency=1) if isnothing(certain) Annuity(life, int, Due(), LifeAnnuity(), start_time, frequency) else @@ -392,11 +391,11 @@ function AnnuityDue(life, int; certain = nothing, start_time = 0, frequency = 1) end end -function AnnuityDue(lc::L, term; certain = nothing, start_time = 0, frequency = 1) where {L<:LifeContingency} +function AnnuityDue(lc::L, term; certain=nothing, start_time=0, frequency=1) where {L<:LifeContingency} return AnnuityDue(lc.life, lc.int, term; certain, start_time, frequency) end -function AnnuityDue(lc::L; certain = nothing, start_time = 0, frequency = 1) where {L<:LifeContingency} +function AnnuityDue(lc::L; certain=nothing, start_time=0, frequency=1) where {L<:LifeContingency} return AnnuityDue(lc.life, lc.int; certain, start_time, frequency) end @@ -411,13 +410,13 @@ Annuity immediate with the benefit period starting at `start_time` and ending af ``` ins = AnnuityImmediate( SingleLife(mortality = UltimateMortality([0.5,0.5]),issue_age = 0), - Yields.Constant(0.05), + FinanceModels.Yield.Constant(0.05), 1 # term of policy ) ``` """ -function AnnuityImmediate(life, int, term; certain = nothing, start_time = 0, frequency = 1) +function AnnuityImmediate(life, int, term; certain=nothing, start_time=0, frequency=1) term < 1 && return ZeroBenefit(life, int) if isnothing(certain) Annuity(life, int, Immediate(), TermAnnuity(term), start_time, frequency) @@ -426,7 +425,7 @@ function AnnuityImmediate(life, int, term; certain = nothing, start_time = 0, fr end end -function AnnuityImmediate(life, int; certain = nothing, start_time = 0, frequency = 1) +function AnnuityImmediate(life, int; certain=nothing, start_time=0, frequency=1) if isnothing(certain) Annuity(life, int, Immediate(), LifeAnnuity(), start_time, frequency) else @@ -434,11 +433,11 @@ function AnnuityImmediate(life, int; certain = nothing, start_time = 0, frequenc end end -function AnnuityImmediate(lc::L, term; certain = nothing, start_time = 0, frequency = 1) where {L<:LifeContingency} +function AnnuityImmediate(lc::L, term; certain=nothing, start_time=0, frequency=1) where {L<:LifeContingency} return AnnuityImmediate(lc.life, lc.int, term; certain, start_time, frequency) end -function AnnuityImmediate(lc::L; certain = nothing, start_time = 0, frequency = 1) where {L<:LifeContingency} +function AnnuityImmediate(lc::L; certain=nothing, start_time=0, frequency=1) where {L<:LifeContingency} return AnnuityImmediate(lc.life, lc.int; certain, start_time, frequency) end @@ -449,7 +448,7 @@ The survivorship for the given insurance from time zero to `time`. """ function MortalityTables.survival(ins::I, t) where {I<:Insurance} - survival(ins.life,t) + survival(ins.life, t) end """ @@ -475,8 +474,8 @@ The discount vector for the given insurance. To get the fully computed and allocated vector, call `collect(discount(...))`. """ -function Yields.discount(ins::I) where {I<:Insurance} - return Iterators.map(t -> Yields.discount.(ins.int, t), timepoints(ins)) +function FinanceCore.discount(ins::I) where {I<:Insurance} + return Iterators.map(t -> FinanceCore.discount.(ins.int, t), timepoints(ins)) end @@ -631,7 +630,7 @@ end The actuarial present value of the given insurance benefits. """ -function ActuaryUtilities.present_value(ins::T) where {T<:Insurance} +function FinanceCore.present_value(ins::T) where {T<:Insurance} cfs = cashflows(ins) times = timepoints(ins) yield = ins.int @@ -653,10 +652,10 @@ To get an undecremented present value, divide by the survivorship to that timepo present_value(ins,10) / survival(ins,10) ``` """ -function ActuaryUtilities.present_value(ins::T,time) where {T<:Insurance} - ts =timepoints(ins) +function FinanceCore.present_value(ins::T, time) where {T<:Insurance} + 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) + cfs = (cf for (cf, t) in zip(cashflows(ins), ts) if t > time) yield = ins.int pv = present_value(yield, cfs, times) return pv @@ -786,9 +785,9 @@ function mt.survival(l::JointLife, from_time, to_time) end function mt.survival(ins::LastSurvivor, assump::JointAssumption, l::JointLife, from_time, to_time) - a = survival(ins,assump,l,from_time) - b = survival(ins,assump,l,to_time) - return b/a + a = survival(ins, assump, l, from_time) + b = survival(ins, assump, l, to_time) + return b / a end function mt.survival(ins::LastSurvivor, assump::JointAssumption, l::JointLife, to_time) @@ -800,19 +799,19 @@ function mt.survival(ins::LastSurvivor, assump::JointAssumption, l::JointLife, t return ₜpₓ + ₜpᵧ - ₜpₓ * ₜpᵧ end -Yields.discount(lc::LifeContingency, t) = discount(lc.int, t) -Yields.discount(lc::LifeContingency, t1, t2) = discount(lc.int, t1, t2) +FinanceCore.discount(lc::LifeContingency, t) = discount(lc.int, t) +FinanceCore.discount(lc::LifeContingency, t1, t2) = discount(lc.int, t1, t2) # unexported aliases const V = reserve_premium_net -const v = Yields.discount +const v = FinanceCore.discount # A(args) = present_value(Insurance(args)) # a(args, kwargs) = present_value(AnnuityImmediate(args...; kwargs...)) # ä(args) = present_value(AnnuityDue(args)) -const A = present_value ∘ Insurance -const a = present_value ∘ AnnuityImmediate -const ä = present_value ∘ AnnuityDue +const A = FinanceCore.present_value ∘ Insurance +const a = FinanceCore.present_value ∘ AnnuityImmediate +const ä = FinanceCore.present_value ∘ AnnuityDue const P = premium_net const ω = omega diff --git a/test/AMLCR.jl b/test/AMLCR.jl index 57f4cb6..a8b5fdf 100644 --- a/test/AMLCR.jl +++ b/test/AMLCR.jl @@ -3,39 +3,39 @@ # §5.7 payment frequency and certain (Tables 5.1 and 5.2) function f(age) - life = SingleLife(mortality = AMLCR_std, issue_age = age) - int = Yields.Constant(0.05) + life = SingleLife(mortality=AMLCR_std, issue_age=age) + int = 0.05 lc = [ AnnuityImmediate(life, int) - AnnuityImmediate(life, int, frequency = 4) + AnnuityImmediate(life, int, frequency=4) AnnuityDue(life, int) - AnnuityDue(life, int, frequency = 4) + AnnuityDue(life, int, frequency=4) AnnuityImmediate(life, int, 10) - AnnuityImmediate(life, int, 10, frequency = 4) - AnnuityDue(life, int, 10, frequency = 4) + AnnuityImmediate(life, int, 10, frequency=4) + AnnuityDue(life, int, 10, frequency=4) ] return present_value.(lc) end # atol only to two decimals b/c AMLCR uses rounded rates? - @test all(isapprox.(f(20), [18.966, 19.338, 19.966, 19.588, 7.711, 7.855, 7.952], atol = 0.01)) - @test all(isapprox.(f(80), [7.548, 7.917, 8.548, 8.167, 6.128, 6.373, 6.539], atol = 0.01)) + @test all(isapprox.(f(20), [18.966, 19.338, 19.966, 19.588, 7.711, 7.855, 7.952], atol=0.01)) + @test all(isapprox.(f(80), [7.548, 7.917, 8.548, 8.167, 6.128, 6.373, 6.539], atol=0.01)) - life = SingleLife(mortality = AMLCR_std, issue_age = 20) - int = Yields.Constant(0.05) + life = SingleLife(mortality=AMLCR_std, issue_age=20) + int = 0.05 # property tests - @test present_value(AnnuityDue(life, int)) > present_value(AnnuityDue(life, int, start_time = 5)) + @test present_value(AnnuityDue(life, int)) > present_value(AnnuityDue(life, int, start_time=5)) - @test present_value(AnnuityDue(life, int, 5, certain = 5, start_time = 5)) == sum([1 for t = 5:9] .* [1 / 1.05^t for t = 5:9]) + @test present_value(AnnuityDue(life, int, 5, certain=5, start_time=5)) == sum([1 for t = 5:9] .* [1 / 1.05^t for t = 5:9]) # relation on pg 124 @test issorted(present_value.([ AnnuityImmediate(life, int) - AnnuityImmediate(life, int, frequency = 4) - AnnuityDue(life, int, frequency = 4) + AnnuityImmediate(life, int, frequency=4) + AnnuityDue(life, int, frequency=4) AnnuityDue(life, int) ])) diff --git a/test/Project.toml b/test/Project.toml index 098583d..60d8dd5 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,5 +1,5 @@ [deps] -ActuaryUtilities = "bdd23359-8b1c-4f88-b89b-d11982a786f4" +FinanceCore = "b9b1ffdd-6612-4b69-8227-7663be06e089" MortalityTables = "4780e19d-04b9-53dc-86c2-9e9aa59b5a12" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/interest_rates.jl b/test/interest_rates.jl deleted file mode 100644 index 479d21c..0000000 --- a/test/interest_rates.jl +++ /dev/null @@ -1,78 +0,0 @@ -@testset "basic interest rates" begin - - - ## vector interest rate - @testset "vector interest rate" begin - i = InterestRate([0.05, 0.05, 0.05]) - - @test disc(i,0) == 1.0 - @test disc(i,1) == 1 / 1.05 - @test disc(i,2) == 1 / 1.05 ^ 2 - @test disc(i,3) == 1 / 1.05 ^ 3 - @test disc(i,1,2) == 1 / 1.05 - @test rate(i,1) == 0.05 - @test rate(i,2) == 0.05 - - @test disc.(i,1:3) == [1 / 1.05 ^ t for t in 1:3] - end - - ## real interest rate - @testset "constant interest rate" begin - i = InterestRate(0.05) - - @test disc(i,0) == 1.0 - @test disc(i,1) == 1 / 1.05 - @test disc(i,2) == 1 / 1.05 ^ 2 - @test disc(i,3) == 1 / 1.05 ^ 3 - @test disc(i,1,2) == 1 / 1.05 - @test rate(i,1) == 0.05 - @test rate(i,2) == 0.05 - - @test disc.(i,1:3) == [1 / 1.05 ^ t for t in 1:3] - end -end - -@testset "DiscountFactor Iterator" begin - - target = [1 / 1.05 ^ t for t in 0:4 ] - - @testset "contsant" begin - df = DiscountFactor(InterestRate(0.05),1) - - ds = Iterators.take(df,5) |> collect - - @test all(ds .≈ target) - - @test length(Iterators.take(df,100)) == 100 - - # can't collect an infinite series - @test_throws MethodError collect(df) - - @test df == InterestRate(0.05)(1) - end - - @testset "vector" begin - df = DiscountFactor(InterestRate(repeat([0.05],4)),1) - - ds = Iterators.take(df,5) |> collect - @test all(ds .≈ target) - @test all(collect(df) .≈ target) - @test length(df) == 5 - - @test_broken InterestRate([0.05,0.05,0.05,0.05])(1) == df #https://stackoverflow.com/questions/62336686/struct-equality-with-arrays - end - -end - -@testset "passthrough life contingency" begin - ins = LifeContingency( - SingleLife( - mort = UltimateMortality([0.5]), - issue_age = 0 - ), - InterestRate(0.05) - ) - - @test disc(ins,1) ≈ 1/1.05 - @test disc(ins,1,2) ≈ 1/1.05 -end \ No newline at end of file diff --git a/test/joint_life.jl b/test/joint_life.jl index 0a91b98..6372f68 100644 --- a/test/joint_life.jl +++ b/test/joint_life.jl @@ -4,29 +4,29 @@ @testset "Joint Last Survivor unknown status" begin @testset "ASM Manual 13th Ed., Example 54B" begin - q1 = UltimateMortality([.10,.12,.14,.16,.18],start_age=80) - q2 = UltimateMortality([.07,.09,.11,.13,.15],start_age=80) - - l1 = SingleLife(mortality=q1,issue_age=82) - l2 = SingleLife(mortality=q2,issue_age=80) - j = JointLife(lives=(l1,l2)) - - @test survival(j,2) ≈ 0.95733 atol=1e-5 - @test survival(j,3) ≈ 0.899399 atol=1e-5 + q1 = UltimateMortality([0.10, 0.12, 0.14, 0.16, 0.18], start_age=80) + q2 = UltimateMortality([0.07, 0.09, 0.11, 0.13, 0.15], start_age=80) + + l1 = SingleLife(mortality=q1, issue_age=82) + l2 = SingleLife(mortality=q2, issue_age=80) + j = JointLife(lives=(l1, l2)) + + @test survival(j, 2) ≈ 0.95733 atol = 1e-5 + @test survival(j, 3) ≈ 0.899399 atol = 1e-5 end @testset "misc properties" begin - q = UltimateMortality([.1 for t in 1:200]) + q = UltimateMortality([0.1 for t in 1:200]) l1 = SingleLife(mortality=q) l2 = SingleLife(mortality=q) - j = JointLife(lives=(l1,l2)) - - ins0 = Insurance(j,Yields.Constant(0.0)) - survival(j,1,2) - @test 1- survival(j,4)/survival(j,3) ≈ decrement(j,3,4) - @test pv(ins0) ≈ 1.0 atol=1e-6 # at zero percent discount the benefit should sum to the unit - @test last(collect(survival(ins0))) ≈ 0.0 atol=1e-6 + j = JointLife(lives=(l1, l2)) + + ins0 = Insurance(j, 0.0) + survival(j, 1, 2) + @test 1 - survival(j, 4) / survival(j, 3) ≈ decrement(j, 3, 4) + @test pv(ins0) ≈ 1.0 atol = 1e-6 # at zero percent discount the benefit should sum to the unit + @test last(collect(survival(ins0))) ≈ 0.0 atol = 1e-6 end @@ -40,36 +40,36 @@ ps₂ = ℓ₂ ./ ℓ₂[1] qs₂ = [1 - ps₂[t] / ps₂[t-1] for t = 2:5] - m1 = UltimateMortality(qs₁, start_age = 65) - m2 = UltimateMortality(qs₂, start_age = 60) + m1 = UltimateMortality(qs₁, start_age=65) + m2 = UltimateMortality(qs₂, start_age=60) @test decrement(m1, 65, 66) == 1 - ℓ₁[2] / ℓ₁[1] @test decrement(m2, 60, 61) == 1 - ℓ₂[2] / ℓ₂[1] - l1 = SingleLife(mortality = m1, issue_age = 65) - l2 = SingleLife(mortality = m2, issue_age = 60) + l1 = SingleLife(mortality=m1, issue_age=65) + l2 = SingleLife(mortality=m2, issue_age=60) - jl = JointLife(lives = (l1, l2), contingency = LastSurvivor(), joint_assumption = Frasier()) + jl = JointLife(lives=(l1, l2), contingency=LastSurvivor(), joint_assumption=Frasier()) - @test isapprox(survival(jl, 2), 0.9997, atol = 1e-4) + @test isapprox(survival(jl, 2), 0.9997, atol=1e-4) - ins = LifeContingency(jl, Yields.Constant(0.05)) - ins_l1 = LifeContingency(jl.lives[1], Yields.Constant(0.05)) - ins_l2 = LifeContingency(jl.lives[2], Yields.Constant(0.05)) + ins = LifeContingency(jl, 0.05) + ins_l1 = LifeContingency(jl.lives[1], 0.05) + ins_l2 = LifeContingency(jl.lives[2], 0.05) # problem 9.1.f - @test isapprox(present_value(AnnuityDue(ins, 5)), 4.5437, atol = 1e-4) + @test isapprox(present_value(AnnuityDue(ins, 5)), 4.5437, atol=1e-4) - @test isapprox(present_value(AnnuityDue(ins)), 4.5437, atol = 1e-4) + @test isapprox(present_value(AnnuityDue(ins)), 4.5437, atol=1e-4) end @testset "CIA tables" begin m1 = MortalityTables.table("1986-92 CIA – Male Smoker, ANB") m2 = MortalityTables.table("1986-92 CIA – Female Nonsmoker, ANB") - l1 = SingleLife(mortality = m1.ultimate, issue_age = 40) - l2 = SingleLife(mortality = m2.ultimate, issue_age = 37) + l1 = SingleLife(mortality=m1.ultimate, issue_age=40) + l2 = SingleLife(mortality=m2.ultimate, issue_age=37) - jl = JointLife(lives = (l1, l2), contingency = LastSurvivor(), joint_assumption = Frasier()) + jl = JointLife(lives=(l1, l2), contingency=LastSurvivor(), joint_assumption=Frasier()) @testset "independent lives" begin for time = 1:40 @@ -84,17 +84,17 @@ q_cumulative = [0.00000141120, 0.00000619469, 0.00001540563, 0.00003049492, 0.00005304748, 0.00008483992, 0.00012810660, 0.00018592774, 0.00026115553, 0.00035808060, 0.00048174038, 0.00063772939, 0.00083372522, 0.00107881691, 0.00138351443, 0.00176067732, 0.00222572986, 0.00279771301, 0.00349813669, 0.00435465818, 0.00539795948, 0.00666528343, 0.00820138084, 0.01005975458, 0.01229886182, 0.01498992747, 0.01821281880, 0.02206126957, 0.02663884979, 0.03206804441, 0.03847706939, 0.04601237389, 0.05483175625, 0.06510286552, 0.07700389465, 0.09071177542, 0.10641770955, 0.12429441828, 0.14451430984, 0.16722048170, 0.19253547997, 0.22054884408, 0.25129614928, 0.28476269870, 0.32086998531, 0.35946849466, 0.40033347120, 0.44316542761, 0.48758225561, 0.53312891378, 0.57927637250, 0.62543608637, 0.67098101782, 0.71525516126, 0.75760741222, 0.79741168545, 0.83410325108, 0.86720651682, 0.89637071504, 0.92139174110, 0.94223548231, 0.95904493828, 0.97211041164, 0.98190281145, 0.98909354112, 0.99422007198, 0.99758716905, 0.99939322200, 1.00000000000] @testset "precalced vectors" begin for time = 1:40 - @test isapprox(decrement(jl, time), q_cumulative[time], atol = 1e-6) + @test isapprox(decrement(jl, time), q_cumulative[time], atol=1e-6) end for time = 1:40 q′ = 1 - survival(jl, time) / survival(jl, time - 1) - @test isapprox(q′, q_annual[time], atol = 1e-6) + @test isapprox(q′, q_annual[time], atol=1e-6) end for time = 1:40 - @test isapprox(survival(jl, time), 1 - q_cumulative[time], atol = 1e-6) + @test isapprox(survival(jl, time), 1 - q_cumulative[time], atol=1e-6) end end end diff --git a/test/runtests.jl b/test/runtests.jl index ae47c37..8ab5980 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,10 +1,10 @@ using LifeContingencies -using LifeContingencies: l,D,M,N,C +using LifeContingencies: l, D, M, N, C using Test +using FinanceCore using MortalityTables -using ActuaryUtilities -const Yields = LifeContingencies.Yields +FinanceCore.present_value(0.05, 10) include("test_mortality.jl") include("simple_mort.jl") include("AMLCR.jl") diff --git a/test/simple_mort.jl b/test/simple_mort.jl index e821657..f202920 100644 --- a/test/simple_mort.jl +++ b/test/simple_mort.jl @@ -3,11 +3,11 @@ # whole life insurance ins = Insurance( - SingleLife(mortality = mt, issue_age = 0), - Yields.Constant(0.05) + SingleLife(mortality=mt, issue_age=0), + 0.05 ) - @test all(survival(SingleLife(mortality = mt, issue_age = 0)) .== [1.0, 0.5, 0.25]) + @test all(survival(SingleLife(mortality=mt, issue_age=0)) .== [1.0, 0.5, 0.25]) @test [timepoints(ins)...] == [1.0, 2.0] @test [survival(ins)...] == [1.0, 0.5] @@ -16,8 +16,9 @@ @test [probability(ins)...] == [0.5, 0.25] @test [cashflows(ins)...] == [0.5, 0.25] @test [cashflows(ins)...] == benefit(ins) .* probability(ins) - @test present_value(ins) ≈ 0.5 / 1.05 + 0.5 * 0.5 / 1.05^2 - @test present_value(ins) ≈ LifeContingencies.A(ins.life, ins.int) + @test FinanceCore.present_value(0.05, 1) ≈ 1 / 1.05 + @test FinanceCore.present_value(ins) ≈ 0.5 / 1.05 + 0.5 * 0.5 / 1.05^2 + @test FinanceCore.present_value(ins) ≈ LifeContingencies.A(ins.life, ins.int) # basic life contingency tests @test survival(LifeContingency(ins), 0, 1) ≈ 0.5 @@ -27,8 +28,8 @@ # term life insurance ins = Insurance( - SingleLife(mortality = mt, issue_age = 0), - Yields.Constant(0.05), + SingleLife(mortality=mt, issue_age=0), + 0.05, 1 ) @@ -39,12 +40,12 @@ @test [cashflows(ins)...] == [0.5] @test [cashflows(ins)...] == benefit(ins) .* probability(ins) @test [timepoints(ins)...] == [1.0] - @test present_value(ins) ≈ 0.5 / 1.05 + @test FinanceCore.present_value(ins) ≈ 0.5 / 1.05 # annuity due ins = AnnuityDue( - SingleLife(mortality = mt, issue_age = 0), - Yields.Constant(0.05) + SingleLife(mortality=mt, issue_age=0), + 0.05 ) @test [survival(ins)...] == [1.0, 0.5, 0.25] @@ -54,43 +55,43 @@ @test [probability(ins)...] == [1.0, 0.5, 0.25] @test [cashflows(ins)...] == [1.0, 0.5, 0.25] @test [cashflows(ins)...] == benefit(ins) .* probability(ins) - @test present_value(ins) ≈ 1 + 1 * 0.5 / 1.05 + 1 * 0.25 / 1.05^2 + @test FinanceCore.present_value(ins) ≈ 1 + 1 * 0.5 / 1.05 + 1 * 0.25 / 1.05^2 ins = AnnuityDue( - SingleLife(mortality = mt, issue_age = 0), - Yields.Constant(0.05), + SingleLife(mortality=mt, issue_age=0), + 0.05, 2 ) - @test present_value(ins) ≈ 1 + 1 * 0.5 / 1.05 + @test FinanceCore.present_value(ins) ≈ 1 + 1 * 0.5 / 1.05 ins = AnnuityImmediate( - SingleLife(mortality = mt, issue_age = 0), - Yields.Constant(0.05), + SingleLife(mortality=mt, issue_age=0), + 0.05, 1 ) - @test present_value(ins) ≈ 1 * 0.5 * 1 / 1.05 + @test FinanceCore.present_value(ins) ≈ 1 * 0.5 * 1 / 1.05 ins = AnnuityDue( - SingleLife(mortality = mt, issue_age = 0), - Yields.Constant(0.05), + SingleLife(mortality=mt, issue_age=0), + 0.05, 1, - start_time = 1 + start_time=1 ) @test timepoints(ins) == [1.0] @test [probability(ins)...] == [0.5] - @test present_value(ins) ≈ 1 * 0.5 * 1 / 1.05 + @test FinanceCore.present_value(ins) ≈ 1 * 0.5 * 1 / 1.05 end @testset "one year" begin ins = LifeContingency( SingleLife( - mortality = UltimateMortality([0.5]), - issue_age = 0 + mortality=UltimateMortality([0.5]), + issue_age=0 ), - Yields.Constant(0.05) + 0.05 ) @test survival(ins, 1) ≈ 0.5 @@ -99,73 +100,73 @@ end @test survival(ins, 0.5) ≈ 1 - 0.5 * 0.5 @test omega(ins) ≈ 1 - @test present_value(AnnuityDue(ins)) ≈ 1 + 1 * 0.5 / 1.05 - @test present_value(AnnuityDue(ins, 1)) ≈ 1 - @test present_value(AnnuityDue(ins, 0)) == 0 - @test present_value(AnnuityImmediate(ins, 1)) ≈ 1 * 0.5 / 1.05 - @test present_value(AnnuityImmediate(ins, 0)) == 0 + @test FinanceCore.present_value(AnnuityDue(ins)) ≈ 1 + 1 * 0.5 / 1.05 + @test FinanceCore.present_value(AnnuityDue(ins, 1)) ≈ 1 + @test FinanceCore.present_value(AnnuityDue(ins, 0)) == 0 + @test FinanceCore.present_value(AnnuityImmediate(ins, 1)) ≈ 1 * 0.5 / 1.05 + @test FinanceCore.present_value(AnnuityImmediate(ins, 0)) == 0 - @test present_value(Insurance(ins)) ≈ 0.5 / 1.05 - @test present_value(Insurance(ins, 1)) ≈ 0.5 / 1.05 - @test present_value(Insurance(ins, 0)) ≈ 0 + @test FinanceCore.present_value(Insurance(ins)) ≈ 0.5 / 1.05 + @test FinanceCore.present_value(Insurance(ins, 1)) ≈ 0.5 / 1.05 + @test FinanceCore.present_value(Insurance(ins, 0)) ≈ 0 ins_jl = LifeContingency( JointLife((ins.life, ins.life), LastSurvivor(), Frasier()), - Yields.Constant(0.05) + 0.05 ) @test omega(ins_jl) ≈ 1 - @test present_value(AnnuityDue(ins_jl)) ≈ 1 + 1 * 0.75 / 1.05 - @test present_value(AnnuityDue(ins_jl, 1)) ≈ 1 - @test present_value(AnnuityImmediate(ins_jl, 1)) ≈ 1 * 0.75 / 1.05 - @test present_value(AnnuityDue(ins_jl, 2)) ≈ 1 + 1 * 0.75 / 1.05 - @test present_value(AnnuityDue(ins_jl, 2; certain = 2)) ≈ 1 + 1 / 1.05 - @test present_value(AnnuityDue(ins_jl, 0)) == 0 + @test FinanceCore.present_value(AnnuityDue(ins_jl)) ≈ 1 + 1 * 0.75 / 1.05 + @test FinanceCore.present_value(AnnuityDue(ins_jl, 1)) ≈ 1 + @test FinanceCore.present_value(AnnuityImmediate(ins_jl, 1)) ≈ 1 * 0.75 / 1.05 + @test FinanceCore.present_value(AnnuityDue(ins_jl, 2)) ≈ 1 + 1 * 0.75 / 1.05 + @test FinanceCore.present_value(AnnuityDue(ins_jl, 2; certain=2)) ≈ 1 + 1 / 1.05 + @test FinanceCore.present_value(AnnuityDue(ins_jl, 0)) == 0 @test survival(ins_jl, 1) ≈ 0.5 + 0.5 - 0.5 * 0.5 @test all(survival(JointLife((ins.life, ins.life), LastSurvivor(), Frasier())) .== [1.0, 0.75]) - @test present_value(Insurance(ins_jl)) ≈ 0.25 / 1.05 - @test present_value(Insurance(ins_jl, 1)) ≈ 0.25 / 1.05 - @test present_value(Insurance(ins_jl, 0)) ≈ 0 + @test FinanceCore.present_value(Insurance(ins_jl)) ≈ 0.25 / 1.05 + @test FinanceCore.present_value(Insurance(ins_jl, 1)) ≈ 0.25 / 1.05 + @test FinanceCore.present_value(Insurance(ins_jl, 0)) ≈ 0 end @testset "two year no discount" begin ins = LifeContingency( SingleLife( - mortality = UltimateMortality([0.0, 0.0]), - issue_age = 0 + mortality=UltimateMortality([0.0, 0.0]), + issue_age=0 ), - Yields.Constant(0.00) + 0.0 ) @test omega(ins) ≈ 2 - @test present_value(AnnuityDue(ins)) ≈ 3 - @test present_value(AnnuityDue(ins; start_time = 1)) ≈ 2 - @test present_value(AnnuityDue(ins, 1)) ≈ 1 - @test present_value(AnnuityDue(ins, 2)) ≈ 2 - @test_throws BoundsError present_value(AnnuityDue(ins, 2; start_time = 2)) - @test present_value(AnnuityDue(ins, 3)) ≈ 3 - @test present_value(AnnuityDue(ins, 0)) == 0 - @test present_value(AnnuityImmediate(ins, 0)) == 0 - @test present_value(AnnuityImmediate(ins, 1)) ≈ 1 - @test present_value(AnnuityImmediate(ins, 1; start_time = 1)) ≈ 1 - @test_throws BoundsError present_value(AnnuityImmediate(ins; start_time = 2)) - @test present_value(AnnuityImmediate(ins, 2)) ≈ 2 - @test present_value(AnnuityImmediate(ins)) ≈ 2 - @test present_value(AnnuityImmediate(ins; certain = 0)) ≈ 2 - @test present_value(AnnuityImmediate(ins; certain = 2)) ≈ 2 - - @test present_value(Insurance(ins)) ≈ 0 - @test present_value(Insurance(ins, 1)) ≈ 0 - @test present_value(Insurance(ins, 0)) ≈ 0 + @test FinanceCore.present_value(AnnuityDue(ins)) ≈ 3 + @test FinanceCore.present_value(AnnuityDue(ins; start_time=1)) ≈ 2 + @test FinanceCore.present_value(AnnuityDue(ins, 1)) ≈ 1 + @test FinanceCore.present_value(AnnuityDue(ins, 2)) ≈ 2 + @test_throws BoundsError FinanceCore.present_value(AnnuityDue(ins, 2; start_time=2)) + @test FinanceCore.present_value(AnnuityDue(ins, 3)) ≈ 3 + @test FinanceCore.present_value(AnnuityDue(ins, 0)) == 0 + @test FinanceCore.present_value(AnnuityImmediate(ins, 0)) == 0 + @test FinanceCore.present_value(AnnuityImmediate(ins, 1)) ≈ 1 + @test FinanceCore.present_value(AnnuityImmediate(ins, 1; start_time=1)) ≈ 1 + @test_throws BoundsError FinanceCore.present_value(AnnuityImmediate(ins; start_time=2)) + @test FinanceCore.present_value(AnnuityImmediate(ins, 2)) ≈ 2 + @test FinanceCore.present_value(AnnuityImmediate(ins)) ≈ 2 + @test FinanceCore.present_value(AnnuityImmediate(ins; certain=0)) ≈ 2 + @test FinanceCore.present_value(AnnuityImmediate(ins; certain=2)) ≈ 2 + + @test FinanceCore.present_value(Insurance(ins)) ≈ 0 + @test FinanceCore.present_value(Insurance(ins, 1)) ≈ 0 + @test FinanceCore.present_value(Insurance(ins, 0)) ≈ 0 ins_jl = LifeContingency( JointLife( - lives = (ins.life, ins.life), - contingency = LastSurvivor(), - joint_assumption = Frasier()), - Yields.Constant(0.00) + lives=(ins.life, ins.life), + contingency=LastSurvivor(), + joint_assumption=Frasier()), + 0.0 ) @test survival(ins_jl, 0) ≈ 1.0 @@ -177,31 +178,31 @@ end @testset "two year with interest" begin ins = LifeContingency( SingleLife( - mortality = UltimateMortality([0.5, 0.5]), - issue_age = 0 + mortality=UltimateMortality([0.5, 0.5]), + issue_age=0 ), - Yields.Constant(0.05) + 0.05 ) @test omega(ins) ≈ 2 - @test present_value(AnnuityDue(ins)) ≈ 1 + 1 * 0.5 * 1 / 1.05 + 1 * 0.25 / 1.05^2 - @test present_value(AnnuityDue(ins, 1)) ≈ 1 - @test present_value(AnnuityDue(ins, 2)) ≈ 1 + 1 * 0.5 * 1 / 1.05 - @test present_value(AnnuityDue(ins, 3)) ≈ 1 + 1 * 0.5 * 1 / 1.05 + 1 * 0.25 / 1.05^2 - @test present_value(AnnuityDue(ins, 0)) ≈ 0 - @test present_value(AnnuityImmediate(ins, 0)) ≈ 0 - @test present_value(AnnuityImmediate(ins, 1)) ≈ 1 * 0.5 * 1 / 1.05 - - @test present_value(Insurance(ins)) ≈ 0.5 / 1.05 + 0.5 * 0.5 / 1.05^2 - @test present_value(Insurance(ins, 1)) ≈ 0.5 / 1.05 - @test present_value(Insurance(ins, 0)) ≈ 0 + @test FinanceCore.present_value(AnnuityDue(ins)) ≈ 1 + 1 * 0.5 * 1 / 1.05 + 1 * 0.25 / 1.05^2 + @test FinanceCore.present_value(AnnuityDue(ins, 1)) ≈ 1 + @test FinanceCore.present_value(AnnuityDue(ins, 2)) ≈ 1 + 1 * 0.5 * 1 / 1.05 + @test FinanceCore.present_value(AnnuityDue(ins, 3)) ≈ 1 + 1 * 0.5 * 1 / 1.05 + 1 * 0.25 / 1.05^2 + @test FinanceCore.present_value(AnnuityDue(ins, 0)) ≈ 0 + @test FinanceCore.present_value(AnnuityImmediate(ins, 0)) ≈ 0 + @test FinanceCore.present_value(AnnuityImmediate(ins, 1)) ≈ 1 * 0.5 * 1 / 1.05 + + @test FinanceCore.present_value(Insurance(ins)) ≈ 0.5 / 1.05 + 0.5 * 0.5 / 1.05^2 + @test FinanceCore.present_value(Insurance(ins, 1)) ≈ 0.5 / 1.05 + @test FinanceCore.present_value(Insurance(ins, 0)) ≈ 0 ins_jl = LifeContingency( JointLife( - lives = (ins.life, ins.life), - contingency = LastSurvivor(), - joint_assumption = Frasier()), - Yields.Constant(0.05) + lives=(ins.life, ins.life), + contingency=LastSurvivor(), + joint_assumption=Frasier()), + 0.05 ) @test survival(ins_jl, 0) ≈ 1.0 @@ -213,39 +214,39 @@ end @testset "two years" begin ins = LifeContingency( SingleLife( - mortality = UltimateMortality([0.5, 0.5]), - issue_age = 0 + mortality=UltimateMortality([0.5, 0.5]), + issue_age=0 ), - Yields.Constant(0.05) + 0.05 ) @test omega(ins) ≈ 2 - @test present_value(AnnuityDue(ins, 1)) ≈ 1 - @test present_value(AnnuityDue(ins, 2)) ≈ 1 + 1 * 0.5 * 1 / 1.05 - @test present_value(AnnuityDue(ins, 3)) ≈ 1 + 1 * 0.5 * 1 / 1.05 + 1 * 0.25 * 1 / 1.05^2 - @test present_value(AnnuityDue(ins, 0)) == 0 - @test present_value(AnnuityImmediate(ins, 0)) == 0 - @test present_value(AnnuityImmediate(ins, 1)) ≈ 1 * 0.5 * 1 / 1.05 + @test FinanceCore.present_value(AnnuityDue(ins, 1)) ≈ 1 + @test FinanceCore.present_value(AnnuityDue(ins, 2)) ≈ 1 + 1 * 0.5 * 1 / 1.05 + @test FinanceCore.present_value(AnnuityDue(ins, 3)) ≈ 1 + 1 * 0.5 * 1 / 1.05 + 1 * 0.25 * 1 / 1.05^2 + @test FinanceCore.present_value(AnnuityDue(ins, 0)) == 0 + @test FinanceCore.present_value(AnnuityImmediate(ins, 0)) == 0 + @test FinanceCore.present_value(AnnuityImmediate(ins, 1)) ≈ 1 * 0.5 * 1 / 1.05 - @test present_value(Insurance(ins)) ≈ 0.5 / 1.05 + 0.5 * 0.5 / 1.05^2 - @test present_value(Insurance(ins, 1)) ≈ 0.5 / 1.05 - @test present_value(Insurance(ins, 0)) ≈ 0 + @test FinanceCore.present_value(Insurance(ins)) ≈ 0.5 / 1.05 + 0.5 * 0.5 / 1.05^2 + @test FinanceCore.present_value(Insurance(ins, 1)) ≈ 0.5 / 1.05 + @test FinanceCore.present_value(Insurance(ins, 0)) ≈ 0 ins_jl = LifeContingency( JointLife( - lives = (ins.life, ins.life), - contingency = LastSurvivor(), - joint_assumption = Frasier()), - Yields.Constant(0.05) + lives=(ins.life, ins.life), + contingency=LastSurvivor(), + joint_assumption=Frasier()), + 0.05 ) @test omega(ins_jl) ≈ 2 - @test present_value(AnnuityDue(ins_jl, 1)) ≈ 1 - @test present_value(AnnuityDue(ins_jl, 2)) ≈ 1 + 1 * 0.75 / 1.05 - @test present_value(AnnuityDue(ins_jl, 3)) ≈ 1 + 1 * 0.75 / 1.05 + 1 * survival(ins_jl, 2) / 1.05^2 - @test present_value(AnnuityDue(ins_jl, 0)) == 0 - @test present_value(AnnuityImmediate(ins_jl, 0)) == 0 - @test present_value(AnnuityImmediate(ins_jl, 1)) ≈ 1 * 0.75 / 1.05 + @test FinanceCore.present_value(AnnuityDue(ins_jl, 1)) ≈ 1 + @test FinanceCore.present_value(AnnuityDue(ins_jl, 2)) ≈ 1 + 1 * 0.75 / 1.05 + @test FinanceCore.present_value(AnnuityDue(ins_jl, 3)) ≈ 1 + 1 * 0.75 / 1.05 + 1 * survival(ins_jl, 2) / 1.05^2 + @test FinanceCore.present_value(AnnuityDue(ins_jl, 0)) == 0 + @test FinanceCore.present_value(AnnuityImmediate(ins_jl, 0)) == 0 + @test FinanceCore.present_value(AnnuityImmediate(ins_jl, 1)) ≈ 1 * 0.75 / 1.05 end @@ -253,8 +254,8 @@ end t = UltimateMortality(maleMort) @testset "demo mortality" begin - i = Yields.Constant(0.05) - ins = LifeContingency(SingleLife(mortality = t, issue_age = 0), i) + i = 0.05 + ins = LifeContingency(SingleLife(mortality=t, issue_age=0), i) @test l(ins, 0) ≈ 1.0 @test l(ins, 1) ≈ 0.993010000000000 @@ -276,20 +277,20 @@ t = UltimateMortality(maleMort) @test M(ins, 1) ≈ 0.0355801393752753 @test M(ins, 2) ≈ 0.0351775312392208 - @test present_value(Insurance(ins)) ≈ 0.04223728223 - @test present_value(Insurance(ins, 0)) ≈ 0.0 - @test present_value(Insurance(ins, 1)) ≈ 0.0066571428571429 - @test present_value(AnnuityDue(ins, 0)) ≈ 0.0 - @test present_value(AnnuityDue(ins, 1)) ≈ 1.0 - @test present_value(AnnuityDue(ins, 2)) ≈ 1.0 + survival(ins, 1) / 1.05 - @test present_value(AnnuityImmediate(ins, 0)) ≈ 0.0 - @test present_value(AnnuityImmediate(ins, 1)) ≈ survival(ins, 1) / 1.05 + @test FinanceCore.present_value(Insurance(ins)) ≈ 0.04223728223 + @test FinanceCore.present_value(Insurance(ins, 0)) ≈ 0.0 + @test FinanceCore.present_value(Insurance(ins, 1)) ≈ 0.0066571428571429 + @test FinanceCore.present_value(AnnuityDue(ins, 0)) ≈ 0.0 + @test FinanceCore.present_value(AnnuityDue(ins, 1)) ≈ 1.0 + @test FinanceCore.present_value(AnnuityDue(ins, 2)) ≈ 1.0 + survival(ins, 1) / 1.05 + @test FinanceCore.present_value(AnnuityImmediate(ins, 0)) ≈ 0.0 + @test FinanceCore.present_value(AnnuityImmediate(ins, 1)) ≈ survival(ins, 1) / 1.05 - @test present_value(Insurance(ins, 30)) ≈ 0.0137761089686975 + @test FinanceCore.present_value(Insurance(ins, 30)) ≈ 0.0137761089686975 @test N(ins, 26) ≈ 5.156762988852310 @test D(ins, 26) ≈ 0.275358702015970 - @test present_value(AnnuityDue(ins, 26)) ≈ 14.9562540842669 + @test FinanceCore.present_value(AnnuityDue(ins, 26)) ≈ 14.9562540842669 @@ -297,8 +298,8 @@ end @testset "consistency with MortaliyTables.jl" begin - q = UltimateMortality([.1 for t in 1:200]) + q = UltimateMortality([0.1 for t in 1:200]) l = SingleLife(mortality=q) - @test survival(l,0,5) == survival(q,0,5) - @test survival(l,1,5) == survival(q,1,5) + @test survival(l, 0, 5) == survival(q, 0, 5) + @test survival(l, 1, 5) == survival(q, 1, 5) end \ No newline at end of file diff --git a/test/single_life.jl b/test/single_life.jl index 5c8e6d8..e9f00d2 100644 --- a/test/single_life.jl +++ b/test/single_life.jl @@ -1,8 +1,8 @@ @testset "Single Life" begin @testset "issue age 116" begin t = MortalityTables.table("2001 VBT Residual Standard Select and Ultimate - Male Nonsmoker, ANB") - i = Yields.Constant(0.05) - lc = LifeContingency(SingleLife(mortality = t.ultimate, issue_age = 116), i) + i = 0.05 + lc = LifeContingency(SingleLife(mortality=t.ultimate, issue_age=116), i) @test l(lc, 0) ≈ 1.0 @@ -33,7 +33,7 @@ @test present_value(AnnuityDue(lc, 3)) ≈ sum([1; cumprod(1 .- qs[1:2])] .* [1.05^-t for t = 0:2]) @test LifeContingencies.V(lc, 1) == reserve_premium_net(lc, 1) - @test LifeContingencies.v(lc, 1) == Yields.discount(lc, 1) + @test LifeContingencies.v(lc, 1) == FinanceCore.discount(lc, 1) @test LifeContingencies.A(lc) == present_value(Insurance(lc)) @test LifeContingencies.ä(lc) == present_value(AnnuityDue(lc)) @test LifeContingencies.a(lc) == present_value(AnnuityImmediate(lc)) @@ -43,13 +43,13 @@ @testset "issue age 30" begin t = MortalityTables.table("2001 VBT Residual Standard Select and Ultimate - Male Nonsmoker, ANB") - i = Yields.Constant(0.05) - life = SingleLife(mortality = t.select[30], issue_age = 30) + i = 0.05 + life = SingleLife(mortality=t.select[30], issue_age=30) ins = LifeContingency(life, i) - @test life.issue_age == SingleLife(mortality = t.select[30]).issue_age + @test life.issue_age == SingleLife(mortality=t.select[30]).issue_age @test life.issue_age == SingleLife(t.select[30]).issue_age - @test life.issue_age == SingleLife(t.select, issue_age = 30).issue_age + @test life.issue_age == SingleLife(t.select, issue_age=30).issue_age @test l(ins, 0) ≈ 1.0 @@ -75,8 +75,8 @@ @test M(ins, 2) ≈ 0.1100531118446950 @test present_value(Insurance(ins)) ≈ 0.1107844934319970 - @test present_value(Insurance(ins),0) ≈ 0.1107844934319970 - @test present_value(Insurance(ins),90) / survival(Insurance(ins),90) ≈ 1 / 1.05 + @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 premium_net(ins) ≈ 0.0059327036350854 @test reserve_premium_net(ins, 1) ≈ 0.0059012862412992