Skip to contents

Projects each origin's latest cumulative value to ultimate with age-to-age factors estimated from the triangle and a tail factor. The factors are Mack's weighted regressions: average = "volume" is the volume-weighted chain-ladder factor (Mack's alpha = 1), "simple" the mean of the link ratios (alpha = 0) and "regression" least squares through the origin (alpha = 2). Results match R ChainLadder's MackChainLadder() and chainladder-python (validation/reference/reserving_chainladder_r.csv).

Usage

chain_ladder_fit(ptr)

chain_ladder(
  triangle,
  column = NULL,
  average = "volume",
  sigma_interpolation = "log-linear",
  tail = 1
)

Arguments

ptr

A ChainLadderFit pointer; used internally.

triangle

A triangle, with any number of segments.

column

Name of the column to fit; by default the only one.

average

"volume", "simple" or "regression".

sigma_interpolation

How a variance parameter that cannot be estimated (an age with a single link ratio) is filled in: "log-linear" (regress log(sigma) on age and extrapolate, the default of R ChainLadder) or "mack" (Mack 1993).

tail

Development past the oldest age: a number, the factor from the oldest age to ultimate (1 means no tail), or a tail_constant(), tail_curve(), tail_bondy() or tail_log_linear().

Value

A chain_ladder_fit object.

Details

Every segment of the triangle is fitted on its own. Per-origin properties run over the origins of each segment in turn, like the rows of as.data.frame(), and are named by origin, or by "segment / origin" (key values joined by " / ") when there are several segments; a single-segment fit has one value per origin.

Development past the oldest age is a tail: a given factor, or one estimated by tail_constant(), tail_curve(), tail_bondy() or tail_log_linear() (validation/reference/reserving_tails_python.csv and reserving_tails_r.csv). A tail attached before the oldest age replaces the estimated factors from there.

Properties of the fit, per origin: latest, ultimate, reserve; per link (named "12-24" and so on): ldf (the selected factors, which the projection uses: the estimated ones, replaced by the tail's from its attachment age), estimated_ldf (the factors as estimated, before the tail replaced any), sigma (with unestimable ones interpolated), std_err; per age: cdf (age to ultimate, including the tail); the tail: tail (the factor from the oldest age to ultimate), tail_attachment_age (the age from which ldf holds the tail's factors; the oldest age when it replaced none), tail_ldf (the factors past the oldest age, which multiply to tail: one per development period of the next year and one to ultimate, or a single one for tail_log_linear()), tail_sigma and tail_std_err (the tail's variance parameter and standard error, extrapolated log-linearly; 0 without a tail, a factor of 1; a tail below 1 is read where a tail of 1.001 would be, as chainladder-python does); and keys, index (one row per segment, as a triangle's), origins, development, alpha, total_ultimate and total_reserve (summed over segments). The per-link, per-age and tail properties need a single-segment fit: with several segments use development_frame() (ldf, cdf, sigma and std_err; the oldest age has the tail as its cdf), totals_frame() (tail, tail_sigma and tail_std_err) or segment(). as.data.frame() gives one row per segment and origin, totals_frame() one per segment.

See also

mack() for standard errors, segment() for one segment.

Examples

long <- data.frame(year = c(2020, 2020, 2020, 2021, 2021, 2022),
                   age = c(12, 24, 36, 12, 24, 12),
                   paid = c(100, 150, 165, 110, 170, 120))
fit <- chain_ladder(triangle(long, "year", "age", "paid"), tail = 1.05)
fit@ldf
#>   12-24   24-36 
#> 1.52381 1.10000 
fit@reserve
#>  2020  2021  2022 
#>  8.25 26.35 91.20 
fit@total_reserve
#> [1] 125.8
chain_ladder(triangle(long, "year", "age", "paid"), tail = tail_bondy())@tail
#> [1] 1.1

# Every line of business at once.
by_lob <- rbind(transform(long, lob = "auto"), transform(long, lob = "home", paid = paid / 2))
fits <- chain_ladder(triangle(by_lob, "year", "age", "paid", keys = "lob"))
fits@reserve
#> auto / 2020 auto / 2021 auto / 2022 home / 2020 home / 2021 home / 2022 
#>     0.00000    17.00000    81.14286     0.00000     8.50000    40.57143 
as.data.frame(fits)
#>    lob origin latest ultimate  reserve
#> 1 auto   2020  165.0 165.0000  0.00000
#> 2 auto   2021  170.0 187.0000 17.00000
#> 3 auto   2022  120.0 201.1429 81.14286
#> 4 home   2020   82.5  82.5000  0.00000
#> 5 home   2021   85.0  93.5000  8.50000
#> 6 home   2022   60.0 100.5714 40.57143
totals_frame(fits)
#>    lob latest ultimate  reserve tail tail_sigma tail_std_err
#> 1 auto  455.0 553.1429 98.14286    1          0            0
#> 2 home  227.5 276.5714 49.07143    1          0            0