ChainLadder icon indicating copy to clipboard operation
ChainLadder copied to clipboard

Simple and and vol-weighted averages for "non-square" triangules

Open actuaryactually opened this issue 7 years ago • 8 comments

If I want a volume all, or simple all set of link ratios a call to ata(paid.tri) works fine (where paid.tri = cumulative paid development triangle).

If I want to create a volume-weighted fit on the last five years worth of LDF factors I have found that I can utilise the weights argument as follows: chainladder(paid.tri,weights=wgt.vol.5), where I supply an input triangle of weights.

For "annual-annual" triangles, a five-year weighted average can be achieved using this method provided that the user supplies a weights triangle where the latest 6 diagonals are set = 1 and the balance are NAs. However, when I try the same on a annual/quarterly input triangle (i.e. non square) it fails and returns the following...

"Error in checkTriangle(Triangle) : Number of origin periods, 10, is less than the number of development periods, 39."

By annual/quarterly I mean annual origin cohorts but tracking development progress quarter on quarter, which is common for Lloyd's and reinsurance companies. In my case I had data running out to 9.75 development years, and had 10 origin years. The error message therefore makes sense but essentially indicates that the chainladder algorithm requires a square-input matrix.

I'm wondering if there is a smart way to overcome this? Personally i know that a lot of people would find it easier to adopt Chainladder if they could easily derive simple and volume-weighted averages using a call to a pre-built UDF, e.g. chainladder(paid.tri, no.diag=5, TypeOfAverage="Vol")

I've found that a n-year simple average can be achieved using the following and I think this would be a good addition to your help file even if you don't recast the "chainladder"function.

###################################### require(zoo)

#quick UDF to get rid of NAs: link_ratio_simple_n_yrs<-function(x,no.diag){
mean(tail(na.trim(x,sides="both"),no.diag)) }

#derive link ratios using build in UDF: paid.link.ratios<-ata(paid.tri)

#get simple average as: apply(paid.link.ratios,2,link_ratio_simple_n_yrs,no.diag=5)

######################################

However I can't tell (and this is annoying) how to achieve the same type of thing for volume weighted averages? Any thoughts? or a pain in a a$$?

I suspect my work around for simple weighted average will fail if there are inf values in the triangle. The following page may provide a better alternative to na.trim above: https://artax.karlin.mff.cuni.cz/r-help/library/IDPmisc/html/NaRV.omit.html

actuaryactually avatar May 17 '17 07:05 actuaryactually

Wow what a great find! Great idea!!

chiefmurph avatar May 17 '17 15:05 chiefmurph

@mages:

The error has nothing to do with @actuaryactually 's weights. It is purposely generated in line 127 of ChainLadder.R because qpaid has n>m. Try

ChainLadder:::checkTriangle(qpaid))

Is that condition of a Triangle still required?

@actuaryactually:

I agree that the ability of ChainLadder to replicate popular deterministic calculations might improve its accessibility to a wider audience of actuaries. But a 5-year weighted average is simply the weighted average you would get by ignoring all other data. IMO, that is better accomplished within the data processing step, if only because it makes the fact of the ignored data more transparent. IMO, it would be much more informative for "reserving software" to measure the value of data being ignored. I look forward to the day when someone writes that code.

Your point about how to treat "not Regular" values is non-trivial and requires a bit of thought regarding the conditions that must exist for your package's set of "non Regular" values to occur in a link ratio triangle (I assume those are the values to which you refer because "inf values in the triangle" cannot occur in the loss triangle itself). As you work through the chainladder algorithm's underlying model of the development process, you may find the desire to modify the model for each irregular value individually rather than for the entire group collectively.

In conclusion, I hope that by fixing the qpaid-related bug in checkTriangle we can spread the word about @actuaryactually 's idea for ChainLadder to produce n-year average link ratios.

chiefmurph avatar May 30 '17 03:05 chiefmurph

Not sure about your meaning re. the data prep stage @chiefmurph - Let me attempt to write a function for vol weighted average then let's regroup.

Sent from my iPhone

On 30 May 2017, at 11:33, chiefmurph [email protected] wrote:

@mages:

The error has nothing to do with @actuaryactually 's weights. It is purposely generated in line 127 of ChainLadder.R because qpaid has n>m. Try

ChainLadder:::checkTriangle(qpaid))

Is that condition of a Triangle still required?

@actuaryactually:

I agree that the ability of ChainLadder to replicate popular deterministic calculations might improve its accessibility to a wider audience of actuaries. But a 5-year weighted average is simply the weighted average you would get by ignoring all other data. IMO, that is better accomplished within the data processing step, if only because it makes the fact of the ignored data more transparent. IMO, it would be much more informative for "reserving software" to measure the value of data being ignored. I look forward to the day when someone writes that code.

