Customizing models

LifeSimulator.jl is designed to allow users to provide their own models. Notably, custom models for mortality and lapses may be specified, to allow for a more realistic parametrization than the default values. On the longer term, it might be possible to bundle common interfaces for whole Models, but we haven't explored enough insurance products to be able to design a cohesive interface between different types of products. Curently, we have a very basic categorization of insurance product models into term life and universal life models (via the types TermLifeModel and UniversalLifeModel). These don't represent all the types of insurance models, and have a single implementation with limited functionality at the moment, LifelibBasiclife and LifelibSavings (greatly inspired by lifelib, as their name suggests).

Nonetheless, these insurance models allow us to carry out simulations and produce reasonable data, and even more so with custom mortality and lapse models.

Mortality model

Mortality models are defined as subtypes of MortalityModel. Documentation for this abstract type reveals that we need to extend monthly_mortality_rate or annual_mortality_rate, whichever we prefer.

Let's create our own mortality model which believes that female individuals are immortal and that men very frequently die. After creating a basic struct, we extend annual_mortality_rate with the corresponding logic:

using LifeSimulator, Dates

Base.@kwdef struct SexDiscriminatingMortality <: MortalityModel
  annual_rate::Float64 = 0.4
end

function LifeSimulator.annual_mortality_rate(model::SexDiscriminatingMortality, ::Month, policy::Policy)
  if policy.sex == FEMALE
    0.0
  else
    model.annual_rate
  end
end

We can now try to simulate using this model and see how that affects the population after 5 years:

mortality = SexDiscriminatingMortality()
lapse = ConstantLapse(0.0) # this ensures all policy decrements are due to deaths
model = LifelibBasiclife(; mortality, lapse)
policies = [
  PolicySet(Policy(sex = MALE), 100),
  PolicySet(Policy(sex = FEMALE), 100),
]
sim = simulate(model, policies, 60)

male, female = sim.active_policies
(male.count, female.count)
(7.776000000000018, 100.0)

Lapse model

Similarly to mortality models, lapse models are defined as subtypes of a LapseModel abstract type. We also need to implement a method to compute an annual or monthly lapse rate, but this time the default is not to associate lapse rates to individual policies for performance reasons. Instead, if we wish to do so, we will have to specify rates_are_per_policy(model::MyModel) = true. Let's define a model that thinks only females lapse their contracts, and with a very high default probability:

Base.@kwdef struct SexDiscriminatingLapse <: LapseModel
  annual_rate::Float64 = 0.7
end

function LifeSimulator.annual_lapse_rate(model::SexDiscriminatingLapse, ::Month, policy::Policy)
  if policy.sex == MALE
    0.0
  else
    model.annual_rate
  end
end

LifeSimulator.rates_are_per_policy(::SexDiscriminatingLapse) = true

Let's see this in action:

mortality = ConstantMortality(0.0) # this ensures all policy decrements are due to lapses
lapse = SexDiscriminatingLapse()
model = LifelibBasiclife(; mortality, lapse)
policies = [
  PolicySet(Policy(sex = MALE), 100),
  PolicySet(Policy(sex = FEMALE), 100),
]
sim = simulate(model, policies, 60)

male, female = sim.active_policies
(male.count, female.count)
(100.0, 0.24300000000000035)

Of course, we can customize both mortality and lapse models at the same time:

mortality = SexDiscriminatingMortality()
lapse = SexDiscriminatingLapse()
model = LifelibBasiclife(; mortality, lapse)
policies = [
  PolicySet(Policy(sex = MALE), 100),
  PolicySet(Policy(sex = FEMALE), 100),
]
sim = simulate(model, policies, 60)

male, female = sim.active_policies
(male.count, female.count)
(7.776000000000018, 0.24300000000000035)

And this also works with the LifelibSavings model:

model = LifelibSavings(; mortality, lapse)
sim = simulate(model, policies, 60)

male, female = sim.active_policies
(male.count, female.count)
(7.776000000000018, 0.24300000000000035)

This page was generated using Literate.jl.