Chapter 11 - Frequency-Severity Techniques#

On this page, we will recreating Chapter 11 of Friendland. There is not a dedicated FrequencySeverity method in the package. Instead, we will chain together existing methods and use Triangle arithmetic to calculate the utimate.

We will begin by import packages and setting up some helper functions.

import numpy as np
import pandas as pd
import chainladder as cl
from IPython.display import display as nb_display

#create reported/paid summary exhibit
def summary_exh(reported_tri:cl.Triangle,paid_tri:cl.Triangle,ult:cl.Triangle) -> pd.DataFrame:
    output = reported_tri.latest_diagonal.to_frame()
    output.columns = ['Reported']
    output['Paid'] = paid_tri.latest_diagonal.to_frame()
    #using floating point offset to achieve standard rounding
    output['Ult Claims'] = (ult.latest_diagonal + 1e-9).round(0).to_frame()
    output['Case Outstanding'] = output["Reported"] - output["Paid"]
    output['IBNR'] = output["Ult Claims"] - output["Reported"]
    output['Unpaid'] = output["Ult Claims"] - output["Paid"]
    return output.round(0)

#create a dict of developed triangles for each of the selection assumptions on a given page
def average_dev(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.Triangle]:
    return {k:cl.Development(**v).fit_transform(tri) for k,v in avg_params.items()}

#combine a dict of triangles into a singla triangle
def combine_tri(devs: dict[cl.Triangle]) -> cl.Triangle:
    avgs = [v.rename('index',k) for k,v in devs.items()]
    return cl.concat(avgs,axis=0)

#combine the ldf_ of a dict of triangles into a singla triangle
def combine_ldf(devs: dict[cl.Triangle]) -> cl.Triangle:
    return combine_tri({k:v.ldf_ for k,v in devs.items()})

#create a dict of DisposalRate estimators for each of the selection assumptions on a given page
def average_dr(tri: cl.Triangle, w:cl.Triangle, avg_params: dict[str,int]) -> dict[cl.DisposalRate]:
    return {k:cl.DisposalRate(**v).fit(X=tri,sample_weight = w) for k,v in avg_params.items()}

#combine the disposal_rate_ of a dict of triangles into a singla triangle
def combine_disposal(devs: dict[cl.Triangle]) -> cl.Triangle:
    return combine_tri({k:v.disposal_rate_ for k,v in devs.items()})

#create a dict of weighted regression objects for each of the selection assumptions on a given page
def regs(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.WeightedRegression]:
    return {k:cl.WeightedRegression(
        axis=2,
        thru_orig=False,
        xp=tri.get_array_module()
    ).fit(
        np.ones(tri.shape) * tri.origin.year.values[None,None,:,None],
        np.log(tri.values),
        cl.TriangleWeight(**v).fit(tri).w_.values
    ) for k,v in avg_params.items()}

#combine all the trends and r-squared values into pandas DataFrame
def reg_outputs(regs: dict[cl.WeightedRegression],devs:pd.Series) -> pd.DataFrame:
    trend = {k:(np.exp(v.slope_)-1).flatten() for k,v in regs.items()}
    trend = pd.DataFrame.from_dict(trend,orient='index')
    trend.columns = devs
    rsq = {k:v.rsq_.flatten() for k,v in regs.items()}
    rsq = pd.DataFrame.from_dict(rsq,orient='index')
    rsq.columns = devs
    return trend,rsq

#create a dict of weighted regression objects for each of the selection assumptions on a given page
def average_sev(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.Triangle]:
    output = {}
    for k,v in avg_params.items():
        w = cl.TriangleWeight(**v).fit(tri)
        weighted_tri = tri * w.w_.values
        output[k] = weighted_tri.mean()
    return output

Exhibit I Analysis#

Displaying all the triangles and dataframes take up quite a bit of code. We will distill the core analysis code at the top of each exhibit.

Exhibit I follows Approach 1. Count and Severity are developed separately and multiplied together.

#loading data and assumptions
e1_tri = cl.load_sample('friedland_auto_freq_sev')
e1_cnt_assumptions = {}
e1_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}
e1_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}
e1_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}
e1_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}
e1_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}
e1_sev_assumptions = {}
e1_sev_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}
e1_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}
e1_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}

#developing closed claim counts
e1_ccc_devs = average_dev(e1_tri['Closed Claim Counts'],e1_cnt_assumptions)
e1_ccc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_ccc_devs['simple_3'])
e1_ccc_selected.ldf_ = e1_ccc_selected.ldf_.round(3)

#developing reported claim counts
e1_rcc_devs = average_dev(e1_tri['Reported Claim Counts'],e1_cnt_assumptions)
e1_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_rcc_devs['simple_3'])
e1_rcc_selected.ldf_ = e1_rcc_selected.ldf_.round(3)

#combining closed and reported claim counts
e1_ccc_cl = cl.Chainladder().fit(e1_ccc_selected)
e1_rcc_cl = cl.Chainladder().fit(e1_rcc_selected)
e1_cc_ult = (e1_ccc_cl.ultimate_ + e1_rcc_cl.ultimate_)/2
e1_cc_ult.iloc[:,:,-1,:] = e1_rcc_cl.ultimate_.iloc[:,:,-1,:]

#calculating and developing reported severity
e1_rsev = e1_tri['Reported Claims'] / e1_tri['Reported Claim Counts']
e1_rsev_devs = average_dev(e1_rsev,e1_sev_assumptions)
e1_rsev_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_rsev_devs['medial_5x1'])
e1_rsev_selected.ldf_ = e1_rsev_selected.ldf_.round(3)
e1_rsev_cl = cl.Chainladder().fit(e1_rsev_selected)

#combining developed count and severity
e1_ult = e1_rsev_cl.ultimate_ * e1_cc_ult / 1000
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)

Exhibit I Sheet 1#

