Chapter 7 — Part II#

This second part concludes Chapter 7 of Friedland by generating Exhibits III and IV.

import chainladder as cl
from IPython.display import display, HTML
import numpy as np
import pandas as pd
import warnings

pd.set_option("display.max_columns", None)
pd.set_option("display.width", 1000)

warnings.filterwarnings(
    "ignore",
    message=r"Some exclusions have been ignored\..*link ratio\(s\) is required.*",
    category=UserWarning,
)

Exhibit III — U.S. Industry P.P. Auto (Impact of Changing Conditions)#

In order to illustrate the assumptions and corresponding limitations of the Development method, Exhibit III applies the method to the U.S. Private Passenger Industry Auto (USPP Auto) example under four different scenarios:

  1. stable claim ratios and no change in case outstanding strength (steady state);

  2. increasing claim ratios but no change in case outstanding strength;

  3. stable claim ratios but increasing case outstanding strength; and

  4. increasing claim ratios and increasing case outstanding strength.

The key assumptions of this study are as follows:

  • the earned premium for the first year (1999) is assumed to be $1 million with a 5% annual premium trend;

  • (discuss assumed claim ratios)

Since the USPP Auto dataset is contained in the chainladder package, we simply load it into a triangle as follows:

triangles = cl.load_sample("friedland_uspp")

For the sheets depicting the claim triangles and age-to-age factors, we reuse the function dev_exhibit used in Part I:

def dev_exhibit(
    tri: cl.Triangle, avg_params: dict[str, int], selected_avg: str, tail: float
) -> dict[cl.Triangle()]:
    display("")
    display(
        HTML("""
    <h2 style='text-align:left;'>
    PART 1 - Data Triangle
    </h2>
    """)
    )
    display(tri)
    display(
        HTML("""
    <h2 style='text-align:left;'>
    PART 2 - Age-to-Age Factors
    </h2>
    """)
    )
    age_to_age_df = tri.age_to_age.to_frame(origin_as_datetime=False)
    display(age_to_age_df.style.format(precision=3, na_rep=""))
    devs = {}
    display(
        HTML("""
    <h2 style='text-align:left;'>
    PART 3 - Average Age-to-Age Factor
    </h2>
    """)
    )
    for k, v in avg_params.items():
        devs[k] = cl.Development(**v).fit_transform(tri)

    def print_ldfs(ldf_dict: dict[cl.Triangle()]):
        with pd.option_context("display.float_format", "{:.3f}".format):
            display(
                pd.concat([
                    v.to_frame().rename(index={"(All)": k}) for k, v in ldf_dict.items()
                ])
            )
        return None

    print_ldfs({k: v.ldf_.round(decimals=3) for k, v in devs.items()})
    devs["Selected"] = cl.TailConstant(tail=tail, projection_period=0).fit_transform(
        devs[selected_avg]
    )
    selected = {}
    selected["CDF to Ultimate"] = (
        devs["Selected"].ldf_.round(decimals=3).incr_to_cum().round(decimals=3)
    )
    selected["Percent Reported"] = (1 / selected["CDF to Ultimate"]).round(decimals=3)
    display(
        HTML("""
    <h2 style='text-align:left;'>
    PART 4 - Selected Age-to-Age Factor
    </h2>
    """)
    )
    print_ldfs({"Selected": devs["Selected"].ldf_.round(decimals=3)})
    print_ldfs(selected)
    return devs

We also define a special formatting function ex_sht1 in order to create Sheet 1 of Exhibit III, which has its own unique layout:

def ex3sht1(tri: cl.Triangle, dev_input: dict, tail_input: dict) -> tuple:

    def format_col(x, reverse=False):
        if reverse is True:
            return x.to_frame().squeeze()[::-1].reset_index(drop=True)
        return x.to_frame().squeeze().reset_index(drop=True)

    def col_diff(x, y):
        return x.squeeze() - y.squeeze()

    def col_div(x, y):
        return x.squeeze() / y.squeeze()

    def col_mult(x, y):
        return x.squeeze() * y.squeeze()

    # scenario = "Steady State"

    dev = cl.Development(**dev_input).fit(tri)
    model = cl.Chainladder().fit(dev.transform(tri))
    ult = model.ultimate_

    col1 = pd.DataFrame({"Accident Year": format_col(tri.origin.astype(str))})
    col2 = pd.DataFrame({
        "Earned Premium": format_col(
            tri.latest_diagonal.loc["Steady State", "Earned Premium"]
        )
    })
    col4 = pd.DataFrame({
        "Ult. Claims": format_col(ult.loc["Steady State", "Reported Claims"])
    })
    col3 = pd.DataFrame({"Ult. Claim Ratio": col_div(col4, col2)})
    col5 = pd.DataFrame({
        "Rep. Claims 12/31/08": format_col(
            tri.latest_diagonal.loc["Steady State", "Reported Claims"]
        )
    })
    col6 = pd.DataFrame({"Actual IBNR": col_diff(col4, col5)})
    col8 = pd.DataFrame({
        "Ult. Claims": format_col(ult.loc["Increasing Claim", "Reported Claims"])
    })
    col9 = pd.DataFrame({
        "Rep. Claims 12/31/08": format_col(
            tri.latest_diagonal.loc["Increasing Claim", "Reported Claims"]
        )
    })
    col7 = pd.DataFrame({"Ult. Claim Ratio": col_div(col8, col2)})
    col10 = pd.DataFrame({"Actual IBNR": col_diff(col8, col9)})
    col11 = pd.DataFrame({"Accident Year": format_col(tri.origin.astype(str))})
    col12 = pd.DataFrame({
        "Earned Premium": format_col(
            tri.latest_diagonal.loc["Steady State", "Earned Premium"]
        )
    })
    col14 = pd.DataFrame({
        "Ult. Claims": format_col(ult.loc["Steady State", "Reported Claims"])
    })
    col13 = pd.DataFrame({"Ult. Claim Ratio": col_div(col14, col12)})
    col15 = pd.DataFrame({
        "Rep. Claims 12/31/08": format_col(
            tri.latest_diagonal.loc["Increasing Case", "Reported Claims"]
        )
    })
    col16 = pd.DataFrame({"Actual IBNR": col_diff(col14, col15)})
    col18 = pd.DataFrame({
        "Ult. Claims": format_col(ult.loc["Increasing Claim", "Reported Claims"])
    })
    col17 = pd.DataFrame({"Ult. Claim Ratio": col_div(col18, col12)})
    col19 = pd.DataFrame({
        "Rep. Claims 12/31/08": format_col(
            tri.latest_diagonal.loc["Increasing Claim Case", "Reported Claims"]
        )
    })
    col20 = pd.DataFrame({"Actual IBNR": col_diff(col18, col19)})

    df1 = pd.concat([col1, col2], axis=1)
    df2 = pd.concat([col3, col4, col5, col6], axis=1)
    df3 = pd.concat([col7, col8, col9, col10], axis=1)
    df4 = pd.concat([col11, col12], axis=1)
    df5 = pd.concat([col13, col14, col15, col16], axis=1)
    df6 = pd.concat([col17, col18, col19, col20], axis=1)

    dfs_upper = [df1, df2, df3]
    dfs_lower = [df4, df5, df6]

    results_upper = pd.concat(
        dfs_upper, axis=1, keys=["", "Steady State", "Increasing Claim Ratios"]
    )
    results_upper = results_upper.set_index((
        "",
        "Accident Year",
    ))  # since its multi-index, I needed to use the tuple to designate the index col
    results_upper.index.name = "Accident Year"
    results_upper.loc["Total"] = results_upper.sum()

    # remove the totals from the ratio columns (where totals do not make sense)
    results_upper.loc["Total", ("Steady State", "Ult. Claim Ratio")] = np.nan
    results_upper.loc["Total", ("Increasing Claim Ratios", "Ult. Claim Ratio")] = np.nan

    results_lower = pd.concat(
        dfs_lower,
        axis=1,
        keys=[
            "",
            "Increasing Case Outstanding Strength",
            "Increasing Claim Ratios and Case Outstanding Strength",
        ],
    )
    results_lower = results_lower.set_index((
        "",
        "Accident Year",
    ))  # since its multi-index, I needed to use the tuple to designate the index col
    results_lower.index.name = "Accident Year"
    results_lower.loc["Total"] = results_lower.sum()

    # remove the totals from the ratio columns (where totals do not make sense)
    results_lower.loc[
        "Total", ("Increasing Case Outstanding Strength", "Ult. Claim Ratio")
    ] = np.nan
    results_lower.loc[
        "Total",
        ("Increasing Claim Ratios and Case Outstanding Strength", "Ult. Claim Ratio"),
    ] = np.nan

    display(
        HTML("""
    <h2 style='text-align:center;'>
    Exhibit III Sheet 1: Summary of Earned Premium and Claim Ratio Assumptions and Actual IBNR
    </h2>
    """)
    )

    upper_formats = {
        ("Steady State", "Ult. Claim Ratio"): "{:.1%}",
        ("Steady State", "Ult. Claims"): "{:,.0f}",
        ("Steady State", "Rep. Claims 12/31/08"): "{:,.0f}",
        ("Steady State", "Actual IBNR"): "{:,.0f}",
        ("Increasing Claim Ratios", "Ult. Claim Ratio"): "{:.1%}",
        ("Increasing Claim Ratios", "Ult. Claims"): "{:,.0f}",
        ("Increasing Claim Ratios", "Rep. Claims 12/31/08"): "{:,.0f}",
        ("Increasing Claim Ratios", "Actual IBNR"): "{:,.0f}",
        ("", "Earned Premium"): "{:,.0f}",
    }

    display(results_upper.style.format(upper_formats, na_rep=""))

    lower_formats = {
        ("Increasing Case Outstanding Strength", "Ult. Claim Ratio"): "{:.1%}",
        ("Increasing Case Outstanding Strength", "Ult. Claims"): "{:,.0f}",
        ("Increasing Case Outstanding Strength", "Rep. Claims 12/31/08"): "{:,.0f}",
        ("Increasing Case Outstanding Strength", "Actual IBNR"): "{:,.0f}",
        (
            "Increasing Claim Ratios and Case Outstanding Strength",
            "Ult. Claim Ratio",
        ): "{:.1%}",
        (
            "Increasing Claim Ratios and Case Outstanding Strength",
            "Ult. Claims",
        ): "{:,.0f}",
        (
            "Increasing Claim Ratios and Case Outstanding Strength",
            "Rep. Claims 12/31/08",
        ): "{:,.0f}",
        (
            "Increasing Claim Ratios and Case Outstanding Strength",
            "Actual IBNR",
        ): "{:,.0f}",
        ("", "Earned Premium"): "{:,.0f}",
    }

    display(results_lower.style.format(lower_formats, na_rep=""))

    return (results_upper, results_lower)

