-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3958f76
commit f681732
Showing
1 changed file
with
100 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,143 +1,126 @@ | ||
# LifeContingencies - v0.1.1 | ||
## A new actuarial modeling library | ||
# LifeContingencies.jl | ||
|
||
#### Code Review: [![Build Status](https://travis-ci.org/JuliaActuary/LifeContingencies.jl.svg?branch=master)](https://travis-ci.org/JuliaActuary/LifeContingencies.jl) [![Coverage Status](https://coveralls.io/repos/github/JuliaActuary/LifeContingencies.jl/badge.svg?branch=master)](https://coveralls.io/github/JuliaActuary/LifeContingencies.jl?branch=master) [![codecov.io](http://codecov.io/github/JuliaActuary/LifeContingencies.jl/coverage.svg?branch=master)](http://codecov.io/github/JuliaActuary/LifeContingencies.jl?branch=master) | ||
LifeContingencies is a package enabling actuarial life contingent calculations. | ||
The benefits are: | ||
|
||
A library to bring actuarial science to Julia. | ||
- Integration with other JuliaActuary packages such as [MortalityTables](https://github.com/JuliaActuary/MortalityTables.jl) | ||
- Fast calculations, with some parts utilizing parallel processing power automatically | ||
- Use functions that look more like the math you are used to (e.g. `Ax`, `ä`) | ||
with [Unicode support](https://docs.julialang.org/en/v1/manual/unicode-input/index.html) | ||
- All of the power, speed, convenience, tooling, and ecosystem of Julia | ||
- Flexible and modular modeling approach | ||
|
||
## Project Goals | ||
The goal is ultimately to build out a modeling package, capable of doing much more than simple commutations. | ||
## Package Overview | ||
|
||
## New in this version | ||
- Refine interest rate periods: | ||
- Period `0` now is meaningless, period `1` now refers to the time period `(0,1]` | ||
- Add ability to use serial correlation to interest rates (see interest rate section for example) | ||
- Add memory of functional interest rates | ||
- Prior calls to interest rates record the interst rate, so each call to a stochastic interest rate function don't generate an entirely new stream of interest rates, even if it's the same interest rate object | ||
- Leverages [MortalityTables](https://github.com/JuliaActuary/MortalityTables.jl) for | ||
the mortality calculations | ||
- Contains common insurance calculations such as: | ||
- `Ax`: Whole life | ||
- `Axn`: Term life for `n` years | ||
- `äx`: Life contingent annuity due | ||
- `äxn`: Life contingent annuity due for `n` years | ||
- Contains various commutaion functions such as `Dx`,`Mx`,`Cx`, etc. | ||
- Various interest rate mechanics (e.g. stochastic, constant, etc.) | ||
- API documentation available soon™ | ||
|
||
## Usage | ||
|
||
|
||
|
||
```julia | ||
using LifeContingencies | ||
using Plots | ||
plotlyjs() | ||
using Distributions | ||
``` | ||
## Mortality | ||
## Examples | ||
|
||
Calculate the whole life insurance rate for a 30-year-old male nonsmoker using | ||
2015 VBT base table and a 5% interest rate | ||
|
||
```julia | ||
# LifeContingencies will have a number of mortality tables built into the package | ||
# for now, there are two Social Security tables built in, maleMort and femaleMort | ||
# e.g. femaleMort = femaleMort = [0.005728,0.000373,0.000241,...] | ||
|
||
# to turn a vector into an interactable mortality table object, create a MortalityTable Object | ||
m = MortalityTable(maleMort) | ||
f = MortalityTable(femaleMort) | ||
|
||
t = MortalityTable(maleMort) | ||
|
||
|
||
|
||
## Examples ## | ||
|
||
0.00699 ≈ qx(t,0) | ||
0.000447 ≈ qx(t,1) | ||
1000.0 == lx(t,0) # the convention is that lx is based on 1000 lives | ||
993.010 ≈ lx(t,1) | ||
1000.0-1000*qx(t,0) ≈ lx(t,1) | ||
992.5661245 ≈ lx(t,2) | ||
120 == w(t) | ||
0 == dx(t,150) | ||
6.99 ≈ dx(t,0) | ||
76.8982069 ≈ ex(t,0) | ||
tpx(t,15,3) >= tpx(t,15,4) | ||
tqx(t,16,2) >= tqx(t,15,2) | ||
0 <= ex(t,15) | ||
0.003664839851 ≈ tpx(t,22,80) | ||
|
||
# also supports joint last survivor | ||
tqxy(table, table, age_x, age_y,time), e.g. | ||
tqxy(t,t,0,0,1) ≈ 0.0000488601000 | ||
tqx̅y̅(t,t,0,0,1) ≈ 0.0000488601000 #overbar notation is an option | ||
using LifeContingencies, MortalityTables | ||
|
||
tbls = MortalityTables.tables() | ||
vbt2001 = tbls["2001 VBT Residual Standard Select and Ultimate - Male Nonsmoker, ANB"] | ||
issue_age = 30 | ||
l = LifeContingency( | ||
vbt2001.select, | ||
InterestRate(0.05), | ||
issue_age | ||
) | ||
|
||
start_time = 0 | ||
Ax(l,start_time) # 0.111... | ||
``` | ||
|
||
## Interest | ||
|
||
Use a stochastic interest rate calculation to price a term policy: | ||
|
||
```julia | ||
# LifeContingencies provides an easy way to specify interest rates: | ||
|
||
i = InterestRate(.05) # you can pass interest rate a decimal value, a vector, or a function that returns a value | ||
using LifeContingencies, MortalityTables | ||
using Distributions | ||
|
||
# LifeContingencies currently lets you use a basic stochastic interest rate form | ||
tbls = MortalityTables.tables() | ||
vbt2001 = tbls["2001 VBT Residual Standard Select and Ultimate - Male Nonsmoker, ANB"] | ||
|
||
i2 = InterestRate((x -> rand(Normal(.05,.01)))) # anonymous function provides an easy way to add a stochastic interest rate | ||
# use an interest rate that's normally distirbuted | ||
μ = 0.05 | ||
σ = 0.01 | ||
int = InterestRate(t -> rand(Normal(μ,σ))) | ||
|
||
# Serial correlation is also allowed: | ||
i3 = InterestRate((x -> rand(Normal(i(i3,-1),0.01))), .05) | ||
# InterestRate(f,x...) where x is the first x... interest rates | ||
# i(i3,-1) returns the prior period's interest rate | ||
l = LifeContingency( | ||
vbt2001.select, | ||
int, | ||
30 # issue age | ||
) | ||
|
||
# Julia's power as a language comes in really handy here! | ||
start_time = 0 | ||
term = 10 | ||
Axn(l,start_time,term) # somewhere around 0.055 | ||
``` | ||
|
||
## Modeling | ||
|
||
You can use autocorrelated interest rates - substitute the following in the prior example | ||
using the ability to self reference: | ||
|
||
```julia | ||
## the assumptions are joined with a "LifeInsurance" Object | ||
insM = LifeInsurance(m,i2) | ||
insF = LifeInsurance(f,i2) | ||
|
||
## from there, you can calculate a number of actuarial commutations: | ||
|
||
ins = LifeInsurance(t,i) | ||
# Ax(ins,0) ≈ 0.04223728223 | ||
σ = 0.01 | ||
initial_rate = 0.05 | ||
int = InterestRate( | ||
function intAR(time) | ||
if time <= 1 | ||
initial_rate | ||
else | ||
i′ = last(int.rate_vector) | ||
rand(Normal(i′,σ)) | ||
end | ||
end | ||
) | ||
|
||
# Axn(ins,26,1) ≈ 0.001299047619 | ||
# Ax(ins,26) ≈ 0.1082172434 | ||
# äx(ins,26) = 18.727437887738578 # Julia lets you use unicode characters, so you can use the a-dot-dot as the actual function | ||
# äx(ins,26) = 18.727437887738578 # many code editors make the unicode characters really easy, but helper functions provide compatibility | ||
``` | ||
|
||
Compare the cost of annual premium, whole life insurance between multiple tables visually: | ||
|
||
```julia | ||
# calculating the net premium for a whole life policy for males and females | ||
# using a random interest rate on | ||
|
||
|
||
plot([map((x->1000000*Ax(insM,x)/äx(insM,x)),0:100),map((x->1000000*Ax(insF,x)/äx(insF,x)),0:100)],xlabel="Age",ylabel="Yearly Cost",yscale = :log10) | ||
using LifeContingencies, MortalityTables, Plots | ||
|
||
tbls = MortalityTables.tables() | ||
tables = [ | ||
tbls["1980 CET - Male Nonsmoker, ANB"], | ||
tbls["2001 VBT Residual Standard Select and Ultimate - Male Nonsmoker, ANB"], | ||
tbls["2015 VBT Male Non-Smoker RR100 ANB"], | ||
] | ||
|
||
issue_ages = 30:90 | ||
int = InterestRate(0.05) | ||
|
||
whole_life_costs = map(tables) do t | ||
map(issue_ages) do ia | ||
lc = LifeContingency( | ||
t.ultimate, | ||
int, | ||
ia | ||
) | ||
|
||
Ax(lc,0) / äx(lc,0) | ||
|
||
end | ||
end | ||
|
||
plt = plot(ylabel="Annual Premium per unit", xlabel="Issue Age", | ||
legend=:topleft, legendfontsize=8) | ||
for (i,t) in enumerate(tables) | ||
plot!(plt,issue_ages,whole_life_costs[i], label="$(t.d.name)") | ||
end | ||
display(plt) | ||
``` | ||
#### The annual net premium for a whole life policy, by age, with a random discount rate. | ||
|
||
![plot of insurance premiums](http://i.imgur.com/UbjrWci.png) | ||
|
||
*This is different than what you'd actually pay for a policy, which is called a "gross premium".* | ||
|
||
|
||
|
||
## Roadmap | ||
- Continue building out basic life and annuity functions | ||
- Implement lapses | ||
- Add reserves | ||
- Docs | ||
- More robust tests | ||
- More built-in mortality tables | ||
- TBD | ||
|
||
|
||
## References | ||
Sources for help with the commutation functions (since I have long since taken MLC) | ||
- https://www.soa.org/files/pdf/edu-2009-fall-ea-sn-com.pdf | ||
- www.math.umd.edu/~evs/s470/BookChaps/Chp6.pdf | ||
- www.macs.hw.ac.uk/~angus/papers/eas_offprints/commfunc.pdf | ||
|
||
Shout out to a similar Python project, whose Readme I one day hope to live up to and provided inspiration, including some of the function syntax. | ||
|
||
- https://github.com/franciscogarate/pyliferisk | ||
|
||
## Disclaimer | ||
I provide no warranty or guarantees. This is an open source project and I encourage you to submit feedback or pull requests. It's my first foray into the promising language of Juilia, so I encourage feedback about the package desgin and code architecture. | ||
![Comparison of three different mortality tables' effect on insurance cost](https://user-images.githubusercontent.com/711879/79941879-032d9300-842b-11ea-8427-a7dd36fbf2a6.png) |
f681732
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JuliaRegistrator register
f681732
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Registration pull request created: JuliaRegistries/General/13448
After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.
This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:
Also, note the warning: This looks like a new registration that registers version 0.2.0.
Ideally, you should register an initial release with 0.0.1, 0.1.0 or 1.0.0 version numbers
This can be safely ignored. However, if you want to fix this you can do so. Call register() again after making the fix. This will update the Pull request.