Your point about how to treat "not Regular" values is non-trivial and requires a bit of thought regarding the conditions that must exist for your package's set of "non Regular" values to occur in a link ratio triangle (I assume those are the values to which you refer because "inf values in the triangle" cannot occur in the loss triangle itself). As you work through the chainladder algorithm's underlying model of the development process, you may find the desire to modify the model for each irregular value individually rather than for the entire group collectively.

In conclusion, I hope that by fixing the qpaid-related bug in checkTriangle we can spread the word about @actuaryactually 's idea for ChainLadder to produce n-year average link ratios.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

actuaryactually avatar May 30 '17 05:05 actuaryactually

x <- as.LongTriangle(GenIns) x$cy <- with(x, as.numeric(as.character(dev))+as.numeric(as.character(origin)) - 1)

Here's the crucial data processing step

y <- subset(x, cy >= 5)

5 year simple and weighted averages

ata(as.triangle(y))

This could be generalized.

chiefmurph avatar May 30 '17 18:05 chiefmurph

AFAICT the error message is generated at line 127 of Chainladder.R. My hypothesis is that the "n<=m" condition is required by only one Chainladder package model/function. Otherwise, the ata function would need to work around its use of the 'checkTriangle function. In any case, once that restriction is dealt with, @actuaryactually's suggested algorithm can be more thoroughly explored. I have had the best of intentions to comment out line 127 and check/build the package, but have not found the time. (Besides, I already have one side branch I've been working on extensively and must polish my git skills before starting another.) OTOH, @actuaryactually , if you would like to give this a shot ... .

chiefmurph avatar Jun 21 '17 03:06 chiefmurph

@chiefmurph - currently trying to close out my q2 reserving work, but will give it a shot in mid july. thanks for keeping it on the radar.

On Wed, Jun 21, 2017 at 11:51 AM, chiefmurph [email protected] wrote:

AFAICT the error message is generated at line 127 of Chainladder.R. My hypothesis is that the "n<=m" condition is required by only one Chainladder package model/function. Otherwise, the ata function would need to work around its use of the 'checkTriangle function. In any case, once that restriction is dealt with, @actuaryactually https://github.com/actuaryactually's suggested algorithm can be more thoroughly explored. I have had the best of intentions to comment out line 127 and check/build the package, but have not found the time. (Besides, I already have one side branch I've been working on extensively and must polish my git skills before starting another.) OTOH, @actuaryactually https://github.com/actuaryactually , if you would like to give this a shot ... .

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/mages/ChainLadder/issues/36#issuecomment-309955454, or mute the thread https://github.com/notifications/unsubscribe-auth/ADtMFobshrpr12K0lI4dGh-8DOk8-9sUks5sGJM-gaJpZM4NdfLc .

actuaryactually avatar Jun 21 '17 15:06 actuaryactually

Hi David,

Sorry for the long radio silence from my side.

If you would like to base your chain-ladder factors on the last 5 diagonals, ie calendar years, you do the following:

## Only use the last 5 diagonals, i.e. the last 5 calendar years
(weights <- ifelse(row(RAA) + col(RAA) <= 6 | row(RAA) + col(RAA) > 11, 0, 1))
MackChainLadder(GenIns, weights=weights, est.sigma = "Mack")

The argument alpha in MackChainLadder allows you to decide if you want to use volume weighted (alpha=1), simple averages (alpha=0), or a classic linear regression through the origin (alpha=2). Of course alpha can be a vector as well.

In the development version of ChainLadder 0.2.8 we have updated and hopefully also clarified the documentation of the weights argument in chainladder and MackChainLadder and added examples like the above.

I hope this helps.

mages avatar Nov 01 '18 17:11 mages

@mages I am coming around to your way of limiting the number of years in the average. However I have a few comments:

  1. Your example should run the Mack Method on RAA, not GenIns, although they are the same shape.
  2. When I use your 'weights' matrix, I get infinite S.E.'s. By not zeroing out the "future cells", the infinite S.E.'s go away: (weights <- ifelse(row(RAA) + col(RAA) <= 6 , 0, 1)) I don't know why "future cell" weights must be unity.
  3. That formula actually gives the four-year average because the 1's correspond to the beginning value in the regressions.

cl <- chainladder(RAA, weights=weights) sapply(cl$Models, coef) x x x x x x x x x 3.479860 1.912592 1.266065 1.157990 1.099869 1.041935 1.033264 1.016936 1.009217 The first factor "manually calculated": sum(RAA[6:9,2])/sum(RAA[6:9, 1]) [1] 3.47986

trinostics avatar Mar 21 '19 03:03 trinostics