Exhibit III Sheet 1: Summary of Earned Premium and Claim Ratio Assumptions and Actual IBNR#

We begin by generating Exhibit III, Sheet 1:

dev_input = {"average": "volume", "n_periods": 5}

tail_input = {"tail": 1.0, "projection_period": -1}

res_up, res_low = ex3sht1(
    triangles,
    dev_input,
    tail_input,
)

Exhibit III Sheet 1: Summary of Earned Premium and Claim Ratio Assumptions and Actual IBNR

  Steady State Increasing Claim Ratios
  Earned Premium Ult. Claim Ratio Ult. Claims Rep. Claims 12/31/08 Actual IBNR Ult. Claim Ratio Ult. Claims Rep. Claims 12/31/08 Actual IBNR
Accident Year                  
1999 1,000,000 70.0% 700,000 700,000 0 70.0% 700,000 700,000 0
2000 1,050,000 70.0% 735,000 735,000 0 70.0% 735,000 735,000 0
2001 1,102,500 70.0% 771,750 771,750 0 70.0% 771,750 771,750 0
2002 1,157,625 70.0% 810,338 810,338 0 70.0% 810,338 810,338 0
2003 1,215,506 70.0% 850,855 842,346 8,509 70.0% 850,855 842,346 8,509
2004 1,276,282 70.0% 893,397 884,463 8,934 80.0% 1,021,025 1,010,815 10,210
2005 1,340,096 70.0% 938,068 919,306 18,762 85.0% 1,139,082 1,116,300 22,782
2006 1,407,100 70.0% 984,970 935,722 49,248 90.0% 1,266,391 1,203,071 63,320
2007 1,477,455 70.0% 1,034,219 930,797 103,422 95.0% 1,403,582 1,263,224 140,358
2008 1,551,328 70.0% 1,085,930 836,166 249,764 100.0% 1,551,328 1,194,523 356,805
Total 12,577,893 8,804,527 8,365,888 438,639 10,249,351 9,647,367 601,984
  Increasing Case Outstanding Strength Increasing Claim Ratios and Case Outstanding Strength
  Earned Premium Ult. Claim Ratio Ult. Claims Rep. Claims 12/31/08 Actual IBNR Ult. Claim Ratio Ult. Claims Rep. Claims 12/31/08 Actual IBNR
Accident Year                  
1999 1,000,000 70.0% 700,000 700,000 0 70.0% 700,000 700,000 0
2000 1,050,000 70.0% 735,000 735,000 0 70.0% 735,000 735,000 0
2001 1,102,500 70.0% 771,750 771,750 0 70.0% 771,750 771,750 0
2002 1,157,625 70.0% 810,338 810,338 0 70.0% 810,338 810,338 0
2003 1,215,506 70.0% 850,855 842,346 8,509 70.0% 850,855 842,346 8,509
2004 1,276,282 70.0% 893,397 884,463 8,934 80.0% 1,021,025 1,010,815 10,210
2005 1,340,096 70.0% 938,068 933,377 4,691 85.0% 1,139,082 1,133,386 5,696
2006 1,407,100 70.0% 984,970 962,808 22,162 90.0% 1,266,391 1,237,897 28,494
2007 1,477,455 70.0% 1,034,219 979,922 54,297 95.0% 1,403,582 1,329,895 73,687
2008 1,551,328 70.0% 1,085,930 931,185 154,745 100.0% 1,551,328 1,330,264 221,064
Total 12,577,893 8,804,527 8,551,189 253,338 10,249,351 9,901,691 347,660

Exhibit III, Sheet 2 - Sheet 2: USPP Auto Steady-State - Reported Claims#

We then generate Sheet 2, Parts 1-4 which contain the data triangle and age-to-age factors for USPP reported claims under the steady-state scenario (p. 115).