print('PART 1 - Data Triangle')
nb_display(e1_tri['Closed Claim Counts'])
print('PART 2 - Age-to-Age Factors')
nb_display(e1_tri['Closed Claim Counts'].age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factor')
nb_display(combine_ldf(e1_ccc_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
print('Selected')
nb_display(e1_ccc_selected.ldf_)
print('CDF to Ultimate')
nb_display(e1_ccc_selected.cdf_.round(3))
print('Percent Closed')
nb_display(1/e1_ccc_selected.cdf_.round(3))
PART 1 - Data Triangle
6 12 18 24 30 36 42 48 54 60
2003H2 2,547 3,262 3,287 3,291 3,292 3,292 3,292 3,292 3,292 3,292
2004H1 2,791 3,217 3,240 3,242 3,243 3,243 3,243 3,243 3,242
2004H2 2,099 2,677 2,695 2,697 2,697 2,698 2,698 2,698
2005H1 2,370 2,735 2,751 2,754 2,755 2,755 2,756
2005H2 1,966 2,609 2,630 2,634 2,634 2,634
2006H1 2,261 2,671 2,694 2,696 2,697
2006H2 1,949 2,637 2,659 2,662
2007H1 2,059 2,496 2,520
2007H2 2,083 2,732
2008H1 2,533
PART 2 - Age-to-Age Factors
6-12 12-18 18-24 24-30 30-36 36-42 42-48 48-54 54-60
2003H2 1.2810 1.0080 1.0010 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000
2004H1 1.1530 1.0070 1.0010 1.0000 1.0000 1.0000 1.0000 1.0000
2004H2 1.2750 1.0070 1.0010 1.0000 1.0000 1.0000 1.0000
2005H1 1.1540 1.0060 1.0010 1.0000 1.0000 1.0000
2005H2 1.3270 1.0080 1.0020 1.0000 1.0000
2006H1 1.1810 1.0090 1.0010 1.0000
2006H2 1.3530 1.0080 1.0010
2007H1 1.2120 1.0100
2007H2 1.3120
PART 3 - Average Age-to-Age Factor
development 6-12 12-18 18-24 24-30 30-36 36-42 42-48 48-54 54-60
Total
simple_5 1.277 1.008 1.001 1.0 1.0 1.0 1.0 1.0 1.0
simple_3 1.292 1.009 1.001 1.0 1.0 1.0 1.0 1.0 1.0
medial_5x1 1.284 1.008 1.001 1.0 1.0 1.0 1.0 1.0 1.0
volume_5 1.274 1.008 1.001 1.0 1.0 1.0 1.0 1.0 1.0
volume_3 1.291 1.009 1.001 1.0 1.0 1.0 1.0 1.0 1.0
PART 4 - Selected Age-to-Age Factors
Selected
6-12 12-18 18-24 24-30 30-36 36-42 42-48 48-54 54-60 60-66
(All) 1.2920 1.0090 1.0010 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000
CDF to Ultimate
6-Ult 12-Ult 18-Ult 24-Ult 30-Ult 36-Ult 42-Ult 48-Ult 54-Ult 60-Ult
(All) 1.3050 1.0100 1.0010 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000
Percent Closed
6-Ult 12-Ult 18-Ult 24-Ult 30-Ult 36-Ult 42-Ult 48-Ult 54-Ult 60-Ult
(All) 0.7663 0.9901 0.9990 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000

Exhibit I Sheet 2#

print('PART 1 - Data Triangle')
nb_display(e1_tri['Reported Claim Counts'])
print('PART 2 - Age-to-Age Factors')
nb_display(e1_tri['Reported Claim Counts'].age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factor')
nb_display(combine_ldf(e1_rcc_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
print('Selected')
nb_display(e1_rcc_selected.ldf_)
print('CDF to Ultimate')
nb_display(e1_rcc_selected.cdf_.round(3))
print('Percent Reported')
nb_display(1/e1_rcc_selected.cdf_.round(3))
PART 1 - Data Triangle
6 12 18 24 30 36 42 48 54 60
2003H2 3,556 3,314 3,301 3,299 3,295 3,294 3,293 3,293 3,293 3,292
2004H1 3,492 3,262 3,250 3,247 3,247 3,245 3,245 3,244 3,243
2004H2 2,980 2,712 2,704 2,702 2,700 2,700 2,699 2,699
2005H1 2,896 2,768 2,761 2,758 2,758 2,758 2,757
2005H2 2,814 2,650 2,640 2,639 2,638 2,636
2006H1 2,808 2,712 2,704 2,701 2,700
2006H2 2,799 2,675 2,670 2,668
2007H1 2,578 2,533 2,529
2007H2 2,791 2,778
2008H1 3,139
PART 2 - Age-to-Age Factors
6-12 12-18 18-24 24-30 30-36 36-42 42-48 48-54 54-60
2003H2 0.9320 0.9960 0.9990 0.9990 1.0000 1.0000 1.0000 1.0000 1.0000
2004H1 0.9340 0.9960 0.9990 1.0000 0.9990 1.0000 1.0000 1.0000
2004H2 0.9100 0.9970 0.9990 0.9990 1.0000 1.0000 1.0000
2005H1 0.9560 0.9970 0.9990 1.0000 1.0000 1.0000
2005H2 0.9420 0.9960 1.0000 1.0000 0.9990
2006H1 0.9660 0.9970 0.9990 1.0000
2006H2 0.9560 0.9980 0.9990
2007H1 0.9830 0.9980
2007H2 0.9950
PART 3 - Average Age-to-Age Factor
development 6-12 12-18 18-24 24-30 30-36 36-42 42-48 48-54 54-60
Total
simple_5 0.968 0.997 0.999 1.0 1.0 1.0 1.0 1.0 1.0
simple_3 0.978 0.998 0.999 1.0 1.0 1.0 1.0 1.0 1.0
medial_5x1 0.968 0.998 0.999 1.0 1.0 1.0 1.0 1.0 1.0
volume_5 0.968 0.997 0.999 1.0 1.0 1.0 1.0 1.0 1.0
volume_3 0.978 0.998 0.999 1.0 1.0 1.0 1.0 1.0 1.0
PART 4 - Selected Age-to-Age Factors
Selected
6-12 12-18 18-24 24-30 30-36 36-42 42-48 48-54 54-60 60-66
(All) 0.9780 0.9980 0.9990 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000
CDF to Ultimate
6-Ult 12-Ult 18-Ult 24-Ult 30-Ult 36-Ult 42-Ult 48-Ult 54-Ult 60-Ult
(All) 0.9750 0.9970 0.9990 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000
Percent Reported
6-Ult 12-Ult 18-Ult 24-Ult 30-Ult 36-Ult 42-Ult 48-Ult 54-Ult 60-Ult
(All) 1.0256 1.0030 1.0010 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000

Exhibit I Sheet 3#

e1_ccc_df = cl.model_diagnostics(e1_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e1_rcc_df = cl.model_diagnostics(e1_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e1_s3 = e1_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})
e1_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e1_rcc_df[['Latest','CDF','Ultimate']]
e1_s3["Selected Ult CC"] = e1_cc_ult.latest_diagonal.to_frame()
e1_s3[['Closed CDF','Reported CDF']] = e1_s3[['Closed CDF','Reported CDF']].round(3)
#using floating point offset to achieve standard rounding
e1_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e1_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)
e1_s3[['development','Closed Claim Counts','Reported Claim Counts','Closed CDF','Reported CDF','Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']]
development Closed Claim Counts Reported Claim Counts Closed CDF Reported CDF Ult Count Using CCC Ult Count Using RCC Selected Ult CC
origin
2003-07-01 60 3292.0 3292.0 1.000 1.000 3292.0 3292.0 3292.0
2004-01-01 54 3242.0 3243.0 1.000 1.000 3242.0 3243.0 3243.0
2004-07-01 48 2698.0 2699.0 1.000 1.000 2698.0 2699.0 2699.0
2005-01-01 42 2756.0 2757.0 1.000 1.000 2756.0 2757.0 2757.0
2005-07-01 36 2634.0 2636.0 1.000 1.000 2634.0 2636.0 2635.0
2006-01-01 30 2697.0 2700.0 1.000 1.000 2697.0 2700.0 2699.0
2006-07-01 24 2662.0 2668.0 1.000 1.000 2662.0 2668.0 2665.0
2007-01-01 18 2520.0 2529.0 1.001 0.999 2523.0 2526.0 2524.0
2007-07-01 12 2732.0 2778.0 1.010 0.997 2759.0 2770.0 2765.0
2008-01-01 6 2533.0 3139.0 1.305 0.975 3305.0 3061.0 3061.0

Exhibit I Sheet 4#

(e1_tri['Closed Claim Counts']/e1_tri['Reported Claim Counts']).round(3)
6 12 18 24 30 36 42 48 54 60
2003H2 0.7160 0.9840 0.9960 0.9980 0.9990 0.9990 1.0000 1.0000 1.0000 1.0000
2004H1 0.7990 0.9860 0.9970 0.9980 0.9990 0.9990 0.9990 1.0000 1.0000
2004H2 0.7040 0.9870 0.9970 0.9980 0.9990 0.9990 1.0000 1.0000
2005H1 0.8180 0.9880 0.9960 0.9990 0.9990 0.9990 1.0000
2005H2 0.6990 0.9850 0.9960 0.9980 0.9980 0.9990
2006H1 0.8050 0.9850 0.9960 0.9980 0.9990
2006H2 0.6960 0.9860 0.9960 0.9980
2007H1 0.7990 0.9850 0.9960
2007H2 0.7460 0.9830
2008H1 0.8070

Exhibit I Sheet 5#

nb_display(e1_tri['Reported Claims']/1000)
nb_display(e1_rsev)
6 12 18 24 30 36 42 48 54 60
2003H2 14,235 14,959 14,921 14,911 14,926 14,863 14,861 14,855 14,851 14,847
2004H1 14,548 14,672 14,644 14,628 14,621 14,609 14,609 14,611 14,616
2004H2 12,129 12,576 12,541 12,532 12,523 12,523 12,510 12,502
2005H1 11,981 11,922 11,883 11,862 11,854 11,843 11,841
2005H2 11,284 11,843 11,806 11,788 11,771 11,770
2006H1 11,945 11,857 11,819 11,774 11,761
2006H2 12,503 12,762 12,707 12,697
2007H1 11,663 11,523 11,492
2007H2 12,646 12,854
2008H1 14,072
6 12 18 24 30 36 42 48 54 60
2003H2 4,003 4,514 4,520 4,520 4,530 4,512 4,513 4,511 4,510 4,510
2004H1 4,166 4,498 4,506 4,505 4,503 4,502 4,502 4,504 4,507
2004H2 4,070 4,637 4,638 4,638 4,638 4,638 4,635 4,632
2005H1 4,137 4,307 4,304 4,301 4,298 4,294 4,295
2005H2 4,010 4,469 4,472 4,467 4,462 4,465
2006H1 4,254 4,372 4,371 4,359 4,356
2006H2 4,467 4,771 4,759 4,759
2007H1 4,524 4,549 4,544
2007H2 4,531 4,627
2008H1 4,483

Exhibit I Sheet 6#

print('PART 1 - Data Triangle')
nb_display(e1_rsev)
print('PART 2 - Age-to-Age Factors')
nb_display(e1_rsev.age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factors')
nb_display(combine_ldf(e1_rsev_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
nb_display(e1_rsev_selected.ldf_)
print('CDF to Ultimate')
nb_display(e1_rsev_selected.cdf_.round(3))
PART 1 - Data Triangle
6 12 18 24 30 36 42 48 54 60
2003H2 4,003 4,514 4,520 4,520 4,530 4,512 4,513 4,511 4,510 4,510
2004H1 4,166 4,498 4,506 4,505 4,503 4,502 4,502 4,504 4,507
2004H2 4,070 4,637 4,638 4,638 4,638 4,638 4,635 4,632
2005H1 4,137 4,307 4,304 4,301 4,298 4,294 4,295
2005H2 4,010 4,469 4,472 4,467 4,462 4,465
2006H1 4,254 4,372 4,371 4,359 4,356
2006H2 4,467 4,771 4,759 4,759
2007H1 4,524 4,549 4,544
2007H2 4,531 4,627
2008H1 4,483
PART 2 - Age-to-Age Factors
6-12 12-18 18-24 24-30 30-36 36-42 42-48 48-54 54-60
2003H2 1.1280 1.0010 1.0000 1.0020 0.9960 1.0000 1.0000 1.0000 1.0000
2004H1 1.0800 1.0020 1.0000 1.0000 1.0000 1.0000 1.0000 1.0010
2004H2 1.1390 1.0000 1.0000 1.0000 1.0000 0.9990 0.9990
2005H1 1.0410 0.9990 0.9990 0.9990 0.9990 1.0000
2005H2 1.1140 1.0010 0.9990 0.9990 1.0010
2006H1 1.0280 1.0000 0.9970 0.9990
2006H2 1.0680 0.9970 1.0000
2007H1 1.0060 0.9990
2007H2 1.0210
PART 3 - Average Age-to-Age Factors
development 6-12 12-18 18-24 24-30 30-36 36-42 42-48 48-54 54-60
Total
simple_5 1.047 0.999 0.999 0.999 0.999 1.0 1.0 1.0 1.0
simple_3 1.032 0.999 0.999 0.999 1.000 1.0 1.0 1.0 1.0
medial_5x1 1.039 0.999 0.999 0.999 1.000 1.0 1.0 1.0 1.0
PART 4 - Selected Age-to-Age Factors
6-12 12-18 18-24 24-30 30-36 36-42 42-48 48-54 54-60 60-66
(All) 1.0390 0.9990 0.9990 0.9990 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000
CDF to Ultimate
6-Ult 12-Ult 18-Ult 24-Ult 30-Ult 36-Ult 42-Ult 48-Ult 54-Ult 60-Ult
(All) 1.0360 0.9970 0.9980 0.9990 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000

Exhibit I Sheet 7#

e1_rsev_ult_df = cl.model_diagnostics(e1_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e1_s7 = e1_rsev_ult_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Reported Severity','Ultimate':'Ult Severity'})
e1_s7['Selected Ult CC'] = e1_s3['Selected Ult CC']
e1_s7["Ult Claims"] = e1_ult.latest_diagonal.to_frame()
e1_s7['CDF'] = e1_s7['CDF'].round(3)
#using floating point offset to achieve standard rounding
e1_s7[['Reported Severity','Ult Severity','Ult Claims']] = (e1_s7[['Reported Severity','Ult Severity','Ult Claims']]+1e-9).round(0)
e1_s7
development Reported Severity CDF Ult Severity Selected Ult CC Ult Claims
origin
2003-07-01 60 4510.0 1.000 4510.0 3292.0 14847.0
2004-01-01 54 4507.0 1.000 4507.0 3243.0 14614.0
2004-07-01 48 4632.0 1.000 4632.0 2699.0 12499.0
2005-01-01 42 4295.0 1.000 4295.0 2757.0 11839.0
2005-07-01 36 4465.0 1.000 4465.0 2635.0 11765.0
2006-01-01 30 4356.0 1.000 4356.0 2699.0 11755.0
2006-07-01 24 4759.0 0.999 4754.0 2665.0 12670.0
2007-01-01 18 4544.0 0.998 4535.0 2524.0 11448.0
2007-07-01 12 4627.0 0.997 4613.0 2765.0 12753.0
2008-01-01 6 4483.0 1.036 4644.0 3061.0 14214.0

Exhibit I Sheet 8#

summary_exh(e1_tri['Reported Claims']/1000,e1_tri['Paid Claims']/1000,e1_ult)
Reported Paid Ult Claims Case Outstanding IBNR Unpaid
2003-07-01 14847.0 14846.0 14847.0 1.0 0.0 1.0
2004-01-01 14616.0 14614.0 14614.0 2.0 -2.0 0.0
2004-07-01 12502.0 12502.0 12499.0 -0.0 -3.0 -3.0
2005-01-01 11841.0 11840.0 11839.0 1.0 -2.0 -1.0
2005-07-01 11770.0 11765.0 11765.0 5.0 -5.0 0.0
2006-01-01 11761.0 11755.0 11755.0 6.0 -6.0 0.0
2006-07-01 12697.0 12679.0 12670.0 18.0 -27.0 -9.0
2007-01-01 11492.0 11406.0 11448.0 86.0 -44.0 42.0
2007-07-01 12854.0 12648.0 12753.0 206.0 -101.0 105.0
2008-01-01 14072.0 11833.0 14214.0 2239.0 142.0 2381.0

At the end of each exhibit, we reconcile to hardcoded figures from Friedland using a series of assert statemenets. When any of these statements errors out, we know some bug has been introduced into the package.

#Exhibit I Sheet 1
assert np.all(e1_ccc_selected.cdf_.round(3).values == np.array([1.305, 1.010, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))
#Exhibit I Sheet 2
assert np.all(e1_rcc_selected.cdf_.round(3).values == np.array([0.975, 0.997, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))
#Exhibit I Sheet 3
assert np.allclose(
    e1_s3['Selected Ult CC'].values,
    np.array([3292, 3243, 2699, 2757, 2635, 2699, 2665, 2524, 2764, 3061]),
    atol=1
)
#Exhibit I Sheet 6
assert np.all(e1_rsev_selected.cdf_.round(3).values == np.array([1.036, 0.997, 0.998, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))
#Exhibit I Sheet 7
assert np.allclose(
    e1_s7['Ult Severity'].values,
    np.array([4510, 4507, 4632, 4295, 4465, 4356, 4754, 4535, 4613, 4644]),
    atol=1
)

Exhibit II Analysis#

Exhibit II also follows Approach 1, and is largely similar to Exhibit I.

#loading data and assumptions
e2_tri = cl.load_sample('friedland_xyz_auto_bi')
e2_cnt_assumptions = {}
e2_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}
e2_cnt_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}
e2_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}
e2_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}
e2_cnt_assumptions['volume_2'] = {'n_periods':2, 'average':'volume'}
e2_sev_assumptions = {}
e2_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}
e2_sev_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}
e2_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}

#developing closed claim counts
e2_ccc_devs = average_dev(e2_tri['Closed Claim Counts'],e2_cnt_assumptions)
e2_ccc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_ccc_devs['volume_2'])
e2_ccc_selected.ldf_ = e2_ccc_selected.ldf_.round(3)

#developing reported claim counts
e2_rcc_devs = average_dev(e2_tri['Reported Claim Counts'],e2_cnt_assumptions)
e2_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_rcc_devs['volume_2'])
e2_rcc_selected.ldf_ = e2_rcc_selected.ldf_.round(3)

#combining closed and reported claim counts
e2_ccc_cl = cl.Chainladder().fit(e2_ccc_selected)
e2_rcc_cl = cl.Chainladder().fit(e2_rcc_selected)
e2_cc_ult = (e2_ccc_cl.ultimate_ + e2_rcc_cl.ultimate_)/2

#calculating and developing reported severity
e2_rsev = e2_tri['Reported Claims'] / e2_tri['Reported Claim Counts'] * 1000
e2_rsev_devs = average_dev(e2_rsev,e2_sev_assumptions)
e2_rsev_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_rsev_devs['simple_2'])
e2_rsev_selected.ldf_ = e2_rsev_selected.ldf_.round(3)
e2_rsev_cl = cl.Chainladder().fit(e2_rsev_selected)

#combining developed count and severity
e2_ult = e2_rsev_cl.ultimate_ * e2_cc_ult / 1000
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)

Exhibit II Sheet 1#

print('PART 1 - Data Triangle')
nb_display(e2_tri['Closed Claim Counts'])
print('PART 2 - Age-to-Age Factors')
nb_display(e2_tri['Closed Claim Counts'].age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factor')
nb_display(combine_ldf(e2_ccc_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
print('Selected')
nb_display(e2_ccc_selected.ldf_)
print('CDF to Ultimate')
nb_display(e2_ccc_selected.cdf_.round(3))
print('Percent Closed')
nb_display(1/e2_ccc_selected.cdf_.round(3))
PART 1 - Data Triangle
12 24 36 48 60 72 84 96 108 120 132
1998 510.00 547.00 575.00 598.00 612.00 620.00 635.00 637.00
1999 686.00 819.00 910.00 980.00 1,007.00 1,036.00 1,039.00 1,044.00
2000 650.00 932.00 1,095.00 1,216.00 1,292.00 1,367.00 1,391.00 1,402.00
2001 304.00 681.00 936.00 1,092.00 1,225.00 1,357.00 1,432.00 1,446.00
2002 203.00 607.00 841.00 1,089.00 1,327.00 1,464.00 1,523.00
2003 181.00 614.00 941.00 1,263.00 1,507.00 1,568.00
2004 235.00 848.00 1,442.00 1,852.00 2,029.00
2005 295.00 1,119.00 1,664.00 1,946.00
2006 307.00 906.00 1,201.00
2007 329.00 791.00
2008 276.00
PART 2 - Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
1998 1.0730 1.0510 1.0400 1.0230 1.0130 1.0240 1.0030
1999 1.1940 1.1110 1.0770 1.0280 1.0290 1.0030 1.0050
2000 1.4340 1.1750 1.1110 1.0620 1.0580 1.0180 1.0080
2001 2.2400 1.3740 1.1670 1.1220 1.1080 1.0550 1.0100
2002 2.9900 1.3860 1.2950 1.2190 1.1030 1.0400
2003 3.3920 1.5330 1.3420 1.1930 1.0400
2004 3.6090 1.7000 1.2840 1.0960
2005 3.7930 1.4870 1.1690
2006 2.9510 1.3260
2007 2.4040
PART 3 - Average Age-to-Age Factor
development 12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Total
simple_3 3.050 1.504 1.265 1.169 1.084 1.051 1.019 1.008 1.015 1.003
simple_2 2.678 1.406 1.227 1.144 1.072 1.048 1.014 1.005 1.015 1.003
medial_5x1 3.317 1.468 1.250 1.142 1.081 1.045 1.020 1.008 1.015 1.003
volume_3 3.025 1.499 1.251 1.157 1.081 1.051 1.018 1.007 1.012 1.003
volume_2 2.668 1.415 1.223 1.135 1.070 1.048 1.014 1.006 1.012 1.003
PART 4 - Selected Age-to-Age Factors
Selected
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132 132-144
(All) 2.6680 1.4150 1.2230 1.1350 1.0700 1.0480 1.0140 1.0060 1.0120 1.0030 1.0000
CDF to Ultimate
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult 132-Ult
(All) 6.0850 2.2810 1.6120 1.3180 1.1610 1.0850 1.0350 1.0210 1.0150 1.0030 1.0000
Percent Closed
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult 132-Ult
(All) 0.1643 0.4384 0.6203 0.7587 0.8613 0.9217 0.9662 0.9794 0.9852 0.9970 1.0000

Exhibit II Sheet 2#

print('PART 1 - Data Triangle')
nb_display(e2_tri['Reported Claim Counts'])
print('PART 2 - Age-to-Age Factors')
nb_display(e2_tri['Reported Claim Counts'].age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factor')
nb_display(combine_ldf(e2_rcc_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
print('Selected')
nb_display(e2_rcc_selected.ldf_)
print('CDF to Ultimate')
nb_display(e2_rcc_selected.cdf_.round(3))
print('Percent Reported')
nb_display(1/e2_rcc_selected.cdf_.round(3))
PART 1 - Data Triangle
12 24 36 48 60 72 84 96 108 120 132
1998 634 635 635 637 637 637 637 637
1999 1,026 1,039 1,047 1,050 1,053 1,047 1,047 1,047
2000 1,354 1,397 1,411 1,410 1,408 1,408 1,408 1,408
2001 1,305 1,421 1,449 1,458 1,458 1,455 1,455 1,455
2002 1,342 1,514 1,548 1,557 1,549 1,552 1,554
2003 1,373 1,616 1,630 1,626 1,629 1,629
2004 1,932 2,168 2,234 2,249 2,258
2005 2,067 2,293 2,367 2,390
2006 1,473 1,645 1,657
2007 1,192 1,264
2008 1,036
PART 2 - Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
1998 1.0020 1.0000 1.0030 1.0000 1.0000 1.0000 1.0000
1999 1.0130 1.0080 1.0030 1.0030 0.9940 1.0000 1.0000
2000 1.0320 1.0100 0.9990 0.9990 1.0000 1.0000 1.0000
2001 1.0890 1.0200 1.0060 1.0000 0.9980 1.0000 1.0000
2002 1.1280 1.0220 1.0060 0.9950 1.0020 1.0010
2003 1.1770 1.0090 0.9980 1.0020 1.0000
2004 1.1220 1.0300 1.0070 1.0040
2005 1.1090 1.0320 1.0100
2006 1.1170 1.0070
2007 1.0600
PART 3 - Average Age-to-Age Factor
development 12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Total
simple_3 1.096 1.023 1.005 1.000 1.000 1.000 0.998 1.0 1.0 1.0
simple_2 1.089 1.020 1.008 1.003 1.001 1.001 1.000 1.0 1.0 1.0
medial_5x1 1.116 1.021 1.006 1.000 1.000 1.001 1.000 1.0 1.0 1.0
volume_3 1.099 1.025 1.005 1.001 1.000 1.000 0.998 1.0 1.0 1.0
volume_2 1.092 1.022 1.008 1.003 1.001 1.001 1.000 1.0 1.0 1.0
PART 4 - Selected Age-to-Age Factors
Selected
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132 132-144
(All) 1.0920 1.0220 1.0080 1.0030 1.0010 1.0010 1.0000 1.0000 1.0000 1.0000 1.0000
CDF to Ultimate
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult 132-Ult
(All) 1.1310 1.0350 1.0130 1.0050 1.0020 1.0010 1.0000 1.0000 1.0000 1.0000 1.0000
Percent Reported
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult 132-Ult
(All) 0.8842 0.9662 0.9872 0.9950 0.9980 0.9990 1.0000 1.0000 1.0000 1.0000 1.0000

Exhibit II Sheet 3#

e2_ccc_df = cl.model_diagnostics(e2_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e2_rcc_df = cl.model_diagnostics(e2_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e2_s3 = e2_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})
e2_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e2_rcc_df[['Latest','CDF','Ultimate']]
e2_s3["Selected Ult CC"] = e2_cc_ult.latest_diagonal.to_frame()
e2_s3[['Closed CDF','Reported CDF']] = e2_s3[['Closed CDF','Reported CDF']].round(3)
#using floating point offset to achieve standard rounding
e2_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e2_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)
e2_s3[['development','Closed Claim Counts','Reported Claim Counts','Closed CDF','Reported CDF','Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']]
development Closed Claim Counts Reported Claim Counts Closed CDF Reported CDF Ult Count Using CCC Ult Count Using RCC Selected Ult CC
origin
1998-01-01 132 637.0 637.0 1.000 1.000 637.0 637.0 637.0
1999-01-01 120 1044.0 1047.0 1.003 1.000 1047.0 1047.0 1047.0
2000-01-01 108 1402.0 1408.0 1.015 1.000 1423.0 1408.0 1416.0
2001-01-01 96 1446.0 1455.0 1.021 1.000 1477.0 1455.0 1466.0
2002-01-01 84 1523.0 1554.0 1.035 1.000 1577.0 1554.0 1565.0
2003-01-01 72 1568.0 1629.0 1.085 1.001 1701.0 1631.0 1666.0
2004-01-01 60 2029.0 2258.0 1.161 1.002 2356.0 2263.0 2309.0
2005-01-01 48 1946.0 2390.0 1.318 1.005 2564.0 2402.0 2483.0
2006-01-01 36 1201.0 1657.0 1.612 1.013 1936.0 1679.0 1807.0
2007-01-01 24 791.0 1264.0 2.281 1.035 1804.0 1309.0 1556.0
2008-01-01 12 276.0 1036.0 6.085 1.131 1679.0 1171.0 1425.0

Exhibit II Sheet 4#

nb_display(e2_tri['Reported Claims'])
nb_display(e2_rsev)
12 24 36 48 60 72 84 96 108 120 132
1998 11,171 12,380 13,216 14,067 14,688 16,366 16,163 15,835 15,822
1999 13,255 16,405 19,639 22,473 23,764 25,094 24,795 25,071 25,107
2000 15,676 18,749 21,900 27,144 29,488 34,458 36,949 37,505 37,246
2001 11,827 16,004 21,022 26,578 34,205 37,136 38,541 38,798
2002 12,811 20,370 26,656 37,667 44,414 48,701 48,169
2003 9,651 16,995 30,354 40,594 44,231 44,373
2004 16,995 40,180 58,866 71,707 70,288
2005 28,674 47,432 70,340 70,655
2006 27,066 46,783 48,804
2007 19,477 31,732
2008 18,632
12 24 36 48 60 72 84 96 108 120 132
1998 19,527 20,813 22,153 23,058 25,692 25,374 24,859 24,838
1999 15,989 18,902 21,464 22,632 23,831 23,682 23,946 23,980
2000 13,847 15,676 19,237 20,913 24,473 26,242 26,637 26,453
2001 9,063 11,262 14,508 18,229 23,460 25,523 26,489 26,665
2002 9,546 13,454 17,220 24,192 28,673 31,380 30,997
2003 7,029 10,517 18,622 24,966 27,152 27,239
2004 8,797 18,533 26,350 31,884 31,128
2005 13,872 20,686 29,717 29,563
2006 18,375 28,440 29,453
2007 16,340 25,104
2008 17,985

Exhibit II Sheet 5#

print('PART 1 - Data Triangle')
nb_display(e2_rsev)
print('PART 2 - Age-to-Age Factors')
nb_display(e2_rsev.age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factors')
nb_display(combine_ldf(e2_rsev_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
nb_display(e2_rsev_selected.ldf_)
print('CDF to Ultimate')
nb_display(e2_rsev_selected.cdf_.round(3))
PART 1 - Data Triangle
12 24 36 48 60 72 84 96 108 120 132
1998 19,527 20,813 22,153 23,058 25,692 25,374 24,859 24,838
1999 15,989 18,902 21,464 22,632 23,831 23,682 23,946 23,980
2000 13,847 15,676 19,237 20,913 24,473 26,242 26,637 26,453
2001 9,063 11,262 14,508 18,229 23,460 25,523 26,489 26,665
2002 9,546 13,454 17,220 24,192 28,673 31,380 30,997
2003 7,029 10,517 18,622 24,966 27,152 27,239
2004 8,797 18,533 26,350 31,884 31,128
2005 13,872 20,686 29,717 29,563
2006 18,375 28,440 29,453
2007 16,340 25,104
2008 17,985
PART 2 - Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
1998 1.0660 1.0640 1.0410 1.1140 0.9880 0.9800 0.9990
1999 1.1820 1.1360 1.0540 1.0530 0.9940 1.0110 1.0010
2000 1.1320 1.2270 1.0870 1.1700 1.0720 1.0150 0.9930
2001 1.2430 1.2880 1.2560 1.2870 1.0880 1.0380 1.0070
2002 1.4090 1.2800 1.4050 1.1850 1.0940 0.9880
2003 1.4960 1.7710 1.3410 1.0880 1.0030
2004 2.1070 1.4220 1.2100 0.9760
2005 1.4910 1.4370 0.9950
2006 1.5480 1.0360
2007 1.5360
PART 3 - Average Age-to-Age Factors
development 12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Total
simple_3 1.525 1.298 1.182 1.083 1.062 1.033 1.005 0.997 0.991 0.999
simple_2 1.542 1.236 1.102 1.032 1.049 1.013 1.011 1.002 0.991 0.999
medial_5x1 1.527 1.379 1.269 1.120 1.079 1.044 1.011 0.993 0.991 0.999
PART 4 - Selected Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132 132-144
(All) 1.5420 1.2360 1.1020 1.0320 1.0490 1.0130 1.0110 1.0020 0.9910 0.9990 1.0000
CDF to Ultimate
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult 132-Ult
(All) 2.3100 1.4980 1.2120 1.1000 1.0660 1.0160 1.0030 0.9920 0.9900 0.9990 1.0000

Exhibit II Sheet 6#

e2_rsev_df = cl.model_diagnostics(e2_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e2_s6 = e2_rsev_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Reported Severity','Ultimate':'Ult Severity'})
e2_s6['Selected Ult CC'] = e2_s3['Selected Ult CC']
e2_s6["Ult Claims"] = e2_ult.latest_diagonal.to_frame()
e2_s6['CDF'] = e2_s6['CDF'].round(3)
#using floating point offset to achieve standard rounding
e2_s6[['Reported Severity','Ult Severity','Ult Claims']] = (e2_s6[['Reported Severity','Ult Severity','Ult Claims']]+1e-9).round(0)
e2_s6
development Reported Severity CDF Ult Severity Selected Ult CC Ult Claims
origin
1998-01-01 132 24838.0 1.000 24838.0 637.0 15822.0
1999-01-01 120 23980.0 0.999 23956.0 1047.0 25083.0
2000-01-01 108 26453.0 0.990 26189.0 1416.0 37071.0
2001-01-01 96 26665.0 0.992 26452.0 1466.0 38772.0
2002-01-01 84 30997.0 1.003 31087.0 1565.0 48665.0
2003-01-01 72 27239.0 1.016 27674.0 1666.0 46106.0
2004-01-01 60 31128.0 1.066 33174.0 2309.0 76605.0
2005-01-01 48 29563.0 1.100 32514.0 2483.0 80739.0
2006-01-01 36 29453.0 1.212 35697.0 1807.0 64510.0
2007-01-01 24 25104.0 1.498 37607.0 1556.0 58528.0
2008-01-01 12 17985.0 2.310 41544.0 1425.0 59213.0

Exhibit II Sheet 7#

summary_exh(e2_tri['Reported Claims'],e2_tri['Paid Claims'],e2_ult)
Reported Paid Ult Claims Case Outstanding IBNR Unpaid
1998-01-01 15822.0 15822.0 15822.0 0.0 0.0 0.0
1999-01-01 25107.0 24817.0 25083.0 290.0 -24.0 266.0
2000-01-01 37246.0 36782.0 37071.0 464.0 -175.0 289.0
2001-01-01 38798.0 38520.0 38772.0 278.0 -26.0 252.0
2002-01-01 48169.0 44438.0 48665.0 3731.0 496.0 4227.0
2003-01-01 44373.0 39320.0 46106.0 5053.0 1733.0 6786.0
2004-01-01 70288.0 52811.0 76605.0 17477.0 6317.0 23794.0
2005-01-01 70655.0 40026.0 80739.0 30629.0 10084.0 40713.0
2006-01-01 48804.0 22819.0 64510.0 25985.0 15706.0 41691.0
2007-01-01 31732.0 11865.0 58528.0 19867.0 26796.0 46663.0
2008-01-01 18632.0 3409.0 59213.0 15223.0 40581.0 55804.0
#Exhibit II Sheet 1
assert np.all(e2_ccc_selected.cdf_.round(3).values == np.array([6.085, 2.281, 1.612, 1.318, 1.161, 1.085, 1.035, 1.021, 1.015, 1.003, 1.000]))
#Exhibit II Sheet 2
assert np.all(e2_rcc_selected.cdf_.round(3).values == np.array([1.131, 1.035, 1.013, 1.005, 1.002, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000]))
#Exhibit II Sheet 3
assert np.allclose(
    e2_s3['Selected Ult CC'].values,
    np.array([637, 1047, 1416, 1466, 1565, 1666, 2309, 2483, 1807, 1556, 1426]),
    atol=1
)
#Exhibit II Sheet 5
assert np.all(e2_rsev_selected.cdf_.round(3).values == np.array([2.310, 1.498, 1.212, 1.100, 1.066, 1.016, 1.003, 0.992, 0.990, 0.999, 1.000]))
#Exhibit II Sheet 6
assert np.allclose(
    e2_s6['Ult Severity'].values,
    np.array([24839, 23956, 26189, 26452, 31090, 27675, 33183, 32519, 35697, 37606, 41544]),
    rtol=0.001
)

Exhibit III Analysis#

Exhibit III follows Approach 2, which adds explicit selections for the most recent frequency and severity. To calculate various averages for frequency and severity, we use the TriangleWeight utility class, and supply n_periods and drop_high as we would with Development. We also leverage the Trend adjustment method.

#loading data and assumptions
e3_tri = cl.load_sample('friedland_wc_self_insurer')
e3_cnt_assumptions = {}
e3_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}
e3_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}
e3_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}
e3_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}
e3_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}
e3_sev_assumptions = {}
e3_sev_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}
e3_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}
e3_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}
e3_sevavg_assumptions = {}
e3_sevavg_assumptions['all_years'] = {}
e3_sevavg_assumptions['all_years_excl_hilo'] = {'drop_high':1, 'drop_low':1}
e3_sevavg_assumptions['latest_3'] = {'n_periods':3}

#developing closed claim counts
#There is a typo in the text. We will use a tail of 1.002 to match the 84-ult factor'
e3_ccc_devs = average_dev(e3_tri['Closed Claim Counts'],e3_cnt_assumptions)
e3_ccc_selected = cl.TailConstant(tail = 1.002, projection_period = 0).fit_transform(e3_ccc_devs['volume_5'])
e3_ccc_selected.ldf_ = e3_ccc_selected.ldf_.round(3)

#developing reported claim counts
e3_rcc_devs = average_dev(e3_tri['Reported Claim Counts'],e3_cnt_assumptions)
e3_rcc_selected = cl.TailConstant(tail = 1.000, projection_period = 0).fit_transform(e3_rcc_devs['volume_5'])
e3_rcc_selected.ldf_ = e3_rcc_selected.ldf_.round(3)

#combining closed and reported claim counts
e3_ccc_cl = cl.Chainladder().fit(e3_ccc_selected)
e3_rcc_cl = cl.Chainladder().fit(e3_rcc_selected)
e3_cc_ult = (e3_ccc_cl.ultimate_ + e3_rcc_cl.ultimate_)/2

#calculating trended frequency
e3_freq_trend = cl.Trend(-.01).fit(e3_tri['Reported Claim Counts']).trend_
e3_payroll_trend = cl.Trend(.025).fit(e3_tri['Payroll']).trend_
e3_freq_2008 = .0036
e3_freq_2007 = e3_freq_2008 / e3_freq_trend.values[0,0,-2,0] * e3_payroll_trend.latest_diagonal.values[0,0,-2,0]

#calculating and developing paid severity
e3_psev = e3_tri["Paid Claims"] / e3_tri['Closed Claim Counts']
e3_psev_devs = average_dev(e3_psev,e3_sev_assumptions)
e3_psev_selected = cl.TailConstant(tail = 1.150, projection_period = 0).fit_transform(e3_psev_devs['medial_5x1'])
e3_psev_selected.ldf_ = e3_psev_selected.ldf_.round(3)

#calculating and developing reported severity
e3_rsev = e3_tri["Reported Claims"] / e3_tri['Reported Claim Counts']
e3_rsev_devs = average_dev(e3_rsev,e3_sev_assumptions)
e3_rsev_selected = cl.TailConstant(tail = 1.025, projection_period = 0).fit_transform(e3_rsev_devs['medial_5x1'])
e3_rsev_selected.ldf_ = e3_rsev_selected.ldf_.round(3)

#combining developed count and severity
e3_psev_cl = cl.Chainladder().fit(e3_psev_selected)
e3_resv_cl = cl.Chainladder().fit(e3_rsev_selected)
e3_sev_ult = (e3_psev_cl.ultimate_ + e3_resv_cl.ultimate_)/2

#calculated trended severity
e3_sev_trend = cl.Trend(.075).fit(e3_tri['Reported Claims']).trend_
e3_sev_ult_trended = e3_sev_ult * e3_sev_trend.latest_diagonal.values
#prep ult severity for average calculations
e3_sev_ult_trended_2006 = e3_sev_ult_trended[e3_sev_ult_trended.origin<='2006']
e3_sevs = average_sev(e3_sev_ult_trended_2006,e3_sevavg_assumptions)
#calculate selected severities
e3_sev_2008 = 7100.
e3_sev_2007 = e3_sev_2008 / e3_sev_trend.values[0,0,-2,0]
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)

Exhibit III Sheet 1#

print('PART 1 - Data Triangle')
nb_display(e3_tri["Closed Claim Counts"])
print('PART 2 - Age-to-Age Factors')
nb_display(e3_tri['Closed Claim Counts'].age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factor')
nb_display(combine_ldf(e3_ccc_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
print('Selected')
nb_display(e3_ccc_selected.ldf_)
print('CDF to Ultimate')
nb_display(e3_ccc_selected.cdf_.round(3))
print('Percent Closed')
nb_display((1/e3_ccc_selected.cdf_).round(3))
PART 1 - Data Triangle
12 24 36 48 60 72 84 96
2001 789 1,196 1,255 1,310 1,324 1,327 1,332 1,343
2002 978 1,506 1,609 1,629 1,669 1,676 1,683
2003 1,070 1,557 1,665 1,721 1,738 1,748
2004 1,029 1,525 1,618 1,688 1,717
2005 974 1,459 1,532 1,597
2006 1,746 2,632 2,761
2007 1,683 2,572
2008 1,560
PART 2 - Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96
2001 1.5160 1.0490 1.0440 1.0110 1.0020 1.0040 1.0080
2002 1.5400 1.0680 1.0120 1.0250 1.0040 1.0040
2003 1.4550 1.0690 1.0340 1.0100 1.0060
2004 1.4820 1.0610 1.0430 1.0170
2005 1.4980 1.0500 1.0420
2006 1.5070 1.0490
2007 1.5280
PART 3 - Average Age-to-Age Factor
development 12-24 24-36 36-48 48-60 60-72 72-84 84-96
Total
simple_5 1.494 1.060 1.035 1.016 1.004 1.004 1.008
simple_3 1.511 1.053 1.040 1.017 1.004 1.004 1.008
medial_5x1 1.496 1.060 1.040 1.014 1.004 1.004 1.008
volume_5 1.499 1.058 1.035 1.016 1.004 1.004 1.008
volume_3 1.513 1.053 1.040 1.017 1.004 1.004 1.008
PART 4 - Selected Age-to-Age Factors
Selected
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108
(All) 1.4990 1.0580 1.0350 1.0160 1.0040 1.0040 1.0080 1.0020
CDF to Ultimate
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
(All) 1.6980 1.1330 1.0710 1.0340 1.0180 1.0140 1.0100 1.0020
Percent Closed
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
(All) 0.5890 0.8830 0.9340 0.9670 0.9820 0.9860 0.9900 0.9980

Exhibit III Sheet 2#

print('PART 1 - Data Triangle')
nb_display(e3_tri["Reported Claim Counts"])
print('PART 2 - Age-to-Age Factors')
nb_display(e3_tri['Reported Claim Counts'].age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factor')
nb_display(combine_ldf(e3_rcc_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
nb_display(e3_rcc_selected.ldf_)
print('CDF to Ultimate')
nb_display(e3_rcc_selected.cdf_.round(3))
print('Percent Reported')
nb_display((1/e3_rcc_selected.cdf_).round(3))
PART 1 - Data Triangle
12 24 36 48 60 72 84 96
2001 1,235 1,321 1,342 1,349 1,350 1,350 1,350 1,350
2002 1,555 1,660 1,685 1,695 1,700 1,700 1,700
2003 1,628 1,740 1,762 1,771 1,775 1,775
2004 1,600 1,714 1,740 1,747 1,750
2005 1,510 1,612 1,639 1,647
2006 2,750 2,941 2,985
2007 2,650 2,842
2008 2,438
PART 2 - Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96
2001 1.0700 1.0160 1.0050 1.0010 1.0000 1.0000 1.0000
2002 1.0680 1.0150 1.0060 1.0030 1.0000 1.0000
2003 1.0690 1.0130 1.0050 1.0020 1.0000
2004 1.0710 1.0150 1.0040 1.0020
2005 1.0680 1.0170 1.0050
2006 1.0690 1.0150
2007 1.0720
PART 3 - Average Age-to-Age Factor
development 12-24 24-36 36-48 48-60 60-72 72-84 84-96
Total
simple_5 1.07 1.015 1.005 1.002 1.0 1.0 1.0
simple_3 1.07 1.016 1.005 1.002 1.0 1.0 1.0
medial_5x1 1.07 1.015 1.005 1.002 1.0 1.0 1.0
volume_5 1.07 1.015 1.005 1.002 1.0 1.0 1.0
volume_3 1.07 1.015 1.005 1.002 1.0 1.0 1.0
PART 4 - Selected Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108
(All) 1.0700 1.0150 1.0050 1.0020 1.0000 1.0000 1.0000 1.0000
CDF to Ultimate
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
(All) 1.0940 1.0220 1.0070 1.0020 1.0000 1.0000 1.0000 1.0000
Percent Reported
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
(All) 0.9140 0.9780 0.9930 0.9980 1.0000 1.0000 1.0000 1.0000

Exhibit III Sheet 3#

e3_ccc_df = cl.model_diagnostics(e3_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e3_rcc_df = cl.model_diagnostics(e3_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e3_s3 = e3_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})
e3_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e3_rcc_df[['Latest','CDF','Ultimate']]
e3_s3["Selected Ult CC"] = e3_cc_ult.latest_diagonal.to_frame()
e3_s3[['Closed CDF','Reported CDF']] = e3_s3[['Closed CDF','Reported CDF']].round(3)
#using floating point offset to achieve standard rounding
e3_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e3_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)
e3_s3
development Closed Claim Counts Closed CDF Ult Count Using CCC Reported Claim Counts Reported CDF Ult Count Using RCC Selected Ult CC
origin
2001-01-01 96 1343.0 1.002 1346.0 1350.0 1.000 1350.0 1348.0
2002-01-01 84 1683.0 1.010 1700.0 1700.0 1.000 1700.0 1700.0
2003-01-01 72 1748.0 1.014 1773.0 1775.0 1.000 1775.0 1774.0
2004-01-01 60 1717.0 1.018 1748.0 1750.0 1.000 1750.0 1749.0
2005-01-01 48 1597.0 1.034 1652.0 1647.0 1.002 1650.0 1651.0
2006-01-01 36 2761.0 1.071 2956.0 2985.0 1.007 3006.0 2981.0
2007-01-01 24 2572.0 1.133 2913.0 2842.0 1.022 2905.0 2909.0
2008-01-01 12 1560.0 1.698 2649.0 2438.0 1.094 2666.0 2658.0

Exhibit III Sheet 4#

e3_s4 = e3_s3[['Selected Ult CC']].copy()
e3_s4['CC Trend'] = e3_freq_trend.latest_diagonal.to_frame()
e3_s4['Trended Ult CC'] = e3_s4['Selected Ult CC'] * e3_s4['CC Trend']
e3_s4['Payroll'] = e3_tri['Payroll'].latest_diagonal.to_frame()
e3_s4['Payroll Trend'] = e3_payroll_trend.latest_diagonal.to_frame()
e3_s4['Trended Payroll'] = e3_s4['Payroll'] * e3_s4['Payroll Trend']
e3_s4['Trended Ultimate Freq'] = e3_s4['Trended Ult CC'] / e3_s4['Trended Payroll']
e3_s4[['CC Trend','Payroll Trend']] = e3_s4[['CC Trend','Payroll Trend']].round(3)
#using floating point offset to achieve standard rounding
e3_s4[['Trended Ult CC','Trended Payroll']] = (e3_s4[['Trended Ult CC','Trended Payroll']] + 1e-9).round(0)
e3_s4['Trended Ultimate Freq'] = e3_s4["Trended Ultimate Freq"].map("{:.2%}".format)
nb_display(e3_s4)
print(f"Selected Frequency at 2008 level {e3_freq_2008:.2%}")
print(f"Selected Frequency at 2007 level {float(e3_freq_2007):.2%}")
Selected Ult CC CC Trend Trended Ult CC Payroll Payroll Trend Trended Payroll Trended Ultimate Freq
origin
2001-01-01 1348.0 0.932 1256.0 195000.0 1.189 231794.0 0.54%
2002-01-01 1700.0 0.941 1601.0 260000.0 1.160 301520.0 0.53%
2003-01-01 1774.0 0.951 1687.0 280000.0 1.131 316794.0 0.53%
2004-01-01 1749.0 0.961 1680.0 280000.0 1.104 309068.0 0.54%
2005-01-01 1651.0 0.970 1602.0 350000.0 1.077 376912.0 0.43%
2006-01-01 2981.0 0.980 2922.0 790000.0 1.051 829994.0 0.35%
2007-01-01 2909.0 0.990 2880.0 780000.0 1.025 799500.0 0.36%
2008-01-01 2658.0 1.000 2658.0 740000.0 1.000 740000.0 0.36%
Selected Frequency at 2008 level 0.36%
Selected Frequency at 2007 level 0.37%

Exhibit III Sheet 5#

print('Paid Claims')
nb_display(e3_tri["Paid Claims"])
print('Paid Severities')
nb_display(e3_psev)
print('Reported Claims')
nb_display(e3_tri["Reported Claims"])
print('Reported Severities')
nb_display(e3_rsev)
Paid Claims
12 24 36 48 60 72 84 96
2001 1,318,000 2,842,000 3,750,000 4,300,000 4,650,000 4,850,000 5,050,000 5,200,000
2002 1,780,000 3,817,000 5,016,000 5,750,000 6,100,000 6,300,000 6,555,000
2003 1,890,000 4,184,000 5,500,000 6,300,000 6,800,000 7,100,000
2004 1,900,000 4,100,000 5,560,000 6,430,000 6,950,000
2005 1,960,000 4,290,000 5,688,000 6,570,000
2006 4,030,000 8,650,000 11,400,000
2007 4,200,000 9,043,000
2008 4,170,000
Paid Severities
12 24 36 48 60 72 84 96
2001 1,670 2,376 2,988 3,282 3,512 3,655 3,791 3,872
2002 1,820 2,535 3,117 3,530 3,655 3,759 3,895
2003 1,766 2,687 3,303 3,661 3,913 4,062
2004 1,846 2,689 3,436 3,809 4,048
2005 2,012 2,940 3,713 4,114
2006 2,308 3,286 4,129
2007 2,496 3,516
2008 2,673
Reported Claims
12 24 36 48 60 72 84 96
2001 3,200,000 4,300,000 4,900,000 5,200,000 5,300,000 5,400,000 5,550,000 5,650,000
2002 4,300,000 5,900,000 6,600,000 6,950,000 7,200,000 7,400,000 7,500,000
2003 4,800,000 6,600,000 7,400,000 7,800,000 8,100,000 8,300,000
2004 4,900,000 6,700,000 7,700,000 8,150,000 8,600,000
2005 5,200,000 7,100,000 7,900,000 8,350,000
2006 10,100,000 13,800,000 15,500,000
2007 10,500,000 14,400,000
2008 10,300,000
Reported Severities
12 24 36 48 60 72 84 96
2001 2,591 3,255 3,651 3,855 3,926 4,000 4,111 4,185
2002 2,765 3,554 3,917 4,100 4,235 4,353 4,412
2003 2,948 3,793 4,200 4,404 4,563 4,676
2004 3,062 3,909 4,425 4,665 4,914
2005 3,444 4,404 4,820 5,070
2006 3,673 4,692 5,193
2007 3,962 5,067
2008 4,225

Exhibit III Sheet 6#

print('PART 1 - Data Triangle')
nb_display(e3_psev)
print('PART 2 - Age-to-Age Factors')
nb_display(e3_psev.age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factor')
nb_display(combine_ldf(e3_psev_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
print('Selected')
nb_display(e3_psev_selected.ldf_)
print('CDF to Ultimate')
nb_display(e3_psev_selected.cdf_.round(3))
PART 1 - Data Triangle
12 24 36 48 60 72 84 96
2001 1,670 2,376 2,988 3,282 3,512 3,655 3,791 3,872
2002 1,820 2,535 3,117 3,530 3,655 3,759 3,895
2003 1,766 2,687 3,303 3,661 3,913 4,062
2004 1,846 2,689 3,436 3,809 4,048
2005 2,012 2,940 3,713 4,114
2006 2,308 3,286 4,129
2007 2,496 3,516
2008 2,673
PART 2 - Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96
2001 1.4230 1.2570 1.0990 1.0700 1.0410 1.0370 1.0210
2002 1.3930 1.2300 1.1320 1.0350 1.0280 1.0360
2003 1.5210 1.2290 1.1080 1.0690 1.0380
2004 1.4560 1.2780 1.1090 1.0630
2005 1.4610 1.2630 1.1080
2006 1.4240 1.2560
2007 1.4090
PART 3 - Average Age-to-Age Factor
development 12-24 24-36 36-48 48-60 60-72 72-84 84-96
Total
simple_5 1.454 1.251 1.111 1.059 1.036 1.037 1.021
simple_3 1.431 1.266 1.108 1.056 1.036 1.037 1.021
medial_5x1 1.447 1.250 1.108 1.066 1.038 1.037 1.021
PART 4 - Selected Age-to-Age Factors
Selected
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108
(All) 1.4470 1.2500 1.1080 1.0660 1.0380 1.0370 1.0210 1.1500
CDF to Ultimate
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
(All) 2.7000 1.8660 1.4930 1.3470 1.2640 1.2180 1.1740 1.1500

Exhibit III Sheet 7#

print('PART 1 - Data Triangle')
nb_display(e3_rsev)
print('PART 2 - Age-to-Age Factors')
nb_display(e3_rsev.age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factor')
nb_display(combine_ldf(e3_rsev_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
print('Selected')
nb_display(e3_rsev_selected.ldf_)
print('CDF to Ultimate')
nb_display(e3_rsev_selected.cdf_.round(3))
PART 1 - Data Triangle
12 24 36 48 60 72 84 96
2001 2,591 3,255 3,651 3,855 3,926 4,000 4,111 4,185
2002 2,765 3,554 3,917 4,100 4,235 4,353 4,412
2003 2,948 3,793 4,200 4,404 4,563 4,676
2004 3,062 3,909 4,425 4,665 4,914
2005 3,444 4,404 4,820 5,070
2006 3,673 4,692 5,193
2007 3,962 5,067
2008 4,225
PART 2 - Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96
2001 1.2560 1.1220 1.0560 1.0180 1.0190 1.0280 1.0180
2002 1.2850 1.1020 1.0470 1.0330 1.0280 1.0140
2003 1.2860 1.1070 1.0490 1.0360 1.0250
2004 1.2760 1.1320 1.0540 1.0530
2005 1.2790 1.0940 1.0520
2006 1.2780 1.1070
2007 1.2790
PART 3 - Average Age-to-Age Factor
development 12-24 24-36 36-48 48-60 60-72 72-84 84-96
Total
simple_5 1.280 1.108 1.051 1.035 1.024 1.021 1.018
simple_3 1.278 1.111 1.052 1.041 1.024 1.021 1.018
medial_5x1 1.278 1.105 1.052 1.035 1.025 1.021 1.018
PART 4 - Selected Age-to-Age Factors
Selected
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108
(All) 1.2780 1.1050 1.0520 1.0350 1.0250 1.0210 1.0180 1.0250
CDF to Ultimate
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
(All) 1.6790 1.3140 1.1890 1.1300 1.0920 1.0650 1.0430 1.0250

Exhibit III Sheet 8#

e3_psev_df = cl.model_diagnostics(e3_psev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e3_resv_df = cl.model_diagnostics(e3_resv_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e3_s8 = e3_psev_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Paid Sev','CDF':'Paid CDF','Ultimate':'Ult Paid Sev'})
e3_s8[['Reported Sev','Reported CDF','Ult Reported Sev']] = e3_resv_df[['Latest','CDF','Ultimate']]
e3_s8["Selected Ult Sev"] = e3_sev_ult.latest_diagonal.to_frame()
e3_s8[['Paid CDF','Reported CDF']] = e3_s8[['Paid CDF','Reported CDF']].round(3)
#using floating point offset to achieve standard rounding
e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] = (e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] + 1e-9).round(0)
e3_s8.iloc[:-2,:][['development','Paid Sev','Reported Sev','Paid CDF','Reported CDF','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']]
development Paid Sev Reported Sev Paid CDF Reported CDF Ult Paid Sev Ult Reported Sev Selected Ult Sev
origin
2001-01-01 96 3872.0 4185.0 1.150 1.025 4453.0 4290.0 4371.0
2002-01-01 84 3895.0 4412.0 1.174 1.043 4573.0 4603.0 4588.0
2003-01-01 72 4062.0 4676.0 1.218 1.065 4946.0 4982.0 4964.0
2004-01-01 60 4048.0 4914.0 1.264 1.092 5116.0 5366.0 5241.0
2005-01-01 48 4114.0 5070.0 1.347 1.130 5543.0 5730.0 5636.0
2006-01-01 36 4129.0 5193.0 1.493 1.189 6164.0 6174.0 6169.0

Exhibit III Sheet 9#

e3_s9 = e3_s8[['Selected Ult Sev']].copy()
e3_s9['Sev Trend'] = e3_sev_trend.latest_diagonal.to_frame()
e3_s9['Trended Ult Sev'] = e3_sev_ult_trended.latest_diagonal.to_frame()
e3_s9[['Sev Trend']] = e3_s9[['Sev Trend']].round(3)
#using floating point offset to achieve standard rounding
e3_s9[['Trended Ult Sev']] = (e3_s9[['Trended Ult Sev']] + 1e-9).round(0)
nb_display(e3_s9.iloc[:-2,:])
print('Average Trended Severity at 2008 Cost Level')
for k,v in e3_sevs.items():
    print(f"\t{k:<20} \t{v.round(0)}")
print(f"Selected 2008 Severity \t\t{e3_sev_2008:.0f}")
print(f"Estimated 2007 Severity \t{e3_sev_2007:.0f}")
Selected Ult Sev Sev Trend Trended Ult Sev
origin
2001-01-01 4371.0 1.659 7252.0
2002-01-01 4588.0 1.543 7081.0
2003-01-01 4964.0 1.436 7126.0
2004-01-01 5241.0 1.335 6999.0
2005-01-01 5636.0 1.242 7002.0
2006-01-01 6169.0 1.156 7129.0
Average Trended Severity at 2008 Cost Level
	all_years            	7098.0
	all_years_excl_hilo  	7084.0
	latest_3             	7043.0
Selected 2008 Severity 		7100
Estimated 2007 Severity 	6605

Exhibit III Sheet 10#

e3_s10 = e3_s4[['Payroll']].copy()[-2:]
e3_s10['Selected Frequency'] = [e3_freq_2007,e3_freq_2008]
e3_s10['Ult CC'] = e3_s10['Payroll'] * e3_s10['Selected Frequency']
e3_s10['Selected Severity'] = [e3_sev_2007,e3_sev_2008]
e3_s10['Ult Claims'] = e3_s10['Ult CC'] * e3_s10['Selected Severity']
e3_s10['Reported Claims'] = e3_tri['Reported Claims'].latest_diagonal.to_frame()
e3_s10['Paid Claims'] = e3_tri['Paid Claims'].latest_diagonal.to_frame()
e3_s10['Case Outstanding'] = e3_s10['Reported Claims'] - e3_s10['Paid Claims']
e3_s10['IBNR'] = e3_s10['Ult Claims'] - e3_s10['Reported Claims']
e3_s10['Unpaid'] = e3_s10['IBNR'] + e3_s10['Case Outstanding']
e3_s10.T
origin 2007-01-01 2008-01-01
Payroll 7.800000e+05 7.400000e+05
Selected Frequency 3.727273e-03 3.600000e-03
Ult CC 2.907273e+03 2.664000e+03
Selected Severity 6.604651e+03 7.100000e+03
Ult Claims 1.920152e+07 1.891440e+07
Reported Claims 1.440000e+07 1.030000e+07
Paid Claims 9.043000e+06 4.170000e+06
Case Outstanding 5.357000e+06 6.130000e+06
IBNR 4.801522e+06 8.614400e+06
Unpaid 1.015852e+07 1.474440e+07
#Exhibit III Sheet 1
assert np.all(e3_ccc_selected.cdf_.round(3).values[...,:7] == np.array([1.698, 1.133, 1.071, 1.034, 1.018, 1.014, 1.010]))
#Exhibit III Sheet 2
assert np.all(e3_rcc_selected.cdf_.round(3).values == np.array([1.094, 1.022, 1.007, 1.002, 1.000, 1.000, 1.000, 1.000]))
#Exhibit III Sheet 3
assert np.allclose(
    e3_s3['Selected Ult CC'].values[...,1:],
    np.array([1700, 1774, 1749, 1651, 2982, 2909, 2658]),
    atol=1
)
#Exhibit III Sheet 4
assert np.all(e3_s4['Trended Ultimate Freq'].values[...,1:] == np.array(['0.53%', '0.53%', '0.54%', '0.43%', '0.35%', '0.36%', '0.36%']))
#Exhibit III Sheet 6
assert np.allclose(
    e3_psev_selected.ldf_.round(3).values,
    np.array([1.447, 1.249, 1.108, 1.066, 1.038, 1.037, 1.021, 1.150]),
    atol=.001
)
#Exhibit III Sheet 7
assert np.all(e3_rsev_selected.cdf_.round(3).values == np.array([1.679, 1.314, 1.189, 1.130, 1.092, 1.065, 1.043, 1.025]))
#Exhibit III Sheet 8
assert np.allclose(
    e3_s8['Selected Ult Sev'].values[:-2],
    np.array([4371, 4587, 4963, 5242, 5635, 6169]),
    atol=1
)
#Exhibit III Sheet 9
assert np.allclose(
    e3_s9['Trended Ult Sev'].values[:-2],
    np.array([7251, 7079, 7125, 7000, 7001, 7129]),
    rtol=0.001
)

Exhibit IV Analysis#

Exhibit IV continues to follow Approach 2. We demonstrate two separate ways in the package to achievement the on-leveling and law-change adjustments. Also, there are no full-size triangles in this exhibit. This gives us an opportunity to demontrate utilizing columns in Triangle, as if we were in Pandas.

#loading data and assumptions
xyz_tort_data = pd.DataFrame({
    "EffDate": ["2006-01-01", "2007-01-01"],
    "RateChange": [(0.67/0.75) - 1, 0.75-1]
})
xyz_tort_adjustment = cl.ParallelogramOLF(
    rate_history=xyz_tort_data,
    change_col="RateChange",
    date_col="EffDate",
    vertical_line=True
)
#Both Chapter 6 and Chapter 8 contain the code to derive these on-level factors from underlying rate changes. We will simply use the factors as given
olf = cl.Triangle(
    data = {
        'origin':[1998,2002,2003,2004,2005,2006,2007,2008],
        'valuation':[2008,2008,2008,2008,2008,2008,2008,2008],
        'On-Level Adjustment':[0,.914,.87,.81,.704,.64,.8,1],
    },
    origin='origin',
    development='valuation',
    columns='On-Level Adjustment'
)
e4_freq_assumptions = {}
e4_freq_assumptions['all_years'] = {}
e4_freq_assumptions['all_years_excl_hilo'] = {'drop_high':1, 'drop_low':1}
e4_freq_assumptions['latest_2'] = {'n_periods':2}
e4_sev_assumptions = {}
e4_sev_assumptions['latest_5_years'] = {'n_periods':5}
e4_sev_assumptions['latest_5_years_excl_hilo'] = {'n_periods':5,'drop_high':1, 'drop_low':1}
e4_sev_assumptions['latest_3'] = {'n_periods':3}

# In Exhibit II, the selected ultimate was actually the average of reported and closed ultimates.
e4_s1_tri = e2_rcc_cl.ultimate_.copy()
e4_s1_tri = e4_s1_tri.rename(axis='columns',value='Ult CC')
e4_s1_tri['CC Trend'] = cl.Trend(-.015,dates=('2008-12-31','2002-01-01')).fit(e4_s1_tri).trend_
e4_s1_tri['Trended Ult CC'] = e4_s1_tri['CC Trend'] * e4_s1_tri['Ult CC']
e4_s1_tri['Earned Premium'] = e2_tri['Earned Premium'].latest_diagonal
e4_s1_tri['On-Level Adjustment'] = olf['On-Level Adjustment']
e4_s1_tri['On-Level Premium'] = e4_s1_tri['On-Level Adjustment'] * e4_s1_tri['Earned Premium']
e4_s1_tri['Trended Ult Freq'] = e4_s1_tri['Trended Ult CC'] / e4_s1_tri['On-Level Premium']
e4_s1_tri_2006 = e4_s1_tri.iloc[:,:,4:9,:]
#calculate average frequencies
e4_freq = average_sev(e4_s1_tri_2006['Trended Ult Freq'],e4_freq_assumptions)
#calculate selected frequencies
e4_freq_2008 = e4_freq['latest_2'].round(4)
e4_freq_2007 = e4_freq_2008 / e4_s1_tri['CC Trend'].values[0,0,-2,0] * e4_s1_tri['On-Level Adjustment'].values[0,0,-2,0]
e4_freq_2007 = e4_freq_2007.round(4)

e4_s2_tri = e2_rsev_cl.ultimate_.copy()
e4_s2_tri = e4_s2_tri.rename(axis='columns',value='Ult Sev')
e4_s2_tri['Sev Trend'] = cl.Trend(.05,dates=('2008-12-31','1998-01-01')).fit(e4_s2_tri).trend_.round(3)
#from Chapter 8 Exhibit III sheet 1
e4_s2_tri['Tort Reform Factors'] = xyz_tort_adjustment.fit(e4_s2_tri['Ult Sev']).olf_
e4_s2_tri['Trended Ult Sev'] = e4_s2_tri['Ult Sev'] * e4_s2_tri['Sev Trend'] * e4_s2_tri['Tort Reform Factors']
e4_s2_tri_2006 = e4_s2_tri[e4_s2_tri.origin <= '2006']
#calculate average severities
e4_sev = average_sev(e4_s2_tri_2006['Trended Ult Sev'],e4_sev_assumptions)
#calculate selected severities
e4_sev_2008 = e4_sev['latest_5_years_excl_hilo'].round(0)
e4_sev_2007 = e4_sev_2008 / e4_s2_tri['Sev Trend'].values[0,0,-2,0] / e4_s2_tri['Tort Reform Factors'].values[0,0,-2,0]
e4_sev_2007 = e4_sev_2007.round(0)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/core/triangle.py:480: UserWarning: 
                The cumulative property of your triangle is not set. This may result in
                undesirable behavior. In a future release this will result in an error.
                
  warnings.warn(

Exhibit IV Sheet 1#

e4_s1 = e4_s1_tri.to_frame().T
e4_s1[['CC Trend','On-Level Adjustment']] = e4_s1[['CC Trend','On-Level Adjustment']].round(3)
#using floating point offset to achieve standard rounding
e4_s1[['Trended Ult CC','On-Level Premium']] = (e4_s1[['Trended Ult CC','On-Level Premium']] + 1e-9).round(0)
e4_s1['Trended Ult Freq'] = e4_s1["Trended Ult Freq"].map("{:.2%}".format)
nb_display(e4_s1.iloc[4:9])
print('Average Trended Frequency at 2008 Cost Level')
for k,v in e4_freq.items():
    print(f"\t{k:<20} \t{v:.2%}")
print(f"Selected 2008 Frequency \t{e4_freq_2008:.2%}")
print(f"Estimated 2007 Frequency \t{e4_freq_2007:.2%}")
Ult CC CC Trend Trended Ult CC Earned Premium On-Level Adjustment On-Level Premium Trended Ult Freq
origin
2002 1554.000000 0.913 1419.0 61183.0 0.914 55921.0 2.54%
2003 1630.629000 0.927 1512.0 69175.0 0.870 60182.0 2.51%
2004 2262.518258 0.941 2130.0 99322.0 0.810 80451.0 2.65%
2005 2401.966737 0.956 2295.0 138151.0 0.704 97258.0 2.36%
2006 1678.618977 0.970 1629.0 107578.0 0.640 68850.0 2.37%
Average Trended Frequency at 2008 Cost Level
	all_years            	2.48%
	all_years_excl_hilo  	2.47%
	latest_2             	2.36%
Selected 2008 Frequency 	2.36%
Estimated 2007 Frequency 	1.92%

Exhibit IV Sheet 2#

e4_s2 = e4_s2_tri.to_frame().T
e4_s2[['Sev Trend','Tort Reform Factors']] = e4_s2[['Sev Trend','Tort Reform Factors']].round(3)
#using floating point offset to achieve standard rounding
e4_s2[['Trended Ult Sev','Ult Sev']] = (e4_s2[['Trended Ult Sev','Ult Sev']] + 1e-9).round(0)
nb_display(e4_s2.iloc[:9])
print('Average Trended Severity at 2008 Cost Level')
for k,v in e4_sev.items():
    print(f"\t{k:<25} \t{v.round(0)}")
print(f"Selected 2008 Severity \t\t\t{e4_sev_2008}")
print(f"Estimated 2007 Severity \t\t{e4_sev_2007}")
Ult Sev Sev Trend Tort Reform Factors Trended Ult Sev
origin
1998 24838.0 1.629 0.67 27109.0
1999 23956.0 1.551 0.67 24894.0
2000 26189.0 1.477 0.67 25916.0
2001 26452.0 1.407 0.67 24936.0
2002 31087.0 1.340 0.67 27910.0
2003 27674.0 1.276 0.67 23659.0
2004 33174.0 1.216 0.67 27028.0
2005 32514.0 1.158 0.67 25226.0
2006 35697.0 1.102 0.75 29504.0
Average Trended Severity at 2008 Cost Level
	latest_5_years            	26665.0
	latest_5_years_excl_hilo  	26721.0
	latest_3                  	27253.0
Selected 2008 Severity 			26721.0
Estimated 2007 Severity 		25449.0

Exhibit IV Sheet 3#

e4_s3 = e4_s1_tri['Earned Premium'].to_frame(keepdims=True).set_index('origin').iloc[-2:][['Earned Premium']]
e4_s3['Selected Frequency'] = [e4_freq_2007,e4_freq_2008]
e4_s3['Ult CC'] = e4_s3['Earned Premium'] * e4_s3['Selected Frequency']
e4_s3['Selected Severity'] = [e4_sev_2007,e4_sev_2008]
e4_s3['Ult Claims'] = e4_s3['Ult CC'] * e4_s3['Selected Severity']
e4_s3['Reported Claims'] = (e2_tri['Reported Claims']*1000).latest_diagonal.to_frame()
e4_s3['Paid Claims'] = (e2_tri['Paid Claims']*1000).latest_diagonal.to_frame()
e4_s3['Case Outstanding'] = e4_s3['Reported Claims'] - e4_s3['Paid Claims']
e4_s3['IBNR'] = e4_s3['Ult Claims'] - e4_s3['Reported Claims']
e4_s3['Unpaid'] = e4_s3['IBNR'] + e4_s3['Case Outstanding']
e4_s3.T
origin 2007-01-01 2008-01-01
Earned Premium 6.243800e+04 4.779700e+04
Selected Frequency 1.920000e-02 2.360000e-02
Ult CC 1.198810e+03 1.128009e+03
Selected Severity 2.544900e+04 2.672100e+04
Ult Claims 3.050851e+07 3.014153e+07
Reported Claims 3.173200e+07 1.863200e+07
Paid Claims 1.186472e+07 3.408876e+06
Case Outstanding 1.986728e+07 1.522312e+07
IBNR -1.223494e+06 1.150953e+07
Unpaid 1.864379e+07 2.673266e+07
#Exhibit IV Sheet 1
assert np.all(e4_s1.iloc[4:9]['Trended Ult Freq'].values == np.array(['2.54%', '2.51%', '2.65%', '2.36%', '2.37%']))
#Exhibit IV Sheet 2
assert np.allclose(
    list(e4_sev.values()),
    np.array([26669., 26720., 27254.]),
    rtol=0.001
)
#Exhibit IV Sheet 3
assert np.allclose(
    e4_s3.iloc[:,4].values,
    np.array([30512152., 30140260.]),
    rtol=0.001
)

Exhibit V Analysis#

Exhibit V follows Approach 3, which uses full incremental closed count and paid triangles to estimate the ultimate. Full incremental closed count triangle is estimated after first estimating ultimate count using reported count. We use the DisposalRate adjustment method to perform this calculation. Full incremental paid severity triangle is estimated through trending.

#loading data and assumptions
e5_tri = cl.load_sample('friedland_gl_insurer')
e5_cnt_assumptions = {}
e5_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}
e5_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}
e5_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}
e5_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}
e5_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}
e5_disp_assumptions = {}
e5_disp_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}
e5_disp_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}
e5_disp_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}
e5_trend_assumptions = {}
e5_trend_assumptions['all_years'] = {}
e5_trend_assumptions['latest_6'] = {'n_periods':6}
e5_trend_assumptions['latest_4'] = {'n_periods':4}
e5_sev_assumptions = {}
e5_sev_assumptions['simple_5'] = {'n_periods':5}
e5_sev_assumptions['simple_3'] = {'n_periods':3}
e5_sev_assumptions['medial_5x1'] = {'n_periods':5,'drop_high':1, 'drop_low':1}

#developing closed claim counts
e5_ccc_devs = average_dev(e5_tri['Closed Claim Counts'],e5_cnt_assumptions)
e5_ccc_selected = cl.TailConstant(tail = 1.100, projection_period = 0).fit_transform(e5_ccc_devs['volume_3'])
e5_ccc_selected.ldf_ = e5_ccc_selected.ldf_.round(3)

#developing reported claim counts
e5_rcc_devs = average_dev(e5_tri['Reported Claim Counts'],e5_cnt_assumptions)
e5_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e5_rcc_devs['volume_3'])
e5_rcc_selected.ldf_ = e5_rcc_selected.ldf_.round(3)

#combining closed and reported claim counts
e5_ccc_cl = cl.Chainladder().fit(e5_ccc_selected)
e5_rcc_cl = cl.Chainladder().fit(e5_rcc_selected)
e5_cc_ult = ((e5_ccc_cl.ultimate_ + e5_rcc_cl.ultimate_)/2)

#calculating disposal rate and complete the closed count triangle
e5_drs = average_dr(e5_tri['Closed Claim Counts'],e5_cc_ult,e5_disp_assumptions)
e5_dr_selected = e5_drs['medial_5x1']
e5_dr_selected.disposal_rate_ = e5_dr_selected.disposal_rate_.round(3)
e5_dr_tri = e5_dr_selected.transform(e5_tri['Closed Claim Counts'],sample_weight = e5_cc_ult)

#calculate and select incremental paid severity
e5_ipsev = e5_tri["Paid Claims"].cum_to_incr() / e5_tri["Closed Claim Counts"].cum_to_incr()
e5_regs = regs(e5_ipsev,e5_trend_assumptions)
e5_trend,e5_rsq = reg_outputs(e5_regs,e5_ipsev.development)
e5_regs_ex2001 = regs(e5_ipsev[e5_ipsev.origin>'2001'],{'all_years_ex_2001':{}})
e5_trend_ex2001,e5_rsq_ex2001 = reg_outputs(e5_regs_ex2001,e5_ipsev.development)
e5_sevs = average_sev(e5_ipsev.trend(0.05),e5_sev_assumptions)
e5_sevs_sel = e5_sevs['simple_3'].copy()
e5_sevs_sel.origin = ['2008']

#calculate and select tail paid severity
e5_incr_ccc = e5_tri["Closed Claim Counts"].cum_to_incr()
e5_incr_paid = e5_tri["Paid Claims"].cum_to_incr()
e5_incr_paid_trended = e5_incr_paid.trend(0.05)

#complete the incremental paid severity triangle
e5_ipsev_full = e5_ipsev.copy()
#extending the severity triangle to 108 months
e5_ipsev_full = cl.concat((e5_ipsev_full,e5_ipsev_full.latest_diagonal.rename("development",[9999])),axis=3)
#setting the before 60 months to selected incremental
e5_ipsev_full.iloc[:,:,:,:5] = e5_sevs_sel.iloc[:,:,:,:5]
#setting the after 72 months to selected tail
e5_ipsev_full.iloc[:,:,:,5:] = (
    e5_incr_paid_trended[e5_incr_paid_trended.development >= 72].sum().sum()
    /
    e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum() 
)
#setting the valuation_date to the future
e5_ipsev_full.valuation_date = pd.to_datetime(cl.options.ULT_VAL)
#trending back from latest accident year
e5_ipsev_full_detrended = e5_ipsev_full.trend(1/1.05-1,start='2008-12-31',end='2001-01-01')
#compositing the actual incremental severities with dtrended selected to complete the incremental paid severity triangle
e5_ipsev_full_complete = e5_ipsev[e5_ipsev.valuation <= e5_ipsev.valuation_date] + e5_ipsev_full_detrended[e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date]

#complete the incremental paid triangle
e5_s11_ccc = e5_dr_tri.full_triangle_.cum_to_incr()
e5_s11_ip = e5_s11_ccc * e5_ipsev_full_complete
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/development/base.py:272: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)

Exhibit V Sheet 1#

print('PART 1 - Data Triangle')
nb_display(e5_tri['Closed Claim Counts'])
print('PART 2 - Age-to-Age Factors')
nb_display(e5_tri['Closed Claim Counts'].age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factor')
nb_display(combine_ldf(e5_ccc_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
print('Selected')
nb_display(e5_ccc_selected.ldf_)
print('CDF to Ultimate')
nb_display(e5_ccc_selected.cdf_.round(3))
print('Percent Closed')
nb_display((1/e5_ccc_selected.cdf_).round(3))
PART 1 - Data Triangle
12 24 36 48 60 72 84 96
2001 195.00 375.00 510.00 625.00 702.00 752.00 780.00 796.00
2002 199.00 349.00 445.00 508.00 563.00 594.00 626.00
2003 106.00 294.00 383.00 453.00 499.00 542.00
2004 126.00 281.00 377.00 445.00 494.00
2005 114.00 249.00 315.00 403.00
2006 114.00 229.00 300.00
2007 79.00 188.00
2008 127.00
PART 2 - Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96
2001 1.9230 1.3600 1.2250 1.1230 1.0710 1.0370 1.0210
2002 1.7540 1.2750 1.1420 1.1080 1.0550 1.0540
2003 2.7740 1.3030 1.1830 1.1020 1.0860
2004 2.2300 1.3420 1.1800 1.1100
2005 2.1840 1.2650 1.2790
2006 2.0090 1.3100
2007 2.3800
PART 3 - Average Age-to-Age Factor
development 12-24 24-36 36-48 48-60 60-72 72-84 84-96
Total
simple_5 2.315 1.299 1.202 1.111 1.071 1.046 1.021
simple_3 2.191 1.306 1.214 1.107 1.071 1.046 1.021
medial_5x1 2.265 1.296 1.196 1.109 1.071 1.046 1.021
volume_5 2.302 1.298 1.199 1.112 1.070 1.045 1.021
volume_3 2.169 1.307 1.210 1.107 1.070 1.045 1.021
PART 4 - Selected Age-to-Age Factors
Selected
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108
(All) 2.1690 1.3070 1.2100 1.1070 1.0700 1.0450 1.0210 1.1000
CDF to Ultimate
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
(All) 4.7690 2.1990 1.6820 1.3900 1.2560 1.1740 1.1230 1.1000
Percent Closed
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
(All) 0.2100 0.4550 0.5940 0.7190 0.7960 0.8520 0.8900 0.9090

Exhibit V Sheet 2#

print('PART 1 - Data Triangle')
nb_display(e5_tri['Reported Claim Counts'])
print('PART 2 - Age-to-Age Factors')
nb_display(e5_tri['Reported Claim Counts'].age_to_age.round(3))
print('PART 3 - Average Age-to-Age Factor')
nb_display(combine_ldf(e5_rcc_devs).round(3).to_frame())
print('PART 4 - Selected Age-to-Age Factors')
nb_display(e5_rcc_selected.ldf_)
print('CDF to Ultimate')
nb_display(e5_rcc_selected.cdf_.round(3))
print('Percent Reported')
nb_display((1/e5_rcc_selected.cdf_).round(3))
PART 1 - Data Triangle
12 24 36 48 60 72 84 96
2001 1,299.00 1,077.00 1,057.00 965.00 930.00 917.00 864.00 870.00
2002 847.00 945.00 864.00 787.00 784.00 743.00 731.00
2003 800.00 831.00 762.00 704.00 669.00 636.00
2004 823.00 862.00 797.00 728.00 684.00
2005 828.00 850.00 765.00 687.00
2006 824.00 809.00 734.00
2007 604.00 620.00
2008 812.00
PART 2 - Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96
2001 0.8290 0.9810 0.9130 0.9640 0.9860 0.9420 1.0070
2002 1.1160 0.9140 0.9110 0.9960 0.9480 0.9840
2003 1.0390 0.9170 0.9240 0.9500 0.9510
2004 1.0470 0.9250 0.9130 0.9400
2005 1.0270 0.9000 0.8980
2006 0.9820 0.9070
2007 1.0260
PART 3 - Average Age-to-Age Factor
development 12-24 24-36 36-48 48-60 60-72 72-84 84-96
Total
simple_5 1.024 0.913 0.912 0.962 0.961 0.963 1.007
simple_3 1.012 0.911 0.912 0.962 0.961 0.963 1.007
medial_5x1 1.031 0.913 0.912 0.957 0.951 0.963 1.007
volume_5 1.024 0.913 0.912 0.963 0.963 0.961 1.007
volume_3 1.010 0.911 0.912 0.963 0.963 0.961 1.007
PART 4 - Selected Age-to-Age Factors
12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108
(All) 1.0100 0.9110 0.9120 0.9630 0.9630 0.9610 1.0070 1.0000
CDF to Ultimate
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
(All) 0.7530 0.7460 0.8180 0.8970 0.9320 0.9680 1.0070 1.0000
Percent Reported
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
(All) 1.3280 1.3410 1.2220 1.1140 1.0730 1.0330 0.9930 1.0000

Exhibit V Sheet 3#

e5_ccc_df = cl.model_diagnostics(e5_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e5_rcc_df = cl.model_diagnostics(e5_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')
e5_s3 = e5_ccc_df[['development','Latest','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','Ultimate':'Ult Count Using CCC'})
e5_s3[['Reported Claim Counts','Ult Count Using RCC']] = e5_rcc_df[['Latest','Ultimate']]
e5_s3["Selected Ult CC"] = e5_cc_ult.round(0).latest_diagonal.to_frame()
e5_s3
development Closed Claim Counts Ult Count Using CCC Reported Claim Counts Ult Count Using RCC Selected Ult CC
origin
2001-01-01 96 796.0 875.600000 870.0 870.000000 873.0
2002-01-01 84 626.0 703.060600 731.0 736.117000 720.0
2003-01-01 72 542.0 636.112609 636.0 615.474372 626.0
2004-01-01 60 494.0 620.362367 684.0 637.434033 629.0
2005-01-01 48 403.0 560.236193 687.0 616.541294 588.0
2006-01-01 36 300.0 504.629623 734.0 600.753529 553.0
2007-01-01 24 188.0 413.318575 620.0 462.285570 438.0
2008-01-01 12 127.0 605.606248 812.0 611.499422 609.0

Exhibit V Sheet 4#

print('Part 1 - Disposal Rate Triangle')
nb_display(cl.DisposalRate().fit_transform(e5_tri['Closed Claim Counts'],sample_weight = e5_cc_ult).disposal_rate_tri.round(3))
print('PART 2 - Average Disposal Rate Factors')
nb_display(combine_disposal(e5_drs).round(3).to_frame())
print('PART 3 - Selected Disposal Rate Factors')
nb_display(e5_dr_selected.disposal_rate_)
Part 1 - Disposal Rate Triangle
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
2001 0.2230 0.4300 0.5840 0.7160 0.8040 0.8620 0.8940 0.9120
2002 0.2770 0.4850 0.6180 0.7060 0.7820 0.8250 0.8700
2003 0.1690 0.4700 0.6120 0.7240 0.7970 0.8660
2004 0.2000 0.4470 0.5990 0.7080 0.7860
2005 0.1940 0.4230 0.5350 0.6850
2006 0.2060 0.4140 0.5430
2007 0.1800 0.4290
2008 0.2090
PART 2 - Average Disposal Rate Factors
development 12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult
Total
simple_5 0.198 0.437 0.582 0.708 0.792 0.851 0.882 0.912 1.0
simple_3 0.198 0.422 0.559 0.705 0.788 0.851 0.882 0.912 1.0
medial_5x1 0.200 0.433 0.585 0.710 0.791 0.862 0.882 0.912 1.0
PART 3 - Selected Disposal Rate Factors
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult
(All) 0.2000 0.4330 0.5850 0.7100 0.7910 0.8620 0.8820 0.9120 1.0000

Exhibit V Sheet 5#

print('Closed Claim Counts')
nb_display(e5_tri['Closed Claim Counts'])
print('Projected Incremental Closed Claim Counts')
nb_display(e5_dr_tri.full_triangle_.cum_to_incr().round(0))
Closed Claim Counts
12 24 36 48 60 72 84 96
2001 195.00 375.00 510.00 625.00 702.00 752.00 780.00 796.00
2002 199.00 349.00 445.00 508.00 563.00 594.00 626.00
2003 106.00 294.00 383.00 453.00 499.00 542.00
2004 126.00 281.00 377.00 445.00 494.00
2005 114.00 249.00 315.00 403.00
2006 114.00 229.00 300.00
2007 79.00 188.00
2008 127.00
Projected Incremental Closed Claim Counts
12 24 36 48 60 72 84 96 9999
2001 195.00 180.00 135.00 115.00 77.00 50.00 28.00 16.00 77.00
2002 199.00 150.00 96.00 63.00 55.00 31.00 32.00 24.00 70.00
2003 106.00 188.00 89.00 70.00 46.00 43.00 12.00 18.00 53.00
2004 126.00 155.00 96.00 68.00 49.00 46.00 13.00 19.00 57.00
2005 114.00 135.00 66.00 88.00 52.00 45.00 13.00 19.00 56.00
2006 114.00 115.00 71.00 76.00 49.00 43.00 12.00 18.00 54.00
2007 79.00 109.00 67.00 55.00 36.00 31.00 9.00 13.00 39.00
2008 127.00 140.00 91.00 75.00 49.00 43.00 12.00 18.00 53.00

Exhibit V Sheet 6#

print('Paid Claims')
nb_display(e5_tri["Paid Claims"])
print('Incremental Paid CLaims')
nb_display(e5_tri["Paid Claims"].cum_to_incr())
print('Incremental Closed Claim Counts')
nb_display(e5_tri["Closed Claim Counts"].cum_to_incr())
print('Incremental Paid Severities')
nb_display(e5_ipsev)
Paid Claims
12 24 36 48 60 72 84 96
2001 1,119,962 4,373,268 8,398,345 13,490,793 17,372,233 22,052,662 27,359,691 29,901,361
2002 1,411,957 6,287,005 11,443,820 15,520,552 21,295,572 28,410,418 32,468,911
2003 984,748 6,128,957 10,470,758 14,604,684 21,936,647 23,942,499
2004 1,158,659 5,811,172 10,497,504 15,087,416 18,242,570
2005 1,198,767 5,103,837 9,042,134 15,443,929
2006 1,220,778 4,594,746 8,983,864
2007 796,774 4,233,641
2008 1,445,365
Incremental Paid CLaims
12 24 36 48 60 72 84 96
2001 1,119,962 3,253,306 4,025,077 5,092,448 3,881,440 4,680,429 5,307,029 2,541,670
2002 1,411,957 4,875,048 5,156,815 4,076,732 5,775,020 7,114,846 4,058,493
2003 984,748 5,144,209 4,341,801 4,133,926 7,331,963 2,005,852
2004 1,158,659 4,652,513 4,686,332 4,589,912 3,155,154
2005 1,198,767 3,905,070 3,938,297 6,401,795
2006 1,220,778 3,373,968 4,389,118
2007 796,774 3,436,867
2008 1,445,365
Incremental Closed Claim Counts
12 24 36 48 60 72 84 96
2001 195.00 180.00 135.00 115.00 77.00 50.00 28.00 16.00
2002 199.00 150.00 96.00 63.00 55.00 31.00 32.00
2003 106.00 188.00 89.00 70.00 46.00 43.00
2004 126.00 155.00 96.00 68.00 49.00
2005 114.00 135.00 66.00 88.00
2006 114.00 115.00 71.00
2007 79.00 109.00
2008 127.00
Incremental Paid Severities
12 24 36 48 60 72 84 96
2001 5,743 18,074 29,815 44,282 50,408 93,609 189,537 158,854
2002 7,095 32,500 53,717 64,710 105,000 229,511 126,828
2003 9,290 27,363 48,784 59,056 159,390 46,648
2004 9,196 30,016 48,816 67,499 64,391
2005 10,516 28,926 59,671 72,748
2006 10,709 29,339 61,819
2007 10,086 31,531
2008 11,381

Exhibit V Sheet 7#

print('Incremental Paid Severities')
nb_display(e5_ipsev)
print('Annual Change based on Exponential Regression')
nb_display(pd.concat([e5_trend.round(3),e5_trend_ex2001.round(3)]))
print('Goodness of Fit Test of Exponential Regression (R-Squared)')
nb_display(pd.concat([e5_rsq.round(3),e5_rsq_ex2001.round(3)]))
Incremental Paid Severities
12 24 36 48 60 72 84 96
2001 5,743 18,074 29,815 44,282 50,408 93,609 189,537 158,854
2002 7,095 32,500 53,717 64,710 105,000 229,511 126,828
2003 9,290 27,363 48,784 59,056 159,390 46,648
2004 9,196 30,016 48,816 67,499 64,391
2005 10,516 28,926 59,671 72,748
2006 10,709 29,339 61,819
2007 10,086 31,531
2008 11,381
Annual Change based on Exponential Regression
development 12 24 36 48 60 72 84 96
all_years 0.088 0.056 0.120 0.109 0.122 -0.294 -0.331 NaN
latest_6 0.038 0.001 0.120 0.109 0.122 -0.294 -0.331 NaN
latest_4 0.018 0.016 0.095 0.050 0.122 -0.294 -0.331 NaN
all_years_ex_2001 0.064 0.001 0.049 0.050 -0.217 -0.797 NaN NaN
Goodness of Fit Test of Exponential Regression (R-Squared)
development 12 24 36 48 60 72 84 96
all_years 0.787 0.349 0.644 0.722 0.084 0.19 1.0 NaN
latest_6 0.704 0.000 0.644 0.722 0.084 0.19 1.0 NaN
latest_4 0.208 0.306 0.856 0.518 0.084 0.19 1.0 NaN
all_years_ex_2001 0.737 0.000 0.478 0.518 0.290 1.00 NaN NaN

Exhibit V Sheet 8#

print('Incremental Paid Severities')
nb_display(e5_ipsev)
print('Trended Incremental Paid Severities Assuming 5% Annual Trend')
nb_display(e5_ipsev.trend(0.05))
print('Average Trended Incremental Paid Severities')
nb_display(combine_tri(e5_sevs).round(0).to_frame())
print('Selected Incremental Paid Severities')
nb_display(e5_sevs_sel[e5_sevs_sel.development<=60])
Incremental Paid Severities
12 24 36 48 60 72 84 96
2001 5,743 18,074 29,815 44,282 50,408 93,609 189,537 158,854
2002 7,095 32,500 53,717 64,710 105,000 229,511 126,828
2003 9,290 27,363 48,784 59,056 159,390 46,648
2004 9,196 30,016 48,816 67,499 64,391
2005 10,516 28,926 59,671 72,748
2006 10,709 29,339 61,819
2007 10,086 31,531
2008 11,381
Trended Incremental Paid Severities Assuming 5% Annual Trend
12 24 36 48 60 72 84 96
2001 8,082 25,432 41,953 62,309 70,930 131,717 266,697 223,524
2002 9,508 43,554 71,986 86,718 140,711 307,567 169,962
2003 11,857 34,923 62,262 75,372 203,427 59,536
2004 11,177 36,485 59,336 82,045 78,268
2005 12,173 33,486 69,077 84,215
2006 11,806 32,346 68,155
2007 10,590 33,107
2008 11,381
Average Trended Incremental Paid Severities
development 12 24 36 48 60 72 84 96
Total
simple_5 11426.0 34069.0 66163.0 78132.0 123334.0 166273.0 218329.0 223524.0
simple_3 11259.0 32980.0 65523.0 80544.0 140802.0 166273.0 218329.0 223524.0
medial_5x1 11455.0 33839.0 66498.0 80544.0 109489.0 131717.0 218329.0 223524.0
Selected Incremental Paid Severities
12 24 36 48 60
2008 11,259 32,980 65,523 80,544 140,802

Exhibit V Sheet 9#

e5_s9 = pd.DataFrame(
    data = [[
        e5_incr_ccc[e5_incr_ccc.development >= 60].sum().sum(), 
        e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum()
    ]],
    index = ['Total Closed Claim Counts'],
    columns = ['Age 60 & Older','Age 72 & Older']
)
e5_s9.loc['Total Trended Paid Claims'] = [
        e5_incr_paid_trended[e5_incr_paid_trended.development >= 60].sum().sum(), 
        e5_incr_paid_trended[e5_incr_paid_trended.development >= 72].sum().sum()
    ]
e5_s9.loc['Estimated Trended Tail Severity'] = e5_s9.iloc[1] / e5_s9.iloc[0]
e5_s9.loc['Estimated Incremental Trended Tail Severity'] = e5_sevs_sel.iloc[:,:,:,4:6].values.flatten()
e5_s9.loc['Selected Trended Paid Severity'] = e5_ipsev_full.values[0,0,-1,4:6]
print('Incremental Closed Claim Counts')
nb_display(e5_incr_ccc[e5_incr_ccc.development >= 60][e5_incr_ccc.origin <= '2004'])
print('Incremental Paid CLaims')
nb_display(e5_incr_paid[e5_incr_paid.development >= 60][e5_incr_paid.origin <= '2004'])
print('Trended Incremental Paid Severities')
nb_display(e5_incr_paid_trended[e5_incr_paid_trended.development >= 60][e5_incr_paid_trended.origin <= '2004'])
nb_display(e5_s9)
Incremental Closed Claim Counts
60 72 84 96
2001 77.00 50.00 28.00 16.00
2002 55.00 31.00 32.00
2003 46.00 43.00
2004 49.00
Incremental Paid CLaims
60 72 84 96
2001 3,881,440 4,680,429 5,307,029 2,541,670
2002 5,775,020 7,114,846 4,058,493
2003 7,331,963 2,005,852
2004 3,155,154
Trended Incremental Paid Severities
60 72 84 96
2001 5,461,576 6,585,834 7,467,523 3,576,385
2002 7,739,079 9,534,574 5,438,769
2003 9,357,649 2,560,032
2004 3,835,109
Age 60 & Older Age 72 & Older
Total Closed Claim Counts 4.270000e+02 2.000000e+02
Total Trended Paid Claims 6.155653e+07 3.516312e+07
Estimated Trended Tail Severity 1.441605e+05 1.758156e+05
Estimated Incremental Trended Tail Severity 1.408017e+05 1.662731e+05
Selected Trended Paid Severity 1.408017e+05 1.758156e+05

Exhibit V Sheet 10#

print('Incremental Paid Severities')
nb_display(e5_ipsev)
print('Selected Incremental Paid Severities')
nb_display(e5_ipsev_full[e5_ipsev_full.origin == '2008'])
print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate')
nb_display(e5_ipsev_full_complete)
Incremental Paid Severities
12 24 36 48 60 72 84 96
2001 5,743 18,074 29,815 44,282 50,408 93,609 189,537 158,854
2002 7,095 32,500 53,717 64,710 105,000 229,511 126,828
2003 9,290 27,363 48,784 59,056 159,390 46,648
2004 9,196 30,016 48,816 67,499 64,391
2005 10,516 28,926 59,671 72,748
2006 10,709 29,339 61,819
2007 10,086 31,531
2008 11,381
Selected Incremental Paid Severities
12 24 36 48 60 72 84 96 9999
2008 11,259 32,980 65,523 80,544 140,802 175,816 175,816 175,816 175,816
Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate
12 24 36 48 60 72 84 96 9999
2001 5,743 18,074 29,815 44,282 50,408 93,609 189,537 158,854 124,949
2002 7,095 32,500 53,717 64,710 105,000 229,511 126,828 131,196 131,196
2003 9,290 27,363 48,784 59,056 159,390 46,648 137,756 137,756 137,756
2004 9,196 30,016 48,816 67,499 64,391 144,644 144,644 144,644 144,644
2005 10,516 28,926 59,671 72,748 121,630 151,876 151,876 151,876 151,876
2006 10,709 29,339 61,819 73,056 127,711 159,470 159,470 159,470 159,470
2007 10,086 31,531 62,403 76,709 134,097 167,443 167,443 167,443 167,443
2008 11,381 32,980 65,523 80,544 140,802 175,816 175,816 175,816 175,816

Exhibit V Sheet 11#

print('Projected Incremental Closed Claim Counts')
nb_display(e5_s11_ccc.round(0))
print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate')
nb_display(e5_ipsev_full_complete)
print('Projected Incremental Paid Claims')
nb_display(e5_s11_ip)
print('Projected Cumulative Paid Claims')
nb_display(e5_s11_ip.incr_to_cum())
Projected Incremental Closed Claim Counts
12 24 36 48 60 72 84 96 9999
2001 195.00 180.00 135.00 115.00 77.00 50.00 28.00 16.00 77.00
2002 199.00 150.00 96.00 63.00 55.00 31.00 32.00 24.00 70.00
2003 106.00 188.00 89.00 70.00 46.00 43.00 12.00 18.00 53.00
2004 126.00 155.00 96.00 68.00 49.00 46.00 13.00 19.00 57.00
2005 114.00 135.00 66.00 88.00 52.00 45.00 13.00 19.00 56.00
2006 114.00 115.00 71.00 76.00 49.00 43.00 12.00 18.00 54.00
2007 79.00 109.00 67.00 55.00 36.00 31.00 9.00 13.00 39.00
2008 127.00 140.00 91.00 75.00 49.00 43.00 12.00 18.00 53.00
Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate
12 24 36 48 60 72 84 96 9999
2001 5,743 18,074 29,815 44,282 50,408 93,609 189,537 158,854 124,949
2002 7,095 32,500 53,717 64,710 105,000 229,511 126,828 131,196 131,196
2003 9,290 27,363 48,784 59,056 159,390 46,648 137,756 137,756 137,756
2004 9,196 30,016 48,816 67,499 64,391 144,644 144,644 144,644 144,644
2005 10,516 28,926 59,671 72,748 121,630 151,876 151,876 151,876 151,876
2006 10,709 29,339 61,819 73,056 127,711 159,470 159,470 159,470 159,470
2007 10,086 31,531 62,403 76,709 134,097 167,443 167,443 167,443 167,443
2008 11,381 32,980 65,523 80,544 140,802 175,816 175,816 175,816 175,816
Projected Incremental Paid Claims
12 24 36 48 60 72 84 96 9999
2001 1,119,962 3,253,306 4,025,077 5,092,448 3,881,440 4,680,429 5,307,029 2,541,670 9,596,072
2002 1,411,957 4,875,048 5,156,815 4,076,732 5,775,020 7,114,846 4,058,493 3,121,653 9,156,850
2003 984,748 5,144,209 4,341,801 4,133,926 7,331,963 2,005,852 1,672,908 2,509,362 7,360,795
2004 1,158,659 4,652,513 4,686,332 4,589,912 3,155,154 6,628,548 1,867,197 2,800,795 8,215,665
2005 1,198,767 3,905,070 3,938,297 6,401,795 6,298,114 6,893,395 1,941,801 2,912,702 8,543,926
2006 1,220,778 3,373,968 4,389,118 5,560,412 6,298,790 6,894,135 1,942,010 2,913,015 8,544,843
2007 796,774 3,436,867 4,178,868 4,224,415 4,785,383 5,237,685 1,475,404 2,213,106 6,491,779
2008 1,445,365 4,625,496 5,994,996 6,060,338 6,865,102 7,513,974 2,116,612 3,174,918 9,313,094
Projected Cumulative Paid Claims
12 24 36 48 60 72 84 96 9999
2001 1,119,962 4,373,268 8,398,345 13,490,793 17,372,233 22,052,662 27,359,691 29,901,361 39,497,433
2002 1,411,957 6,287,005 11,443,820 15,520,552 21,295,572 28,410,418 32,468,911 35,590,564 44,747,415
2003 984,748 6,128,957 10,470,758 14,604,684 21,936,647 23,942,499 25,615,407 28,124,769 35,485,564
2004 1,158,659 5,811,172 10,497,504 15,087,416 18,242,570 24,871,118 26,738,314 29,539,109 37,754,774
2005 1,198,767 5,103,837 9,042,134 15,443,929 21,742,043 28,635,438 30,577,240 33,489,942 42,033,868
2006 1,220,778 4,594,746 8,983,864 14,544,276 20,843,065 27,737,200 29,679,210 32,592,225 41,137,068
2007 796,774 4,233,641 8,412,509 12,636,923 17,422,307 22,659,992 24,135,396 26,348,502 32,840,281
2008 1,445,365 6,070,861 12,065,857 18,126,195 24,991,297 32,505,270 34,621,883 37,796,801 47,109,895

Exhibit V Sheet 12#

summary_exh(
    e5_tri['Reported Claims']/1000,
    e5_tri['Paid Claims']/1000,
    e5_s11_ip.incr_to_cum()/1000
)
Reported Paid Ult Claims Case Outstanding IBNR Unpaid
2001-01-01 35592.0 29901.0 39497.0 5691.0 3905.0 9596.0
2002-01-01 36330.0 32469.0 44747.0 3861.0 8417.0 12278.0
2003-01-01 31900.0 23942.0 35486.0 7958.0 3586.0 11544.0
2004-01-01 39716.0 18243.0 37755.0 21473.0 -1961.0 19512.0
2005-01-01 32667.0 15444.0 42034.0 17223.0 9367.0 26590.0
2006-01-01 27774.0 8984.0 41137.0 18790.0 13363.0 32153.0
2007-01-01 16246.0 4234.0 32840.0 12012.0 16594.0 28606.0
2008-01-01 8216.0 1445.0 47110.0 6771.0 38894.0 45665.0
e5_dr_selected.disposal_rate_
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult
(All) 0.2000 0.4330 0.5850 0.7100 0.7910 0.8620 0.8820 0.9120 1.0000
#Exhibit V Sheet 1
assert np.all(e5_ccc_selected.cdf_.round(3).values == np.array([4.769, 2.199, 1.682, 1.390, 1.256, 1.174, 1.123, 1.100]))
#Exhibit V Sheet 2
assert np.all(e5_rcc_selected.cdf_.round(3).values == np.array([0.753, 0.746, 0.818, 0.897, 0.932, 0.968, 1.007, 1.000]))
#Exhibit V Sheet 3
assert np.all(e5_s3['Selected Ult CC'].values==np.array([873., 720., 626., 629., 588., 553, 438., 609.]))
#Exhibit V Sheet 4
assert np.all(e5_dr_selected.disposal_rate_.values == np.array([0.200, 0.433, 0.585, 0.710, 0.791, 0.862, 0.882, 0.912, 1.000]))
#Exhibit V Sheet 5
lhs = (e5_dr_tri.full_triangle_.cum_to_incr()-e5_tri['Closed Claim Counts'].cum_to_incr()).values.flatten()
rhs = np.array([
                                                        77.,  
                                                24.,    70.,  
                                        12.,    18.,    54.,  
                                46.,    13.,    19.,    57.,  
                        52.,    45.,    13.,    19.,    56.,  
                76.,    49.,    43.,    12.,    18.,    54.,  
        67.,    55.,    36.,    31.,    9.,     13.,    39.,  
140.,   91.,    75.,    49.,    43.,    12.,    18.,    53.
])
assert np.all(abs(lhs[~np.isnan(lhs)] - rhs) < 1)
#Exhibit V Sheet 7
assert np.all(e5_trend.loc['latest_4'].values[:7].round(3) == np.array([0.018, 0.016, 0.095, 0.050, 0.122, -0.294, -0.331]))
assert np.all(e5_rsq.loc['latest_6'].values[:7].round(3) == np.array([0.704, 0.000, 0.644, 0.722, 0.084, 0.190, 1.000]))
#Exhibit V Sheet 8
assert np.all(e5_sevs_sel.values[...,:5].round(0) == np.array([11259.,  32980.,  65523.,  80544., 140802.]))
#Exhibit V Sheet 9
assert np.all(e5_s9.iloc[2].values.round(0) == np.array([144160., 175816.]))
#Exhibit V Sheet 10
lhs = e5_ipsev_full_detrended[e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date].round(0).values.flatten()
rhs = np.array([
                                                        124949.,
                                                131196.,131196.,
                                        137756.,137756.,137756.,
                                144644.,144644.,144644.,144644.,
                        121630.,151876.,151876.,151876.,151876.,
                73056.,	127711.,159470.,159470.,159470.,159470.,
        62403.,	76709.,	134097.,167443.,167443.,167443.,167443.,
32980., 65523.,	80544.,	140802.,175816.,175816.,175816.,175816.,
])
assert np.all(lhs[~np.isnan(lhs)] == rhs)
#Exhibit V Sheet 11
assert np.allclose(
    e5_s11_ip.incr_to_cum().iloc[:,:,:,-1].values.round(0).flatten(),
    np.array([39497433., 44743308., 35510981., 37766027., 42007442., 41113459., 32859080., 47109641.]),
    rtol = .001
)

Exhibit VI Analysis#

#loading data and assumptions
e6_tri = e2_tri[e2_tri.origin >= '2001'][e2_tri.development <= 96]
e6_disp_assumptions = {}
e6_disp_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}
e6_disp_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}
e6_disp_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}
e6_sev_assumptions = {}
e6_sev_assumptions['simple_3'] = {'n_periods':3}
e6_sev_assumptions['simple_2'] = {'n_periods':2}
e6_sev_assumptions['medial_5x1'] = {'n_periods':5,'drop_high':1, 'drop_low':1}

#calculating disposal rate and complete the closed count triangle
e6_rcc_ult = e2_rcc_cl.ultimate_[e2_rcc_cl.ultimate_.origin >= '2001']
e6_drs = average_dr(e6_tri['Closed Claim Counts'],e6_rcc_ult,e6_disp_assumptions)
e6_dr_selected = e6_drs['simple_2']
e6_dr_selected.disposal_rate_ = e6_dr_selected.disposal_rate_.round(3)
e6_dr_tri = e6_dr_selected.transform(e6_tri['Closed Claim Counts'],sample_weight = e6_rcc_ult)

#calculate and select incremental paid severity
e6_ipsev = e6_tri["Paid Claims"].cum_to_incr() / e6_tri["Closed Claim Counts"].cum_to_incr() * 1000
e6_ipsev_adj = e6_ipsev.trend(0.05) * xyz_tort_adjustment.fit(e6_ipsev).olf_.values
e6_sevs = average_sev(e6_ipsev_adj,e6_sev_assumptions)
e6_sevs_sel = e6_sevs['simple_2'].copy()
e6_sevs_sel.origin = ['2008']

#calculate and select tail paid severity
e6_incr_ccc = e6_tri["Closed Claim Counts"].cum_to_incr()
e6_incr_paid = e6_tri["Paid Claims"].cum_to_incr() * 1000
e6_incr_paid_trended = e6_ipsev_adj * e6_incr_ccc

e6_ipsev_full = e6_ipsev_adj.copy()
#extending the severity triangle to 108 months
e6_ipsev_full = cl.concat((e6_ipsev_full,e6_ipsev_full.latest_diagonal.rename("development",[9999])),axis=3)
#setting the before 72 months to selected incremental
e6_ipsev_full.iloc[:,:,:,:6] = e6_sevs_sel.iloc[:,:,:,:6]
#setting the after 86 months to selected tail
e6_ipsev_full.iloc[:,:,:,6:] = (
    e6_incr_paid_trended[e6_incr_paid_trended.development >= 84].sum().sum()
    /
    e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum() 
)
#setting the valuation_date to the future
e6_ipsev_full.valuation_date = pd.to_datetime(cl.options.ULT_VAL)
#trending back from latest accident year
e6_ipsev_full_detrended = e6_ipsev_full.trend(1/1.05-1,start='2008-12-31',end='2001-01-01') / xyz_tort_adjustment.fit(e6_ipsev).olf_.values
#compositing the actual incremental severities with dtrended selected
e6_ipsev_full_complete = e6_ipsev[e6_ipsev.valuation <= e6_ipsev.valuation_date] + e6_ipsev_full_detrended[e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date]

#complete the incremental paid triangle
e6_s7_ccc = e6_dr_tri.full_triangle_.cum_to_incr()
e6_s7_ip = e6_s7_ccc * e6_ipsev_full_complete / 1000
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)
/home/docs/checkouts/readthedocs.org/user_builds/chainladder-python/envs/experimental/lib/python3.11/site-packages/chainladder/utils/triangle_weight.py:367: UserWarning: Some exclusions have been ignored. At least 1 (use preserve = ...) link ratio(s) is required for development estimation.
  warnings.warn(warning)

Exhibit VI Sheet 1#

print('Part 1 - Disposal Rate Triangle')
nb_display(cl.DisposalRate().fit_transform(e6_tri['Closed Claim Counts'],sample_weight = e6_rcc_ult).disposal_rate_tri.round(3))

print('PART 2 - Average Disposal Rate Factors')
nb_display(combine_disposal(e6_drs).round(3).to_frame())

print('PART 3 - Selected Disposal Rate Factors')
nb_display(e6_dr_selected.disposal_rate_)
Part 1 - Disposal Rate Triangle
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult
2001 0.2090 0.4680 0.6430 0.7510 0.8420 0.9330 0.9840 0.9940
2002 0.1310 0.3910 0.5410 0.7010 0.8540 0.9420 0.9800
2003 0.1110 0.3770 0.5770 0.7750 0.9240 0.9620
2004 0.1040 0.3750 0.6370 0.8190 0.8970
2005 0.1230 0.4660 0.6930 0.8100
2006 0.1830 0.5400 0.7150
2007 0.2510 0.6040
2008 0.2360
PART 2 - Average Disposal Rate Factors
development 12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult
Total
simple_3 0.223 0.537 0.682 0.801 0.892 0.945 0.982 0.994 1.0
simple_2 0.244 0.572 0.704 0.814 0.910 0.952 0.982 0.994 1.0
medial_5x1 0.180 0.461 0.636 0.778 0.875 0.942 0.982 0.994 1.0
PART 3 - Selected Disposal Rate Factors
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult
(All) 0.2440 0.5720 0.7040 0.8140 0.9100 0.9520 0.9820 0.9940 1.0000

Exhibit VI Sheet 2#

print('Closed Claim Counts')
nb_display(e6_tri['Closed Claim Counts'])
print('Projected Incremental Closed Claim Counts')
nb_display(e6_dr_tri.full_triangle_.cum_to_incr().round(0))
Closed Claim Counts
12 24 36 48 60 72 84 96
2001 304 681 936 1,092 1,225 1,357 1,432 1,446
2002 203 607 841 1,089 1,327 1,464 1,523
2003 181 614 941 1,263 1,507 1,568
2004 235 848 1,442 1,852 2,029
2005 295 1,119 1,664 1,946
2006 307 906 1,201
2007 329 791
2008 276
Projected Incremental Closed Claim Counts
12 24 36 48 60 72 84 96 9999
2001 304.00 377.00 255.00 156.00 133.00 132.00 75.00 14.00 9.00
2002 203.00 404.00 234.00 248.00 238.00 137.00 59.00 21.00 10.00
2003 181.00 433.00 327.00 322.00 244.00 61.00 39.00 16.00 8.00
2004 235.00 613.00 594.00 410.00 177.00 109.00 78.00 31.00 16.00
2005 295.00 824.00 545.00 282.00 235.00 103.00 74.00 29.00 15.00
2006 307.00 599.00 295.00 177.00 155.00 68.00 48.00 19.00 10.00
2007 329.00 462.00 160.00 133.00 116.00 51.00 36.00 15.00 7.00
2008 276.00 388.00 156.00 130.00 114.00 50.00 36.00 14.00 7.00

Exhibit VI Sheet 3#

print('Paid Claims')
nb_display(e6_tri["Paid Claims"])
print('Incremental Paid CLaims')
nb_display(e6_tri["Paid Claims"].cum_to_incr())
print('Incremental Closed Claim Counts')
nb_display(e6_tri["Closed Claim Counts"].cum_to_incr())
print('Incremental Paid Severities')
nb_display(e6_ipsev)
Paid Claims
12 24 36 48 60 72 84 96
2001 1,539 5,952 12,319 18,609 24,387 31,090 37,070 38,520
2002 2,318 7,932 13,823 22,096 31,945 40,629 44,438
2003 1,743 6,240 12,683 22,893 34,505 39,320
2004 2,221 9,898 25,950 43,439 52,811
2005 3,043 12,219 27,073 40,026
2006 3,531 11,778 22,819
2007 3,529 11,865
2008 3,409
Incremental Paid CLaims
12 24 36 48 60 72 84 96
2001 1,539 4,413 6,367 6,289 5,778 6,703 5,980 1,450
2002 2,318 5,614 5,891 8,273 9,850 8,683 3,809
2003 1,743 4,497 6,443 10,209 11,612 4,815
2004 2,221 7,677 16,052 17,489 9,372
2005 3,043 9,176 14,854 12,953
2006 3,531 8,247 11,041
2007 3,529 8,336
2008 3,409
Incremental Closed Claim Counts
12 24 36 48 60 72 84 96
2001 304.00 377.00 255.00 156.00 133.00 132.00 75.00 14.00
2002 203.00 404.00 234.00 248.00 238.00 137.00 59.00
2003 181.00 433.00 327.00 322.00 244.00 61.00
2004 235.00 613.00 594.00 410.00 177.00
2005 295.00 824.00 545.00 282.00
2006 307.00 599.00 295.00
2007 329.00 462.00
2008 276.00
Incremental Paid Severities
12 24 36 48 60 72 84 96
2001 5,064 11,705 24,969 40,317 43,445 50,781 79,730 103,551
2002 11,417 13,896 25,175 33,359 41,386 63,382 64,556
2003 9,631 10,386 19,703 31,706 47,592 78,942
2004 9,452 12,524 27,023 42,657 52,947
2005 10,315 11,136 27,255 45,934
2006 11,502 13,768 37,427
2007 10,726 18,043
2008 12,351

Exhibit VI Sheet 4#

print('Incremental Paid Severities')
nb_display(e6_ipsev)
print('Trended Incremental Paid Severities Assuming 5% Annual Trend and Adjusted for Tort Reform')
nb_display(e6_ipsev_adj)
print('Average Trended Incremental Paid Severities')
nb_display(combine_tri(e6_sevs).round(0).to_frame())
print('Selected Incremental Paid Severities')
nb_display(e6_sevs_sel[e6_sevs_sel.development<=72])
Incremental Paid Severities
12 24 36 48 60 72 84 96
2001 5,064 11,705 24,969 40,317 43,445 50,781 79,730 103,551
2002 11,417 13,896 25,175 33,359 41,386 63,382 64,556
2003 9,631 10,386 19,703 31,706 47,592 78,942
2004 9,452 12,524 27,023 42,657 52,947
2005 10,315 11,136 27,255 45,934
2006 11,502 13,768 37,427
2007 10,726 18,043
2008 12,351
Trended Incremental Paid Severities Assuming 5% Annual Trend and Adjusted for Tort Reform
12 24 36 48 60 72 84 96
2001 4,774 11,035 23,540 38,009 40,958 47,874 75,166 97,623
2002 10,251 12,477 22,604 29,952 37,159 56,908 57,963
2003 8,236 8,881 16,848 27,112 40,696 67,504
2004 7,698 10,199 22,007 34,739 43,119
2005 8,000 8,637 21,139 35,627
2006 9,511 11,384 30,947
2007 11,262 18,945
2008 12,351
Average Trended Incremental Paid Severities
development 12 24 36 48 60 72 84 96
Total
simple_3 11041.0 12989.0 24698.0 32493.0 40325.0 57429.0 66564.0 97623.0
simple_2 11807.0 15165.0 26043.0 35183.0 41908.0 62206.0 66564.0 97623.0
medial_5x1 9591.0 10155.0 21917.0 33439.0 40827.0 56908.0 66564.0 97623.0
Selected Incremental Paid Severities
12 24 36 48 60 72
2008 11,807 15,165 26,043 35,183 41,908 62,206

Exhibit VI Sheet 5#

print('Incremental Closed Claim Counts')
nb_display(e6_incr_ccc[e6_incr_ccc.development >= 72][e6_incr_ccc.origin <= '2003'])
print('Incremental Paid CLaims')
nb_display(e6_incr_paid[e6_incr_paid.development >= 72][e6_incr_paid.origin <= '2003'])
print('Trended Incremental Paid Severities')
nb_display(e6_incr_paid_trended[e6_incr_paid_trended.development >= 72][e6_incr_paid_trended.origin <= '2003'])
e6_s5 = pd.DataFrame(
    data = [[
        e6_incr_ccc[e6_incr_ccc.development >= 72].sum().sum(), 
        e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum()
    ]],
    index = ['Total Closed Claim Counts'],
    columns = ['Age 72 & Older','Age 84 & Older']
)
e6_s5.loc['Total Trended Paid Claims'] = [
        e6_incr_paid_trended[e6_incr_paid_trended.development >= 72].sum().sum(), 
        e6_incr_paid_trended[e6_incr_paid_trended.development >= 84].sum().sum()
    ]
e6_s5.loc['Estimated Trended Tail Severity'] = e6_s5.iloc[1] / e6_s5.iloc[0]
e6_s5.loc['Estimated Incremental Trended Tail Severity'] = e6_sevs_sel.iloc[:,:,:,5:7].values.flatten()
e6_s5.loc['Selected Trended Paid Severity'] = e6_ipsev_full.values[0,0,-1,5:7]
nb_display(e6_s5)
Incremental Closed Claim Counts
72 84 96
2001 132.00 75.00 14.00
2002 137.00 59.00
2003 61.00
Incremental Paid CLaims
72 84 96
2001 6,703,092 5,979,750 1,449,714
2002 8,683,334 3,808,804
2003 4,815,462
Trended Incremental Paid Severities
72 84 96
2001 6,319,389 5,637,453 1,366,728
2002 7,796,454 3,419,788
2003 4,117,743
Age 72 & Older Age 84 & Older
Total Closed Claim Counts 4.780000e+02 1.480000e+02
Total Trended Paid Claims 2.865756e+07 1.042397e+07
Estimated Trended Tail Severity 5.995304e+04 7.043223e+04
Estimated Incremental Trended Tail Severity 6.220620e+04 6.656428e+04
Selected Trended Paid Severity 6.220620e+04 7.043223e+04

Exhibit VI Sheet 6#

print('Incremental Paid Severities')
nb_display(e6_ipsev)
print('Selected Incremental Paid Severities')
nb_display(e6_ipsev_full[e6_ipsev_full.origin == '2008'])
print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment')
nb_display(e6_ipsev_full_complete)
Incremental Paid Severities
12 24 36 48 60 72 84 96
2001 5,064 11,705 24,969 40,317 43,445 50,781 79,730 103,551
2002 11,417 13,896 25,175 33,359 41,386 63,382 64,556
2003 9,631 10,386 19,703 31,706 47,592 78,942
2004 9,452 12,524 27,023 42,657 52,947
2005 10,315 11,136 27,255 45,934
2006 11,502 13,768 37,427
2007 10,726 18,043
2008 12,351
Selected Incremental Paid Severities
12 24 36 48 60 72 84 96 9999
2008 11,807 15,165 26,043 35,183 41,908 62,206 70,432 70,432 70,432
Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment
12 24 36 48 60 72 84 96 9999
2001 5,064 11,705 24,969 40,317 43,445 50,781 79,730 103,551 74,709
2002 11,417 13,896 25,175 33,359 41,386 63,382 64,556 78,444 78,444
2003 9,631 10,386 19,703 31,706 47,592 78,942 82,366 82,366 82,366
2004 9,452 12,524 27,023 42,657 52,947 76,384 86,485 86,485 86,485
2005 10,315 11,136 27,255 45,934 54,032 80,203 90,809 90,809 90,809
2006 11,502 13,768 37,427 42,549 50,682 75,230 85,179 85,179 85,179
2007 10,726 18,043 24,803 33,508 39,912 59,244 67,078 67,078 67,078
2008 12,351 15,165 26,043 35,183 41,908 62,206 70,432 70,432 70,432

Exhibit VI Sheet 7#

print('Projected Incremental Closed Claim Counts')
nb_display(e6_s7_ccc.round(0))
print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment')
nb_display(e6_ipsev_full_complete)
print('Projected Incremental Paid Claims')
nb_display(e6_s7_ip)
print('Projected Cumulative Paid Claims')
nb_display(e6_s7_ip.incr_to_cum())
Projected Incremental Closed Claim Counts
12 24 36 48 60 72 84 96 9999
2001 304.00 377.00 255.00 156.00 133.00 132.00 75.00 14.00 9.00
2002 203.00 404.00 234.00 248.00 238.00 137.00 59.00 21.00 10.00
2003 181.00 433.00 327.00 322.00 244.00 61.00 39.00 16.00 8.00
2004 235.00 613.00 594.00 410.00 177.00 109.00 78.00 31.00 16.00
2005 295.00 824.00 545.00 282.00 235.00 103.00 74.00 29.00 15.00
2006 307.00 599.00 295.00 177.00 155.00 68.00 48.00 19.00 10.00
2007 329.00 462.00 160.00 133.00 116.00 51.00 36.00 15.00 7.00
2008 276.00 388.00 156.00 130.00 114.00 50.00 36.00 14.00 7.00
Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment
12 24 36 48 60 72 84 96 9999
2001 5,064 11,705 24,969 40,317 43,445 50,781 79,730 103,551 74,709
2002 11,417 13,896 25,175 33,359 41,386 63,382 64,556 78,444 78,444
2003 9,631 10,386 19,703 31,706 47,592 78,942 82,366 82,366 82,366
2004 9,452 12,524 27,023 42,657 52,947 76,384 86,485 86,485 86,485
2005 10,315 11,136 27,255 45,934 54,032 80,203 90,809 90,809 90,809
2006 11,502 13,768 37,427 42,549 50,682 75,230 85,179 85,179 85,179
2007 10,726 18,043 24,803 33,508 39,912 59,244 67,078 67,078 67,078
2008 12,351 15,165 26,043 35,183 41,908 62,206 70,432 70,432 70,432
Projected Incremental Paid Claims
12 24 36 48 60 72 84 96 9999
2001 1,539 4,413 6,367 6,289 5,778 6,703 5,980 1,450 672
2002 2,318 5,614 5,891 8,273 9,850 8,683 3,809 1,621 811
2003 1,743 4,497 6,443 10,209 11,612 4,815 3,224 1,290 645
2004 2,221 7,677 16,052 17,489 9,372 8,324 6,732 2,693 1,346
2005 3,043 9,176 14,854 12,953 12,716 8,258 6,678 2,671 1,336
2006 3,531 8,247 11,041 7,552 7,851 5,098 4,123 1,649 825
2007 3,529 8,336 3,960 4,458 4,634 3,010 2,434 974 487
2008 3,409 5,890 4,071 4,583 4,764 3,094 2,502 1,001 500
Projected Cumulative Paid Claims
12 24 36 48 60 72 84 96 9999
2001 1,539 5,952 12,319 18,609 24,387 31,090 37,070 38,520 39,192
2002 2,318 7,932 13,823 22,096 31,945 40,629 44,438 46,059 46,869
2003 1,743 6,240 12,683 22,893 34,505 39,320 42,545 43,834 44,479
2004 2,221 9,898 25,950 43,439 52,811 61,135 67,867 70,560 71,906
2005 3,043 12,219 27,073 40,026 52,742 61,000 67,678 70,350 71,685
2006 3,531 11,778 22,819 30,371 38,222 43,321 47,444 49,093 49,918
2007 3,529 11,865 15,825 20,283 24,917 27,926 30,360 31,334 31,821
2008 3,409 9,299 13,370 17,954 22,718 25,812 28,314 29,315 29,816

Exhibit VI Sheet 8#

summary_exh(
    e6_tri['Reported Claims'],
    e6_tri['Paid Claims'],
    e6_s7_ip.incr_to_cum()
)
Reported Paid Ult Claims Case Outstanding IBNR Unpaid
2001-01-01 38798.0 38520.0 39192.0 278.0 394.0 672.0
2002-01-01 48169.0 44438.0 46869.0 3731.0 -1300.0 2431.0
2003-01-01 44373.0 39320.0 44479.0 5053.0 106.0 5159.0
2004-01-01 70288.0 52811.0 71906.0 17477.0 1618.0 19095.0
2005-01-01 70655.0 40026.0 71685.0 30629.0 1030.0 31659.0
2006-01-01 48804.0 22819.0 49918.0 25985.0 1114.0 27099.0
2007-01-01 31732.0 11865.0 31821.0 19867.0 89.0 19956.0
2008-01-01 18632.0 3409.0 29816.0 15223.0 11184.0 26407.0
#Exhibit VI Sheet 1
assert np.all(e6_dr_selected.disposal_rate_.values == np.array([0.244, 0.572, 0.704, 0.814, 0.910, 0.952, 0.982, 0.994, 1.000]))
#Exhibit VI Sheet 2
lhs = (e6_dr_tri.full_triangle_.cum_to_incr()-e6_tri['Closed Claim Counts'].cum_to_incr()).values.flatten()
rhs = np.array([
                                                        9.,  
                                                21.,    10.,  
                                        39.,    16.,    8.,  
                                109.,   78.,    31.,    16.,  
                        235.,   103.,   74.,    29.,    15.,  
                177.,   155.,   68.,    48.,    19.,    10.,  
        160.,   133.,   116.,   51.,    36.,    15.,    7.,  
389.,   156.,   130.,   114.,   50.,    36.,    14.,    7.
])
assert np.all(abs(lhs[~np.isnan(lhs)] - rhs) < 1)
#Exhibit VI Sheet 4
assert np.all(e6_sevs_sel.values[...,:6].round(0) == np.array([11807.,15165.,26043.,35183.,41908.,62206.]))
#Exhibit VI Sheet 5
assert np.all(e6_s5.iloc[4].values.round(0) == np.array([62206.,70432.]))
#Exhibit VI Sheet 6
lhs = e6_ipsev_full_detrended[e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date].values.flatten()
rhs = np.array([
                                                        74709., 
                                                78444., 78444., 
                                        82367., 82367., 82367., 
                                76384., 86485., 86485., 86485., 
                        54032., 80203., 90809., 90809., 90809., 
                42549., 50682., 75230., 85179., 85179., 85179., 
        24803., 33508., 39912., 59244., 67079., 67079., 67079., 
15165., 26043., 35183., 41908., 62206., 70432., 70432., 70432., 
])
assert np.allclose(lhs[~np.isnan(lhs)], rhs, atol=1)
#Exhibit VI Sheet 7
assert np.allclose(
    e6_s7_ip.incr_to_cum().iloc[:,:,:,-1].values.round(0).flatten(),
    np.array([39192.,46869.,44479.,71906.,71684.,49913.,31805.,29828.]),
    rtol = .001
)