Skip to content

Commit

Permalink
new README
Browse files Browse the repository at this point in the history
  • Loading branch information
alecloudenback committed Apr 22, 2020
1 parent 3958f76 commit f681732
Showing 1 changed file with 100 additions and 117 deletions.
217 changes: 100 additions & 117 deletions README.md
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)

2 comments on commit f681732

@alecloudenback
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

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:

git tag -a v0.2.0 -m "<description of version>" f681732487dd05e6b93cd68b782690f8f924fa57
git push origin v0.2.0

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.

Please sign in to comment.