Simulate Claims"

knitr::opts_chunk$set(echo = TRUE, results = 'asis')
library(dplyr)
library(knitr)

Individual claim simulation fits into two basic categories: 1) wait-time and 2) link ratio. An example of the first may be found in Stanard and an example of the second may be found in Guszcza.

Wait-time modelling

Claim simulation occurs once we have a data frame of policies. For each row in this data frame, we will simulate zero or more claims and zero or more claim transactions.

library(imaginator)
set.seed(12345)
tbl_policy <- policies_simulate(2, 2001:2005)

We'll begin with non-stochastic wait times and claim frequencies.

tbl_claim_transaction <- claims_by_wait_time(
  tbl_policy,
  claim_frequency = 2,
  payment_frequency = 3,
  occurrence_wait = 10,
  report_wait = 5,
  pay_wait = 5,
  pay_severity = 50)

Here we have assumed that each policy will generate 2 claims and each claim will produce 3 payment. Because we have 10 policies, this means we have 60 claim payments. Here they are for the first policy:

tbl_claim_transaction %>% 
  filter(policyholder_id == 1, lubridate::year(policy_effective_date) == 2001) %>% 
  select(claim_id, occurrence_date, report_date, payment_date, payment_amount) %>% 
  kable()

Let's do that again with some random amounts. We'll keep the claim frequency fixed so that we can compare to the output above.

library(distributions3)
tbl_claim_transaction <- claims_by_wait_time(
  tbl_policy,
  claim_frequency = 2,
  payment_frequency = Poisson(2),
  occurrence_wait = Poisson(10),
  report_wait = Poisson(5),
  pay_wait = Poisson(5),
  pay_severity = LogNormal(log(50), 0.5 * log(50)))
tbl_claim_transaction %>% 
  filter(policyholder_id == 1, lubridate::year(policy_effective_date) == 2001) %>% 
  select(claim_id, occurrence_date, report_date, payment_date, payment_amount) %>% 
  kable()

Note that the transaction data is denormalized. The policy and claim information fields are repeated.

Simulate by link ratio

This is basically chain ladder applied to individual claims. First, we'll need to generate a random number of claims by developmemt lag. This is effectively a triangle of "IBNYR", or Incurred But Not Yet Reported claims. With that in place, we can then develop the claims with (probably) randomized link ratios.

As usual, we'll start with fixed values and then display a randomized example.

set.seed(12345)
tbl_policy <- policies_simulate(2, 2001:2005)

lstFreq <- list(
    4
  , 3
  , 2
  , 1
)

lstSev <- list(
  250
)
lstSev[1:4] <- lstSev[1]

tbl_ibnyr_fixed <- claims_by_first_report(
    tbl_policy
  , frequency = lstFreq
  , payment_severity = lstSev
  , lags = 1:4)

Because we're using fixed values for the frequencies, we'll have 10 claims per policy.

tbl_ibnyr_fixed %>% 
  filter(policyholder_id == 1) %>% 
  filter(policy_effective_date == min(policy_effective_date)) %>% 
  kable()

Let's try that again with some randomness:

lstFreq <- list(
    Poisson(4)
  , Poisson(3)
  , Poisson(2)
  , Poisson(1)
)

lstSev <- list(
  LogNormal(log_mu = log(10000), log_sigma = .5*log(10000))
)
lstSev[1:4] <- lstSev[1]

tbl_ibnyr_random <- claims_by_first_report(
    tbl_policy
  , frequency = lstFreq
  , payment_severity = lstSev
  , lags = 1:4)

We see that in this case, the first policy does not have 10 claims.

tbl_ibnyr_random %>% 
  filter(policyholder_id == 1) %>% 
  filter(policy_effective_date == min(policy_effective_date)) %>% 
  kable()

We can now develop the claims in the IBNYR triangle. Again we'll start with fixed link ratios.

fixedLinks <- list(2, 1.5, 1.25)
tbl_claims_fixed <- claims_by_link_ratio(
  tbl_ibnyr_fixed,
  links = fixedLinks,
  lags = 1:4)
tbl_claims_fixed %>% 
  filter(policyholder_id == 1) %>% 
  filter(
    policy_effective_date == min(policy_effective_date), 
    claim_id %in% c(1, 41)) %>%
  arrange(claim_id, lag) %>% 
  kable()

Note that the second claim was unknown as of Lag 1.

We can make things a bit more complicated by introducing variable link ratios

normalLinks <- list(  
  Normal(2, 1),
  Normal(1.5, .5),
  Normal(1.25, .5))

tbl_claims_random <- claims_by_link_ratio(
  tbl_ibnyr_random, 
  links = normalLinks, 
  lags = 1:4)
tbl_claims_random %>% 
  filter(policyholder_id == 1) %>% 
  filter(
    policy_effective_date == min(policy_effective_date), 
    claim_id %in% c(1, 41)) %>%
  arrange(claim_id, lag) %>% 
  kable()

Note that the link ratios apply to individual claims only. IBNYR This means that it's possible for individual claim development



Try the imaginator package in your browser

Any scripts or data that you put into this service are public.

imaginator documentation built on Jan. 27, 2022, 9:06 a.m.