devs = dev_exhibit(
    triangles.loc["Steady State", "Reported Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 539,000 630,000 665,000 686,000 693,000 693,000 700,000 700,000 700,000 700,000
2000 565,950 661,500 698,250 720,300 727,650 727,650 735,000 735,000 735,000
2001 594,248 694,575 733,163 756,315 764,033 764,033 771,750 771,750
2002 623,960 729,304 769,821 794,131 802,234 802,234 810,338
2003 655,158 765,769 808,312 833,837 842,346 842,346
2004 687,916 804,057 848,727 875,529 884,463
2005 722,312 844,260 891,164 919,306
2006 758,427 886,473 935,722
2007 796,348 930,797
2008 836,166

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000 1.000
2000 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000
2001 1.169 1.056 1.032 1.010 1.000 1.010 1.000
2002 1.169 1.056 1.032 1.010 1.000 1.010
2003 1.169 1.056 1.032 1.010 1.000
2004 1.169 1.056 1.032 1.010
2005 1.169 1.056 1.032
2006 1.169 1.056
2007 1.169

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000 1.000

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000 1.000 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 1.300 1.112 1.053 1.020 1.010 1.010 1.000 1.000 1.000 1.000
Percent Reported 0.769 0.899 0.950 0.980 0.990 0.990 1.000 1.000 1.000 1.000

Exhibit III, Sheet 3: USPP Auto Steady-State - Paid Claims#

We then generate Sheet 3, Parts 1-4 which contain the data triangle and age-to-age factors for USPP paid claims under the steady-state scenario (p. 116).

devs = dev_exhibit(
    triangles.loc["Steady State", "Paid Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 294,000 497,000 588,000 644,000 672,000 686,000 693,000 693,000 700,000 700,000
2000 308,700 521,850 617,400 676,200 705,600 720,300 727,650 727,650 735,000
2001 324,135 547,943 648,270 710,010 740,880 756,315 764,033 764,033
2002 340,342 575,340 680,684 745,511 777,924 794,131 802,234
2003 357,359 604,107 714,718 782,786 816,820 833,837
2004 375,227 634,312 750,454 821,925 857,661
2005 393,988 666,028 787,976 863,022
2006 413,688 699,329 827,375
2007 434,372 734,295
2008 456,090

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000
2000 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010
2001 1.690 1.183 1.095 1.043 1.021 1.010 1.000
2002 1.690 1.183 1.095 1.043 1.021 1.010
2003 1.690 1.183 1.095 1.043 1.021
2004 1.690 1.183 1.095 1.043
2005 1.690 1.183 1.095
2006 1.690 1.183
2007 1.690

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 2.378 1.407 1.190 1.086 1.042 1.020 1.010 1.010 1.000 1.000
Percent Reported 0.421 0.711 0.840 0.921 0.960 0.980 0.990 0.990 1.000 1.000

Exhibit III, Sheet 4: USPP Auto Increasing Claim Ratios - Reported Claims#

We then generate Sheet 4, Parts 1-4 which contain the data triangle and age-to-age factors for USPP reported claims under the increasing claim ratio scenario (p. 117).

devs = dev_exhibit(
    triangles.loc["Increasing Claim", "Reported Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 539,000 630,000 665,000 686,000 693,000 693,000 700,000 700,000 700,000 700,000
2000 565,950 661,500 698,250 720,300 727,650 727,650 735,000 735,000 735,000
2001 594,248 694,575 733,163 756,315 764,033 764,033 771,750 771,750
2002 623,960 729,304 769,821 794,131 802,234 802,234 810,338
2003 655,158 765,769 808,312 833,837 842,346 842,346
2004 786,189 918,923 969,974 1,000,605 1,010,815
2005 877,093 1,025,173 1,082,127 1,116,300
2006 975,121 1,139,751 1,203,071
2007 1,080,759 1,263,224
2008 1,194,523

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000 1.000
2000 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000
2001 1.169 1.056 1.032 1.010 1.000 1.010 1.000
2002 1.169 1.056 1.032 1.010 1.000 1.010
2003 1.169 1.056 1.032 1.010 1.000
2004 1.169 1.056 1.032 1.010
2005 1.169 1.056 1.032
2006 1.169 1.056
2007 1.169

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000 1.000

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000 1.000 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 1.300 1.112 1.053 1.020 1.010 1.010 1.000 1.000 1.000 1.000
Percent Reported 0.769 0.899 0.950 0.980 0.990 0.990 1.000 1.000 1.000 1.000

Exhibit III, Sheet 5: USPP Auto Increasing Claim Ratios - Paid Claims#

We then generate Sheet 5, Parts 1-4 which contain the data triangle and age-to-age factors for USPP paid claims under the increasing claim ratio scenario (p. 118).

devs = dev_exhibit(
    triangles.loc["Increasing Claim", "Paid Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 294,000 497,000 588,000 644,000 672,000 686,000 693,000 693,000 700,000 700,000
2000 308,700 521,850 617,400 676,200 705,600 720,300 727,650 727,650 735,000
2001 324,135 547,943 648,270 710,010 740,880 756,315 764,033 764,033
2002 340,342 575,340 680,684 745,511 777,924 794,131 802,234
2003 357,359 604,107 714,718 782,786 816,820 833,837
2004 428,831 724,928 857,661 939,343 980,184
2005 478,414 808,748 956,828 1,047,955
2006 531,884 899,137 1,063,768
2007 589,505 996,544
2008 651,558

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000
2000 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010
2001 1.690 1.183 1.095 1.043 1.021 1.010 1.000
2002 1.690 1.183 1.095 1.043 1.021 1.010
2003 1.690 1.183 1.095 1.043 1.021
2004 1.690 1.183 1.095 1.043
2005 1.690 1.183 1.095
2006 1.690 1.183
2007 1.690

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 2.378 1.407 1.190 1.086 1.042 1.020 1.010 1.010 1.000 1.000
Percent Reported 0.421 0.711 0.840 0.921 0.960 0.980 0.990 0.990 1.000 1.000

Exhibit III, Sheet 6: USPP Auto Increasing Case Outstanding Strength - Reported Claims#

We then generate Sheet 6, Parts 1-4 which contain the data triangle and age-to-age factors for USPP reported claims under the increasing case outstanding strength scenario (p. 119).

devs = dev_exhibit(
    triangles.loc["Increasing Case", "Reported Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 539,000 630,000 665,000 686,000 693,000 693,000 700,000 700,000 700,000 700,000
2000 565,950 661,500 698,250 720,300 727,650 727,650 735,000 735,000 735,000
2001 594,248 694,575 733,163 756,315 764,033 764,033 771,750 771,750
2002 623,960 729,304 769,821 794,131 802,234 802,234 810,338
2003 655,158 765,769 808,312 833,837 842,346 842,346
2004 687,916 804,057 848,727 878,745 884,463
2005 722,312 844,260 897,355 933,377
2006 758,427 897,702 962,808
2007 818,067 979,922
2008 931,185

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000 1.000
2000 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000
2001 1.169 1.056 1.032 1.010 1.000 1.010 1.000
2002 1.169 1.056 1.032 1.010 1.000 1.010
2003 1.169 1.056 1.032 1.010 1.000
2004 1.169 1.056 1.035 1.007
2005 1.169 1.063 1.040
2006 1.184 1.073
2007 1.198

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.178 1.061 1.034 1.009 1.000 1.010 1.000 1.000 1.000

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.178 1.061 1.034 1.009 1.000 1.010 1.000 1.000 1.000 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 1.317 1.118 1.054 1.019 1.010 1.010 1.000 1.000 1.000 1.000
Percent Reported 0.759 0.894 0.949 0.981 0.990 0.990 1.000 1.000 1.000 1.000

Exhibit III, Sheet 7: USPP Auto Increasing Case Outstanding Strength - Paid Claims#

We then generate Sheet 7, Parts 1-4 which contain the data triangle and age-to-age factors for USPP paid claims under the increasing case outstanding strength scenario (p. 120).

devs = dev_exhibit(
    triangles.loc["Increasing Case", "Paid Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 294,000 497,000 588,000 644,000 672,000 686,000 693,000 693,000 700,000 700,000
2000 308,700 521,850 617,400 676,200 705,600 720,300 727,650 727,650 735,000
2001 324,135 547,943 648,270 710,010 740,880 756,315 764,033 764,033
2002 340,342 575,340 680,684 745,511 777,924 794,131 802,234
2003 357,359 604,107 714,718 782,786 816,820 833,837
2004 375,227 634,312 750,454 821,925 857,661
2005 393,988 666,028 787,976 863,022
2006 413,688 699,329 827,375
2007 434,372 734,295
2008 456,090

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000
2000 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010
2001 1.690 1.183 1.095 1.043 1.021 1.010 1.000
2002 1.690 1.183 1.095 1.043 1.021 1.010
2003 1.690 1.183 1.095 1.043 1.021
2004 1.690 1.183 1.095 1.043
2005 1.690 1.183 1.095
2006 1.690 1.183
2007 1.690

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 2.378 1.407 1.190 1.086 1.042 1.020 1.010 1.010 1.000 1.000
Percent Reported 0.421 0.711 0.840 0.921 0.960 0.980 0.990 0.990 1.000 1.000

Exhibit III, Sheet 8: USPP Auto Increasing Claim Ratios and Increasing Case Outstanding Strength - Reported Claims#

We then generate Sheet 8, Parts 1-4 which contain the data triangle and age-to-age factors for USPP reported claims under the increasing claims ratio / increasing case outstanding strength scenario (p. 121).

devs = dev_exhibit(
    triangles.loc["Increasing Claim Case", "Reported Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 539,000 630,000 665,000 686,000 693,000 693,000 700,000 700,000 700,000 700,000
2000 565,950 661,500 698,250 720,300 727,650 727,650 735,000 735,000 735,000
2001 594,248 694,575 733,163 756,315 764,033 764,033 771,750 771,750
2002 623,960 729,304 769,821 794,131 802,234 802,234 810,338
2003 655,158 765,769 808,312 833,837 842,346 842,346
2004 786,189 918,923 969,974 1,004,280 1,010,815
2005 877,093 1,025,173 1,089,645 1,133,386
2006 975,121 1,154,188 1,237,897
2007 1,110,234 1,329,895
2008 1,330,264

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000 1.000
2000 1.169 1.056 1.032 1.010 1.000 1.010 1.000 1.000
2001 1.169 1.056 1.032 1.010 1.000 1.010 1.000
2002 1.169 1.056 1.032 1.010 1.000 1.010
2003 1.169 1.056 1.032 1.010 1.000
2004 1.169 1.056 1.035 1.007
2005 1.169 1.063 1.040
2006 1.184 1.073
2007 1.198

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.179 1.061 1.035 1.009 1.000 1.010 1.000 1.000 1.000

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.179 1.061 1.035 1.009 1.000 1.010 1.000 1.000 1.000 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 1.319 1.119 1.055 1.019 1.010 1.010 1.000 1.000 1.000 1.000
Percent Reported 0.758 0.894 0.948 0.981 0.990 0.990 1.000 1.000 1.000 1.000

Exhibit III, Sheet 9: USPP Auto Increasing Claim Ratios and Increasing Case Outstanding Strength - Paid Claims#

We then generate Sheet 9, Parts 1-4 which contain the data triangle and age-to-age factors for USPP paid claims under the increasing claims ratio / increasing case outstanding strength scenario (p. 122):

devs = dev_exhibit(
    triangles.loc["Increasing Claim Case", "Paid Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 294,000 497,000 588,000 644,000 672,000 686,000 693,000 693,000 700,000 700,000
2000 308,700 521,850 617,400 676,200 705,600 720,300 727,650 727,650 735,000
2001 324,135 547,943 648,270 710,010 740,880 756,315 764,033 764,033
2002 340,342 575,340 680,684 745,511 777,924 794,131 802,234
2003 357,359 604,107 714,718 782,786 816,820 833,837
2004 428,831 724,928 857,661 939,343 980,184
2005 478,414 808,748 956,828 1,047,955
2006 531,884 899,137 1,063,768
2007 589,505 996,544
2008 651,558

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000
2000 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010
2001 1.690 1.183 1.095 1.043 1.021 1.010 1.000
2002 1.690 1.183 1.095 1.043 1.021 1.010
2003 1.690 1.183 1.095 1.043 1.021
2004 1.690 1.183 1.095 1.043
2005 1.690 1.183 1.095
2006 1.690 1.183
2007 1.690

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.690 1.183 1.095 1.043 1.021 1.010 1.000 1.010 1.000 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 2.378 1.407 1.190 1.086 1.042 1.020 1.010 1.010 1.000 1.000
Percent Reported 0.421 0.711 0.840 0.921 0.960 0.980 0.990 0.990 1.000 1.000

Exhibit III, Sheets 10 and 11: USPP Auto Development of Unpaid Claim Estimate#

We first create a function to handle formatting as follows:

We then generate Sheets 10 and 11 which illustrate the differences between the actual IBNR and the IBNR obtained through the development method for each scenario (pp. 123-124).

res_1 = ex3sht10("Steady State", triangles)

res_2 = ex3sht10("Increasing Claim", triangles)

res_3 = ex3sht10("Increasing Case", triangles)

res_4 = ex3sht10("Increasing Claim Case", triangles)

Exhibit III Sheets 10 and 11 — Steady State

  Age of Accident Year at 12/31/08 Claims at 12/31/2008 - Reported Claims at 12/31/2008 - Paid Case Outstanding CDF to Ult. - Reported CDF to Ult. - Paid Projected Ult. Claims Using Dev. Method - Reported Projected Ult. Claims Using Dev. Method - Paid Estimated IBNR Using Dev. Method - Reported Estimated IBNR Using Dev. Method - Paid Actual IBNR Difference from Actual IBNR - Reported Difference from Actual IBNR - Paid
Accident Year                          
1999 120 700,000 700,000 0 1.000 1.000 700,000 700,000 0 0 0 0 0
2000 108 735,000 735,000 0 1.000 1.000 735,000 735,000 0 0 0 0 0
2001 96 771,750 764,033 7,717 1.000 1.010 771,750 771,751 0 1 0 0 -1
2002 84 810,338 802,234 8,104 1.000 1.010 810,338 810,337 0 -1 0 0 1
2003 72 842,346 833,837 8,509 1.010 1.020 850,855 850,854 8,509 8,508 8,509 0 0
2004 60 884,463 857,661 26,802 1.010 1.042 893,397 893,397 8,934 8,934 8,934 0 0
2005 48 919,306 863,022 56,284 1.020 1.087 938,068 938,067 18,762 18,761 18,762 0 0
2006 36 935,722 827,375 108,347 1.053 1.190 984,970 984,970 49,248 49,248 49,248 0 0
2007 24 930,797 734,295 196,502 1.111 1.408 1,034,219 1,034,218 103,422 103,421 103,422 0 1
2008 12 836,166 456,090 380,076 1.299 2.381 1,085,930 1,085,928 249,764 249,762 249,764 0 2
Total 8,365,888 7,573,547 792,341 8,804,527 8,804,522 438,639 438,634 438,639 0 5

Exhibit III Sheets 10 and 11 — Increasing Claim

  Age of Accident Year at 12/31/08 Claims at 12/31/2008 - Reported Claims at 12/31/2008 - Paid Case Outstanding CDF to Ult. - Reported CDF to Ult. - Paid Projected Ult. Claims Using Dev. Method - Reported Projected Ult. Claims Using Dev. Method - Paid Estimated IBNR Using Dev. Method - Reported Estimated IBNR Using Dev. Method - Paid Actual IBNR Difference from Actual IBNR - Reported Difference from Actual IBNR - Paid
Accident Year                          
1999 120 700,000 700,000 0 1.000 1.000 700,000 700,000 0 0 0 0 0
2000 108 735,000 735,000 0 1.000 1.000 735,000 735,000 0 0 0 0 0
2001 96 771,750 764,033 7,717 1.000 1.010 771,750 771,751 0 1 0 0 -1
2002 84 810,338 802,234 8,104 1.000 1.010 810,338 810,337 0 -1 0 0 1
2003 72 842,346 833,837 8,509 1.010 1.020 850,855 850,854 8,509 8,508 8,509 0 0
2004 60 1,010,815 980,184 30,631 1.010 1.042 1,021,025 1,021,025 10,210 10,210 10,210 0 0
2005 48 1,116,300 1,047,955 68,345 1.020 1.087 1,139,082 1,139,081 22,782 22,781 22,782 0 0
2006 36 1,203,071 1,063,768 139,303 1.053 1.190 1,266,391 1,266,390 63,320 63,319 63,320 0 0
2007 24 1,263,224 996,544 266,680 1.111 1.408 1,403,582 1,403,583 140,358 140,359 140,358 0 -0
2008 12 1,194,523 651,558 542,965 1.299 2.381 1,551,328 1,551,328 356,805 356,805 356,805 0 0
Total 9,647,367 8,575,113 1,072,254 10,249,351 10,249,350 601,984 601,983 601,984 0 1

Exhibit III Sheets 10 and 11 — Increasing Case

  Age of Accident Year at 12/31/08 Claims at 12/31/2008 - Reported Claims at 12/31/2008 - Paid Case Outstanding CDF to Ult. - Reported CDF to Ult. - Paid Projected Ult. Claims Using Dev. Method - Reported Projected Ult. Claims Using Dev. Method - Paid Estimated IBNR Using Dev. Method - Reported Estimated IBNR Using Dev. Method - Paid Actual IBNR Difference from Actual IBNR - Reported Difference from Actual IBNR - Paid
Accident Year                          
1999 120 700,000 700,000 0 1.000 1.000 700,000 700,000 0 0 0 0 0
2000 108 735,000 735,000 0 1.000 1.000 735,000 735,000 0 0 0 0 0
2001 96 771,750 764,033 7,717 1.000 1.010 771,750 771,751 0 1 0 0 -1
2002 84 810,338 802,234 8,104 1.000 1.010 810,338 810,337 0 -1 0 0 1
2003 72 842,346 833,837 8,509 1.010 1.020 850,855 850,854 8,509 8,508 8,509 0 0
2004 60 884,463 857,661 26,802 1.010 1.042 893,397 893,397 8,934 8,934 8,934 0 0
2005 48 933,377 863,022 70,355 1.020 1.087 951,657 938,067 18,280 4,690 4,691 -13,589 0
2006 36 962,808 827,375 135,433 1.055 1.190 1,015,301 984,970 52,493 22,162 22,162 -30,331 0
2007 24 979,922 734,295 245,627 1.119 1.408 1,096,235 1,034,218 116,313 54,296 54,297 -62,016 1
2008 12 931,185 456,090 475,095 1.318 2.381 1,227,589 1,085,928 296,404 154,743 154,745 -141,659 2
Total 8,551,189 7,573,547 977,642 9,052,121 8,804,522 500,932 253,333 253,338 -247,595 5

Exhibit III Sheets 10 and 11 — Increasing Claim Case

  Age of Accident Year at 12/31/08 Claims at 12/31/2008 - Reported Claims at 12/31/2008 - Paid Case Outstanding CDF to Ult. - Reported CDF to Ult. - Paid Projected Ult. Claims Using Dev. Method - Reported Projected Ult. Claims Using Dev. Method - Paid Estimated IBNR Using Dev. Method - Reported Estimated IBNR Using Dev. Method - Paid Actual IBNR Difference from Actual IBNR - Reported Difference from Actual IBNR - Paid
Accident Year                          
1999 120 700,000 700,000 0 1.000 1.000 700,000 700,000 0 0 0 0 0
2000 108 735,000 735,000 0 1.000 1.000 735,000 735,000 0 0 0 0 0
2001 96 771,750 764,033 7,717 1.000 1.010 771,750 771,751 0 1 0 0 -1
2002 84 810,338 802,234 8,104 1.000 1.010 810,338 810,337 0 -1 0 0 1
2003 72 842,346 833,837 8,509 1.010 1.020 850,855 850,854 8,509 8,508 8,509 0 0
2004 60 1,010,815 980,184 30,631 1.010 1.042 1,021,025 1,021,025 10,210 10,210 10,210 0 0
2005 48 1,133,386 1,047,955 85,431 1.019 1.087 1,155,482 1,139,081 22,096 5,695 5,696 -16,400 0
2006 36 1,237,897 1,063,768 174,129 1.055 1.190 1,305,639 1,266,390 67,742 28,493 28,494 -39,249 0
2007 24 1,329,895 996,544 333,351 1.120 1.408 1,488,875 1,403,583 158,980 73,688 73,687 -85,293 -0
2008 12 1,330,264 651,558 678,706 1.320 2.381 1,756,504 1,551,328 426,240 221,064 221,064 -205,176 0
Total 9,901,691 8,575,113 1,326,578 10,595,469 10,249,350 693,778 347,659 347,660 -346,118 1

Exhibit IV — Impact of Changing Product Mix Example#

In Exhibit IV, Friedland demonstrates the effect of changes in product mix on the development method in order to further illustrate its limitations before segueing into more advanced methods. The changing product mix in this example refers to the proportion of earned premiums attributable to the private passenger and commercial portfolios of U.S. Auto over a ten-year experience period.

In the steady state scenario, both lines have an equal proportion of the total earned premiums (which increase by 5% annually), while in the changing product mix scenario, the proportionate share of earned premiums of commercial lines increases relative to private passenger (specifically, commercial’s earned premium increases by 30% annually from the seventh development year). Other than the earned premium, the other key assumption of this study are as follows are assumed ultimate claim ratios of 70% and 80%, respectively, for private passenger and commercial.

Since the USPP Auto dataset is contained in the chainladder package, we simply load it into a triangle as follows:

data = cl.load_sample("friedland_us_auto")
tri = data.copy()

Exhibit IV, Sheet 1 - Summary of Assumptions - Earned Premiums and Claim Ratios#

To use Chainladder to reproduce Sheet 1 of Exhibit IV, we create the following function to take care of the necessary formatting, and to perform the appropriate transformations/slicing of the data triangle to apply the development method and determine the IBNR.

def ex4sht1(scenario, tri):

    def format_col(x, reverse=False):
        if reverse is True:
            return x.to_frame().squeeze()[::-1].reset_index(drop=True)
        return x.to_frame().squeeze().reset_index(drop=True)

    def col_add(x, y):
        return x.squeeze() + y.squeeze()

    def col_diff(x, y):
        return x.squeeze() - y.squeeze()

    def col_div(x, y):
        return x.squeeze() / y.squeeze()

    def col_mult(x, y):
        return x.squeeze() * y.squeeze()

    tri = tri.copy()

    dev_input = {"average": "volume", "n_periods": 5}

    tail_input = {"tail": 1.0, "projection_period": -1}

    dev = cl.Development(**dev_input)
    tail = cl.TailConstant(**tail_input)
    tri = tail.fit_transform(dev.fit_transform(tri))

    col1 = pd.DataFrame({"Accident Year": format_col(tri.origin)})

    col2 = pd.DataFrame({
        "Priv Pass Auto": format_col(
            tri.loc["Steady State"]["Earned Premium"].latest_diagonal / 2
        )
    })

    col3 = pd.DataFrame({
        "Comm Auto": col_diff(
            format_col(tri.loc[scenario]["Earned Premium"].latest_diagonal), col2
        )
    })

    col4 = pd.DataFrame({
        "Total": format_col(tri.loc[scenario]["Earned Premium"].latest_diagonal)
    })

    col5 = pd.DataFrame({"Priv Pass Auto (%)": pd.Series([0.7] * 10)})

    col6 = pd.DataFrame({"Comm Auto (%)": pd.Series([0.8] * 10)})

    col8 = pd.DataFrame({"Priv Pass Auto": col_mult(col2, col5)})

    col9 = pd.DataFrame({"Comm Auto": col_mult(col3, col6)})

    col10 = pd.DataFrame({"Comm Auto": col_add(col8, col9)})

    col7 = pd.DataFrame({"Total (%)": col_div(col10, col4)})

    col11 = pd.DataFrame({
        "Reported Claims as at 12/31/2008": format_col(
            tri.loc[scenario]["Reported Claims"].latest_diagonal
        )
    })

    col12 = pd.DataFrame({"Actual IBNR": col_diff(col10, col11)})

    cols = [col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12]

    results = pd.concat(cols, axis=1)

    format_dict = {
        # Dollar amounts
        "Priv Pass Auto": "{:,.0f}",
        "Comm Auto": "{:,.0f}",
        "Total": "{:,.0f}",
        "Reported Claims as at 12/31/2008": "{:,.0f}",
        "Actual IBNR": "{:,.0f}",
        # Percentages
        "Priv Pass Auto (%)": "{:.1%}",
        "Comm Auto (%)": "{:.1%}",
        "Total (%)": "{:.1%}",
        # Factors
        "CDF to Ult. - Reported": "{:.3f}",
        "CDF to Ult. - Paid": "{:.3f}",
    }

    results = results.set_index("Accident Year")

    total_cols = [
        "Priv Pass Auto",
        "Comm Auto",
        "Total",
        "Reported Claims as at 12/31/2008",
        "Actual IBNR",
    ]

    results.loc["Total", total_cols] = results[total_cols].sum()

    display(HTML(f"<h3>Exhibit IV, Sheet 1 — {scenario}</h3>"))

    display(
        results.style.format(format_dict, na_rep="").set_table_styles([
            {
                "selector": "th.col_heading",
                "props": [
                    ("white-space", "normal"),
                    ("max-width", "80px"),
                ],
            }
        ])
    )

    return results

Having defined the function, we generate Sheet 1, which contains the development of losses under the Steady State and Changing Product Mix scenarios (p. 125):

res_1 = ex4sht1("Steady State", data)

res_2 = ex4sht1("Changing Product Mix", data)

Exhibit IV, Sheet 1 — Steady State

  Priv Pass Auto Comm Auto Total Priv Pass Auto (%) Comm Auto (%) Total (%) Priv Pass Auto Comm Auto Comm Auto Reported Claims as at 12/31/2008 Actual IBNR
Accident Year                      
1999 1,000,000 1,000,000 2,000,000 70.0% 80.0% 75.0% 700,000 800,000 1,500,000 1,500,000 0
2000 1,050,000 1,050,000 2,100,000 70.0% 80.0% 75.0% 735,000 840,000 1,575,000 1,575,000 0
2001 1,102,500 1,102,500 2,205,000 70.0% 80.0% 75.0% 771,750 882,000 1,653,750 1,653,750 0
2002 1,157,625 1,157,625 2,315,250 70.0% 80.0% 75.0% 810,338 926,100 1,736,438 1,736,438 -0
2003 1,215,506 1,215,506 2,431,013 70.0% 80.0% 75.0% 850,855 972,405 1,823,260 1,814,751 8,509
2004 1,276,282 1,276,282 2,552,563 70.0% 80.0% 75.0% 893,397 1,021,025 1,914,422 1,885,068 29,354
2005 1,340,096 1,340,096 2,680,191 70.0% 80.0% 75.0% 938,067 1,072,076 2,010,143 1,948,499 61,644
2006 1,407,100 1,407,100 2,814,201 70.0% 80.0% 75.0% 984,970 1,125,680 2,110,651 1,937,577 173,074
2007 1,477,456 1,477,456 2,954,911 70.0% 80.0% 75.0% 1,034,219 1,181,964 2,216,183 1,852,729 363,454
2008 1,551,328 1,551,328 3,102,656 70.0% 80.0% 75.0% 1,085,930 1,241,062 2,326,992 1,568,393 758,599
Total 12,577,892 12,577,892 25,155,785 8,804,525 10,062,314 18,866,839 17,472,205 1,394,634

Exhibit IV, Sheet 1 — Changing Product Mix

  Priv Pass Auto Comm Auto Total Priv Pass Auto (%) Comm Auto (%) Total (%) Priv Pass Auto Comm Auto Comm Auto Reported Claims as at 12/31/2008 Actual IBNR
Accident Year                      
1999 1,000,000 1,000,000 2,000,000 70.0% 80.0% 75.0% 700,000 800,000 1,500,000 1,500,000 0
2000 1,050,000 1,050,000 2,100,000 70.0% 80.0% 75.0% 735,000 840,000 1,575,000 1,575,000 0
2001 1,102,500 1,102,500 2,205,000 70.0% 80.0% 75.0% 771,750 882,000 1,653,750 1,653,750 0
2002 1,157,625 1,157,625 2,315,250 70.0% 80.0% 75.0% 810,338 926,100 1,736,438 1,736,438 -0
2003 1,215,506 1,215,506 2,431,013 70.0% 80.0% 75.0% 850,855 972,405 1,823,260 1,814,751 8,509
2004 1,276,282 1,276,282 2,552,563 70.0% 80.0% 75.0% 893,397 1,021,025 1,914,422 1,885,068 29,354
2005 1,340,096 1,659,166 2,999,262 70.0% 80.0% 75.5% 938,067 1,327,333 2,265,400 2,193,545 71,855
2006 1,407,100 2,156,916 3,564,016 70.0% 80.0% 76.1% 984,970 1,725,532 2,710,503 2,471,446 239,057
2007 1,477,456 2,803,990 4,281,446 70.0% 80.0% 76.5% 1,034,219 2,243,192 3,277,411 2,680,487 596,924
2008 1,551,328 3,645,188 5,196,516 70.0% 80.0% 77.0% 1,085,930 2,916,150 4,002,080 2,556,695 1,445,385
Total 12,577,892 17,067,174 29,645,066 8,804,525 13,653,739 22,458,264 20,067,180 2,391,084

Exhibit IV, Sheet 2 - U.S. Auto Steady-State - Reported Claims#

We now use the ‘dev_exhibit’ function to generate Exhibit IV, Sheet 2 (p. 126):

devs = dev_exhibit(
    data.loc["Steady State", "Reported Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 1,011,000 1,254,000 1,377,000 1,454,000 1,477,000 1,493,000 1,500,000 1,500,000 1,500,000 1,500,000
2000 1,061,550 1,316,700 1,445,850 1,526,700 1,550,850 1,567,650 1,575,000 1,575,000 1,575,000
2001 1,114,628 1,382,535 1,518,143 1,603,035 1,628,393 1,646,033 1,653,750 1,653,750
2002 1,170,359 1,451,662 1,594,050 1,683,187 1,709,812 1,728,334 1,736,438
2003 1,228,877 1,524,245 1,673,752 1,767,346 1,795,303 1,814,751
2004 1,290,321 1,600,457 1,757,440 1,855,713 1,885,068
2005 1,354,837 1,680,480 1,845,312 1,948,499
2006 1,422,579 1,764,504 1,937,577
2007 1,493,707 1,852,729
2008 1,568,393

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.240 1.098 1.056 1.016 1.011 1.005 1.000 1.000 1.000
2000 1.240 1.098 1.056 1.016 1.011 1.005 1.000 1.000
2001 1.240 1.098 1.056 1.016 1.011 1.005 1.000
2002 1.240 1.098 1.056 1.016 1.011 1.005
2003 1.240 1.098 1.056 1.016 1.011
2004 1.240 1.098 1.056 1.016
2005 1.240 1.098 1.056
2006 1.240 1.098
2007 1.240

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.240 1.098 1.056 1.016 1.011 1.005 1.000 1.000 1.000

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.240 1.098 1.056 1.016 1.011 1.005 1.000 1.000 1.000 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 1.484 1.197 1.090 1.032 1.016 1.005 1.000 1.000 1.000 1.000
Percent Reported 0.674 0.835 0.917 0.969 0.984 0.995 1.000 1.000 1.000 1.000

Exhibit IV, Sheet 3 - U.S. Auto Steady-State - Paid Claims#

We now use the ‘dev_exhibit’ function to generate Exhibit IV, Sheet 3 (p. 127):

devs = dev_exhibit(
    data.loc["Steady State", "Paid Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 470,000 865,000 1,124,000 1,300,000 1,400,000 1,446,000 1,469,000 1,477,000 1,492,000 1,500,000
2000 493,500 908,250 1,180,200 1,365,000 1,470,000 1,518,300 1,542,450 1,550,850 1,566,600
2001 518,175 953,663 1,239,210 1,433,250 1,543,500 1,594,215 1,619,573 1,628,393
2002 544,084 1,001,346 1,301,171 1,504,913 1,620,675 1,673,926 1,700,551
2003 571,288 1,051,413 1,366,229 1,580,158 1,701,709 1,757,622
2004 599,852 1,103,984 1,434,540 1,659,166 1,786,794
2005 629,845 1,159,183 1,506,268 1,742,124
2006 661,337 1,217,142 1,581,581
2007 694,404 1,277,999
2008 729,124

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.840 1.299 1.157 1.077 1.033 1.016 1.005 1.010 1.005
2000 1.840 1.299 1.157 1.077 1.033 1.016 1.005 1.010
2001 1.840 1.299 1.157 1.077 1.033 1.016 1.005
2002 1.840 1.299 1.157 1.077 1.033 1.016
2003 1.840 1.299 1.157 1.077 1.033
2004 1.840 1.299 1.157 1.077
2005 1.840 1.299 1.157
2006 1.840 1.299
2007 1.840

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.840 1.299 1.157 1.077 1.033 1.016 1.005 1.010 1.005

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.840 1.299 1.157 1.077 1.033 1.016 1.005 1.010 1.005 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 3.189 1.733 1.334 1.153 1.071 1.036 1.020 1.015 1.005 1.000
Percent Reported 0.314 0.577 0.750 0.867 0.934 0.965 0.980 0.985 0.995 1.000

Exhibit IV, Sheet 4 - U.S. Auto Changing Product Mix - Reported Claims#

We now use the ‘dev_exhibit’ function to generate Exhibit IV, Sheet 4 (p. 128):

devs = dev_exhibit(
    data.loc["Changing Product Mix", "Reported Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 1,011,000 1,254,000 1,377,000 1,454,000 1,477,000 1,493,000 1,500,000 1,500,000 1,500,000 1,500,000
2000 1,061,550 1,316,700 1,445,850 1,526,700 1,550,850 1,567,650 1,575,000 1,575,000 1,575,000
2001 1,114,628 1,382,535 1,518,143 1,603,035 1,628,393 1,646,033 1,653,750 1,653,750
2002 1,170,359 1,451,662 1,594,050 1,683,187 1,709,812 1,728,334 1,736,438
2003 1,228,877 1,524,245 1,673,752 1,767,346 1,795,303 1,814,751
2004 1,290,321 1,600,457 1,757,440 1,855,713 1,885,068
2005 1,505,438 1,879,580 2,072,490 2,193,545
2006 1,776,491 2,232,389 2,471,446
2007 2,119,832 2,680,487
2008 2,556,695

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.240 1.098 1.056 1.016 1.011 1.005 1.000 1.000 1.000
2000 1.240 1.098 1.056 1.016 1.011 1.005 1.000 1.000
2001 1.240 1.098 1.056 1.016 1.011 1.005 1.000
2002 1.240 1.098 1.056 1.016 1.011 1.005
2003 1.240 1.098 1.056 1.016 1.011
2004 1.240 1.098 1.056 1.016
2005 1.249 1.103 1.058
2006 1.257 1.107
2007 1.264

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.252 1.101 1.057 1.016 1.011 1.005 1.000 1.000 1.000

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.252 1.101 1.057 1.016 1.011 1.005 1.000 1.000 1.000 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 1.504 1.201 1.091 1.032 1.016 1.005 1.000 1.000 1.000 1.000
Percent Reported 0.665 0.833 0.917 0.969 0.984 0.995 1.000 1.000 1.000 1.000

Exhibit IV, Sheet 5 - U.S. Auto Changing Product Mix - Paid Claims#

We now use the ‘dev_exhibit’ function to generate Exhibit IV, Sheet 5 (p. 129):

devs = dev_exhibit(
    data.loc["Changing Product Mix", "Paid Claims"],
    avg_params={"volume_5": {"n_periods": 5, "average": "volume"}},
    selected_avg="volume_5",
    tail=1,
)
''

PART 1 - Data Triangle

12 24 36 48 60 72 84 96 108 120
1999 470,000 865,000 1,124,000 1,300,000 1,400,000 1,446,000 1,469,000 1,477,000 1,492,000 1,500,000
2000 493,500 908,250 1,180,200 1,365,000 1,470,000 1,518,300 1,542,450 1,550,850 1,566,600
2001 518,175 953,663 1,239,210 1,433,250 1,543,500 1,594,215 1,619,573 1,628,393
2002 544,084 1,001,346 1,301,171 1,504,913 1,620,675 1,673,926 1,700,551
2003 571,288 1,051,413 1,366,229 1,580,158 1,701,709 1,757,622
2004 599,852 1,103,984 1,434,540 1,659,166 1,786,794
2005 686,001 1,276,601 1,677,289 1,951,435
2006 793,305 1,493,074 1,983,482
2007 927,874 1,766,164
2008 1,097,644

PART 2 - Age-to-Age Factors

  12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
1999 1.840 1.299 1.157 1.077 1.033 1.016 1.005 1.010 1.005
2000 1.840 1.299 1.157 1.077 1.033 1.016 1.005 1.010
2001 1.840 1.299 1.157 1.077 1.033 1.016 1.005
2002 1.840 1.299 1.157 1.077 1.033 1.016
2003 1.840 1.299 1.157 1.077 1.033
2004 1.840 1.299 1.157 1.077
2005 1.861 1.314 1.163
2006 1.882 1.328
2007 1.903

PART 3 - Average Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120
volume_5 1.870 1.310 1.158 1.077 1.033 1.016 1.005 1.010 1.005

PART 4 - Selected Age-to-Age Factor

12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132
Selected 1.870 1.310 1.158 1.077 1.033 1.016 1.005 1.010 1.005 1.000
12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult
CDF to Ultimate 3.271 1.749 1.335 1.153 1.071 1.036 1.020 1.015 1.005 1.000
Percent Reported 0.306 0.572 0.749 0.867 0.934 0.965 0.980 0.985 0.995 1.000

Exhibit IV, Sheet 6#

Finally, we produce the summary table contained in Exhibit IV, Sheet 6 (p. 130) by defining the following function to handle formatting:

def ex4sht6(scenario, tr):

    def format_col(x, reverse=False):
        if reverse is True:
            return x.to_frame().squeeze()[::-1].reset_index(drop=True)
        return x.to_frame().squeeze().reset_index(drop=True)

    def col_diff(x, y):
        return x.squeeze() - y.squeeze()

    def col_mult(x, y):
        return x.squeeze() * y.squeeze()

    def col_sum(x, y):
        return x.squeeze() + y.squeeze()

    tri = tr.copy()

    dev_input = {"average": "volume", "n_periods": 5}

    tail_input = {"tail": 1.0, "projection_period": -1}

    dev = cl.Development(**dev_input)
    tail = cl.TailConstant(**tail_input)
    tri = tail.fit_transform(dev.fit_transform(tri))

    col1 = pd.DataFrame({"Accident Year": format_col(tr.origin)})

    col2 = pd.DataFrame({
        "Age of Accident Year at 12/31/08": format_col(tr.development, reverse=True)
    })

    col3 = pd.DataFrame({
        "Claims at 12/31/2008 - Reported": format_col(
            tri.loc[scenario]["Reported Claims"].latest_diagonal
        )
    })

    col4 = pd.DataFrame({
        "Claims at 12/31/2008 - Paid": format_col(
            tri.loc[scenario]["Paid Claims"].latest_diagonal
        )
    })

    col5 = pd.DataFrame({"Case Outstanding": col_diff(col3, col4)})

    col6 = pd.DataFrame({
        "CDF to Ult. - Reported": format_col(
            tri.cdf_.loc[scenario]["Reported Claims"], reverse=True
        )
    })

    col7 = pd.DataFrame({
        "CDF to Ult. - Paid": format_col(
            tri.cdf_.loc[scenario]["Paid Claims"], reverse=True
        )
    })

    col8 = pd.DataFrame({
        "Projected Ult. Claims Using Dev. Method - Reported": col_mult(col3, col6)
    })

    col9 = pd.DataFrame({
        "Projected Ult. Claims Using Dev. Method - Paid": col_mult(col4, col7)
    })

    col10 = pd.DataFrame({
        "Estimated IBNR Using Dev. Method - Reported": col_diff(col8, col3)
    })

    col11 = pd.DataFrame({
        "Estimated IBNR Using Dev. Method - Paid": col_diff(col9, col3)
    })

    colA = col_mult(
        format_col(tri.loc["Steady State"]["Earned Premium"].latest_diagonal / 2),
        pd.Series([0.7] * 10),
    )
    colB = col_diff(
        format_col(tri.loc[scenario]["Earned Premium"].latest_diagonal),
        format_col(tri.loc["Steady State"]["Earned Premium"].latest_diagonal / 2),
    ) * pd.Series([0.8] * 10)
    colC = format_col(tri.loc[scenario]["Reported Claims"].latest_diagonal)
    colD = col_diff(col_sum(colA, colB), colC)

    col12 = pd.DataFrame({"Actual IBNR": format_col(colD)})

    col13 = pd.DataFrame({
        "Difference from Actual IBNR - Reported": col_diff(col12, col10)
    })

    col14 = pd.DataFrame({"Difference from Actual IBNR - Paid": col_diff(col12, col11)})

    cols = [
        col1,
        col2,
        col3,
        col4,
        col5,
        col6,
        col7,
        col8,
        col9,
        col10,
        col11,
        col12,
        col13,
        col14,
    ]

    results = pd.concat(cols, axis=1)

    format_dict = {
        # Dollar amounts
        "Claims at 12/31/2008 - Reported": "{:,.0f}",
        "Claims at 12/31/2008 - Paid": "{:,.0f}",
        "Case Outstanding": "{:,.0f}",
        "Projected Ult. Claims Using Dev. Method - Reported": "{:,.0f}",
        "Projected Ult. Claims Using Dev. Method - Paid": "{:,.0f}",
        "Estimated IBNR Using Dev. Method - Reported": "{:,.0f}",
        "Estimated IBNR Using Dev. Method - Paid": "{:,.0f}",
        "Actual IBNR": "{:,.0f}",
        "Difference from Actual IBNR - Reported": "{:,.0f}",
        "Difference from Actual IBNR - Paid": "{:,.0f}",
        # Ages
        "Age of Accident Year at 12/31/08": "{:.0f}",
        # Factors
        "CDF to Ult. - Reported": "{:.3f}",
        "CDF to Ult. - Paid": "{:.3f}",
    }

    results = results.set_index("Accident Year")

    total_cols = [
        "Claims at 12/31/2008 - Reported",
        "Claims at 12/31/2008 - Paid",
        "Case Outstanding",
        "Projected Ult. Claims Using Dev. Method - Reported",
        "Projected Ult. Claims Using Dev. Method - Paid",
        "Estimated IBNR Using Dev. Method - Reported",
        "Estimated IBNR Using Dev. Method - Paid",
        "Actual IBNR",
        "Difference from Actual IBNR - Reported",
        "Difference from Actual IBNR - Paid",
    ]

    results.loc["Total", total_cols] = results[total_cols].sum()

    display(HTML(f"<h3>Exhibit IV Sheet 6 — {scenario}</h3>"))

    display(results.style.format(format_dict, na_rep=""))

    return results

Finally, we generate the tables in Sheet 6 calling the function once for each scenario:

res_1 = ex4sht6("Steady State", data)

res_2 = ex4sht6("Changing Product Mix", data)

Exhibit IV Sheet 6 — Steady State

  Age of Accident Year at 12/31/08 Claims at 12/31/2008 - Reported Claims at 12/31/2008 - Paid Case Outstanding CDF to Ult. - Reported CDF to Ult. - Paid Projected Ult. Claims Using Dev. Method - Reported Projected Ult. Claims Using Dev. Method - Paid Estimated IBNR Using Dev. Method - Reported Estimated IBNR Using Dev. Method - Paid Actual IBNR Difference from Actual IBNR - Reported Difference from Actual IBNR - Paid
Accident Year                          
1999 120 1,500,000 1,500,000 0 1.000 1.000 1,500,000 1,500,000 0 0 0 0 0
2000 108 1,575,000 1,566,600 8,400 1.000 1.005 1,575,000 1,575,000 0 0 0 0 0
2001 96 1,653,750 1,628,393 25,357 1.000 1.016 1,653,750 1,653,751 0 1 0 0 -1
2002 84 1,736,438 1,700,551 35,887 1.000 1.021 1,736,438 1,736,437 0 -1 -0 -0 0
2003 72 1,814,751 1,757,622 57,129 1.005 1.037 1,823,260 1,823,259 8,509 8,508 8,509 0 0
2004 60 1,885,068 1,786,794 98,274 1.016 1.071 1,914,422 1,914,422 29,354 29,354 29,354 -0 0
2005 48 1,948,499 1,742,124 206,375 1.032 1.154 2,010,144 2,010,143 61,645 61,644 61,644 -0 0
2006 36 1,937,577 1,581,581 355,996 1.089 1.335 2,110,650 2,110,651 173,073 173,074 173,074 1 0
2007 24 1,852,729 1,277,999 574,730 1.196 1.734 2,216,183 2,216,183 363,454 363,454 363,454 0 1
2008 12 1,568,393 729,124 839,269 1.484 3.191 2,326,992 2,326,992 758,599 758,599 758,599 -0 0
Total 17,472,205 15,270,788 2,201,417 18,866,839 18,866,837 1,394,634 1,394,632 1,394,634 -0 1

Exhibit IV Sheet 6 — Changing Product Mix

  Age of Accident Year at 12/31/08 Claims at 12/31/2008 - Reported Claims at 12/31/2008 - Paid Case Outstanding CDF to Ult. - Reported CDF to Ult. - Paid Projected Ult. Claims Using Dev. Method - Reported Projected Ult. Claims Using Dev. Method - Paid Estimated IBNR Using Dev. Method - Reported Estimated IBNR Using Dev. Method - Paid Actual IBNR Difference from Actual IBNR - Reported Difference from Actual IBNR - Paid
Accident Year                          
1999 120 1,500,000 1,500,000 0 1.000 1.000 1,500,000 1,500,000 0 0 0 0 0
2000 108 1,575,000 1,566,600 8,400 1.000 1.005 1,575,000 1,575,000 0 0 0 0 0
2001 96 1,653,750 1,628,393 25,357 1.000 1.016 1,653,750 1,653,751 0 1 0 0 -1
2002 84 1,736,438 1,700,551 35,887 1.000 1.021 1,736,438 1,736,437 0 -1 -0 -0 0
2003 72 1,814,751 1,757,622 57,129 1.005 1.037 1,823,260 1,823,259 8,509 8,508 8,509 0 0
2004 60 1,885,068 1,786,794 98,274 1.016 1.071 1,914,422 1,914,422 29,354 29,354 29,354 -0 0
2005 48 2,193,545 1,951,435 242,110 1.032 1.154 2,262,942 2,251,656 69,397 58,111 71,855 2,458 13,744
2006 36 2,471,446 1,983,482 487,964 1.090 1.336 2,693,735 2,650,749 222,289 179,303 239,057 16,768 59,754
2007 24 2,680,487 1,766,164 914,323 1.200 1.750 3,217,775 3,091,665 537,288 411,178 596,924 59,637 185,746
2008 12 2,556,695 1,097,644 1,459,051 1.503 3.273 3,842,646 3,592,941 1,285,951 1,036,246 1,445,385 159,434 409,139
Total 20,067,180 16,738,685 3,328,495 22,219,968 21,789,881 2,152,788 1,722,701 2,391,084 238,296 668,383