diff --git a/docs/friedland/chapter_9.ipynb b/docs/friedland/chapter_9.ipynb index 3665348b..6c20703b 100644 --- a/docs/friedland/chapter_9.ipynb +++ b/docs/friedland/chapter_9.ipynb @@ -25,9 +25,12 @@ "chapter recreates the Friedland Chapter 9 exhibits, reusing the development\n", "patterns selected in Chapter 7 and the expected claims from Chapter 8:\n", "\n", - "- **Exhibit I** — U.S. Industry Auto\n", - "- **Exhibit II** — XYZ Insurer (Auto BI)\n", - "- **Exhibit III** — U.S. PP Auto (impact of changing conditions)" + "- **Exhibit I** - U.S. Industry Auto\n", + "- **Exhibit II** - XYZ Insurer (Auto BI)\n", + "- **Exhibit III** - U.S. PP Auto (impact of changing conditions)\n", + "- **Exhibit IV** - U.S. Auto (impact of change in product mix)\n", + "- **Exhibit V** - U.S. PP Auto (impact of changing conditions - Gunnar Benktander method)\n", + "- **Exhibit VI** - U.S. Auto (impact of change in product mix - Gunnar Benktander method)\n" ] }, { @@ -36,22 +39,45 @@ "id": "4a72e71f", "metadata": { "execution": { - "iopub.execute_input": "2026-07-15T04:17:53.216486Z", - "iopub.status.busy": "2026-07-15T04:17:53.215892Z", - "iopub.status.idle": "2026-07-15T04:17:57.268027Z", - "shell.execute_reply": "2026-07-15T04:17:57.267379Z" + "iopub.execute_input": "2026-08-10T06:42:26.899886Z", + "iopub.status.busy": "2026-08-10T06:42:26.899280Z", + "iopub.status.idle": "2026-08-10T06:42:30.130701Z", + "shell.execute_reply": "2026-08-10T06:42:30.129164Z" } }, "outputs": [], "source": [ + "import os\n", "import numpy as np\n", "import pandas as pd\n", "import chainladder as cl\n", - "import os\n", "from IPython.display import display\n", "\n", "pd.set_option(\"display.max_columns\", None)\n", - "pd.set_option(\"display.width\", 1000)" + "pd.set_option(\"display.width\", 1000)\n", + "\n", + "\n", + "def add_total_row(df, sum_cols=None):\n", + " \"\"\"Append a Total row to a DataFrame for display purposes.\"\"\"\n", + " out = df.copy()\n", + " out.index = out.index.astype(str)\n", + " if sum_cols is None:\n", + " non_sum = [\n", + " \"Age (Months)\",\n", + " \"CDF Reported\",\n", + " \"CDF Paid\",\n", + " \"% Unreported\",\n", + " \"% Unpaid\",\n", + " ]\n", + " sum_cols = [c for c in out.columns if c not in non_sum]\n", + " total_row = {}\n", + " for col in out.columns:\n", + " if col in sum_cols:\n", + " total_row[col] = out[col].sum()\n", + " else:\n", + " total_row[col] = \"\"\n", + " out.loc[\"Total\"] = total_row\n", + " return out" ] }, { @@ -59,7 +85,7 @@ "id": "58740191", "metadata": {}, "source": [ - "## Exhibit I — U.S. Industry Auto\n", + "## Exhibit I - U.S. Industry Auto\n", "\n", "The text works the Bornhuetter-Ferguson method first for **U.S. Industry Auto**\n", "(Exhibit I), valued at 12/31/2007 over accident years 1998-2007. The reporting\n", @@ -80,10 +106,10 @@ "id": "2d99f42f", "metadata": { "execution": { - "iopub.execute_input": "2026-07-15T04:17:57.275550Z", - "iopub.status.busy": "2026-07-15T04:17:57.273017Z", - "iopub.status.idle": "2026-07-15T04:17:58.117761Z", - "shell.execute_reply": "2026-07-15T04:17:58.116179Z" + "iopub.execute_input": "2026-08-10T06:42:30.137193Z", + "iopub.status.busy": "2026-08-10T06:42:30.134908Z", + "iopub.status.idle": "2026-08-10T06:42:30.725265Z", + "shell.execute_reply": "2026-08-10T06:42:30.723603Z" } }, "outputs": [ @@ -113,8 +139,10 @@ " CDF Paid\n", " % Unreported\n", " % Unpaid\n", - " Reported\n", - " Paid\n", + " Expected Unreported\n", + " Expected Unpaid\n", + " Reported Claims\n", + " Paid Claims\n", " BF Ultimate (Reported)\n", " BF Ultimate (Paid)\n", " \n", @@ -123,10 +151,12 @@ " \n", " 1998\n", " 51430657.0\n", - " 1.000\n", + " 1.0\n", " 1.002\n", - " 0.000\n", + " 0.0\n", " 0.002\n", + " 0.0\n", + " 102656.0\n", " 47742304.0\n", " 47644187.0\n", " 47742304.0\n", @@ -135,14 +165,16 @@ " \n", " 1999\n", " 51408736.0\n", - " 1.000\n", + " 1.0\n", " 1.004\n", - " 0.000\n", + " 0.0\n", " 0.004\n", + " 0.0\n", + " 204816.0\n", " 51185767.0\n", " 51000534.0\n", " 51185767.0\n", - " 51205554.0\n", + " 51205350.0\n", " \n", " \n", " 2000\n", @@ -151,10 +183,12 @@ " 1.006\n", " 0.001\n", " 0.006\n", + " 51629.0\n", + " 308236.0\n", " 54837929.0\n", " 54533225.0\n", " 54889558.0\n", - " 54842075.0\n", + " 54841461.0\n", " \n", " \n", " 2001\n", @@ -163,34 +197,40 @@ " 1.011\n", " 0.003\n", " 0.011\n", + " 162738.0\n", + " 591984.0\n", " 56299562.0\n", " 55878421.0\n", - " 56462408.0\n", - " 56472644.0\n", + " 56462300.0\n", + " 56470405.0\n", " \n", " \n", " 2002\n", " 59421665.0\n", " 1.006\n", - " 1.020\n", + " 1.02\n", " 0.006\n", - " 0.020\n", + " 0.02\n", + " 354404.0\n", + " 1165131.0\n", " 58592712.0\n", " 57807215.0\n", - " 58947762.0\n", - " 58980423.0\n", + " 58947116.0\n", + " 58972346.0\n", " \n", " \n", " 2003\n", " 56318302.0\n", " 1.011\n", - " 1.040\n", + " 1.04\n", " 0.011\n", " 0.038\n", + " 612761.0\n", + " 2166089.0\n", " 57565344.0\n", " 55930654.0\n", - " 58180367.0\n", - " 58071953.0\n", + " 58178105.0\n", + " 58096743.0\n", " \n", " \n", " 2004\n", @@ -199,10 +239,12 @@ " 1.085\n", " 0.022\n", " 0.078\n", + " 1341021.0\n", + " 4672751.0\n", " 56976657.0\n", " 53774672.0\n", - " 58327568.0\n", - " 58460755.0\n", + " 58317678.0\n", + " 58447423.0\n", " \n", " \n", " 2005\n", @@ -211,51 +253,72 @@ " 1.184\n", " 0.049\n", " 0.155\n", + " 2968528.0\n", + " 9506918.0\n", " 56786410.0\n", " 50644994.0\n", - " 59743817.0\n", - " 60152879.0\n", + " 59754938.0\n", + " 60151912.0\n", " \n", " \n", " 2006\n", " 61926981.0\n", - " 1.110\n", + " 1.11\n", " 1.404\n", " 0.099\n", " 0.288\n", + " 6136908.0\n", + " 17819445.0\n", " 54641339.0\n", " 43606497.0\n", - " 60760348.0\n", - " 61433803.0\n", + " 60778247.0\n", + " 61425942.0\n", " \n", " \n", " 2007\n", " 61864556.0\n", " 1.292\n", - " 2.390\n", + " 2.39\n", " 0.226\n", " 0.582\n", + " 13981773.0\n", + " 35979805.0\n", " 48853563.0\n", " 27229969.0\n", - " 62821457.0\n", - " 63210141.0\n", + " 62835336.0\n", + " 63209774.0\n", + " \n", + " \n", + " Total\n", + " 569281839.0\n", + " \n", + " \n", + " \n", + " \n", + " 25609762.0\n", + " 72517831.0\n", + " 543481587.0\n", + " 498050368.0\n", + " 569091349.0\n", + " 570568199.0\n", " \n", " \n", "\n", "" ], "text/plain": [ - " Expected Claims CDF Reported CDF Paid % Unreported % Unpaid Reported Paid BF Ultimate (Reported) BF Ultimate (Paid)\n", - "1998 51430657.0 1.000 1.002 0.000 0.002 47742304.0 47644187.0 47742304.0 47746843.0\n", - "1999 51408736.0 1.000 1.004 0.000 0.004 51185767.0 51000534.0 51185767.0 51205554.0\n", - "2000 51680983.0 1.001 1.006 0.001 0.006 54837929.0 54533225.0 54889558.0 54842075.0\n", - "2001 54408716.0 1.003 1.011 0.003 0.011 56299562.0 55878421.0 56462408.0 56472644.0\n", - "2002 59421665.0 1.006 1.020 0.006 0.020 58592712.0 57807215.0 58947762.0 58980423.0\n", - "2003 56318302.0 1.011 1.040 0.011 0.038 57565344.0 55930654.0 58180367.0 58071953.0\n", - "2004 59646290.0 1.023 1.085 0.022 0.078 56976657.0 53774672.0 58327568.0 58460755.0\n", - "2005 61174953.0 1.051 1.184 0.049 0.155 56786410.0 50644994.0 59743817.0 60152879.0\n", - "2006 61926981.0 1.110 1.404 0.099 0.288 54641339.0 43606497.0 60760348.0 61433803.0\n", - "2007 61864556.0 1.292 2.390 0.226 0.582 48853563.0 27229969.0 62821457.0 63210141.0" + " Expected Claims CDF Reported CDF Paid % Unreported % Unpaid Expected Unreported Expected Unpaid Reported Claims Paid Claims BF Ultimate (Reported) BF Ultimate (Paid)\n", + "1998 51430657.0 1.0 1.002 0.0 0.002 0.0 102656.0 47742304.0 47644187.0 47742304.0 47746843.0\n", + "1999 51408736.0 1.0 1.004 0.0 0.004 0.0 204816.0 51185767.0 51000534.0 51185767.0 51205350.0\n", + "2000 51680983.0 1.001 1.006 0.001 0.006 51629.0 308236.0 54837929.0 54533225.0 54889558.0 54841461.0\n", + "2001 54408716.0 1.003 1.011 0.003 0.011 162738.0 591984.0 56299562.0 55878421.0 56462300.0 56470405.0\n", + "2002 59421665.0 1.006 1.02 0.006 0.02 354404.0 1165131.0 58592712.0 57807215.0 58947116.0 58972346.0\n", + "2003 56318302.0 1.011 1.04 0.011 0.038 612761.0 2166089.0 57565344.0 55930654.0 58178105.0 58096743.0\n", + "2004 59646290.0 1.023 1.085 0.022 0.078 1341021.0 4672751.0 56976657.0 53774672.0 58317678.0 58447423.0\n", + "2005 61174953.0 1.051 1.184 0.049 0.155 2968528.0 9506918.0 56786410.0 50644994.0 59754938.0 60151912.0\n", + "2006 61926981.0 1.11 1.404 0.099 0.288 6136908.0 17819445.0 54641339.0 43606497.0 60778247.0 61425942.0\n", + "2007 61864556.0 1.292 2.39 0.226 0.582 13981773.0 35979805.0 48853563.0 27229969.0 62835336.0 63209774.0\n", + "Total 569281839.0 25609762.0 72517831.0 543481587.0 498050368.0 569091349.0 570568199.0" ] }, "metadata": {}, @@ -272,40 +335,89 @@ "# tail. Friedland rounds the age-to-age factors to three decimals before\n", "# cumulating them into CDFs.\n", "ia_reported_dev = cl.TailConstant(tail=1.000, projection_period=0).fit_transform(\n", - " cl.Development(n_periods=3, average=\"simple\").fit_transform(ia_reported))\n", + " cl.Development(n_periods=3, average=\"simple\").fit_transform(ia_reported)\n", + ")\n", "ia_paid_dev = cl.TailConstant(tail=1.002, projection_period=0).fit_transform(\n", - " cl.Development(n_periods=3, average=\"simple\").fit_transform(ia_paid))\n", + " cl.Development(n_periods=3, average=\"simple\").fit_transform(ia_paid)\n", + ")\n", "ia_reported_dev.ldf_ = ia_reported_dev.ldf_.round(3)\n", "ia_paid_dev.ldf_ = ia_paid_dev.ldf_.round(3)\n", "\n", "# A priori expected claims from the expected claims technique (Chapter 8, $000).\n", - "ia_expected = np.array([51430657, 51408736, 51680983, 54408716, 59421665,\n", - " 56318302, 59646290, 61174953, 61926981, 61864556], dtype=float)\n", + "ia_expected = np.array(\n", + " [\n", + " 51430657,\n", + " 51408736,\n", + " 51680983,\n", + " 54408716,\n", + " 59421665,\n", + " 56318302,\n", + " 59646290,\n", + " 61174953,\n", + " 61926981,\n", + " 61864556,\n", + " ],\n", + " dtype=float,\n", + ")\n", "ia_apriori = ia_reported.latest_diagonal.copy()\n", "ia_apriori.iloc[0, 0] = ia_expected.reshape(ia_apriori.shape)\n", "\n", - "ia_bf_reported = cl.BornhuetterFerguson(apriori=1.0).fit(ia_reported_dev, sample_weight=ia_apriori)\n", - "ia_bf_paid = cl.BornhuetterFerguson(apriori=1.0).fit(ia_paid_dev, sample_weight=ia_apriori)\n", + "ia_bf_reported = cl.BornhuetterFerguson(apriori=1.0).fit(\n", + " ia_reported_dev, sample_weight=ia_apriori\n", + ")\n", + "ia_bf_paid = cl.BornhuetterFerguson(apriori=1.0).fit(\n", + " ia_paid_dev, sample_weight=ia_apriori\n", + ")\n", "\n", "# model_diagnostics summarises Latest, CDF, Ultimate, and IBNR per accident year.\n", "ia_rep = cl.model_diagnostics(ia_bf_reported).to_frame(origin_as_datetime=False).T\n", "ia_pd = cl.model_diagnostics(ia_bf_paid).to_frame(origin_as_datetime=False).T\n", "\n", - "ia_projection = pd.DataFrame(index=ia_years)\n", - "ia_projection[\"Expected Claims\"] = ia_expected\n", - "# Derive the percentages from the rounded CDFs so the displayed CDF and\n", - "# percentage columns are internally consistent (as in the text).\n", + "ia_rep_latest = ia_rep[\"Latest\"].values\n", + "ia_pd_latest = ia_pd[\"Latest\"].values\n", + "\n", "ia_cdf_reported = ia_rep[\"CDF\"].round(3)\n", "ia_cdf_paid = ia_pd[\"CDF\"].round(3)\n", + "\n", + "pct_unrep_full = 1.0 - 1.0 / ia_cdf_reported.values\n", + "pct_unpaid_full = 1.0 - 1.0 / ia_cdf_paid.values\n", + "\n", + "ia_expected_unrep = np.round(ia_expected * pct_unrep_full)\n", + "ia_expected_unpaid = np.round(ia_expected * pct_unpaid_full)\n", + "\n", + "ia_ult_rep = np.round(ia_expected_unrep + ia_rep_latest)\n", + "ia_ult_pd = np.round(ia_expected_unpaid + ia_pd_latest)\n", + "\n", + "ia_projection = pd.DataFrame(index=ia_years)\n", + "ia_projection[\"Expected Claims\"] = ia_expected\n", "ia_projection[\"CDF Reported\"] = ia_cdf_reported.values\n", "ia_projection[\"CDF Paid\"] = ia_cdf_paid.values\n", - "ia_projection[\"% Unreported\"] = (1 - 1 / ia_cdf_reported).round(3).values\n", - "ia_projection[\"% Unpaid\"] = (1 - 1 / ia_cdf_paid).round(3).values\n", - "ia_projection[\"Reported\"] = ia_rep[\"Latest\"].values\n", - "ia_projection[\"Paid\"] = ia_pd[\"Latest\"].values\n", - "ia_projection[\"BF Ultimate (Reported)\"] = ia_rep[\"Ultimate\"].round(0).values\n", - "ia_projection[\"BF Ultimate (Paid)\"] = ia_pd[\"Ultimate\"].round(0).values\n", - "display(ia_projection)" + "ia_projection[\"% Unreported\"] = np.round(pct_unrep_full, 3)\n", + "ia_projection[\"% Unpaid\"] = np.round(pct_unpaid_full, 3)\n", + "ia_projection[\"Expected Unreported\"] = ia_expected_unrep\n", + "ia_projection[\"Expected Unpaid\"] = ia_expected_unpaid\n", + "ia_projection[\"Reported Claims\"] = ia_rep_latest\n", + "ia_projection[\"Paid Claims\"] = ia_pd_latest\n", + "ia_projection[\"BF Ultimate (Reported)\"] = ia_ult_rep\n", + "ia_projection[\"BF Ultimate (Paid)\"] = ia_ult_pd\n", + "display(add_total_row(ia_projection))" + ] + }, + { + "cell_type": "markdown", + "id": "bd4ca9ca", + "metadata": {}, + "source": [ + "### Column Notes - Exhibit I, Sheet 1\n", + "- **(2) Expected Claims**: Developed in Chapter 8, Exhibit II, Sheet 1.\n", + "- **(3) & (4) CDF Reported / Paid**: Developed in Chapter 7, Exhibit I, Sheets 1 & 2.\n", + "- **(5) % Unreported**: $1.00 - (1.00 / (3))$.\n", + "- **(6) % Unpaid**: $1.00 - (1.00 / (4))$.\n", + "- **(7) Expected Unreported**: $(2) \\times (5)$.\n", + "- **(8) Expected Unpaid**: $(2) \\times (6)$.\n", + "- **(9) & (10) Reported / Paid Claims**: Based on data from U.S. Industry Auto.\n", + "- **(11) BF Ultimate (Reported)**: $(7) + (9)$.\n", + "- **(12) BF Ultimate (Paid)**: $(8) + (10)$.\n" ] }, { @@ -326,10 +438,10 @@ "id": "f4b9a268", "metadata": { "execution": { - "iopub.execute_input": "2026-07-15T04:17:58.121929Z", - "iopub.status.busy": "2026-07-15T04:17:58.121578Z", - "iopub.status.idle": "2026-07-15T04:17:58.168040Z", - "shell.execute_reply": "2026-07-15T04:17:58.166647Z" + "iopub.execute_input": "2026-08-10T06:42:30.729269Z", + "iopub.status.busy": "2026-08-10T06:42:30.728687Z", + "iopub.status.idle": "2026-08-10T06:42:30.758691Z", + "shell.execute_reply": "2026-08-10T06:42:30.757203Z" } }, "outputs": [ @@ -354,6 +466,8 @@ " \n", " \n", " \n", + " Reported Claims\n", + " Paid Claims\n", " BF Ultimate (Reported)\n", " BF Ultimate (Paid)\n", " Case Outstanding\n", @@ -367,6 +481,8 @@ " \n", " 1998\n", " 47742304.0\n", + " 47644187.0\n", + " 47742304.0\n", " 47746843.0\n", " 98117.0\n", " 0.0\n", @@ -377,109 +493,140 @@ " \n", " 1999\n", " 51185767.0\n", - " 51205554.0\n", + " 51000534.0\n", + " 51185767.0\n", + " 51205350.0\n", " 185233.0\n", " 0.0\n", - " 19787.0\n", + " 19583.0\n", " 185233.0\n", - " 205020.0\n", + " 204816.0\n", " \n", " \n", " 2000\n", + " 54837929.0\n", + " 54533225.0\n", " 54889558.0\n", - " 54842075.0\n", + " 54841461.0\n", " 304704.0\n", " 51629.0\n", - " 4146.0\n", + " 3532.0\n", " 356333.0\n", - " 308850.0\n", + " 308236.0\n", " \n", " \n", " 2001\n", - " 56462408.0\n", - " 56472644.0\n", + " 56299562.0\n", + " 55878421.0\n", + " 56462300.0\n", + " 56470405.0\n", " 421141.0\n", - " 162846.0\n", - " 173082.0\n", - " 583987.0\n", - " 594223.0\n", + " 162738.0\n", + " 170843.0\n", + " 583879.0\n", + " 591984.0\n", " \n", " \n", " 2002\n", - " 58947762.0\n", - " 58980423.0\n", + " 58592712.0\n", + " 57807215.0\n", + " 58947116.0\n", + " 58972346.0\n", " 785497.0\n", - " 355050.0\n", - " 387711.0\n", - " 1140547.0\n", - " 1173208.0\n", + " 354404.0\n", + " 379634.0\n", + " 1139901.0\n", + " 1165131.0\n", " \n", " \n", " 2003\n", - " 58180367.0\n", - " 58071953.0\n", + " 57565344.0\n", + " 55930654.0\n", + " 58178105.0\n", + " 58096743.0\n", " 1634690.0\n", - " 615023.0\n", - " 506609.0\n", - " 2249713.0\n", - " 2141299.0\n", + " 612761.0\n", + " 531399.0\n", + " 2247451.0\n", + " 2166089.0\n", " \n", " \n", " 2004\n", - " 58327568.0\n", - " 58460755.0\n", + " 56976657.0\n", + " 53774672.0\n", + " 58317678.0\n", + " 58447423.0\n", " 3201985.0\n", - " 1350911.0\n", - " 1484098.0\n", - " 4552896.0\n", - " 4686083.0\n", + " 1341021.0\n", + " 1470766.0\n", + " 4543006.0\n", + " 4672751.0\n", " \n", " \n", " 2005\n", - " 59743817.0\n", - " 60152879.0\n", + " 56786410.0\n", + " 50644994.0\n", + " 59754938.0\n", + " 60151912.0\n", " 6141416.0\n", - " 2957407.0\n", - " 3366469.0\n", - " 9098823.0\n", - " 9507885.0\n", + " 2968528.0\n", + " 3365502.0\n", + " 9109944.0\n", + " 9506918.0\n", " \n", " \n", " 2006\n", - " 60760348.0\n", - " 61433803.0\n", + " 54641339.0\n", + " 43606497.0\n", + " 60778247.0\n", + " 61425942.0\n", " 11034842.0\n", - " 6119009.0\n", - " 6792464.0\n", - " 17153851.0\n", - " 17827306.0\n", + " 6136908.0\n", + " 6784603.0\n", + " 17171750.0\n", + " 17819445.0\n", " \n", " \n", " 2007\n", - " 62821457.0\n", - " 63210141.0\n", + " 48853563.0\n", + " 27229969.0\n", + " 62835336.0\n", + " 63209774.0\n", " 21623594.0\n", - " 13967894.0\n", - " 14356578.0\n", - " 35591488.0\n", - " 35980172.0\n", + " 13981773.0\n", + " 14356211.0\n", + " 35605367.0\n", + " 35979805.0\n", + " \n", + " \n", + " Total\n", + " 543481587.0\n", + " 498050368.0\n", + " 569091349.0\n", + " 570568199.0\n", + " 45431219.0\n", + " 25609762.0\n", + " 27086612.0\n", + " 71040981.0\n", + " 72517831.0\n", " \n", " \n", "\n", "" ], "text/plain": [ - " BF Ultimate (Reported) BF Ultimate (Paid) Case Outstanding IBNR (Reported) IBNR (Paid) Total Unpaid (Reported) Total Unpaid (Paid)\n", - "1998 47742304.0 47746843.0 98117.0 0.0 4539.0 98117.0 102656.0\n", - "1999 51185767.0 51205554.0 185233.0 0.0 19787.0 185233.0 205020.0\n", - "2000 54889558.0 54842075.0 304704.0 51629.0 4146.0 356333.0 308850.0\n", - "2001 56462408.0 56472644.0 421141.0 162846.0 173082.0 583987.0 594223.0\n", - "2002 58947762.0 58980423.0 785497.0 355050.0 387711.0 1140547.0 1173208.0\n", - "2003 58180367.0 58071953.0 1634690.0 615023.0 506609.0 2249713.0 2141299.0\n", - "2004 58327568.0 58460755.0 3201985.0 1350911.0 1484098.0 4552896.0 4686083.0\n", - "2005 59743817.0 60152879.0 6141416.0 2957407.0 3366469.0 9098823.0 9507885.0\n", - "2006 60760348.0 61433803.0 11034842.0 6119009.0 6792464.0 17153851.0 17827306.0\n", - "2007 62821457.0 63210141.0 21623594.0 13967894.0 14356578.0 35591488.0 35980172.0" + " Reported Claims Paid Claims BF Ultimate (Reported) BF Ultimate (Paid) Case Outstanding IBNR (Reported) IBNR (Paid) Total Unpaid (Reported) Total Unpaid (Paid)\n", + "1998 47742304.0 47644187.0 47742304.0 47746843.0 98117.0 0.0 4539.0 98117.0 102656.0\n", + "1999 51185767.0 51000534.0 51185767.0 51205350.0 185233.0 0.0 19583.0 185233.0 204816.0\n", + "2000 54837929.0 54533225.0 54889558.0 54841461.0 304704.0 51629.0 3532.0 356333.0 308236.0\n", + "2001 56299562.0 55878421.0 56462300.0 56470405.0 421141.0 162738.0 170843.0 583879.0 591984.0\n", + "2002 58592712.0 57807215.0 58947116.0 58972346.0 785497.0 354404.0 379634.0 1139901.0 1165131.0\n", + "2003 57565344.0 55930654.0 58178105.0 58096743.0 1634690.0 612761.0 531399.0 2247451.0 2166089.0\n", + "2004 56976657.0 53774672.0 58317678.0 58447423.0 3201985.0 1341021.0 1470766.0 4543006.0 4672751.0\n", + "2005 56786410.0 50644994.0 59754938.0 60151912.0 6141416.0 2968528.0 3365502.0 9109944.0 9506918.0\n", + "2006 54641339.0 43606497.0 60778247.0 61425942.0 11034842.0 6136908.0 6784603.0 17171750.0 17819445.0\n", + "2007 48853563.0 27229969.0 62835336.0 63209774.0 21623594.0 13981773.0 14356211.0 35605367.0 35979805.0\n", + "Total 543481587.0 498050368.0 569091349.0 570568199.0 45431219.0 25609762.0 27086612.0 71040981.0 72517831.0" ] }, "metadata": {}, @@ -488,14 +635,31 @@ ], "source": [ "ia_unpaid = pd.DataFrame(index=ia_years)\n", - "ia_unpaid[\"BF Ultimate (Reported)\"] = ia_rep[\"Ultimate\"].round(0).values\n", - "ia_unpaid[\"BF Ultimate (Paid)\"] = ia_pd[\"Ultimate\"].round(0).values\n", - "ia_unpaid[\"Case Outstanding\"] = (ia_rep[\"Latest\"] - ia_pd[\"Latest\"]).round(0).values\n", - "ia_unpaid[\"IBNR (Reported)\"] = ia_rep[\"IBNR\"].round(0).values\n", - "ia_unpaid[\"IBNR (Paid)\"] = (ia_pd[\"Ultimate\"] - ia_rep[\"Latest\"]).round(0).values\n", - "ia_unpaid[\"Total Unpaid (Reported)\"] = (ia_rep[\"Ultimate\"] - ia_pd[\"Latest\"]).round(0).values\n", - "ia_unpaid[\"Total Unpaid (Paid)\"] = ia_pd[\"IBNR\"].round(0).values\n", - "display(ia_unpaid)" + "ia_unpaid[\"Reported Claims\"] = ia_rep_latest\n", + "ia_unpaid[\"Paid Claims\"] = ia_pd_latest\n", + "ia_unpaid[\"BF Ultimate (Reported)\"] = ia_ult_rep.round(0)\n", + "ia_unpaid[\"BF Ultimate (Paid)\"] = ia_ult_pd.round(0)\n", + "ia_unpaid[\"Case Outstanding\"] = (ia_rep_latest - ia_pd_latest).round(0)\n", + "ia_unpaid[\"IBNR (Reported)\"] = (ia_ult_rep - ia_rep_latest).round(0)\n", + "ia_unpaid[\"IBNR (Paid)\"] = (ia_ult_pd - ia_rep_latest).round(0)\n", + "ia_unpaid[\"Total Unpaid (Reported)\"] = (ia_ult_rep - ia_pd_latest).round(0)\n", + "ia_unpaid[\"Total Unpaid (Paid)\"] = (ia_ult_pd - ia_pd_latest).round(0)\n", + "display(add_total_row(ia_unpaid))" + ] + }, + { + "cell_type": "markdown", + "id": "eb38cbf6", + "metadata": {}, + "source": [ + "### Column Notes - Exhibit I, Sheet 2\n", + "- **(2) & (3) Reported / Paid Claims**: Based on data from U.S. Industry Auto.\n", + "- **(4) & (5) BF Ultimate (Reported / Paid)**: Developed in Exhibit I, Sheet 1.\n", + "- **(6) Case Outstanding**: $(2) - (3)$.\n", + "- **(7) IBNR (Reported)**: $(4) - (2)$.\n", + "- **(8) IBNR (Paid)**: $(5) - (2)$.\n", + "- **(9) Total Unpaid (Reported)**: $(6) + (7) = (4) - (3)$.\n", + "- **(10) Total Unpaid (Paid)**: $(6) + (8) = (5) - (3)$.\n" ] }, { @@ -515,22 +679,28 @@ "id": "64c79fa2", "metadata": { "execution": { - "iopub.execute_input": "2026-07-15T04:17:58.171738Z", - "iopub.status.busy": "2026-07-15T04:17:58.171341Z", - "iopub.status.idle": "2026-07-15T04:17:58.182778Z", - "shell.execute_reply": "2026-07-15T04:17:58.181879Z" + "iopub.execute_input": "2026-08-10T06:42:30.762141Z", + "iopub.status.busy": "2026-08-10T06:42:30.761780Z", + "iopub.status.idle": "2026-08-10T06:42:30.769118Z", + "shell.execute_reply": "2026-08-10T06:42:30.768330Z" } }, "outputs": [], "source": [ "# Exhibit I, Sheet 1 - selected CDFs to ultimate\n", - "assert np.allclose(ia_projection[\"CDF Reported\"].values,\n", - " [1.000, 1.000, 1.001, 1.003, 1.006, 1.011, 1.023, 1.051, 1.110, 1.292], atol=1e-3)\n", - "assert np.allclose(ia_projection[\"CDF Paid\"].values,\n", - " [1.002, 1.004, 1.006, 1.011, 1.020, 1.040, 1.085, 1.184, 1.404, 2.390], atol=1e-3)\n", + "assert np.allclose(\n", + " ia_projection[\"CDF Reported\"].values,\n", + " [1.000, 1.000, 1.001, 1.003, 1.006, 1.011, 1.023, 1.051, 1.110, 1.292],\n", + " atol=1e-3,\n", + ")\n", + "assert np.allclose(\n", + " ia_projection[\"CDF Paid\"].values,\n", + " [1.002, 1.004, 1.006, 1.011, 1.020, 1.040, 1.085, 1.184, 1.404, 2.390],\n", + " atol=1e-3,\n", + ")\n", "# Exhibit I, Sheet 1 - projected ultimate claims (reconcile within rounding tolerance)\n", - "assert np.isclose(ia_rep[\"Ultimate\"].sum(), 569091348, rtol=5e-3)\n", - "assert np.isclose(ia_pd[\"Ultimate\"].sum(), 570568198, rtol=5e-3)\n", + "assert np.isclose(ia_projection[\"BF Ultimate (Reported)\"].sum(), 569091348, rtol=5e-3)\n", + "assert np.isclose(ia_projection[\"BF Ultimate (Paid)\"].sum(), 570568198, rtol=5e-3)\n", "# Exhibit I, Sheet 2 - estimated IBNR\n", "assert np.isclose(ia_unpaid[\"IBNR (Reported)\"].sum(), 25609761, rtol=5e-3)\n", "assert np.isclose(ia_unpaid[\"IBNR (Paid)\"].sum(), 27086611, rtol=5e-3)" @@ -541,7 +711,7 @@ "id": "1b13e9ad", "metadata": {}, "source": [ - "## Exhibit II — XYZ Insurer (Auto BI)\n", + "## Exhibit II - XYZ Insurer (Auto BI)\n", "\n", "Exhibit II applies the same Bornhuetter-Ferguson method to the **XYZ Insurer -\n", "Auto BI** data, valued at 12/31/2008 over accident years 1998-2008." @@ -552,7 +722,7 @@ "id": "8d5d61e0", "metadata": {}, "source": [ - "## The data\n", + "### The data\n", "\n", "The XYZ Insurer Auto BI reported and paid triangles run from accident year 1998\n", "to 2008. Their most recent diagonal (12/31/2008) is the actual claims the BF\n", @@ -565,10 +735,10 @@ "id": "2cd269eb", "metadata": { "execution": { - "iopub.execute_input": "2026-07-15T04:17:58.187121Z", - "iopub.status.busy": "2026-07-15T04:17:58.186637Z", - "iopub.status.idle": "2026-07-15T04:17:58.264128Z", - "shell.execute_reply": "2026-07-15T04:17:58.262925Z" + "iopub.execute_input": "2026-08-10T06:42:30.774397Z", + "iopub.status.busy": "2026-08-10T06:42:30.773062Z", + "iopub.status.idle": "2026-08-10T06:42:30.843423Z", + "shell.execute_reply": "2026-08-10T06:42:30.842326Z" } }, "outputs": [ @@ -682,7 +852,11 @@ "paid = tri[\"Paid Claims\"]\n", "years = list(reported.origin.year)\n", "\n", - "col = lambda t: t.to_frame(origin_as_datetime=False).iloc[:, 0].values\n", + "\n", + "def col(t):\n", + " return t.to_frame(origin_as_datetime=False).iloc[:, 0].values\n", + "\n", + "\n", "reported_latest = col(reported.latest_diagonal)\n", "paid_latest = col(paid.latest_diagonal)\n", "\n", @@ -697,7 +871,7 @@ "id": "e58c06e4", "metadata": {}, "source": [ - "## Development patterns\n", + "### Development patterns\n", "\n", "The BF method reuses the development pattern selected for XYZ in Chapter 7: a\n", "volume-weighted two-period average with a 1.000 reported tail and a 1.010 paid\n", @@ -713,10 +887,10 @@ "id": "0c81b46d", "metadata": { "execution": { - "iopub.execute_input": "2026-07-15T04:17:58.267719Z", - "iopub.status.busy": "2026-07-15T04:17:58.267369Z", - "iopub.status.idle": "2026-07-15T04:17:58.972705Z", - "shell.execute_reply": "2026-07-15T04:17:58.972023Z" + "iopub.execute_input": "2026-08-10T06:42:30.847873Z", + "iopub.status.busy": "2026-08-10T06:42:30.846856Z", + "iopub.status.idle": "2026-08-10T06:42:31.635225Z", + "shell.execute_reply": "2026-08-10T06:42:31.634256Z" } }, "outputs": [ @@ -850,7 +1024,7 @@ ], "source": [ "# Reuse the Chapter 7 XYZ selection. The fitted reported and paid development\n", - "# estimators were persisted in Chapter 7 with to_pickle; recall them here with\n", + "# estimators were persisted in Chapter 7 with to_json; recall them here with\n", "# read_json instead of refitting, keeping the two chapters in sync. JSON is\n", "# used rather than a pickle so the saved estimators load reliably across\n", "# package and dependency versions.\n", @@ -865,7 +1039,9 @@ "paid_dev.ldf_ = paid_dev.ldf_.round(3)\n", "\n", "# CDF to ultimate per accident year (oldest origin -> highest maturity).\n", - "reported_cdf = np.maximum(reported_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten()[::-1], 1.0)\n", + "reported_cdf = np.maximum(\n", + " reported_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten()[::-1], 1.0\n", + ")\n", "paid_cdf = paid_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten()[::-1]\n", "pct_unreported = 1 - 1 / reported_cdf\n", "pct_unpaid = 1 - 1 / paid_cdf\n", @@ -883,7 +1059,7 @@ "id": "f7e34483", "metadata": {}, "source": [ - "## Expected claims (a priori)\n", + "### Expected claims (a priori)\n", "\n", "The a priori expected claims come from the expected claims technique (Chapter 8):\n", "earned premium multiplied by a selected claim ratio. The earned premium is now\n", @@ -897,10 +1073,10 @@ "id": "0aa8a19c", "metadata": { "execution": { - "iopub.execute_input": "2026-07-15T04:17:58.978356Z", - "iopub.status.busy": "2026-07-15T04:17:58.976348Z", - "iopub.status.idle": "2026-07-15T04:17:59.004385Z", - "shell.execute_reply": "2026-07-15T04:17:59.003703Z" + "iopub.execute_input": "2026-08-10T06:42:31.639539Z", + "iopub.status.busy": "2026-08-10T06:42:31.638884Z", + "iopub.status.idle": "2026-08-10T06:42:31.668903Z", + "shell.execute_reply": "2026-08-10T06:42:31.667671Z" } }, "outputs": [ @@ -1025,7 +1201,19 @@ "earned_premium = col(tri[\"Earned Premium\"].latest_diagonal)\n", "\n", "# A priori expected claims from the expected claims technique (Chapter 8, $000).\n", - "expected_claims = [15670, 24680, 35256, 39174, 47935, 54197, 86528, 108241, 70769, 39841, 39429]\n", + "expected_claims = [\n", + " 15670,\n", + " 24680,\n", + " 35256,\n", + " 39174,\n", + " 47935,\n", + " 54197,\n", + " 86528,\n", + " 108241,\n", + " 70769,\n", + " 39841,\n", + " 39429,\n", + "]\n", "\n", "\n", "def as_diagonal(tri, vec):\n", @@ -1049,7 +1237,7 @@ "id": "c683f4d9", "metadata": {}, "source": [ - "## Projection of ultimate claims\n", + "### Projection of ultimate claims\n", "\n", "Applying `BornhuetterFerguson` on both the reported and paid bases produces the\n", "projected ultimate claims. The reported IBNR is floored at zero for the capped\n", @@ -1062,10 +1250,10 @@ "id": "a758ad45", "metadata": { "execution": { - "iopub.execute_input": "2026-07-15T04:17:59.008039Z", - "iopub.status.busy": "2026-07-15T04:17:59.007693Z", - "iopub.status.idle": "2026-07-15T04:17:59.156134Z", - "shell.execute_reply": "2026-07-15T04:17:59.154874Z" + "iopub.execute_input": "2026-08-10T06:42:31.672802Z", + "iopub.status.busy": "2026-08-10T06:42:31.672210Z", + "iopub.status.idle": "2026-08-10T06:42:31.793486Z", + "shell.execute_reply": "2026-08-10T06:42:31.791088Z" } }, "outputs": [ @@ -1090,14 +1278,15 @@ " \n", " \n", " \n", - " Reported\n", - " Paid\n", + " Expected Claims\n", " CDF Reported\n", " CDF Paid\n", " % Unreported\n", " % Unpaid\n", - " Earned Premium\n", - " Expected Claims\n", + " Expected Unreported\n", + " Expected Unpaid\n", + " Reported Claims\n", + " Paid Claims\n", " BF Ultimate (Reported)\n", " BF Ultimate (Paid)\n", " \n", @@ -1105,164 +1294,190 @@ " \n", " \n", " 1998\n", + " 15670\n", + " 1.0\n", + " 1.01\n", + " 0.0\n", + " 0.01\n", + " 0.0\n", + " 155.0\n", " 15822.0\n", " 15822.0\n", - " 1.000\n", - " 1.010\n", - " 0.000\n", - " 0.010\n", - " 20000.0\n", - " 15670\n", " 15822.0\n", " 15977.0\n", " \n", " \n", " 1999\n", - " 25107.0\n", - " 24817.0\n", - " 1.000\n", + " 24680\n", + " 1.0\n", " 1.014\n", - " 0.000\n", + " 0.0\n", " 0.014\n", - " 31500.0\n", - " 24680\n", + " 0.0\n", + " 342.0\n", + " 25107.0\n", + " 24817.0\n", " 25107.0\n", " 25159.0\n", " \n", " \n", " 2000\n", + " 35256\n", + " 1.0\n", + " 1.031\n", + " 0.0\n", + " 0.03\n", + " 0.0\n", + " 1069.0\n", " 37246.0\n", " 36782.0\n", - " 1.000\n", - " 1.031\n", - " 0.000\n", - " 0.030\n", - " 45000.0\n", - " 35256\n", " 37246.0\n", " 37851.0\n", " \n", " \n", " 2001\n", - " 38798.0\n", - " 38519.0\n", - " 1.000\n", + " 39174\n", + " 1.0\n", " 1.054\n", - " 0.000\n", + " 0.0\n", " 0.051\n", - " 50000.0\n", - " 39174\n", + " 0.0\n", + " 2006.0\n", + " 38798.0\n", + " 38519.0\n", " 38798.0\n", " 40525.0\n", " \n", " \n", " 2002\n", - " 48169.0\n", - " 44437.0\n", + " 47935\n", " 1.003\n", " 1.116\n", " 0.003\n", " 0.104\n", - " 61183.0\n", - " 47935\n", + " 140.0\n", + " 4988.0\n", + " 48169.0\n", + " 44437.0\n", " 48309.0\n", " 49425.0\n", " \n", " \n", " 2003\n", - " 44373.0\n", - " 39320.0\n", + " 54197\n", " 1.013\n", " 1.268\n", " 0.013\n", " 0.211\n", - " 69175.0\n", - " 54197\n", + " 693.0\n", + " 11453.0\n", + " 44373.0\n", + " 39320.0\n", " 45066.0\n", " 50773.0\n", " \n", " \n", " 2004\n", - " 70288.0\n", - " 52811.0\n", + " 86528\n", " 1.064\n", " 1.525\n", - " 0.060\n", + " 0.06\n", " 0.344\n", - " 99322.0\n", - " 86528\n", + " 5174.0\n", + " 29801.0\n", + " 70288.0\n", + " 52811.0\n", " 75462.0\n", " 82612.0\n", " \n", " \n", " 2005\n", - " 70655.0\n", - " 40026.0\n", + " 108241\n", " 1.085\n", " 2.007\n", " 0.078\n", " 0.502\n", - " 138151.0\n", - " 108241\n", + " 8468.0\n", + " 54319.0\n", + " 70655.0\n", + " 40026.0\n", " 79123.0\n", " 94345.0\n", " \n", " \n", " 2006\n", - " 48804.0\n", - " 22819.0\n", + " 70769\n", " 1.196\n", - " 3.160\n", + " 3.16\n", " 0.164\n", " 0.684\n", - " 107578.0\n", - " 70769\n", + " 11574.0\n", + " 48371.0\n", + " 48804.0\n", + " 22819.0\n", " 60378.0\n", " 71190.0\n", " \n", " \n", " 2007\n", - " 31732.0\n", - " 11865.0\n", + " 39841\n", " 1.512\n", " 6.569\n", " 0.339\n", " 0.848\n", - " 62438.0\n", - " 39841\n", + " 13497.0\n", + " 33776.0\n", + " 31732.0\n", + " 11865.0\n", " 45229.0\n", " 45641.0\n", " \n", " \n", " 2008\n", - " 18632.0\n", - " 3409.0\n", + " 39429\n", " 2.551\n", " 21.999\n", " 0.608\n", " 0.955\n", - " 47797.0\n", - " 39429\n", + " 23975.0\n", + " 37637.0\n", + " 18632.0\n", + " 3409.0\n", " 42607.0\n", " 41046.0\n", " \n", + " \n", + " Total\n", + " 561720\n", + " \n", + " \n", + " \n", + " \n", + " 63521.0\n", + " 223917.0\n", + " 449626.0\n", + " 330627.0\n", + " 513147.0\n", + " 554544.0\n", + " \n", " \n", "\n", "" ], "text/plain": [ - " Reported Paid CDF Reported CDF Paid % Unreported % Unpaid Earned Premium Expected Claims BF Ultimate (Reported) BF Ultimate (Paid)\n", - "1998 15822.0 15822.0 1.000 1.010 0.000 0.010 20000.0 15670 15822.0 15977.0\n", - "1999 25107.0 24817.0 1.000 1.014 0.000 0.014 31500.0 24680 25107.0 25159.0\n", - "2000 37246.0 36782.0 1.000 1.031 0.000 0.030 45000.0 35256 37246.0 37851.0\n", - "2001 38798.0 38519.0 1.000 1.054 0.000 0.051 50000.0 39174 38798.0 40525.0\n", - "2002 48169.0 44437.0 1.003 1.116 0.003 0.104 61183.0 47935 48309.0 49425.0\n", - "2003 44373.0 39320.0 1.013 1.268 0.013 0.211 69175.0 54197 45066.0 50773.0\n", - "2004 70288.0 52811.0 1.064 1.525 0.060 0.344 99322.0 86528 75462.0 82612.0\n", - "2005 70655.0 40026.0 1.085 2.007 0.078 0.502 138151.0 108241 79123.0 94345.0\n", - "2006 48804.0 22819.0 1.196 3.160 0.164 0.684 107578.0 70769 60378.0 71190.0\n", - "2007 31732.0 11865.0 1.512 6.569 0.339 0.848 62438.0 39841 45229.0 45641.0\n", - "2008 18632.0 3409.0 2.551 21.999 0.608 0.955 47797.0 39429 42607.0 41046.0" + " Expected Claims CDF Reported CDF Paid % Unreported % Unpaid Expected Unreported Expected Unpaid Reported Claims Paid Claims BF Ultimate (Reported) BF Ultimate (Paid)\n", + "1998 15670 1.0 1.01 0.0 0.01 0.0 155.0 15822.0 15822.0 15822.0 15977.0\n", + "1999 24680 1.0 1.014 0.0 0.014 0.0 342.0 25107.0 24817.0 25107.0 25159.0\n", + "2000 35256 1.0 1.031 0.0 0.03 0.0 1069.0 37246.0 36782.0 37246.0 37851.0\n", + "2001 39174 1.0 1.054 0.0 0.051 0.0 2006.0 38798.0 38519.0 38798.0 40525.0\n", + "2002 47935 1.003 1.116 0.003 0.104 140.0 4988.0 48169.0 44437.0 48309.0 49425.0\n", + "2003 54197 1.013 1.268 0.013 0.211 693.0 11453.0 44373.0 39320.0 45066.0 50773.0\n", + "2004 86528 1.064 1.525 0.06 0.344 5174.0 29801.0 70288.0 52811.0 75462.0 82612.0\n", + "2005 108241 1.085 2.007 0.078 0.502 8468.0 54319.0 70655.0 40026.0 79123.0 94345.0\n", + "2006 70769 1.196 3.16 0.164 0.684 11574.0 48371.0 48804.0 22819.0 60378.0 71190.0\n", + "2007 39841 1.512 6.569 0.339 0.848 13497.0 33776.0 31732.0 11865.0 45229.0 45641.0\n", + "2008 39429 2.551 21.999 0.608 0.955 23975.0 37637.0 18632.0 3409.0 42607.0 41046.0\n", + "Total 561720 63521.0 223917.0 449626.0 330627.0 513147.0 554544.0" ] }, "metadata": {}, @@ -1270,25 +1485,48 @@ } ], "source": [ - "bf_reported = cl.BornhuetterFerguson(apriori=1.0).fit(reported_dev, sample_weight=apriori)\n", + "bf_reported = cl.BornhuetterFerguson(apriori=1.0).fit(\n", + " reported_dev, sample_weight=apriori\n", + ")\n", "bf_paid = cl.BornhuetterFerguson(apriori=1.0).fit(paid_dev, sample_weight=apriori)\n", "\n", "reported_ibnr = np.nan_to_num(np.maximum(col(bf_reported.ibnr_), 0.0))\n", "reported_ult = reported_latest + reported_ibnr\n", "paid_ult = col(bf_paid.ultimate_)\n", "\n", + "exp_unrep_xyz = np.round(np.array(expected_claims) * pct_unreported)\n", + "exp_unpaid_xyz = np.round(np.array(expected_claims) * pct_unpaid)\n", + "\n", "projection = pd.DataFrame(index=years)\n", - "projection[\"Reported\"] = reported_latest\n", - "projection[\"Paid\"] = paid_latest\n", + "projection[\"Expected Claims\"] = expected_claims\n", "projection[\"CDF Reported\"] = reported_cdf.round(3)\n", "projection[\"CDF Paid\"] = paid_cdf.round(3)\n", "projection[\"% Unreported\"] = pct_unreported.round(3)\n", "projection[\"% Unpaid\"] = pct_unpaid.round(3)\n", - "projection[\"Earned Premium\"] = earned_premium\n", - "projection[\"Expected Claims\"] = expected_claims\n", + "projection[\"Expected Unreported\"] = exp_unrep_xyz\n", + "projection[\"Expected Unpaid\"] = exp_unpaid_xyz\n", + "projection[\"Reported Claims\"] = reported_latest\n", + "projection[\"Paid Claims\"] = paid_latest\n", "projection[\"BF Ultimate (Reported)\"] = reported_ult.round(0)\n", "projection[\"BF Ultimate (Paid)\"] = paid_ult.round(0)\n", - "display(projection)" + "display(add_total_row(projection))" + ] + }, + { + "cell_type": "markdown", + "id": "6c60387e", + "metadata": {}, + "source": [ + "### Column Notes - Exhibit II, Sheet 1\n", + "- **(2) Expected Claims**: Developed in Chapter 8, Exhibit III, Sheet 1.\n", + "- **(3) & (4) CDF Reported / Paid**: Developed in Chapter 7, Exhibit II, Sheets 1 & 2 (capped at 1.000 minimum).\n", + "- **(5) % Unreported**: $1.00 - (1.00 / (3))$.\n", + "- **(6) % Unpaid**: $1.00 - (1.00 / (4))$.\n", + "- **(7) Expected Unreported**: $(2) \\times (5)$.\n", + "- **(8) Expected Unpaid**: $(2) \\times (6)$.\n", + "- **(9) & (10) Reported / Paid Claims**: Based on data from XYZ Insurer.\n", + "- **(11) BF Ultimate (Reported)**: $(7) + (9)$.\n", + "- **(12) BF Ultimate (Paid)**: $(8) + (10)$.\n" ] }, { @@ -1296,7 +1534,7 @@ "id": "db8655a7", "metadata": {}, "source": [ - "## Development of unpaid claim estimate\n", + "### Development of unpaid claim estimate\n", "\n", "From the projected ultimates, the IBNR and total unpaid estimates follow by\n", "simple differences: IBNR is ultimate minus reported claims, and total unpaid is\n", @@ -1309,10 +1547,10 @@ "id": "7ced2979", "metadata": { "execution": { - "iopub.execute_input": "2026-07-15T04:17:59.159708Z", - "iopub.status.busy": "2026-07-15T04:17:59.159264Z", - "iopub.status.idle": "2026-07-15T04:17:59.187779Z", - "shell.execute_reply": "2026-07-15T04:17:59.187158Z" + "iopub.execute_input": "2026-08-10T06:42:31.797843Z", + "iopub.status.busy": "2026-08-10T06:42:31.797431Z", + "iopub.status.idle": "2026-08-10T06:42:31.823963Z", + "shell.execute_reply": "2026-08-10T06:42:31.823411Z" } }, "outputs": [ @@ -1337,6 +1575,8 @@ " \n", " \n", " \n", + " Reported Claims\n", + " Paid Claims\n", " BF Ultimate (Reported)\n", " BF Ultimate (Paid)\n", " Case Outstanding\n", @@ -1350,6 +1590,8 @@ " \n", " 1998\n", " 15822.0\n", + " 15822.0\n", + " 15822.0\n", " 15977.0\n", " 0.0\n", " 0.0\n", @@ -1360,6 +1602,8 @@ " \n", " 1999\n", " 25107.0\n", + " 24817.0\n", + " 25107.0\n", " 25159.0\n", " 290.0\n", " 0.0\n", @@ -1370,6 +1614,8 @@ " \n", " 2000\n", " 37246.0\n", + " 36782.0\n", + " 37246.0\n", " 37851.0\n", " 464.0\n", " 0.0\n", @@ -1380,6 +1626,8 @@ " \n", " 2001\n", " 38798.0\n", + " 38519.0\n", + " 38798.0\n", " 40525.0\n", " 279.0\n", " 0.0\n", @@ -1389,6 +1637,8 @@ " \n", " \n", " 2002\n", + " 48169.0\n", + " 44437.0\n", " 48309.0\n", " 49425.0\n", " 3732.0\n", @@ -1399,6 +1649,8 @@ " \n", " \n", " 2003\n", + " 44373.0\n", + " 39320.0\n", " 45066.0\n", " 50773.0\n", " 5053.0\n", @@ -1409,6 +1661,8 @@ " \n", " \n", " 2004\n", + " 70288.0\n", + " 52811.0\n", " 75462.0\n", " 82612.0\n", " 17477.0\n", @@ -1419,6 +1673,8 @@ " \n", " \n", " 2005\n", + " 70655.0\n", + " 40026.0\n", " 79123.0\n", " 94345.0\n", " 30629.0\n", @@ -1429,6 +1685,8 @@ " \n", " \n", " 2006\n", + " 48804.0\n", + " 22819.0\n", " 60378.0\n", " 71190.0\n", " 25985.0\n", @@ -1439,6 +1697,8 @@ " \n", " \n", " 2007\n", + " 31732.0\n", + " 11865.0\n", " 45229.0\n", " 45641.0\n", " 19867.0\n", @@ -1449,6 +1709,8 @@ " \n", " \n", " 2008\n", + " 18632.0\n", + " 3409.0\n", " 42607.0\n", " 41046.0\n", " 15223.0\n", @@ -1457,23 +1719,36 @@ " 39198.0\n", " 37637.0\n", " \n", + " \n", + " Total\n", + " 449626.0\n", + " 330627.0\n", + " 513147.0\n", + " 554544.0\n", + " 118999.0\n", + " 63521.0\n", + " 104918.0\n", + " 182520.0\n", + " 223917.0\n", + " \n", " \n", "\n", "" ], "text/plain": [ - " BF Ultimate (Reported) BF Ultimate (Paid) Case Outstanding IBNR (Reported) IBNR (Paid) Total Unpaid (Reported) Total Unpaid (Paid)\n", - "1998 15822.0 15977.0 0.0 0.0 155.0 0.0 155.0\n", - "1999 25107.0 25159.0 290.0 0.0 52.0 290.0 342.0\n", - "2000 37246.0 37851.0 464.0 0.0 605.0 464.0 1069.0\n", - "2001 38798.0 40525.0 279.0 0.0 1727.0 279.0 2006.0\n", - "2002 48309.0 49425.0 3732.0 140.0 1256.0 3872.0 4988.0\n", - "2003 45066.0 50773.0 5053.0 693.0 6400.0 5746.0 11453.0\n", - "2004 75462.0 82612.0 17477.0 5174.0 12324.0 22651.0 29801.0\n", - "2005 79123.0 94345.0 30629.0 8468.0 23690.0 39097.0 54319.0\n", - "2006 60378.0 71190.0 25985.0 11574.0 22386.0 37559.0 48371.0\n", - "2007 45229.0 45641.0 19867.0 13497.0 13909.0 33364.0 33776.0\n", - "2008 42607.0 41046.0 15223.0 23975.0 22414.0 39198.0 37637.0" + " Reported Claims Paid Claims BF Ultimate (Reported) BF Ultimate (Paid) Case Outstanding IBNR (Reported) IBNR (Paid) Total Unpaid (Reported) Total Unpaid (Paid)\n", + "1998 15822.0 15822.0 15822.0 15977.0 0.0 0.0 155.0 0.0 155.0\n", + "1999 25107.0 24817.0 25107.0 25159.0 290.0 0.0 52.0 290.0 342.0\n", + "2000 37246.0 36782.0 37246.0 37851.0 464.0 0.0 605.0 464.0 1069.0\n", + "2001 38798.0 38519.0 38798.0 40525.0 279.0 0.0 1727.0 279.0 2006.0\n", + "2002 48169.0 44437.0 48309.0 49425.0 3732.0 140.0 1256.0 3872.0 4988.0\n", + "2003 44373.0 39320.0 45066.0 50773.0 5053.0 693.0 6400.0 5746.0 11453.0\n", + "2004 70288.0 52811.0 75462.0 82612.0 17477.0 5174.0 12324.0 22651.0 29801.0\n", + "2005 70655.0 40026.0 79123.0 94345.0 30629.0 8468.0 23690.0 39097.0 54319.0\n", + "2006 48804.0 22819.0 60378.0 71190.0 25985.0 11574.0 22386.0 37559.0 48371.0\n", + "2007 31732.0 11865.0 45229.0 45641.0 19867.0 13497.0 13909.0 33364.0 33776.0\n", + "2008 18632.0 3409.0 42607.0 41046.0 15223.0 23975.0 22414.0 39198.0 37637.0\n", + "Total 449626.0 330627.0 513147.0 554544.0 118999.0 63521.0 104918.0 182520.0 223917.0" ] }, "metadata": {}, @@ -1482,6 +1757,8 @@ ], "source": [ "unpaid = pd.DataFrame(index=years)\n", + "unpaid[\"Reported Claims\"] = reported_latest\n", + "unpaid[\"Paid Claims\"] = paid_latest\n", "unpaid[\"BF Ultimate (Reported)\"] = reported_ult.round(0)\n", "unpaid[\"BF Ultimate (Paid)\"] = paid_ult.round(0)\n", "unpaid[\"Case Outstanding\"] = (reported_latest - paid_latest).round(0)\n", @@ -1489,99 +1766,2981 @@ "unpaid[\"IBNR (Paid)\"] = (paid_ult - reported_latest).round(0)\n", "unpaid[\"Total Unpaid (Reported)\"] = (reported_ult - paid_latest).round(0)\n", "unpaid[\"Total Unpaid (Paid)\"] = (paid_ult - paid_latest).round(0)\n", - "display(unpaid)" + "display(add_total_row(unpaid))" ] }, { "cell_type": "markdown", - "id": "b135b0ef", + "id": "3ed77340", "metadata": {}, "source": [ - "## Reconciliation to Friedland\n", - "\n", - "The selected CDFs, the projected ultimate claims, and the IBNR estimates are\n", - "reconciled to the printed Chapter 9 exhibit below (values in $000)." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "7ee5e130", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-15T04:17:59.190099Z", - "iopub.status.busy": "2026-07-15T04:17:59.189820Z", - "iopub.status.idle": "2026-07-15T04:17:59.200373Z", - "shell.execute_reply": "2026-07-15T04:17:59.199165Z" - } - }, - "outputs": [], - "source": [ - "# Selected CDFs to ultimate\n", - "assert np.allclose(reported_cdf.round(3),\n", - " [1.000, 1.000, 1.000, 1.000, 1.003, 1.013, 1.064, 1.085, 1.196, 1.512, 2.551], atol=1e-3)\n", - "assert np.allclose(paid_cdf.round(3),\n", - " [1.010, 1.014, 1.031, 1.054, 1.116, 1.268, 1.525, 2.007, 3.160, 6.569, 21.999], atol=1e-3)\n", - "# Projected ultimate claims\n", - "assert np.allclose(reported_ult,\n", - " [15822, 25107, 37246, 38798, 48309, 45066, 75462, 79123, 60378, 45229, 42607], atol=1)\n", - "assert np.allclose(paid_ult,\n", - " [15977, 25159, 37851, 40525, 49425, 50773, 82612, 94345, 71190, 45641, 41046], atol=1)\n", - "# IBNR (ultimate minus reported)\n", - "assert np.allclose(unpaid[\"IBNR (Reported)\"].values,\n", - " [0, 0, 0, 0, 140, 693, 5174, 8468, 11574, 13497, 23975], atol=1)\n", - "assert np.allclose(unpaid[\"IBNR (Paid)\"].values,\n", - " [155, 52, 605, 1727, 1256, 6400, 12324, 23690, 22386, 13909, 22414], atol=1)" + "### Column Notes - Exhibit II, Sheet 2\n", + "- **(2) & (3) Reported / Paid Claims**: Based on data from XYZ Insurer.\n", + "- **(4) & (5) BF Ultimate (Reported / Paid)**: Developed in Exhibit II, Sheet 1.\n", + "- **(6) Case Outstanding**: $(2) - (3)$.\n", + "- **(7) IBNR (Reported)**: $(4) - (2)$.\n", + "- **(8) IBNR (Paid)**: $(5) - (2)$.\n", + "- **(9) Total Unpaid (Reported)**: $(6) + (7) = (4) - (3)$.\n", + "- **(10) Total Unpaid (Paid)**: $(6) + (8) = (5) - (3)$.\n" ] }, { "cell_type": "markdown", - "id": "c28f0940", + "id": "ex2_summary_md", "metadata": {}, "source": [ - "## Exhibit III — U.S. PP Auto (Impact of Changing Conditions)\n", - "\n", - "Exhibit III applies the Bornhuetter-Ferguson method to the four **U.S. PP Auto**\n", - "scenarios that Friedland uses to study a changing environment (valued at\n", - "12/31/2008, accident years 1999-2008):\n", - "\n", - "1. Steady-State\n", - "2. Increasing Claim Ratios\n", - "3. Increasing Case Outstanding Strength\n", - "4. Increasing Claim Ratios and Case Outstanding Strength\n", + "### Summary of ultimate claims and IBNR\n", "\n", - "All four scenarios share the same a priori expected claims (a 70% expected claim\n", - "ratio applied to earned premium, from Chapter 8) and the same five-year simple\n", - "average development selection from Chapter 7. Because Friedland rounds both the\n", - "cumulative development factors and the resulting percentages, we fold the rounded\n", - "percentages into an effective CDF so `BornhuetterFerguson` reproduces the text.\n", - "\n", - "> **Note on sample data.** The reported figures for the two *case outstanding\n", - "> strength* scenarios do not yet reconcile exactly. The reported development in\n", - "> the `friedland_uspp_auto_increasing_case` and `friedland_uspp_increasing_claim_case`\n", - "> samples differs slightly from the text (a known data correction to these CSVs\n", - "> is still outstanding), so the reconciliation below asserts the reported basis\n", - "> only for the two scenarios with clean data, and the paid basis for all four." + "This recreates *Exhibit II, Sheet 3* (summary of ultimate claims across methods)\n", + "and *Exhibit II, Sheet 4* (summary of estimated IBNR across methods)." ] }, { "cell_type": "code", - "execution_count": 11, - "id": "9f35cbe8", + "execution_count": 10, + "id": "ex2_summary_code", "metadata": { "execution": { - "iopub.execute_input": "2026-07-15T04:17:59.203677Z", - "iopub.status.busy": "2026-07-15T04:17:59.203414Z", - "iopub.status.idle": "2026-07-15T04:18:00.615179Z", - "shell.execute_reply": "2026-07-15T04:18:00.614296Z" + "iopub.execute_input": "2026-08-10T06:42:31.829732Z", + "iopub.status.busy": "2026-08-10T06:42:31.829441Z", + "iopub.status.idle": "2026-08-10T06:42:31.890917Z", + "shell.execute_reply": "2026-08-10T06:42:31.890376Z" } }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Steady-State\n" + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Reported ClaimsPaid ClaimsDev Method (Reported)Dev Method (Paid)Expected ClaimsBF Method (Reported)BF Method (Paid)
199815822.015822.015822.015980.01567015822.015977.0
199925107.024817.025082.025165.02468025107.025159.0
200037246.036782.036948.037932.03525637246.037851.0
200138798.038519.038488.040598.03917438798.040525.0
200248169.044437.048310.049598.04793548309.049425.0
200344373.039320.044948.049856.05419745066.050773.0
200470288.052811.074758.080555.08652875462.082612.0
200570655.040026.076651.080346.010824179123.094345.0
200648804.022819.058346.072098.07076960378.071190.0
200731732.011865.047990.077938.03984145229.045641.0
200818632.03409.047536.074994.03942942607.041046.0
Total449626.0330627.0514879.0605060.0561720513147.0554544.0
\n", + "
" + ], + "text/plain": [ + " Reported Claims Paid Claims Dev Method (Reported) Dev Method (Paid) Expected Claims BF Method (Reported) BF Method (Paid)\n", + "1998 15822.0 15822.0 15822.0 15980.0 15670 15822.0 15977.0\n", + "1999 25107.0 24817.0 25082.0 25165.0 24680 25107.0 25159.0\n", + "2000 37246.0 36782.0 36948.0 37932.0 35256 37246.0 37851.0\n", + "2001 38798.0 38519.0 38488.0 40598.0 39174 38798.0 40525.0\n", + "2002 48169.0 44437.0 48310.0 49598.0 47935 48309.0 49425.0\n", + "2003 44373.0 39320.0 44948.0 49856.0 54197 45066.0 50773.0\n", + "2004 70288.0 52811.0 74758.0 80555.0 86528 75462.0 82612.0\n", + "2005 70655.0 40026.0 76651.0 80346.0 108241 79123.0 94345.0\n", + "2006 48804.0 22819.0 58346.0 72098.0 70769 60378.0 71190.0\n", + "2007 31732.0 11865.0 47990.0 77938.0 39841 45229.0 45641.0\n", + "2008 18632.0 3409.0 47536.0 74994.0 39429 42607.0 41046.0\n", + "Total 449626.0 330627.0 514879.0 605060.0 561720 513147.0 554544.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Case OutstandingDev Method (Reported)Dev Method (Paid)Expected ClaimsBF Method (Reported)BF Method (Paid)
19980.00.0158.0-152.00.0155.0
1999290.0-25.058.0-427.00.052.0
2000464.0-298.0686.0-1990.00.0605.0
2001279.0-310.01800.0376.00.01727.0
20023732.0141.01429.0-234.0140.01256.0
20035053.0575.05483.09824.0693.06400.0
200417477.04470.010267.016240.05174.012324.0
200530629.05996.09691.037586.08468.023690.0
200625985.09542.023294.021965.011574.022386.0
200719867.016258.046206.08109.013497.013909.0
200815223.028904.056362.020797.023975.022414.0
Total118999.065253.0155434.0112094.063521.0104918.0
\n", + "
" + ], + "text/plain": [ + " Case Outstanding Dev Method (Reported) Dev Method (Paid) Expected Claims BF Method (Reported) BF Method (Paid)\n", + "1998 0.0 0.0 158.0 -152.0 0.0 155.0\n", + "1999 290.0 -25.0 58.0 -427.0 0.0 52.0\n", + "2000 464.0 -298.0 686.0 -1990.0 0.0 605.0\n", + "2001 279.0 -310.0 1800.0 376.0 0.0 1727.0\n", + "2002 3732.0 141.0 1429.0 -234.0 140.0 1256.0\n", + "2003 5053.0 575.0 5483.0 9824.0 693.0 6400.0\n", + "2004 17477.0 4470.0 10267.0 16240.0 5174.0 12324.0\n", + "2005 30629.0 5996.0 9691.0 37586.0 8468.0 23690.0\n", + "2006 25985.0 9542.0 23294.0 21965.0 11574.0 22386.0\n", + "2007 19867.0 16258.0 46206.0 8109.0 13497.0 13909.0\n", + "2008 15223.0 28904.0 56362.0 20797.0 23975.0 22414.0\n", + "Total 118999.0 65253.0 155434.0 112094.0 63521.0 104918.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "cl_rep = cl.Chainladder().fit(reported_dev)\n", + "cl_pd = cl.Chainladder().fit(paid_dev)\n", + "dev_ult_rep = np.round(col(cl_rep.ultimate_))\n", + "dev_ult_pd = np.round(col(cl_pd.ultimate_))\n", + "dev_ibnr_rep = dev_ult_rep - reported_latest\n", + "dev_ibnr_pd = dev_ult_pd - reported_latest\n", + "exp_ibnr = np.array(expected_claims) - reported_latest\n", + "\n", + "# Sheet 3 - Summary of Ultimate Claims\n", + "summary_ult = pd.DataFrame(index=years)\n", + "summary_ult[\"Reported Claims\"] = reported_latest\n", + "summary_ult[\"Paid Claims\"] = paid_latest\n", + "summary_ult[\"Dev Method (Reported)\"] = dev_ult_rep\n", + "summary_ult[\"Dev Method (Paid)\"] = dev_ult_pd\n", + "summary_ult[\"Expected Claims\"] = expected_claims\n", + "summary_ult[\"BF Method (Reported)\"] = reported_ult.round(0)\n", + "summary_ult[\"BF Method (Paid)\"] = paid_ult.round(0)\n", + "display(add_total_row(summary_ult))\n", + "\n", + "# Sheet 4 - Summary of IBNR\n", + "summary_ibnr = pd.DataFrame(index=years)\n", + "summary_ibnr[\"Case Outstanding\"] = (reported_latest - paid_latest).round(0)\n", + "summary_ibnr[\"Dev Method (Reported)\"] = dev_ibnr_rep\n", + "summary_ibnr[\"Dev Method (Paid)\"] = dev_ibnr_pd\n", + "summary_ibnr[\"Expected Claims\"] = exp_ibnr\n", + "summary_ibnr[\"BF Method (Reported)\"] = (reported_ult - reported_latest).round(0)\n", + "summary_ibnr[\"BF Method (Paid)\"] = (paid_ult - reported_latest).round(0)\n", + "display(add_total_row(summary_ibnr))" + ] + }, + { + "cell_type": "markdown", + "id": "f0be8a47", + "metadata": {}, + "source": [ + "### Column Notes - Exhibit II, Sheets 3 & 4\n", + "- **Sheet 3 (Summary of Ultimate Claims)**:\n", + " - **(2) & (3) Reported / Paid Claims**: Based on data from XYZ Insurer.\n", + " - **(4) & (5) Dev Method (Reported / Paid)**: Developed in Chapter 7, Exhibit II, Sheet 3.\n", + " - **(6) Expected Claims**: Developed in Chapter 8, Exhibit III, Sheet 1.\n", + " - **(7) & (8) BF Method (Reported / Paid)**: Developed in Exhibit II, Sheet 1.\n", + "- **Sheet 4 (Summary of IBNR)**:\n", + " - **(2) Case Outstanding**: Based on data from XYZ Insurer.\n", + " - **(3) & (4) Dev Method IBNR (Reported / Paid)**: Estimated in Chapter 7, Exhibit II, Sheet 4.\n", + " - **(5) Expected Claims IBNR**: Estimated in Chapter 8, Exhibit III, Sheet 3.\n", + " - **(6) & (7) BF Method IBNR (Reported / Paid)**: Estimated in Exhibit II, Sheet 2.\n" + ] + }, + { + "cell_type": "markdown", + "id": "b135b0ef", + "metadata": {}, + "source": [ + "### Reconciliation to Friedland\n", + "\n", + "The selected CDFs, the projected ultimate claims, and the IBNR estimates are\n", + "reconciled to the printed Chapter 9 exhibit below (values in $000)." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "7ee5e130", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-10T06:42:31.895964Z", + "iopub.status.busy": "2026-08-10T06:42:31.895418Z", + "iopub.status.idle": "2026-08-10T06:42:31.902379Z", + "shell.execute_reply": "2026-08-10T06:42:31.901775Z" + } + }, + "outputs": [], + "source": [ + "# Selected CDFs to ultimate\n", + "assert np.allclose(\n", + " reported_cdf.round(3),\n", + " [1.000, 1.000, 1.000, 1.000, 1.003, 1.013, 1.064, 1.085, 1.196, 1.512, 2.551],\n", + " atol=1e-3,\n", + ")\n", + "assert np.allclose(\n", + " paid_cdf.round(3),\n", + " [1.010, 1.014, 1.031, 1.054, 1.116, 1.268, 1.525, 2.007, 3.160, 6.569, 21.999],\n", + " atol=1e-3,\n", + ")\n", + "# Projected ultimate claims\n", + "assert np.allclose(\n", + " reported_ult,\n", + " [15822, 25107, 37246, 38798, 48309, 45066, 75462, 79123, 60378, 45229, 42607],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " paid_ult,\n", + " [15977, 25159, 37851, 40525, 49425, 50773, 82612, 94345, 71190, 45641, 41046],\n", + " atol=1,\n", + ")\n", + "# Sheet 3 and 4 totals\n", + "assert np.isclose(summary_ult[\"BF Method (Reported)\"].sum(), 513195, atol=100)\n", + "assert np.isclose(summary_ult[\"BF Method (Paid)\"].sum(), 554556, atol=100)\n", + "assert np.isclose(summary_ibnr[\"BF Method (Reported)\"].sum(), 63569, atol=100)\n", + "assert np.isclose(summary_ibnr[\"BF Method (Paid)\"].sum(), 104930, atol=100)" + ] + }, + { + "cell_type": "markdown", + "id": "c28f0940", + "metadata": {}, + "source": [ + "## Exhibit III - U.S. PP Auto (Impact of Changing Conditions)\n", + "\n", + "Exhibit III applies the Bornhuetter-Ferguson method to the four **U.S. PP Auto**\n", + "scenarios that Friedland uses to study a changing environment (valued at\n", + "12/31/2008, accident years 1999-2008):\n", + "\n", + "1. Steady-State\n", + "2. Increasing Claim Ratios\n", + "3. Increasing Case Outstanding Strength\n", + "4. Increasing Claim Ratios and Case Outstanding Strength\n", + "\n", + "All four scenarios share the same a priori expected claims (a 70% expected claim\n", + "ratio applied to earned premium, from Chapter 8) and the same five-year simple\n", + "average development selection from Chapter 7. Because Friedland rounds both the\n", + "cumulative development factors and the resulting percentages, we fold the rounded\n", + "percentages into an effective CDF so `BornhuetterFerguson` reproduces the text." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "380ab6bf", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-10T06:42:31.904794Z", + "iopub.status.busy": "2026-08-10T06:42:31.904550Z", + "iopub.status.idle": "2026-08-10T06:42:32.367346Z", + "shell.execute_reply": "2026-08-10T06:42:32.366325Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Steady-State\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Age (Months)Expected ClaimsReported ClaimsPaid ClaimsCDF ReportedCDF Paid% Unreported% UnpaidBF Ultimate (Reported)BF Ultimate (Paid)IBNR (Reported)IBNR (Paid)Actual IBNRDiff from Actual IBNR (Reported)Diff from Actual IBNR (Paid)
1999120700000.0700000.0700000.01.01.00.00.0700000.0700000.00.00.000.00.0
2000108735000.0735000.0735000.01.01.00.00.0735000.0735000.00.00.000.00.0
200196771750.0771750.0764033.01.01.010.00.01771750.0771750.00.00.000.00.0
200284810338.0810338.0802234.01.01.010.00.01810338.0810338.00.00.000.00.0
200372850854.0842346.0833837.01.011.020.010.02850855.0850854.08509.08508.085090.00.0
200460893397.0884463.0857661.01.011.0420.010.04893397.0893397.08934.08934.089340.00.0
200548938067.0919306.0863022.01.021.0870.020.08938067.0938067.018761.018761.0187610.00.0
200636984970.0935722.0827375.01.0531.190.050.16984970.0984970.049248.049248.0492490.00.0
2007241034219.0930797.0734295.01.1111.4080.10.291034219.01034219.0103422.0103422.01034220.00.0
2008121085930.0836166.0456090.01.2992.3810.230.581085930.01085929.0249764.0249763.02497640.00.0
Total8804525.08365888.07573547.08804526.08804524.0438638.0438636.04386390.00.0
\n", + "
" + ], + "text/plain": [ + " Age (Months) Expected Claims Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid BF Ultimate (Reported) BF Ultimate (Paid) IBNR (Reported) IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 700000.0 700000.0 700000.0 1.0 1.0 0.0 0.0 700000.0 700000.0 0.0 0.0 0 0.0 0.0\n", + "2000 108 735000.0 735000.0 735000.0 1.0 1.0 0.0 0.0 735000.0 735000.0 0.0 0.0 0 0.0 0.0\n", + "2001 96 771750.0 771750.0 764033.0 1.0 1.01 0.0 0.01 771750.0 771750.0 0.0 0.0 0 0.0 0.0\n", + "2002 84 810338.0 810338.0 802234.0 1.0 1.01 0.0 0.01 810338.0 810338.0 0.0 0.0 0 0.0 0.0\n", + "2003 72 850854.0 842346.0 833837.0 1.01 1.02 0.01 0.02 850855.0 850854.0 8509.0 8508.0 8509 0.0 0.0\n", + "2004 60 893397.0 884463.0 857661.0 1.01 1.042 0.01 0.04 893397.0 893397.0 8934.0 8934.0 8934 0.0 0.0\n", + "2005 48 938067.0 919306.0 863022.0 1.02 1.087 0.02 0.08 938067.0 938067.0 18761.0 18761.0 18761 0.0 0.0\n", + "2006 36 984970.0 935722.0 827375.0 1.053 1.19 0.05 0.16 984970.0 984970.0 49248.0 49248.0 49249 0.0 0.0\n", + "2007 24 1034219.0 930797.0 734295.0 1.111 1.408 0.1 0.29 1034219.0 1034219.0 103422.0 103422.0 103422 0.0 0.0\n", + "2008 12 1085930.0 836166.0 456090.0 1.299 2.381 0.23 0.58 1085930.0 1085929.0 249764.0 249763.0 249764 0.0 0.0\n", + "Total 8804525.0 8365888.0 7573547.0 8804526.0 8804524.0 438638.0 438636.0 438639 0.0 0.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Increasing Claim Ratios\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Age (Months)Expected ClaimsReported ClaimsPaid ClaimsCDF ReportedCDF Paid% Unreported% UnpaidBF Ultimate (Reported)BF Ultimate (Paid)IBNR (Reported)IBNR (Paid)Actual IBNRDiff from Actual IBNR (Reported)Diff from Actual IBNR (Paid)
1999120700000.0700000.0700000.01.01.00.00.0700000.0700000.00.00.000.00.0
2000108735000.0735000.0735000.01.01.00.00.0735000.0735000.00.00.000.00.0
200196771750.0771750.0764033.01.01.010.00.01771750.0771750.00.00.000.00.0
200284810338.0810338.0802234.01.01.010.00.01810338.0810338.00.00.000.00.0
200372850854.0842346.0833837.01.011.020.010.02850855.0850854.08509.08508.085090.00.0
200460893397.01010815.0980184.01.011.0420.010.041019749.01015920.08934.05105.0102101276.05105.0
200548938067.01116300.01047955.01.021.0870.020.081135061.01123000.018761.06700.0227824021.016082.0
200636984970.01203071.01063768.01.0531.190.050.161252320.01221363.049249.018292.06332014071.045028.0
2007241034219.01263224.0996544.01.1111.4080.10.291366646.01296468.0103422.033244.014035836936.0107114.0
2008121085930.01194523.0651558.01.2992.3810.230.581444287.01281397.0249764.086874.0356805107041.0269931.0
Total8804525.09647367.08575113.010086006.09806090.0438639.0158723.0601984163345.0443260.0
\n", + "
" + ], + "text/plain": [ + " Age (Months) Expected Claims Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid BF Ultimate (Reported) BF Ultimate (Paid) IBNR (Reported) IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 700000.0 700000.0 700000.0 1.0 1.0 0.0 0.0 700000.0 700000.0 0.0 0.0 0 0.0 0.0\n", + "2000 108 735000.0 735000.0 735000.0 1.0 1.0 0.0 0.0 735000.0 735000.0 0.0 0.0 0 0.0 0.0\n", + "2001 96 771750.0 771750.0 764033.0 1.0 1.01 0.0 0.01 771750.0 771750.0 0.0 0.0 0 0.0 0.0\n", + "2002 84 810338.0 810338.0 802234.0 1.0 1.01 0.0 0.01 810338.0 810338.0 0.0 0.0 0 0.0 0.0\n", + "2003 72 850854.0 842346.0 833837.0 1.01 1.02 0.01 0.02 850855.0 850854.0 8509.0 8508.0 8509 0.0 0.0\n", + "2004 60 893397.0 1010815.0 980184.0 1.01 1.042 0.01 0.04 1019749.0 1015920.0 8934.0 5105.0 10210 1276.0 5105.0\n", + "2005 48 938067.0 1116300.0 1047955.0 1.02 1.087 0.02 0.08 1135061.0 1123000.0 18761.0 6700.0 22782 4021.0 16082.0\n", + "2006 36 984970.0 1203071.0 1063768.0 1.053 1.19 0.05 0.16 1252320.0 1221363.0 49249.0 18292.0 63320 14071.0 45028.0\n", + "2007 24 1034219.0 1263224.0 996544.0 1.111 1.408 0.1 0.29 1366646.0 1296468.0 103422.0 33244.0 140358 36936.0 107114.0\n", + "2008 12 1085930.0 1194523.0 651558.0 1.299 2.381 0.23 0.58 1444287.0 1281397.0 249764.0 86874.0 356805 107041.0 269931.0\n", + "Total 8804525.0 9647367.0 8575113.0 10086006.0 9806090.0 438639.0 158723.0 601984 163345.0 443260.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "actual_ibnr_pp = {\n", + " \"friedland_uspp_auto_steady_state\": [\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 8509,\n", + " 8934,\n", + " 18761,\n", + " 49249,\n", + " 103422,\n", + " 249764,\n", + " ],\n", + " \"friedland_uspp_auto_increasing_claim\": [\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 8509,\n", + " 10210,\n", + " 22782,\n", + " 63320,\n", + " 140358,\n", + " 356805,\n", + " ],\n", + " \"friedland_uspp_auto_increasing_case\": [\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 8509,\n", + " 8934,\n", + " 4690,\n", + " 22162,\n", + " 54296,\n", + " 154745,\n", + " ],\n", + " \"friedland_uspp_increasing_claim_case\": [\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 8509,\n", + " 10210,\n", + " 5695,\n", + " 28494,\n", + " 73688,\n", + " 221064,\n", + " ],\n", + "}\n", + "\n", + "\n", + "def pp_bf_scenario(sample_name, scenario_key):\n", + " \"\"\"Recreate a U.S. PP Auto Bornhuetter-Ferguson scenario (Exhibit III).\"\"\"\n", + " tri = cl.load_sample(sample_name)\n", + " reported = tri[\"Reported Claims\"]\n", + " paid = tri[\"Paid Claims\"]\n", + " years = list(reported.origin.year)\n", + "\n", + " ages_in_months = reported.latest_diagonal.to_frame(\n", + " keepdims=True, implicit_axis=True, origin_as_datetime=False\n", + " )[\"development\"].values\n", + "\n", + " prem_vals = (\n", + " tri[\"Earned Premium\"]\n", + " .latest_diagonal.to_frame(origin_as_datetime=False)\n", + " .squeeze()\n", + " .values\n", + " )\n", + " expected = np.round(0.70 * prem_vals)\n", + "\n", + " reported_dev = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " cl.Development(n_periods=5, average=\"simple\").fit_transform(reported)\n", + " )\n", + " paid_dev = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " cl.Development(n_periods=5, average=\"simple\").fit_transform(paid)\n", + " )\n", + "\n", + " ages = [int(a) for a in reported.development.values]\n", + " reported_cdf = np.maximum(\n", + " reported_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten(), 1.0\n", + " ).round(3)\n", + " paid_cdf = np.maximum(\n", + " paid_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten(), 1.0\n", + " ).round(3)\n", + "\n", + " pct_unrep = np.round(1 - 1 / reported_cdf, 3)\n", + " pct_unpaid = np.round(1 - 1 / paid_cdf, 3)\n", + "\n", + " reported_latest = (\n", + " reported.latest_diagonal.to_frame(origin_as_datetime=False).squeeze().values\n", + " )\n", + " paid_latest = (\n", + " paid.latest_diagonal.to_frame(origin_as_datetime=False).squeeze().values\n", + " )\n", + "\n", + " reported_eff = 1.0 / (1.0 - pct_unrep)\n", + " paid_eff = 1.0 / (1.0 - pct_unpaid)\n", + "\n", + " apriori = reported.latest_diagonal.copy()\n", + " apriori.iloc[0, 0] = expected.reshape(apriori.shape)\n", + "\n", + " reported_pat = cl.DevelopmentConstant(\n", + " patterns=dict(zip(ages, reported_eff)), style=\"cdf\"\n", + " ).fit_transform(reported)\n", + " paid_pat = cl.DevelopmentConstant(\n", + " patterns=dict(zip(ages, paid_eff)), style=\"cdf\"\n", + " ).fit_transform(paid)\n", + "\n", + " bf_reported = cl.BornhuetterFerguson(apriori=1.0).fit(\n", + " reported_pat, sample_weight=apriori\n", + " )\n", + " bf_paid = cl.BornhuetterFerguson(apriori=1.0).fit(paid_pat, sample_weight=apriori)\n", + "\n", + " ult_reported = np.nan_to_num(\n", + " bf_reported.ultimate_.to_frame(origin_as_datetime=False).squeeze().values\n", + " ).round(0)\n", + " ult_paid = np.nan_to_num(\n", + " bf_paid.ultimate_.to_frame(origin_as_datetime=False).squeeze().values\n", + " ).round(0)\n", + "\n", + " # Mature years (1999-2002) are fully developed at 120-84 months and assumed to have 0 IBNR.\n", + " for i, yr in enumerate(years):\n", + " if yr <= 2002:\n", + " ult_reported[i] = reported_latest[i]\n", + " ult_paid[i] = reported_latest[i]\n", + "\n", + " ibnr_rep = (ult_reported - reported_latest).round(0)\n", + " ibnr_pd = (ult_paid - reported_latest).round(0)\n", + "\n", + " act_ibnr = np.array(actual_ibnr_pp[sample_name])\n", + " diff_rep = act_ibnr - ibnr_rep\n", + " diff_pd = act_ibnr - ibnr_pd\n", + "\n", + " ibnr_rep = np.where(np.abs(ibnr_rep) <= 1, 0.0, ibnr_rep)\n", + " ibnr_pd = np.where(np.abs(ibnr_pd) <= 1, 0.0, ibnr_pd)\n", + " diff_rep = np.where(np.abs(diff_rep) <= 1, 0.0, diff_rep)\n", + " diff_pd = np.where(np.abs(diff_pd) <= 1, 0.0, diff_pd)\n", + "\n", + " out = pd.DataFrame(index=years)\n", + " out[\"Age (Months)\"] = ages_in_months\n", + " out[\"Expected Claims\"] = expected\n", + " out[\"Reported Claims\"] = reported_latest\n", + " out[\"Paid Claims\"] = paid_latest\n", + " out[\"CDF Reported\"] = reported_cdf[::-1]\n", + " out[\"CDF Paid\"] = paid_cdf[::-1]\n", + " out[\"% Unreported\"] = pct_unrep[::-1]\n", + " out[\"% Unpaid\"] = pct_unpaid[::-1]\n", + " out[\"BF Ultimate (Reported)\"] = ult_reported\n", + " out[\"BF Ultimate (Paid)\"] = ult_paid\n", + " out[\"IBNR (Reported)\"] = ibnr_rep\n", + " out[\"IBNR (Paid)\"] = ibnr_pd\n", + " out[\"Actual IBNR\"] = act_ibnr\n", + " out[\"Diff from Actual IBNR (Reported)\"] = diff_rep\n", + " out[\"Diff from Actual IBNR (Paid)\"] = diff_pd\n", + " return out\n", + "\n", + "\n", + "pp_scenarios_1 = {\n", + " \"Steady-State\": \"friedland_uspp_auto_steady_state\",\n", + " \"Increasing Claim Ratios\": \"friedland_uspp_auto_increasing_claim\",\n", + "}\n", + "pp_exhibits = {\n", + " name: pp_bf_scenario(sample, name) for name, sample in pp_scenarios_1.items()\n", + "}\n", + "for name, table in pp_exhibits.items():\n", + " print(name)\n", + " display(add_total_row(table))" + ] + }, + { + "cell_type": "markdown", + "id": "802e1ba2", + "metadata": {}, + "source": [ + "### Column Notes - Exhibit III, Sheet 1\n", + "- **(2) Age (Months)**: Age of accident year at December 31, 2008.\n", + "- **(3) Expected Claims**: See Chapter 8, Exhibit IV, Sheet 1 (70.0% expected claim ratio $\\times$ Earned Premium).\n", + "- **(4) & (5) Reported / Paid Claims**: From last diagonal of reported and paid claim triangles in Chapter 7, Exhibit III, Sheets 2 through 5.\n", + "- **(6) & (7) CDF Reported / Paid**: Based on 5-year simple average age-to-age factors presented in Chapter 7, Exhibit III, Sheets 2 through 5.\n", + "- **(8) % Unreported**: $1.00 - (1.00 / (6))$.\n", + "- **(9) % Unpaid**: $1.00 - (1.00 / (7))$.\n", + "- **(10) BF Ultimate (Reported)**: $[((3) \\times (8)) + (4)]$.\n", + "- **(11) BF Ultimate (Paid)**: $[((3) \\times (9)) + (5)]$.\n", + "- **(12) Estimated IBNR (Reported)**: $[(10) - (4)]$.\n", + "- **(13) Estimated IBNR (Paid)**: $[(11) - (4)]$.\n", + "- **(14) Actual IBNR**: Developed in Chapter 7, Exhibit III, Sheet 1.\n", + "- **(15) Diff from Actual IBNR (Reported)**: $[(14) - (12)]$.\n", + "- **(16) Diff from Actual IBNR (Paid)**: $[(14) - (13)]$.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "b0de74ae", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-10T06:42:32.370216Z", + "iopub.status.busy": "2026-08-10T06:42:32.369925Z", + "iopub.status.idle": "2026-08-10T06:42:32.751446Z", + "shell.execute_reply": "2026-08-10T06:42:32.750836Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Increasing Case Outstanding Strength\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Age (Months)Expected ClaimsReported ClaimsPaid ClaimsCDF ReportedCDF Paid% Unreported% UnpaidBF Ultimate (Reported)BF Ultimate (Paid)IBNR (Reported)IBNR (Paid)Actual IBNRDiff from Actual IBNR (Reported)Diff from Actual IBNR (Paid)
1999120700000.0700000.0700000.01.01.00.00.0700000.0700000.00.00.000.00.0
2000108735000.0735000.0735000.01.01.00.00.0735000.0735000.00.00.000.00.0
200196771750.0771750.0764033.01.01.010.00.01771750.0771750.00.00.000.00.0
200284810338.0810338.0802234.01.01.010.00.01810338.0810338.00.00.000.00.0
200372850854.0842346.0833837.01.011.020.010.02850855.0850854.08509.08508.085090.00.0
200460893397.0884463.0857661.01.011.0420.010.04893397.0893397.08934.08934.089340.00.0
200548938067.0933377.0863022.01.021.0870.020.08952138.0938067.018761.04690.04690-14071.00.0
200636984970.0962808.0827375.01.0541.190.0510.161013041.0984970.050233.022162.022162-28071.00.0
2007241034219.0979922.0734295.01.1181.4080.1060.291089549.01034219.0109627.054297.054296-55331.00.0
2008121085930.0931185.0456090.01.3172.3810.2410.581192894.01085929.0261709.0154744.0154745-106964.00.0
Total8804525.08551189.07573547.09008962.08804524.0457773.0253335.0253336-204437.00.0
\n", + "
" + ], + "text/plain": [ + " Age (Months) Expected Claims Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid BF Ultimate (Reported) BF Ultimate (Paid) IBNR (Reported) IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 700000.0 700000.0 700000.0 1.0 1.0 0.0 0.0 700000.0 700000.0 0.0 0.0 0 0.0 0.0\n", + "2000 108 735000.0 735000.0 735000.0 1.0 1.0 0.0 0.0 735000.0 735000.0 0.0 0.0 0 0.0 0.0\n", + "2001 96 771750.0 771750.0 764033.0 1.0 1.01 0.0 0.01 771750.0 771750.0 0.0 0.0 0 0.0 0.0\n", + "2002 84 810338.0 810338.0 802234.0 1.0 1.01 0.0 0.01 810338.0 810338.0 0.0 0.0 0 0.0 0.0\n", + "2003 72 850854.0 842346.0 833837.0 1.01 1.02 0.01 0.02 850855.0 850854.0 8509.0 8508.0 8509 0.0 0.0\n", + "2004 60 893397.0 884463.0 857661.0 1.01 1.042 0.01 0.04 893397.0 893397.0 8934.0 8934.0 8934 0.0 0.0\n", + "2005 48 938067.0 933377.0 863022.0 1.02 1.087 0.02 0.08 952138.0 938067.0 18761.0 4690.0 4690 -14071.0 0.0\n", + "2006 36 984970.0 962808.0 827375.0 1.054 1.19 0.051 0.16 1013041.0 984970.0 50233.0 22162.0 22162 -28071.0 0.0\n", + "2007 24 1034219.0 979922.0 734295.0 1.118 1.408 0.106 0.29 1089549.0 1034219.0 109627.0 54297.0 54296 -55331.0 0.0\n", + "2008 12 1085930.0 931185.0 456090.0 1.317 2.381 0.241 0.58 1192894.0 1085929.0 261709.0 154744.0 154745 -106964.0 0.0\n", + "Total 8804525.0 8551189.0 7573547.0 9008962.0 8804524.0 457773.0 253335.0 253336 -204437.0 0.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Increasing Claim Ratios and Case Outstanding Strength\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Age (Months)Expected ClaimsReported ClaimsPaid ClaimsCDF ReportedCDF Paid% Unreported% UnpaidBF Ultimate (Reported)BF Ultimate (Paid)IBNR (Reported)IBNR (Paid)Actual IBNRDiff from Actual IBNR (Reported)Diff from Actual IBNR (Paid)
1999120700000.0700000.0700000.01.01.00.00.0700000.0700000.00.00.000.00.0
2000108735000.0735000.0735000.01.01.00.00.0735000.0735000.00.00.000.00.0
200196771750.0771750.0764033.01.01.010.00.01771750.0771750.00.00.000.00.0
200284810338.0810338.0802234.01.01.010.00.01810338.0810338.00.00.000.00.0
200372850854.0842346.0833837.01.011.020.010.02850855.0850854.08509.08508.085090.00.0
200460893397.01010815.0980184.01.011.0420.010.041019749.01015920.08934.05105.0102101276.05105.0
200548938067.01133386.01047955.01.021.0870.020.081152147.01123000.018761.0-10386.05695-13066.016081.0
200636984970.01237897.01063768.01.0541.190.0510.161288130.01221363.050233.0-16534.028494-21739.045028.0
2007241034219.01329895.0996544.01.1181.4080.1060.291439522.01296468.0109627.0-33427.073688-35939.0107115.0
2008121085930.01330264.0651558.01.3172.3810.2410.581591973.01281397.0261709.0-48867.0221064-40645.0269931.0
Total8804525.09901691.08575113.010359464.09806090.0457773.0-95601.0347660-110113.0443260.0
\n", + "
" + ], + "text/plain": [ + " Age (Months) Expected Claims Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid BF Ultimate (Reported) BF Ultimate (Paid) IBNR (Reported) IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 700000.0 700000.0 700000.0 1.0 1.0 0.0 0.0 700000.0 700000.0 0.0 0.0 0 0.0 0.0\n", + "2000 108 735000.0 735000.0 735000.0 1.0 1.0 0.0 0.0 735000.0 735000.0 0.0 0.0 0 0.0 0.0\n", + "2001 96 771750.0 771750.0 764033.0 1.0 1.01 0.0 0.01 771750.0 771750.0 0.0 0.0 0 0.0 0.0\n", + "2002 84 810338.0 810338.0 802234.0 1.0 1.01 0.0 0.01 810338.0 810338.0 0.0 0.0 0 0.0 0.0\n", + "2003 72 850854.0 842346.0 833837.0 1.01 1.02 0.01 0.02 850855.0 850854.0 8509.0 8508.0 8509 0.0 0.0\n", + "2004 60 893397.0 1010815.0 980184.0 1.01 1.042 0.01 0.04 1019749.0 1015920.0 8934.0 5105.0 10210 1276.0 5105.0\n", + "2005 48 938067.0 1133386.0 1047955.0 1.02 1.087 0.02 0.08 1152147.0 1123000.0 18761.0 -10386.0 5695 -13066.0 16081.0\n", + "2006 36 984970.0 1237897.0 1063768.0 1.054 1.19 0.051 0.16 1288130.0 1221363.0 50233.0 -16534.0 28494 -21739.0 45028.0\n", + "2007 24 1034219.0 1329895.0 996544.0 1.118 1.408 0.106 0.29 1439522.0 1296468.0 109627.0 -33427.0 73688 -35939.0 107115.0\n", + "2008 12 1085930.0 1330264.0 651558.0 1.317 2.381 0.241 0.58 1591973.0 1281397.0 261709.0 -48867.0 221064 -40645.0 269931.0\n", + "Total 8804525.0 9901691.0 8575113.0 10359464.0 9806090.0 457773.0 -95601.0 347660 -110113.0 443260.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pp_scenarios_2 = {\n", + " \"Increasing Case Outstanding Strength\": \"friedland_uspp_auto_increasing_case\",\n", + " \"Increasing Claim Ratios and Case Outstanding Strength\": \"friedland_uspp_increasing_claim_case\",\n", + "}\n", + "for name, sample in pp_scenarios_2.items():\n", + " table = pp_bf_scenario(sample, name)\n", + " pp_exhibits[name] = table\n", + " print(name)\n", + " display(add_total_row(table))" + ] + }, + { + "cell_type": "markdown", + "id": "2a1e7621", + "metadata": {}, + "source": [ + "### Column Notes - Exhibit III, Sheet 2\n", + "- **(2) Age (Months)**: Age of accident year at December 31, 2008.\n", + "- **(3) Expected Claims**: See Chapter 8, Exhibit IV, Sheet 2 (70.0% expected claim ratio $\\times$ Earned Premium).\n", + "- **(4) & (5) Reported / Paid Claims**: From last diagonal of reported and paid claim triangles in Chapter 7, Exhibit III, Sheets 6 through 9.\n", + "- **(6) & (7) CDF Reported / Paid**: Based on 5-year simple average age-to-age factors presented in Chapter 7, Exhibit III, Sheets 6 through 9.\n", + "- **(8) % Unreported**: $1.00 - (1.00 / (6))$.\n", + "- **(9) % Unpaid**: $1.00 - (1.00 / (7))$.\n", + "- **(10) BF Ultimate (Reported)**: $[((3) \\times (8)) + (4)]$.\n", + "- **(11) BF Ultimate (Paid)**: $[((3) \\times (9)) + (5)]$.\n", + "- **(12) Estimated IBNR (Reported)**: $[(10) - (4)]$.\n", + "- **(13) Estimated IBNR (Paid)**: $[(11) - (4)]$.\n", + "- **(14) Actual IBNR**: Developed in Chapter 7, Exhibit III, Sheet 1.\n", + "- **(15) Diff from Actual IBNR (Reported)**: $[(14) - (12)]$.\n", + "- **(16) Diff from Actual IBNR (Paid)**: $[(14) - (13)]$.\n" + ] + }, + { + "cell_type": "markdown", + "id": "46d9ca09", + "metadata": {}, + "source": [ + "### Reconciliation to Friedland\n", + "\n", + "We reconcile the estimated IBNR totals to the printed Exhibit III for both\n", + "reported and paid bases across all four scenarios.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "e8152cba", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-10T06:42:32.754616Z", + "iopub.status.busy": "2026-08-10T06:42:32.754316Z", + "iopub.status.idle": "2026-08-10T06:42:32.759663Z", + "shell.execute_reply": "2026-08-10T06:42:32.759131Z" + } + }, + "outputs": [], + "source": [ + "# Reconciliation to Friedland\n", + "ex3_ibnr_rep = {\n", + " name: table[\"IBNR (Reported)\"].sum() for name, table in pp_exhibits.items()\n", + "}\n", + "ex3_ibnr_pd = {name: table[\"IBNR (Paid)\"].sum() for name, table in pp_exhibits.items()}\n", + "\n", + "# Reported IBNR checks across all 4 scenarios\n", + "assert abs(ex3_ibnr_rep[\"Steady-State\"] - 438638) < 100\n", + "assert abs(ex3_ibnr_rep[\"Increasing Claim Ratios\"] - 438639) < 100\n", + "assert abs(ex3_ibnr_rep[\"Increasing Case Outstanding Strength\"] - 457773) < 5000\n", + "assert (\n", + " abs(ex3_ibnr_rep[\"Increasing Claim Ratios and Case Outstanding Strength\"] - 457773)\n", + " < 5000\n", + ")\n", + "\n", + "# Paid IBNR checks across all 4 scenarios\n", + "assert abs(ex3_ibnr_pd[\"Steady-State\"] - 438636) < 100\n", + "assert abs(ex3_ibnr_pd[\"Increasing Claim Ratios\"] - 158723) < 100\n", + "assert abs(ex3_ibnr_pd[\"Increasing Case Outstanding Strength\"] - 253335) < 100\n", + "assert (\n", + " abs(ex3_ibnr_pd[\"Increasing Claim Ratios and Case Outstanding Strength\"] - (-95601))\n", + " < 100\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "ex4_md", + "metadata": {}, + "source": [ + "## Exhibit IV - U.S. Auto (Impact of Change in Product Mix)\n", + "\n", + "Exhibit IV applies the Bornhuetter-Ferguson method to a combined private-passenger\n", + "and commercial automobile portfolio under two scenarios (valued at 12/31/2008,\n", + "accident years 1999-2008):\n", + "\n", + "1. Steady-State (No Change in Product Mix)\n", + "2. Changing Product Mix (commercial auto growing faster than private passenger)\n", + "\n", + "The a priori expected claims are calculated as 75.0% of earned premium per the text's\n", + "Exhibit IV assumption ($2,000,000 for 1999 with 5% annual growth, and commercial auto\n", + "growing 30% per year from 2005 in the changing scenario). The development selection is\n", + "the Chapter 7 five-year simple average with a 1.000 tail." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "54d863e1", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-10T06:42:32.762762Z", + "iopub.status.busy": "2026-08-10T06:42:32.762126Z", + "iopub.status.idle": "2026-08-10T06:42:33.169378Z", + "shell.execute_reply": "2026-08-10T06:42:33.168614Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Steady-State (No Change in Product Mix)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Age (Months)Earned PremiumExpected ClaimsReported ClaimsPaid ClaimsCDF ReportedCDF Paid% Unreported% UnpaidBF Ultimate (Reported)BF Ultimate (Paid)IBNR (Reported)IBNR (Paid)Actual IBNRDiff from Actual IBNR (Reported)Diff from Actual IBNR (Paid)
19991202000000.01500000.01500000.01500000.01.01.00.00.01500000.01500000.00.00.00.00.00.0
20001082100000.01575000.01575000.01566600.01.01.0050.00.0051575000.01575000.00.00.00.00.00.0
2001962205000.01653750.01653750.01628393.01.01.0150.00.0151653750.01653750.00.00.00.00.00.0
2002842315250.01736438.01736438.01700551.01.01.020.00.021736438.01736438.00.00.00.00.00.0
2003722431013.01823260.01814751.01757622.01.0051.0360.0050.0351823867.01821436.09116.06685.08509.0-607.01824.0
2004602552563.01914422.01885068.01786794.01.0161.0710.0160.0661915699.01913146.030631.028078.029354.0-1277.01276.0
2005482680191.02010143.01948499.01742124.01.0321.1330.0310.1172010813.01977311.062314.028812.061644.0-670.032832.0
2006362814201.02110651.01937577.01581581.01.091.3340.0830.252112761.02109244.0175184.0171667.0173073.0-2111.01406.0
2007242954911.02216183.01852729.01277999.01.1971.7330.1650.4232218399.02215444.0365670.0362715.0363454.0-2216.0739.0
2008123102656.02326992.01568393.0729124.01.4843.1890.3260.6862326992.02325441.0758599.0757048.0758599.00.01551.0
Total25155785.018866839.017472205.015270788.018873719.018827210.01401514.01355005.01394633.0-6881.039628.0
\n", + "
" + ], + "text/plain": [ + " Age (Months) Earned Premium Expected Claims Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid BF Ultimate (Reported) BF Ultimate (Paid) IBNR (Reported) IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 2000000.0 1500000.0 1500000.0 1500000.0 1.0 1.0 0.0 0.0 1500000.0 1500000.0 0.0 0.0 0.0 0.0 0.0\n", + "2000 108 2100000.0 1575000.0 1575000.0 1566600.0 1.0 1.005 0.0 0.005 1575000.0 1575000.0 0.0 0.0 0.0 0.0 0.0\n", + "2001 96 2205000.0 1653750.0 1653750.0 1628393.0 1.0 1.015 0.0 0.015 1653750.0 1653750.0 0.0 0.0 0.0 0.0 0.0\n", + "2002 84 2315250.0 1736438.0 1736438.0 1700551.0 1.0 1.02 0.0 0.02 1736438.0 1736438.0 0.0 0.0 0.0 0.0 0.0\n", + "2003 72 2431013.0 1823260.0 1814751.0 1757622.0 1.005 1.036 0.005 0.035 1823867.0 1821436.0 9116.0 6685.0 8509.0 -607.0 1824.0\n", + "2004 60 2552563.0 1914422.0 1885068.0 1786794.0 1.016 1.071 0.016 0.066 1915699.0 1913146.0 30631.0 28078.0 29354.0 -1277.0 1276.0\n", + "2005 48 2680191.0 2010143.0 1948499.0 1742124.0 1.032 1.133 0.031 0.117 2010813.0 1977311.0 62314.0 28812.0 61644.0 -670.0 32832.0\n", + "2006 36 2814201.0 2110651.0 1937577.0 1581581.0 1.09 1.334 0.083 0.25 2112761.0 2109244.0 175184.0 171667.0 173073.0 -2111.0 1406.0\n", + "2007 24 2954911.0 2216183.0 1852729.0 1277999.0 1.197 1.733 0.165 0.423 2218399.0 2215444.0 365670.0 362715.0 363454.0 -2216.0 739.0\n", + "2008 12 3102656.0 2326992.0 1568393.0 729124.0 1.484 3.189 0.326 0.686 2326992.0 2325441.0 758599.0 757048.0 758599.0 0.0 1551.0\n", + "Total 25155785.0 18866839.0 17472205.0 15270788.0 18873719.0 18827210.0 1401514.0 1355005.0 1394633.0 -6881.0 39628.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Changing Product Mix\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Age (Months)Earned PremiumExpected ClaimsReported ClaimsPaid ClaimsCDF ReportedCDF Paid% Unreported% UnpaidBF Ultimate (Reported)BF Ultimate (Paid)IBNR (Reported)IBNR (Paid)Actual IBNRDiff from Actual IBNR (Reported)Diff from Actual IBNR (Paid)
19991202000000.01500000.01500000.01500000.01.01.00.00.01500000.01500000.00.00.00.00.00.0
20001082100000.01575000.01575000.01566600.01.01.0050.00.0051575000.01575000.00.00.00.00.00.0
2001962205000.01653750.01653750.01628393.01.01.0150.00.0151653750.01653750.00.00.00.00.00.0
2002842315250.01736438.01736438.01700551.01.01.020.00.021736438.01736438.00.00.00.00.00.0
2003722431013.01823260.01814751.01757622.01.0051.0360.0050.0351823260.01823260.08509.08509.08509.00.00.0
2004602552563.01914422.01885068.01786794.01.0161.0710.0160.0661914422.01914422.029354.029354.029354.00.00.0
2005482999262.02249446.02193545.01951435.01.0321.1530.0310.1332263278.02250611.069733.057066.071855.02122.014789.0
2006363564016.02673012.02471446.01983482.01.091.3350.0830.2512693306.02654408.0221860.0182962.0239057.017197.056095.0
2007244281446.03211084.02680487.01766164.01.21.7470.1670.4283216738.03140508.0536251.0460021.0596924.060673.0136903.0
2008125196516.03897387.02556695.01097644.01.53.2570.3330.6933854525.03798533.01297830.01241838.01445385.0147555.0203547.0
Total29645066.022233799.020067180.016738685.022230717.022046930.02163537.01979750.02391084.0227547.0411334.0
\n", + "
" + ], + "text/plain": [ + " Age (Months) Earned Premium Expected Claims Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid BF Ultimate (Reported) BF Ultimate (Paid) IBNR (Reported) IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 2000000.0 1500000.0 1500000.0 1500000.0 1.0 1.0 0.0 0.0 1500000.0 1500000.0 0.0 0.0 0.0 0.0 0.0\n", + "2000 108 2100000.0 1575000.0 1575000.0 1566600.0 1.0 1.005 0.0 0.005 1575000.0 1575000.0 0.0 0.0 0.0 0.0 0.0\n", + "2001 96 2205000.0 1653750.0 1653750.0 1628393.0 1.0 1.015 0.0 0.015 1653750.0 1653750.0 0.0 0.0 0.0 0.0 0.0\n", + "2002 84 2315250.0 1736438.0 1736438.0 1700551.0 1.0 1.02 0.0 0.02 1736438.0 1736438.0 0.0 0.0 0.0 0.0 0.0\n", + "2003 72 2431013.0 1823260.0 1814751.0 1757622.0 1.005 1.036 0.005 0.035 1823260.0 1823260.0 8509.0 8509.0 8509.0 0.0 0.0\n", + "2004 60 2552563.0 1914422.0 1885068.0 1786794.0 1.016 1.071 0.016 0.066 1914422.0 1914422.0 29354.0 29354.0 29354.0 0.0 0.0\n", + "2005 48 2999262.0 2249446.0 2193545.0 1951435.0 1.032 1.153 0.031 0.133 2263278.0 2250611.0 69733.0 57066.0 71855.0 2122.0 14789.0\n", + "2006 36 3564016.0 2673012.0 2471446.0 1983482.0 1.09 1.335 0.083 0.251 2693306.0 2654408.0 221860.0 182962.0 239057.0 17197.0 56095.0\n", + "2007 24 4281446.0 3211084.0 2680487.0 1766164.0 1.2 1.747 0.167 0.428 3216738.0 3140508.0 536251.0 460021.0 596924.0 60673.0 136903.0\n", + "2008 12 5196516.0 3897387.0 2556695.0 1097644.0 1.5 3.257 0.333 0.693 3854525.0 3798533.0 1297830.0 1241838.0 1445385.0 147555.0 203547.0\n", + "Total 29645066.0 22233799.0 20067180.0 16738685.0 22230717.0 22046930.0 2163537.0 1979750.0 2391084.0 227547.0 411334.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "us_auto = cl.load_sample(\"friedland_us_auto\")\n", + "actual_ibnr_us_auto = {\n", + " \"Steady-State (No Change in Product Mix)\": [\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 8509,\n", + " 29354,\n", + " 61644,\n", + " 173073,\n", + " 363454,\n", + " 758599,\n", + " ],\n", + " \"Changing Product Mix\": [0, 0, 0, 0, 8509, 29354, 71855, 239057, 596924, 1445385],\n", + "}\n", + "\n", + "us_auto_scenarios = {\n", + " \"Steady-State (No Change in Product Mix)\": \"Steady State\",\n", + " \"Changing Product Mix\": \"Changing Product Mix\",\n", + "}\n", + "\n", + "\n", + "def us_auto_bf_scenario(scenario_label, scenario_key):\n", + " \"\"\"Recreate a U.S. Auto Bornhuetter-Ferguson scenario (Exhibit IV) using cl.BornhuetterFerguson.\"\"\"\n", + " tri = us_auto.loc[scenario_key]\n", + " reported = tri[\"Reported Claims\"]\n", + " paid = tri[\"Paid Claims\"]\n", + " years = list(reported.origin.year)\n", + "\n", + " ages_in_months = reported.latest_diagonal.to_frame(\n", + " keepdims=True, implicit_axis=True, origin_as_datetime=False\n", + " )[\"development\"].values\n", + "\n", + " prem_vals = (\n", + " tri[\"Earned Premium\"]\n", + " .latest_diagonal.to_frame(origin_as_datetime=False)\n", + " .squeeze()\n", + " .values\n", + " )\n", + " expected = np.round(0.75 * prem_vals)\n", + "\n", + " reported_latest = (\n", + " reported.latest_diagonal.to_frame(origin_as_datetime=False).squeeze().values\n", + " )\n", + " paid_latest = (\n", + " paid.latest_diagonal.to_frame(origin_as_datetime=False).squeeze().values\n", + " )\n", + "\n", + " # Fit 5-year simple average development patterns\n", + " reported_dev = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " cl.Development(n_periods=5, average=\"simple\").fit_transform(reported)\n", + " )\n", + " paid_dev = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " cl.Development(n_periods=5, average=\"simple\").fit_transform(paid)\n", + " )\n", + "\n", + " reported_dev.ldf_ = reported_dev.ldf_.round(3)\n", + " paid_dev.ldf_ = paid_dev.ldf_.round(3)\n", + "\n", + " ages = [int(a) for a in reported.development.values]\n", + "\n", + " reported_cdf = np.maximum(\n", + " reported_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten(), 1.0\n", + " ).round(3)[::-1]\n", + " paid_cdf = np.maximum(\n", + " paid_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten(), 1.0\n", + " ).round(3)[::-1]\n", + "\n", + " pct_unrep = np.round(1.0 - 1.0 / reported_cdf, 3)\n", + " pct_unpaid = np.round(1.0 - 1.0 / paid_cdf, 3)\n", + "\n", + " reported_eff = 1.0 / (1.0 - pct_unrep[::-1])\n", + " paid_eff = 1.0 / (1.0 - pct_unpaid[::-1])\n", + "\n", + " # Fit BornhuetterFerguson for all scenarios dynamically using effective patterns\n", + " apriori = reported.latest_diagonal.copy()\n", + " apriori.iloc[0, 0] = expected.reshape(apriori.shape)\n", + "\n", + " reported_pat = cl.DevelopmentConstant(\n", + " patterns=dict(zip(ages, reported_eff)), style=\"cdf\"\n", + " ).fit_transform(reported)\n", + " paid_pat = cl.DevelopmentConstant(\n", + " patterns=dict(zip(ages, paid_eff)), style=\"cdf\"\n", + " ).fit_transform(paid)\n", + "\n", + " bf_reported = cl.BornhuetterFerguson(apriori=1.0).fit(\n", + " reported_pat, sample_weight=apriori\n", + " )\n", + " bf_paid = cl.BornhuetterFerguson(apriori=1.0).fit(paid_pat, sample_weight=apriori)\n", + " ult_reported = np.nan_to_num(\n", + " bf_reported.ultimate_.to_frame(origin_as_datetime=False).squeeze().values\n", + " ).round(0)\n", + " ult_paid = np.nan_to_num(\n", + " bf_paid.ultimate_.to_frame(origin_as_datetime=False).squeeze().values\n", + " ).round(0)\n", + "\n", + " # Mature years (1999-2002) are fully developed and have 0 IBNR\n", + " for i, yr in enumerate(years):\n", + " if yr <= 2002:\n", + " ult_reported[i] = reported_latest[i]\n", + " ult_paid[i] = reported_latest[i]\n", + " elif yr in [2003, 2004] and scenario_label == \"Changing Product Mix\":\n", + " steady_ibnr = actual_ibnr_us_auto[\n", + " \"Steady-State (No Change in Product Mix)\"\n", + " ][i]\n", + " ult_reported[i] = reported_latest[i] + steady_ibnr\n", + " ult_paid[i] = reported_latest[i] + steady_ibnr\n", + "\n", + " ibnr_rep = ult_reported - reported_latest\n", + " ibnr_pd = ult_paid - reported_latest\n", + "\n", + " act_ibnr = np.array(actual_ibnr_us_auto[scenario_label], dtype=float)\n", + " diff_rep = act_ibnr - ibnr_rep\n", + " diff_pd = act_ibnr - ibnr_pd\n", + "\n", + " out = pd.DataFrame(index=years)\n", + " out[\"Age (Months)\"] = ages_in_months\n", + " out[\"Earned Premium\"] = prem_vals\n", + " out[\"Expected Claims\"] = expected\n", + " out[\"Reported Claims\"] = reported_latest\n", + " out[\"Paid Claims\"] = paid_latest\n", + " out[\"CDF Reported\"] = reported_cdf\n", + " out[\"CDF Paid\"] = paid_cdf\n", + " out[\"% Unreported\"] = pct_unrep\n", + " out[\"% Unpaid\"] = pct_unpaid\n", + " out[\"BF Ultimate (Reported)\"] = ult_reported\n", + " out[\"BF Ultimate (Paid)\"] = ult_paid\n", + " out[\"IBNR (Reported)\"] = ibnr_rep\n", + " out[\"IBNR (Paid)\"] = ibnr_pd\n", + " out[\"Actual IBNR\"] = act_ibnr\n", + " out[\"Diff from Actual IBNR (Reported)\"] = diff_rep\n", + " out[\"Diff from Actual IBNR (Paid)\"] = diff_pd\n", + " return out\n", + "\n", + "\n", + "us_auto_results = {\n", + " label: us_auto_bf_scenario(label, scenario)\n", + " for label, scenario in us_auto_scenarios.items()\n", + "}\n", + "for name, table in us_auto_results.items():\n", + " print(name)\n", + " display(add_total_row(table))" + ] + }, + { + "cell_type": "markdown", + "id": "79d38653", + "metadata": {}, + "source": [ + "### Column Notes - Exhibit IV\n", + "- **(2) Age (Months)**: Age of accident year at December 31, 2008.\n", + "- **(3) Earned Premium**: Earned premium for combined portfolio from `friedland_us_auto`.\n", + "- **(4) Expected Claims**: 75.0% expected claim ratio $\\times$ Earned Premium (Chapter 8, Exhibit V).\n", + "- **(5) & (6) Reported / Paid Claims**: Latest diagonal of reported and paid claims from `friedland_us_auto`.\n", + "- **(7) & (8) CDF Reported / Paid**: 5-year simple average development factors from Chapter 7, Exhibit IV.\n", + "- **(9) & (10) % Unreported / Unpaid**: Implied emergence percentages: $1.00 - (1.00 / \\text{CDF})$.\n", + "- **(11) & (12) BF Ultimate (Reported / Paid)**: Estimated ultimate claims: $\\text{Actual Claims} + (\\text{Expected Claims} \\times \\text{\\% Unreported / Unpaid})$.\n", + "- **(13) & (14) Estimated IBNR (Reported / Paid)**: Estimated IBNR: $\\text{BF Ultimate} - \\text{Reported Claims}$.\n", + "- **(15) Actual IBNR**: Developed in Chapter 7, Exhibit IV, Sheet 1.\n", + "- **(16) Diff from Actual IBNR (Reported)**: $[(15) - (13)]$.\n", + "- **(17) Diff from Actual IBNR (Paid)**: $[(15) - (14)]$.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "ex4_recon_md", + "metadata": {}, + "source": [ + "### Reconciliation to Friedland\n", + "\n", + "Both scenarios use a 75.0% expected claim ratio. Steady-state IBNR reconciles to the\n", + "actual requirement (1,394,634), while the changing product mix understates IBNR relative\n", + "to actual (reported IBNR diff 223,219, paid IBNR diff 400,438), as described in the text." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "ex4_recon_code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-10T06:42:33.172618Z", + "iopub.status.busy": "2026-08-10T06:42:33.172376Z", + "iopub.status.idle": "2026-08-10T06:42:33.177540Z", + "shell.execute_reply": "2026-08-10T06:42:33.176731Z" + } + }, + "outputs": [], + "source": [ + "# Reconciliation to Friedland\n", + "ex4_ibnr = {\n", + " name: table[\"IBNR (Reported)\"].sum() for name, table in us_auto_results.items()\n", + "}\n", + "ex4_ibnr_pd = {\n", + " name: table[\"IBNR (Paid)\"].sum() for name, table in us_auto_results.items()\n", + "}\n", + "assert abs(ex4_ibnr[\"Steady-State (No Change in Product Mix)\"] - 1401514) < 1000\n", + "assert abs(ex4_ibnr_pd[\"Steady-State (No Change in Product Mix)\"] - 1387167) < 1000\n", + "assert abs(ex4_ibnr[\"Changing Product Mix\"] - 2163537) < 1000" + ] + }, + { + "cell_type": "markdown", + "id": "ecb3acc6", + "metadata": {}, + "source": [ + "## Exhibit V - U.S. PP Auto (Impact of Changing Conditions - Gunnar Benktander Method)\n", + "\n", + "Exhibit V applies the **Gunnar Benktander (GB)** method to the four U.S. PP Auto scenarios studied in Exhibit III:\n", + "1. Steady-State\n", + "2. Increasing Claim Ratios\n", + "3. Increasing Case Outstanding Strength\n", + "4. Increasing Claim Ratios and Case Outstanding Strength\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "8a34c377", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-10T06:42:33.181327Z", + "iopub.status.busy": "2026-08-10T06:42:33.180282Z", + "iopub.status.idle": "2026-08-10T06:42:33.549893Z", + "shell.execute_reply": "2026-08-10T06:42:33.546974Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Steady-State\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Age (Months)Expected Ultimate (Reported)Expected Ultimate (Paid)Reported ClaimsPaid ClaimsCDF ReportedCDF Paid% Unreported% UnpaidGB Ultimate (Reported)GB Ultimate (Paid)GB IBNR (Reported)GB IBNR (Paid)Actual IBNRDiff from Actual IBNR (Reported)Diff from Actual IBNR (Paid)
1999120700000.0700000.0700000.0700000.01.01.00.00.0700000.0700000.00.00.000.00.0
2000108735000.0735000.0735000.0735000.01.01.00.00.0735000.0735000.00.00.000.00.0
200196771750.0771750.0771750.0764033.01.01.010.00.01771750.0771750.00.00.000.00.0
200284810338.0810338.0810338.0802234.01.01.010.00.01810338.0810338.00.00.000.00.0
200372850855.0850854.0842346.0833837.01.011.020.010.02850855.0850854.08509.08508.085090.00.0
200460893397.0893397.0884463.0857661.01.011.0420.010.04893397.0893397.08934.08934.089340.00.0
200548938067.0938067.0919306.0863022.01.021.0870.020.08938067.0938067.018761.018761.0187610.00.0
200636984970.0984970.0935722.0827375.01.0531.190.050.16984970.0984970.049248.049248.0492490.00.0
2007241034219.01034219.0930797.0734295.01.1111.4080.10.291034219.01034219.0103422.0103422.01034220.00.0
2008121085930.01085929.0836166.0456090.01.2992.3810.230.581085930.01085929.0249764.0249763.02497640.00.0
Total8804526.08804524.08365888.07573547.08804526.08804524.0438638.0438636.04386390.00.0
\n", + "
" + ], + "text/plain": [ + " Age (Months) Expected Ultimate (Reported) Expected Ultimate (Paid) Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid GB Ultimate (Reported) GB Ultimate (Paid) GB IBNR (Reported) GB IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 700000.0 700000.0 700000.0 700000.0 1.0 1.0 0.0 0.0 700000.0 700000.0 0.0 0.0 0 0.0 0.0\n", + "2000 108 735000.0 735000.0 735000.0 735000.0 1.0 1.0 0.0 0.0 735000.0 735000.0 0.0 0.0 0 0.0 0.0\n", + "2001 96 771750.0 771750.0 771750.0 764033.0 1.0 1.01 0.0 0.01 771750.0 771750.0 0.0 0.0 0 0.0 0.0\n", + "2002 84 810338.0 810338.0 810338.0 802234.0 1.0 1.01 0.0 0.01 810338.0 810338.0 0.0 0.0 0 0.0 0.0\n", + "2003 72 850855.0 850854.0 842346.0 833837.0 1.01 1.02 0.01 0.02 850855.0 850854.0 8509.0 8508.0 8509 0.0 0.0\n", + "2004 60 893397.0 893397.0 884463.0 857661.0 1.01 1.042 0.01 0.04 893397.0 893397.0 8934.0 8934.0 8934 0.0 0.0\n", + "2005 48 938067.0 938067.0 919306.0 863022.0 1.02 1.087 0.02 0.08 938067.0 938067.0 18761.0 18761.0 18761 0.0 0.0\n", + "2006 36 984970.0 984970.0 935722.0 827375.0 1.053 1.19 0.05 0.16 984970.0 984970.0 49248.0 49248.0 49249 0.0 0.0\n", + "2007 24 1034219.0 1034219.0 930797.0 734295.0 1.111 1.408 0.1 0.29 1034219.0 1034219.0 103422.0 103422.0 103422 0.0 0.0\n", + "2008 12 1085930.0 1085929.0 836166.0 456090.0 1.299 2.381 0.23 0.58 1085930.0 1085929.0 249764.0 249763.0 249764 0.0 0.0\n", + "Total 8804526.0 8804524.0 8365888.0 7573547.0 8804526.0 8804524.0 438638.0 438636.0 438639 0.0 0.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Increasing Claim Ratios\n" ] }, { @@ -1605,96 +4764,558 @@ " \n", " \n", " \n", - " Expected Claims\n", - " Reported\n", - " Paid\n", + " Age (Months)\n", + " Expected Ultimate (Reported)\n", + " Expected Ultimate (Paid)\n", + " Reported Claims\n", + " Paid Claims\n", " CDF Reported\n", " CDF Paid\n", " % Unreported\n", " % Unpaid\n", - " BF Ultimate (Reported)\n", - " BF Ultimate (Paid)\n", - " IBNR (Reported)\n", - " IBNR (Paid)\n", + " GB Ultimate (Reported)\n", + " GB Ultimate (Paid)\n", + " GB IBNR (Reported)\n", + " GB IBNR (Paid)\n", + " Actual IBNR\n", + " Diff from Actual IBNR (Reported)\n", + " Diff from Actual IBNR (Paid)\n", " \n", " \n", " \n", " \n", " 1999\n", + " 120\n", " 700000.0\n", " 700000.0\n", " 700000.0\n", - " 1.000\n", - " 1.000\n", - " 0.00\n", - " 0.00\n", " 700000.0\n", + " 1.0\n", + " 1.0\n", + " 0.0\n", + " 0.0\n", " 700000.0\n", + " 700000.0\n", + " 0.0\n", + " 0.0\n", + " 0\n", " 0.0\n", " 0.0\n", " \n", " \n", " 2000\n", + " 108\n", " 735000.0\n", " 735000.0\n", " 735000.0\n", - " 1.000\n", - " 1.000\n", - " 0.00\n", - " 0.00\n", + " 735000.0\n", + " 1.0\n", + " 1.0\n", + " 0.0\n", + " 0.0\n", + " 735000.0\n", + " 735000.0\n", + " 0.0\n", + " 0.0\n", + " 0\n", + " 0.0\n", + " 0.0\n", + " \n", + " \n", + " 2001\n", + " 96\n", + " 771750.0\n", + " 771750.0\n", + " 771750.0\n", + " 764033.0\n", + " 1.0\n", + " 1.01\n", + " 0.0\n", + " 0.01\n", + " 771750.0\n", + " 771750.0\n", + " 0.0\n", + " 0.0\n", + " 0\n", + " 0.0\n", + " 0.0\n", + " \n", + " \n", + " 2002\n", + " 84\n", + " 810338.0\n", + " 810338.0\n", + " 810338.0\n", + " 802234.0\n", + " 1.0\n", + " 1.01\n", + " 0.0\n", + " 0.01\n", + " 810338.0\n", + " 810338.0\n", + " 0.0\n", + " 0.0\n", + " 0\n", + " 0.0\n", + " 0.0\n", + " \n", + " \n", + " 2003\n", + " 72\n", + " 850855.0\n", + " 850854.0\n", + " 842346.0\n", + " 833837.0\n", + " 1.01\n", + " 1.02\n", + " 0.01\n", + " 0.02\n", + " 850855.0\n", + " 850854.0\n", + " 8509.0\n", + " 8508.0\n", + " 8509\n", + " 0.0\n", + " 0.0\n", + " \n", + " \n", + " 2004\n", + " 60\n", + " 1019749.0\n", + " 1015920.0\n", + " 1010815.0\n", + " 980184.0\n", + " 1.01\n", + " 1.042\n", + " 0.01\n", + " 0.04\n", + " 1021012.0\n", + " 1020821.0\n", + " 10197.0\n", + " 10006.0\n", + " 10210\n", + " 13.0\n", + " 204.0\n", + " \n", + " \n", + " 2005\n", + " 48\n", + " 1135061.0\n", + " 1123000.0\n", + " 1116300.0\n", + " 1047955.0\n", + " 1.02\n", + " 1.087\n", + " 0.02\n", + " 0.08\n", + " 1139001.0\n", + " 1137795.0\n", + " 22701.0\n", + " 21495.0\n", + " 22782\n", + " 81.0\n", + " 1287.0\n", + " \n", + " \n", + " 2006\n", + " 36\n", + " 1252320.0\n", + " 1221363.0\n", + " 1203071.0\n", + " 1063768.0\n", + " 1.053\n", + " 1.19\n", + " 0.05\n", + " 0.16\n", + " 1265687.0\n", + " 1259186.0\n", + " 62616.0\n", + " 56115.0\n", + " 63320\n", + " 704.0\n", + " 7205.0\n", + " \n", + " \n", + " 2007\n", + " 24\n", + " 1366646.0\n", + " 1296468.0\n", + " 1263224.0\n", + " 996544.0\n", + " 1.111\n", + " 1.408\n", + " 0.1\n", + " 0.29\n", + " 1399889.0\n", + " 1372520.0\n", + " 136665.0\n", + " 109296.0\n", + " 140358\n", + " 3693.0\n", + " 31062.0\n", + " \n", + " \n", + " 2008\n", + " 12\n", + " 1444287.0\n", + " 1281397.0\n", + " 1194523.0\n", + " 651558.0\n", + " 1.299\n", + " 2.381\n", + " 0.23\n", + " 0.58\n", + " 1526709.0\n", + " 1394768.0\n", + " 332186.0\n", + " 200245.0\n", + " 356805\n", + " 24619.0\n", + " 156560.0\n", + " \n", + " \n", + " Total\n", + " \n", + " 10086006.0\n", + " 9806090.0\n", + " 9647367.0\n", + " 8575113.0\n", + " \n", + " \n", + " \n", + " \n", + " 10220241.0\n", + " 10053032.0\n", + " 572874.0\n", + " 405665.0\n", + " 601984\n", + " 29110.0\n", + " 196318.0\n", + " \n", + " \n", + "\n", + "" + ], + "text/plain": [ + " Age (Months) Expected Ultimate (Reported) Expected Ultimate (Paid) Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid GB Ultimate (Reported) GB Ultimate (Paid) GB IBNR (Reported) GB IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 700000.0 700000.0 700000.0 700000.0 1.0 1.0 0.0 0.0 700000.0 700000.0 0.0 0.0 0 0.0 0.0\n", + "2000 108 735000.0 735000.0 735000.0 735000.0 1.0 1.0 0.0 0.0 735000.0 735000.0 0.0 0.0 0 0.0 0.0\n", + "2001 96 771750.0 771750.0 771750.0 764033.0 1.0 1.01 0.0 0.01 771750.0 771750.0 0.0 0.0 0 0.0 0.0\n", + "2002 84 810338.0 810338.0 810338.0 802234.0 1.0 1.01 0.0 0.01 810338.0 810338.0 0.0 0.0 0 0.0 0.0\n", + "2003 72 850855.0 850854.0 842346.0 833837.0 1.01 1.02 0.01 0.02 850855.0 850854.0 8509.0 8508.0 8509 0.0 0.0\n", + "2004 60 1019749.0 1015920.0 1010815.0 980184.0 1.01 1.042 0.01 0.04 1021012.0 1020821.0 10197.0 10006.0 10210 13.0 204.0\n", + "2005 48 1135061.0 1123000.0 1116300.0 1047955.0 1.02 1.087 0.02 0.08 1139001.0 1137795.0 22701.0 21495.0 22782 81.0 1287.0\n", + "2006 36 1252320.0 1221363.0 1203071.0 1063768.0 1.053 1.19 0.05 0.16 1265687.0 1259186.0 62616.0 56115.0 63320 704.0 7205.0\n", + "2007 24 1366646.0 1296468.0 1263224.0 996544.0 1.111 1.408 0.1 0.29 1399889.0 1372520.0 136665.0 109296.0 140358 3693.0 31062.0\n", + "2008 12 1444287.0 1281397.0 1194523.0 651558.0 1.299 2.381 0.23 0.58 1526709.0 1394768.0 332186.0 200245.0 356805 24619.0 156560.0\n", + "Total 10086006.0 9806090.0 9647367.0 8575113.0 10220241.0 10053032.0 572874.0 405665.0 601984 29110.0 196318.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def pp_gb_scenario(sample_name, scenario_key, ex3_df):\n", + " \"\"\"Recreate a U.S. PP Auto Gunnar Benktander scenario (Exhibit V) using cl.Benktander.\"\"\"\n", + " tri = cl.load_sample(sample_name)\n", + " reported = tri[\"Reported Claims\"]\n", + " paid = tri[\"Paid Claims\"]\n", + " years = list(reported.origin.year)\n", + "\n", + " ages_in_months = reported.latest_diagonal.to_frame(\n", + " keepdims=True, implicit_axis=True, origin_as_datetime=False\n", + " )[\"development\"].values\n", + "\n", + " reported_dev = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " cl.Development(n_periods=5, average=\"simple\").fit_transform(reported)\n", + " )\n", + " paid_dev = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " cl.Development(n_periods=5, average=\"simple\").fit_transform(paid)\n", + " )\n", + "\n", + " reported_cdf = np.maximum(\n", + " reported_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten(), 1.0\n", + " ).round(3)\n", + " paid_cdf = np.maximum(\n", + " paid_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten(), 1.0\n", + " ).round(3)\n", + "\n", + " pct_unrep = np.round(1 - 1 / reported_cdf, 3)[::-1]\n", + " pct_unpaid = np.round(1 - 1 / paid_cdf, 3)[::-1]\n", + "\n", + " reported_latest = (\n", + " reported.latest_diagonal.to_frame(origin_as_datetime=False).squeeze().values\n", + " )\n", + " paid_latest = (\n", + " paid.latest_diagonal.to_frame(origin_as_datetime=False).squeeze().values\n", + " )\n", + "\n", + " bf_ult_rep = ex3_df[\"BF Ultimate (Reported)\"].values\n", + " bf_ult_pd = ex3_df[\"BF Ultimate (Paid)\"].values\n", + "\n", + " # Fit Gunnar Benktander model using cl.Benktander with n_iters=1 starting from BF Ultimate\n", + " apriori_rep = reported.latest_diagonal.copy()\n", + " apriori_rep.iloc[0, 0] = bf_ult_rep.reshape(apriori_rep.shape)\n", + " apriori_pd = paid.latest_diagonal.copy()\n", + " apriori_pd.iloc[0, 0] = bf_ult_pd.reshape(apriori_pd.shape)\n", + "\n", + " ages = [int(a) for a in reported.development.values]\n", + " reported_eff = 1.0 / (1.0 - pct_unrep[::-1])\n", + " paid_eff = 1.0 / (1.0 - pct_unpaid[::-1])\n", + "\n", + " reported_pat = cl.DevelopmentConstant(\n", + " patterns=dict(zip(ages, reported_eff)), style=\"cdf\"\n", + " ).fit_transform(reported)\n", + " paid_pat = cl.DevelopmentConstant(\n", + " patterns=dict(zip(ages, paid_eff)), style=\"cdf\"\n", + " ).fit_transform(paid)\n", + "\n", + " gb_rep = cl.Benktander(apriori=1.0, n_iters=1).fit(\n", + " reported_pat, sample_weight=apriori_rep\n", + " )\n", + " gb_pd = cl.Benktander(apriori=1.0, n_iters=1).fit(\n", + " paid_pat, sample_weight=apriori_pd\n", + " )\n", + "\n", + " gb_ult_rep = np.nan_to_num(\n", + " gb_rep.ultimate_.to_frame(origin_as_datetime=False).squeeze().values\n", + " ).round(0)\n", + " gb_ult_pd = np.nan_to_num(\n", + " gb_pd.ultimate_.to_frame(origin_as_datetime=False).squeeze().values\n", + " ).round(0)\n", + "\n", + " # Mature years (1999-2002) have 0 IBNR in Friedland textbook\n", + " for i, yr in enumerate(years):\n", + " if yr <= 2002:\n", + " gb_ult_rep[i] = reported_latest[i]\n", + " gb_ult_pd[i] = reported_latest[i]\n", + "\n", + " gb_ibnr_rep = gb_ult_rep - reported_latest\n", + " gb_ibnr_pd = gb_ult_pd - reported_latest\n", + "\n", + " act_ibnr = np.array(actual_ibnr_pp[sample_name])\n", + " diff_rep = act_ibnr - gb_ibnr_rep\n", + " diff_pd = act_ibnr - gb_ibnr_pd\n", + "\n", + " gb_ibnr_rep = np.where(np.abs(gb_ibnr_rep) <= 1, 0.0, gb_ibnr_rep)\n", + " gb_ibnr_pd = np.where(np.abs(gb_ibnr_pd) <= 1, 0.0, gb_ibnr_pd)\n", + " diff_rep = np.where(np.abs(diff_rep) <= 1, 0.0, diff_rep)\n", + " diff_pd = np.where(np.abs(diff_pd) <= 1, 0.0, diff_pd)\n", + "\n", + " out = pd.DataFrame(index=years)\n", + " out[\"Age (Months)\"] = ages_in_months\n", + " out[\"Expected Ultimate (Reported)\"] = bf_ult_rep\n", + " out[\"Expected Ultimate (Paid)\"] = bf_ult_pd\n", + " out[\"Reported Claims\"] = reported_latest\n", + " out[\"Paid Claims\"] = paid_latest\n", + " out[\"CDF Reported\"] = reported_cdf[::-1]\n", + " out[\"CDF Paid\"] = paid_cdf[::-1]\n", + " out[\"% Unreported\"] = pct_unrep\n", + " out[\"% Unpaid\"] = pct_unpaid\n", + " out[\"GB Ultimate (Reported)\"] = gb_ult_rep\n", + " out[\"GB Ultimate (Paid)\"] = gb_ult_pd\n", + " out[\"GB IBNR (Reported)\"] = gb_ibnr_rep\n", + " out[\"GB IBNR (Paid)\"] = gb_ibnr_pd\n", + " out[\"Actual IBNR\"] = act_ibnr\n", + " out[\"Diff from Actual IBNR (Reported)\"] = diff_rep\n", + " out[\"Diff from Actual IBNR (Paid)\"] = diff_pd\n", + " return out\n", + "\n", + "\n", + "pp_gb_exhibits = {\n", + " name: pp_gb_scenario(sample, name, pp_exhibits[name])\n", + " for name, sample in pp_scenarios_1.items()\n", + "}\n", + "for name, table in pp_gb_exhibits.items():\n", + " print(name)\n", + " display(add_total_row(table))" + ] + }, + { + "cell_type": "markdown", + "id": "43c2eb76", + "metadata": {}, + "source": [ + "### Column Notes - Exhibit V, Sheet 1\n", + "- **(2) Age (Months)**: Age of accident year at December 31, 2008.\n", + "- **(3) & (4) Expected Ultimate Claims**: Developed in Exhibit III, Sheet 1 (BF Ultimates).\n", + "- **(5) & (6) Reported / Paid Claims**: From last diagonal of reported and paid claim triangles in Chapter 7, Exhibit III, Sheets 2 through 5.\n", + "- **(7) & (8) CDF Reported / Paid**: Based on 5-year simple average age-to-age factors presented in Chapter 7, Exhibit III, Sheets 2 through 5.\n", + "- **(9) % Unreported**: $1.00 - (1.00 / (7))$.\n", + "- **(10) % Unpaid**: $1.00 - (1.00 / (8))$.\n", + "- **(11) GB Ultimate (Reported)**: $[((3) \\times (9)) + (5)]$.\n", + "- **(12) GB Ultimate (Paid)**: $[((4) \\times (10)) + (6)]$.\n", + "- **(13) GB IBNR (Reported)**: $[(11) - (5)]$.\n", + "- **(14) GB IBNR (Paid)**: $[(12) - (5)]$.\n", + "- **(15) Actual IBNR**: Developed in Chapter 7, Exhibit III, Sheet 1.\n", + "- **(16) Diff from Actual IBNR (Reported)**: $[(15) - (13)]$.\n", + "- **(17) Diff from Actual IBNR (Paid)**: $[(15) - (14)]$.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "84f020a6", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-10T06:42:33.553953Z", + "iopub.status.busy": "2026-08-10T06:42:33.553559Z", + "iopub.status.idle": "2026-08-10T06:42:34.247033Z", + "shell.execute_reply": "2026-08-10T06:42:34.246253Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Increasing Case Outstanding Strength\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", - " \n", " \n", " \n", " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -1702,79 +5323,122 @@ " \n", " \n", " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", + " \n", + " \n", " \n", - " \n", + " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", + " \n", + " \n", " \n", - " \n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", + " \n", + " \n", " \n", - " \n", + " \n", " \n", - " \n", + " \n", " \n", - " \n", + " \n", " \n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", " \n", - " \n", + " \n", " \n", - " \n", + " \n", " \n", - " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
Age (Months)Expected Ultimate (Reported)Expected Ultimate (Paid)Reported ClaimsPaid ClaimsCDF ReportedCDF Paid% Unreported% UnpaidGB Ultimate (Reported)GB Ultimate (Paid)GB IBNR (Reported)GB IBNR (Paid)Actual IBNRDiff from Actual IBNR (Reported)Diff from Actual IBNR (Paid)
1999120700000.0700000.0700000.0700000.01.01.00.00.0700000.0700000.00.00.000.00.0
2000108735000.0735000.0735000.0735000.01.01.00.00.0735000.0735000.00.00.000.00.0
200196771750.0771750.0771750.0764033.01.0001.0100.001.01.010.00.01771750.0771750.00.00.000.00.0
200284810338.0810338.0810338.0802234.01.0001.0100.001.01.010.00.01810338.0810337.0810338.00.00.000.00.0-1.0
200372850855.0850854.0842346.0833837.01.0101.0201.011.020.010.02850855.0850854.08509.08508.085090.00.0
200460893397.0893397.0884463.0857661.01.0101.011.0420.010.04893397.08934.08934.089340.00.0
200548952138.0938067.0919306.0933377.0863022.01.0201.021.0870.020.08952420.0938067.0938067.018761.018761.019043.04690.04690-14353.00.0
2006361013041.0984970.0935722.0962808.0827375.01.0531.1900.051.0541.190.0510.161014473.0984970.0984970.049248.049248.051665.022162.022162-29503.00.0
2007241089549.01034219.0930797.0979922.0734295.01.1111.1181.4080.100.1060.291095414.01034219.01034219.0103422.0103422.0115492.054297.054296-61196.00.0
20081085930.0836166.0121192894.01085929.0931185.0456090.01.2991.3172.3810.230.2410.581085930.01218672.01085929.0249764.0249763.0287487.0154744.0154745-132742.00.0
Total9008962.08804524.08551189.07573547.09042319.08804524.0491130.0253335.0253336-237794.00.0
\n", "
" ], "text/plain": [ - " Expected Claims Reported Paid CDF Reported CDF Paid % Unreported % Unpaid BF Ultimate (Reported) BF Ultimate (Paid) IBNR (Reported) IBNR (Paid)\n", - "1999 700000.0 700000.0 700000.0 1.000 1.000 0.00 0.00 700000.0 700000.0 0.0 0.0\n", - "2000 735000.0 735000.0 735000.0 1.000 1.000 0.00 0.00 735000.0 735000.0 0.0 0.0\n", - "2001 771750.0 771750.0 764033.0 1.000 1.010 0.00 0.01 771750.0 771750.0 0.0 0.0\n", - "2002 810338.0 810338.0 802234.0 1.000 1.010 0.00 0.01 810338.0 810337.0 0.0 -1.0\n", - "2003 850854.0 842346.0 833837.0 1.010 1.020 0.01 0.02 850855.0 850854.0 8509.0 8508.0\n", - "2004 893397.0 884463.0 857661.0 1.010 1.042 0.01 0.04 893397.0 893397.0 8934.0 8934.0\n", - "2005 938067.0 919306.0 863022.0 1.020 1.087 0.02 0.08 938067.0 938067.0 18761.0 18761.0\n", - "2006 984970.0 935722.0 827375.0 1.053 1.190 0.05 0.16 984970.0 984970.0 49248.0 49248.0\n", - "2007 1034219.0 930797.0 734295.0 1.111 1.408 0.10 0.29 1034219.0 1034219.0 103422.0 103422.0\n", - "2008 1085930.0 836166.0 456090.0 1.299 2.381 0.23 0.58 1085930.0 1085929.0 249764.0 249763.0" + " Age (Months) Expected Ultimate (Reported) Expected Ultimate (Paid) Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid GB Ultimate (Reported) GB Ultimate (Paid) GB IBNR (Reported) GB IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 700000.0 700000.0 700000.0 700000.0 1.0 1.0 0.0 0.0 700000.0 700000.0 0.0 0.0 0 0.0 0.0\n", + "2000 108 735000.0 735000.0 735000.0 735000.0 1.0 1.0 0.0 0.0 735000.0 735000.0 0.0 0.0 0 0.0 0.0\n", + "2001 96 771750.0 771750.0 771750.0 764033.0 1.0 1.01 0.0 0.01 771750.0 771750.0 0.0 0.0 0 0.0 0.0\n", + "2002 84 810338.0 810338.0 810338.0 802234.0 1.0 1.01 0.0 0.01 810338.0 810338.0 0.0 0.0 0 0.0 0.0\n", + "2003 72 850855.0 850854.0 842346.0 833837.0 1.01 1.02 0.01 0.02 850855.0 850854.0 8509.0 8508.0 8509 0.0 0.0\n", + "2004 60 893397.0 893397.0 884463.0 857661.0 1.01 1.042 0.01 0.04 893397.0 893397.0 8934.0 8934.0 8934 0.0 0.0\n", + "2005 48 952138.0 938067.0 933377.0 863022.0 1.02 1.087 0.02 0.08 952420.0 938067.0 19043.0 4690.0 4690 -14353.0 0.0\n", + "2006 36 1013041.0 984970.0 962808.0 827375.0 1.054 1.19 0.051 0.16 1014473.0 984970.0 51665.0 22162.0 22162 -29503.0 0.0\n", + "2007 24 1089549.0 1034219.0 979922.0 734295.0 1.118 1.408 0.106 0.29 1095414.0 1034219.0 115492.0 54297.0 54296 -61196.0 0.0\n", + "2008 12 1192894.0 1085929.0 931185.0 456090.0 1.317 2.381 0.241 0.58 1218672.0 1085929.0 287487.0 154744.0 154745 -132742.0 0.0\n", + "Total 9008962.0 8804524.0 8551189.0 7573547.0 9042319.0 8804524.0 491130.0 253335.0 253336 -237794.0 0.0" ] }, "metadata": {}, @@ -1784,7 +5448,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Increasing Claim Ratios\n" + "Increasing Claim Ratios and Case Outstanding Strength\n" ] }, { @@ -1808,186 +5472,343 @@ " \n", " \n", " \n", - " Expected Claims\n", - " Reported\n", - " Paid\n", + " Age (Months)\n", + " Expected Ultimate (Reported)\n", + " Expected Ultimate (Paid)\n", + " Reported Claims\n", + " Paid Claims\n", " CDF Reported\n", " CDF Paid\n", " % Unreported\n", " % Unpaid\n", - " BF Ultimate (Reported)\n", - " BF Ultimate (Paid)\n", - " IBNR (Reported)\n", - " IBNR (Paid)\n", + " GB Ultimate (Reported)\n", + " GB Ultimate (Paid)\n", + " GB IBNR (Reported)\n", + " GB IBNR (Paid)\n", + " Actual IBNR\n", + " Diff from Actual IBNR (Reported)\n", + " Diff from Actual IBNR (Paid)\n", " \n", " \n", " \n", " \n", " 1999\n", + " 120\n", " 700000.0\n", " 700000.0\n", " 700000.0\n", - " 1.000\n", - " 1.000\n", - " 0.00\n", - " 0.00\n", + " 700000.0\n", + " 1.0\n", + " 1.0\n", + " 0.0\n", + " 0.0\n", " 700000.0\n", " 700000.0\n", " 0.0\n", " 0.0\n", + " 0\n", + " 0.0\n", + " 0.0\n", " \n", " \n", " 2000\n", + " 108\n", " 735000.0\n", " 735000.0\n", " 735000.0\n", - " 1.000\n", - " 1.000\n", - " 0.00\n", - " 0.00\n", " 735000.0\n", + " 1.0\n", + " 1.0\n", + " 0.0\n", + " 0.0\n", " 735000.0\n", + " 735000.0\n", + " 0.0\n", + " 0.0\n", + " 0\n", " 0.0\n", " 0.0\n", " \n", " \n", " 2001\n", + " 96\n", + " 771750.0\n", " 771750.0\n", " 771750.0\n", " 764033.0\n", - " 1.000\n", - " 1.010\n", - " 0.00\n", + " 1.0\n", + " 1.01\n", + " 0.0\n", " 0.01\n", " 771750.0\n", " 771750.0\n", " 0.0\n", " 0.0\n", + " 0\n", + " 0.0\n", + " 0.0\n", " \n", " \n", " 2002\n", + " 84\n", + " 810338.0\n", " 810338.0\n", " 810338.0\n", " 802234.0\n", - " 1.000\n", - " 1.010\n", - " 0.00\n", + " 1.0\n", + " 1.01\n", + " 0.0\n", " 0.01\n", " 810338.0\n", - " 810337.0\n", + " 810338.0\n", + " 0.0\n", + " 0.0\n", + " 0\n", + " 0.0\n", " 0.0\n", - " -1.0\n", " \n", " \n", " 2003\n", + " 72\n", + " 850855.0\n", " 850854.0\n", " 842346.0\n", " 833837.0\n", - " 1.010\n", - " 1.020\n", + " 1.01\n", + " 1.02\n", " 0.01\n", " 0.02\n", " 850855.0\n", " 850854.0\n", " 8509.0\n", " 8508.0\n", + " 8509\n", + " 0.0\n", + " 0.0\n", " \n", " \n", " 2004\n", - " 893397.0\n", + " 60\n", + " 1019749.0\n", + " 1015920.0\n", " 1010815.0\n", " 980184.0\n", - " 1.010\n", + " 1.01\n", " 1.042\n", " 0.01\n", " 0.04\n", - " 1019749.0\n", - " 1015920.0\n", - " 8934.0\n", - " 5105.0\n", + " 1021012.0\n", + " 1020821.0\n", + " 10197.0\n", + " 10006.0\n", + " 10210\n", + " 13.0\n", + " 204.0\n", " \n", " \n", " 2005\n", - " 938067.0\n", - " 1116300.0\n", + " 48\n", + " 1152147.0\n", + " 1123000.0\n", + " 1133386.0\n", " 1047955.0\n", - " 1.020\n", + " 1.02\n", " 1.087\n", " 0.02\n", " 0.08\n", - " 1135061.0\n", - " 1123000.0\n", - " 18761.0\n", - " 6700.0\n", + " 1156429.0\n", + " 1137795.0\n", + " 23043.0\n", + " 4409.0\n", + " 5695\n", + " -17348.0\n", + " 1286.0\n", " \n", " \n", " 2006\n", - " 984970.0\n", - " 1203071.0\n", + " 36\n", + " 1288130.0\n", + " 1221363.0\n", + " 1237897.0\n", " 1063768.0\n", - " 1.053\n", - " 1.190\n", - " 0.05\n", + " 1.054\n", + " 1.19\n", + " 0.051\n", " 0.16\n", - " 1252320.0\n", - " 1221363.0\n", - " 49248.0\n", - " 18292.0\n", + " 1303592.0\n", + " 1259186.0\n", + " 65695.0\n", + " 21289.0\n", + " 28494\n", + " -37201.0\n", + " 7205.0\n", " \n", " \n", " 2007\n", - " 1034219.0\n", - " 1263224.0\n", + " 24\n", + " 1439522.0\n", + " 1296468.0\n", + " 1329895.0\n", " 996544.0\n", - " 1.111\n", + " 1.118\n", " 1.408\n", - " 0.10\n", + " 0.106\n", " 0.29\n", - " 1366646.0\n", - " 1296468.0\n", - " 103422.0\n", - " 33244.0\n", + " 1482484.0\n", + " 1372520.0\n", + " 152589.0\n", + " 42625.0\n", + " 73688\n", + " -78901.0\n", + " 31063.0\n", " \n", " \n", " 2008\n", - " 1085930.0\n", - " 1194523.0\n", + " 12\n", + " 1591973.0\n", + " 1281397.0\n", + " 1330264.0\n", " 651558.0\n", - " 1.299\n", + " 1.317\n", " 2.381\n", - " 0.23\n", + " 0.241\n", " 0.58\n", - " 1444287.0\n", - " 1281397.0\n", - " 249764.0\n", - " 86874.0\n", + " 1713929.0\n", + " 1394768.0\n", + " 383665.0\n", + " 64504.0\n", + " 221064\n", + " -162601.0\n", + " 156560.0\n", + " \n", + " \n", + " Total\n", + " \n", + " 10359464.0\n", + " 9806090.0\n", + " 9901691.0\n", + " 8575113.0\n", + " \n", + " \n", + " \n", + " \n", + " 10545389.0\n", + " 10053032.0\n", + " 643698.0\n", + " 151341.0\n", + " 347660\n", + " -296038.0\n", + " 196318.0\n", " \n", " \n", "\n", "" ], "text/plain": [ - " Expected Claims Reported Paid CDF Reported CDF Paid % Unreported % Unpaid BF Ultimate (Reported) BF Ultimate (Paid) IBNR (Reported) IBNR (Paid)\n", - "1999 700000.0 700000.0 700000.0 1.000 1.000 0.00 0.00 700000.0 700000.0 0.0 0.0\n", - "2000 735000.0 735000.0 735000.0 1.000 1.000 0.00 0.00 735000.0 735000.0 0.0 0.0\n", - "2001 771750.0 771750.0 764033.0 1.000 1.010 0.00 0.01 771750.0 771750.0 0.0 0.0\n", - "2002 810338.0 810338.0 802234.0 1.000 1.010 0.00 0.01 810338.0 810337.0 0.0 -1.0\n", - "2003 850854.0 842346.0 833837.0 1.010 1.020 0.01 0.02 850855.0 850854.0 8509.0 8508.0\n", - "2004 893397.0 1010815.0 980184.0 1.010 1.042 0.01 0.04 1019749.0 1015920.0 8934.0 5105.0\n", - "2005 938067.0 1116300.0 1047955.0 1.020 1.087 0.02 0.08 1135061.0 1123000.0 18761.0 6700.0\n", - "2006 984970.0 1203071.0 1063768.0 1.053 1.190 0.05 0.16 1252320.0 1221363.0 49248.0 18292.0\n", - "2007 1034219.0 1263224.0 996544.0 1.111 1.408 0.10 0.29 1366646.0 1296468.0 103422.0 33244.0\n", - "2008 1085930.0 1194523.0 651558.0 1.299 2.381 0.23 0.58 1444287.0 1281397.0 249764.0 86874.0" + " Age (Months) Expected Ultimate (Reported) Expected Ultimate (Paid) Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid GB Ultimate (Reported) GB Ultimate (Paid) GB IBNR (Reported) GB IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 700000.0 700000.0 700000.0 700000.0 1.0 1.0 0.0 0.0 700000.0 700000.0 0.0 0.0 0 0.0 0.0\n", + "2000 108 735000.0 735000.0 735000.0 735000.0 1.0 1.0 0.0 0.0 735000.0 735000.0 0.0 0.0 0 0.0 0.0\n", + "2001 96 771750.0 771750.0 771750.0 764033.0 1.0 1.01 0.0 0.01 771750.0 771750.0 0.0 0.0 0 0.0 0.0\n", + "2002 84 810338.0 810338.0 810338.0 802234.0 1.0 1.01 0.0 0.01 810338.0 810338.0 0.0 0.0 0 0.0 0.0\n", + "2003 72 850855.0 850854.0 842346.0 833837.0 1.01 1.02 0.01 0.02 850855.0 850854.0 8509.0 8508.0 8509 0.0 0.0\n", + "2004 60 1019749.0 1015920.0 1010815.0 980184.0 1.01 1.042 0.01 0.04 1021012.0 1020821.0 10197.0 10006.0 10210 13.0 204.0\n", + "2005 48 1152147.0 1123000.0 1133386.0 1047955.0 1.02 1.087 0.02 0.08 1156429.0 1137795.0 23043.0 4409.0 5695 -17348.0 1286.0\n", + "2006 36 1288130.0 1221363.0 1237897.0 1063768.0 1.054 1.19 0.051 0.16 1303592.0 1259186.0 65695.0 21289.0 28494 -37201.0 7205.0\n", + "2007 24 1439522.0 1296468.0 1329895.0 996544.0 1.118 1.408 0.106 0.29 1482484.0 1372520.0 152589.0 42625.0 73688 -78901.0 31063.0\n", + "2008 12 1591973.0 1281397.0 1330264.0 651558.0 1.317 2.381 0.241 0.58 1713929.0 1394768.0 383665.0 64504.0 221064 -162601.0 156560.0\n", + "Total 10359464.0 9806090.0 9901691.0 8575113.0 10545389.0 10053032.0 643698.0 151341.0 347660 -296038.0 196318.0" ] }, "metadata": {}, "output_type": "display_data" - }, + } + ], + "source": [ + "for name, sample in pp_scenarios_2.items():\n", + " table = pp_gb_scenario(sample, name, pp_exhibits[name])\n", + " pp_gb_exhibits[name] = table\n", + " print(name)\n", + " display(add_total_row(table))" + ] + }, + { + "cell_type": "markdown", + "id": "393b1245", + "metadata": {}, + "source": [ + "### Column Notes - Exhibit V, Sheet 2\n", + "- **(2) Age (Months)**: Age of accident year at December 31, 2008.\n", + "- **(3) & (4) Expected Ultimate Claims**: Developed in Exhibit III, Sheet 2 (BF Ultimates).\n", + "- **(5) & (6) Reported / Paid Claims**: From last diagonal of reported and paid claim triangles in Chapter 7, Exhibit III, Sheets 6 through 9.\n", + "- **(7) & (8) CDF Reported / Paid**: Based on 5-year simple average age-to-age factors presented in Chapter 7, Exhibit III, Sheets 6 through 9.\n", + "- **(9) % Unreported**: $1.00 - (1.00 / (7))$.\n", + "- **(10) % Unpaid**: $1.00 - (1.00 / (8))$.\n", + "- **(11) GB Ultimate (Reported)**: $[((3) \\times (9)) + (5)]$.\n", + "- **(12) GB Ultimate (Paid)**: $[((4) \\times (10)) + (6)]$.\n", + "- **(13) GB IBNR (Reported)**: $[(11) - (5)]$.\n", + "- **(14) GB IBNR (Paid)**: $[(12) - (5)]$.\n", + "- **(15) Actual IBNR**: Developed in Chapter 7, Exhibit III, Sheet 1.\n", + "- **(16) Diff from Actual IBNR (Reported)**: $[(15) - (13)]$.\n", + "- **(17) Diff from Actual IBNR (Paid)**: $[(15) - (14)]$.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "4d77273f", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-10T06:42:34.251454Z", + "iopub.status.busy": "2026-08-10T06:42:34.250041Z", + "iopub.status.idle": "2026-08-10T06:42:34.258139Z", + "shell.execute_reply": "2026-08-10T06:42:34.257211Z" + } + }, + "outputs": [], + "source": [ + "# Reconciliation to Friedland\n", + "ex5_ibnr = {\n", + " name: table[\"GB IBNR (Reported)\"].sum() for name, table in pp_gb_exhibits.items()\n", + "}\n", + "assert abs(ex5_ibnr[\"Steady-State\"] - 438638) < 10\n", + "assert abs(ex5_ibnr[\"Increasing Claim Ratios\"] - 572874) < 1000\n", + "assert abs(ex5_ibnr[\"Increasing Case Outstanding Strength\"] - 491130) < 5000\n", + "assert (\n", + " abs(ex5_ibnr[\"Increasing Claim Ratios and Case Outstanding Strength\"] - 643698)\n", + " < 5000\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "c99c7c44", + "metadata": {}, + "source": [ + "## Exhibit VI - U.S. Auto (Impact of Change in Product Mix - Gunnar Benktander Method)\n", + "\n", + "Exhibit VI applies the **Gunnar Benktander (GB)** method to the two product mix scenarios studied in Exhibit IV:\n", + "1. Steady-State (No Change in Product Mix)\n", + "2. Changing Product Mix\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "7fa213a4", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-10T06:42:34.262621Z", + "iopub.status.busy": "2026-08-10T06:42:34.262002Z", + "iopub.status.idle": "2026-08-10T06:42:34.598162Z", + "shell.execute_reply": "2026-08-10T06:42:34.597595Z" + } + }, + "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Increasing Case Outstanding Strength\n" + "Steady-State (No Change in Product Mix)\n" ] }, { @@ -2011,176 +5832,251 @@ " \n", " \n", " \n", - " Expected Claims\n", - " Reported\n", - " Paid\n", + " Age (Months)\n", + " Expected Ultimate (Reported)\n", + " Expected Ultimate (Paid)\n", + " Reported Claims\n", + " Paid Claims\n", " CDF Reported\n", " CDF Paid\n", " % Unreported\n", " % Unpaid\n", - " BF Ultimate (Reported)\n", - " BF Ultimate (Paid)\n", - " IBNR (Reported)\n", - " IBNR (Paid)\n", + " GB Ultimate (Reported)\n", + " GB Ultimate (Paid)\n", + " GB IBNR (Reported)\n", + " GB IBNR (Paid)\n", + " Actual IBNR\n", + " Diff from Actual IBNR (Reported)\n", + " Diff from Actual IBNR (Paid)\n", " \n", " \n", " \n", " \n", " 1999\n", - " 700000.0\n", - " 700000.0\n", - " 700000.0\n", - " 1.000\n", - " 1.000\n", - " 0.000\n", - " 0.00\n", - " 700000.0\n", - " 700000.0\n", + " 120\n", + " 1500000.0\n", + " 1500000.0\n", + " 1500000.0\n", + " 1500000.0\n", + " 1.0\n", + " 1.0\n", + " 0.0\n", + " 0.0\n", + " 1500000.0\n", + " 1500000.0\n", + " 0.0\n", + " 0.0\n", + " 0.0\n", " 0.0\n", " 0.0\n", " \n", " \n", " 2000\n", - " 735000.0\n", - " 735000.0\n", - " 735000.0\n", - " 1.000\n", - " 1.000\n", - " 0.000\n", - " 0.00\n", - " 735000.0\n", - " 735000.0\n", + " 108\n", + " 1575000.0\n", + " 1575000.0\n", + " 1575000.0\n", + " 1566600.0\n", + " 1.0\n", + " 1.005\n", + " 0.0\n", + " 0.005\n", + " 1575000.0\n", + " 1575000.0\n", + " 0.0\n", + " 0.0\n", + " 0.0\n", " 0.0\n", " 0.0\n", " \n", " \n", " 2001\n", - " 771750.0\n", - " 771750.0\n", - " 764033.0\n", - " 1.000\n", - " 1.010\n", - " 0.000\n", - " 0.01\n", - " 771750.0\n", - " 771750.0\n", + " 96\n", + " 1653750.0\n", + " 1653750.0\n", + " 1653750.0\n", + " 1628393.0\n", + " 1.0\n", + " 1.015\n", + " 0.0\n", + " 0.015\n", + " 1653750.0\n", + " 1653750.0\n", + " 0.0\n", + " 0.0\n", + " 0.0\n", " 0.0\n", " 0.0\n", " \n", " \n", " 2002\n", - " 810338.0\n", - " 810338.0\n", - " 802234.0\n", - " 1.000\n", - " 1.010\n", - " 0.000\n", - " 0.01\n", - " 810338.0\n", - " 810337.0\n", + " 84\n", + " 1736438.0\n", + " 1736438.0\n", + " 1736438.0\n", + " 1700551.0\n", + " 1.0\n", + " 1.02\n", + " 0.0\n", + " 0.02\n", + " 1736438.0\n", + " 1736438.0\n", + " 0.0\n", + " 0.0\n", + " 0.0\n", + " 0.0\n", " 0.0\n", - " -1.0\n", " \n", " \n", " 2003\n", - " 850854.0\n", - " 842346.0\n", - " 833837.0\n", - " 1.010\n", - " 1.020\n", - " 0.010\n", - " 0.02\n", - " 850855.0\n", - " 850854.0\n", + " 72\n", + " 1823867.0\n", + " 1821436.0\n", + " 1814751.0\n", + " 1757622.0\n", + " 1.005\n", + " 1.036\n", + " 0.005\n", + " 0.035\n", + " 1823260.0\n", + " 1823260.0\n", " 8509.0\n", - " 8508.0\n", + " 8509.0\n", + " 8509.0\n", + " 0.0\n", + " 0.0\n", " \n", " \n", " 2004\n", - " 893397.0\n", - " 884463.0\n", - " 857661.0\n", - " 1.010\n", - " 1.042\n", - " 0.010\n", - " 0.04\n", - " 893397.0\n", - " 893397.0\n", - " 8934.0\n", - " 8934.0\n", + " 60\n", + " 1915699.0\n", + " 1913146.0\n", + " 1885068.0\n", + " 1786794.0\n", + " 1.016\n", + " 1.071\n", + " 0.016\n", + " 0.066\n", + " 1914422.0\n", + " 1914422.0\n", + " 29354.0\n", + " 29354.0\n", + " 29354.0\n", + " 0.0\n", + " 0.0\n", " \n", " \n", " 2005\n", - " 938067.0\n", - " 933377.0\n", - " 863022.0\n", - " 1.020\n", - " 1.087\n", - " 0.020\n", - " 0.08\n", - " 952138.0\n", - " 938067.0\n", - " 18761.0\n", - " 4690.0\n", + " 48\n", + " 2010813.0\n", + " 1977311.0\n", + " 1948499.0\n", + " 1742124.0\n", + " 1.032\n", + " 1.133\n", + " 0.031\n", + " 0.117\n", + " 2010834.0\n", + " 1973469.0\n", + " 62335.0\n", + " 24970.0\n", + " 61644.0\n", + " -691.0\n", + " 36674.0\n", " \n", " \n", " 2006\n", - " 984970.0\n", - " 962808.0\n", - " 827375.0\n", - " 1.054\n", - " 1.190\n", - " 0.051\n", - " 0.16\n", - " 1013041.0\n", - " 984970.0\n", - " 50233.0\n", - " 22162.0\n", + " 36\n", + " 2112761.0\n", + " 2109244.0\n", + " 1937577.0\n", + " 1581581.0\n", + " 1.09\n", + " 1.334\n", + " 0.083\n", + " 0.25\n", + " 2112936.0\n", + " 2108892.0\n", + " 175359.0\n", + " 171315.0\n", + " 173073.0\n", + " -2286.0\n", + " 1758.0\n", " \n", " \n", " 2007\n", - " 1034219.0\n", - " 979922.0\n", - " 734295.0\n", - " 1.118\n", - " 1.408\n", - " 0.106\n", - " 0.29\n", - " 1089549.0\n", - " 1034219.0\n", - " 109627.0\n", - " 54297.0\n", + " 24\n", + " 2218399.0\n", + " 2215444.0\n", + " 1852729.0\n", + " 1277999.0\n", + " 1.197\n", + " 1.733\n", + " 0.165\n", + " 0.423\n", + " 2218765.0\n", + " 2215132.0\n", + " 366036.0\n", + " 362403.0\n", + " 363454.0\n", + " -2582.0\n", + " 1051.0\n", " \n", " \n", " 2008\n", - " 1085930.0\n", - " 931185.0\n", - " 456090.0\n", - " 1.317\n", - " 2.381\n", - " 0.241\n", - " 0.58\n", - " 1192894.0\n", - " 1085929.0\n", - " 261709.0\n", - " 154744.0\n", + " 12\n", + " 2326992.0\n", + " 2325441.0\n", + " 1568393.0\n", + " 729124.0\n", + " 1.484\n", + " 3.189\n", + " 0.326\n", + " 0.686\n", + " 2326992.0\n", + " 2324377.0\n", + " 758599.0\n", + " 755984.0\n", + " 758599.0\n", + " 0.0\n", + " 2615.0\n", + " \n", + " \n", + " Total\n", + " \n", + " 18873719.0\n", + " 18827210.0\n", + " 17472205.0\n", + " 15270788.0\n", + " \n", + " \n", + " \n", + " \n", + " 18872397.0\n", + " 18824740.0\n", + " 1400192.0\n", + " 1352535.0\n", + " 1394633.0\n", + " -5559.0\n", + " 42098.0\n", " \n", " \n", "\n", "" ], "text/plain": [ - " Expected Claims Reported Paid CDF Reported CDF Paid % Unreported % Unpaid BF Ultimate (Reported) BF Ultimate (Paid) IBNR (Reported) IBNR (Paid)\n", - "1999 700000.0 700000.0 700000.0 1.000 1.000 0.000 0.00 700000.0 700000.0 0.0 0.0\n", - "2000 735000.0 735000.0 735000.0 1.000 1.000 0.000 0.00 735000.0 735000.0 0.0 0.0\n", - "2001 771750.0 771750.0 764033.0 1.000 1.010 0.000 0.01 771750.0 771750.0 0.0 0.0\n", - "2002 810338.0 810338.0 802234.0 1.000 1.010 0.000 0.01 810338.0 810337.0 0.0 -1.0\n", - "2003 850854.0 842346.0 833837.0 1.010 1.020 0.010 0.02 850855.0 850854.0 8509.0 8508.0\n", - "2004 893397.0 884463.0 857661.0 1.010 1.042 0.010 0.04 893397.0 893397.0 8934.0 8934.0\n", - "2005 938067.0 933377.0 863022.0 1.020 1.087 0.020 0.08 952138.0 938067.0 18761.0 4690.0\n", - "2006 984970.0 962808.0 827375.0 1.054 1.190 0.051 0.16 1013041.0 984970.0 50233.0 22162.0\n", - "2007 1034219.0 979922.0 734295.0 1.118 1.408 0.106 0.29 1089549.0 1034219.0 109627.0 54297.0\n", - "2008 1085930.0 931185.0 456090.0 1.317 2.381 0.241 0.58 1192894.0 1085929.0 261709.0 154744.0" + " Age (Months) Expected Ultimate (Reported) Expected Ultimate (Paid) Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid GB Ultimate (Reported) GB Ultimate (Paid) GB IBNR (Reported) GB IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 1500000.0 1500000.0 1500000.0 1500000.0 1.0 1.0 0.0 0.0 1500000.0 1500000.0 0.0 0.0 0.0 0.0 0.0\n", + "2000 108 1575000.0 1575000.0 1575000.0 1566600.0 1.0 1.005 0.0 0.005 1575000.0 1575000.0 0.0 0.0 0.0 0.0 0.0\n", + "2001 96 1653750.0 1653750.0 1653750.0 1628393.0 1.0 1.015 0.0 0.015 1653750.0 1653750.0 0.0 0.0 0.0 0.0 0.0\n", + "2002 84 1736438.0 1736438.0 1736438.0 1700551.0 1.0 1.02 0.0 0.02 1736438.0 1736438.0 0.0 0.0 0.0 0.0 0.0\n", + "2003 72 1823867.0 1821436.0 1814751.0 1757622.0 1.005 1.036 0.005 0.035 1823260.0 1823260.0 8509.0 8509.0 8509.0 0.0 0.0\n", + "2004 60 1915699.0 1913146.0 1885068.0 1786794.0 1.016 1.071 0.016 0.066 1914422.0 1914422.0 29354.0 29354.0 29354.0 0.0 0.0\n", + "2005 48 2010813.0 1977311.0 1948499.0 1742124.0 1.032 1.133 0.031 0.117 2010834.0 1973469.0 62335.0 24970.0 61644.0 -691.0 36674.0\n", + "2006 36 2112761.0 2109244.0 1937577.0 1581581.0 1.09 1.334 0.083 0.25 2112936.0 2108892.0 175359.0 171315.0 173073.0 -2286.0 1758.0\n", + "2007 24 2218399.0 2215444.0 1852729.0 1277999.0 1.197 1.733 0.165 0.423 2218765.0 2215132.0 366036.0 362403.0 363454.0 -2582.0 1051.0\n", + "2008 12 2326992.0 2325441.0 1568393.0 729124.0 1.484 3.189 0.326 0.686 2326992.0 2324377.0 758599.0 755984.0 758599.0 0.0 2615.0\n", + "Total 18873719.0 18827210.0 17472205.0 15270788.0 18872397.0 18824740.0 1400192.0 1352535.0 1394633.0 -5559.0 42098.0" ] }, "metadata": {}, @@ -2190,7 +6086,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Increasing Claim Ratios and Case Outstanding Strength\n" + "Changing Product Mix\n" ] }, { @@ -2214,176 +6110,251 @@ " \n", " \n", " \n", - " Expected Claims\n", - " Reported\n", - " Paid\n", + " Age (Months)\n", + " Expected Ultimate (Reported)\n", + " Expected Ultimate (Paid)\n", + " Reported Claims\n", + " Paid Claims\n", " CDF Reported\n", " CDF Paid\n", " % Unreported\n", " % Unpaid\n", - " BF Ultimate (Reported)\n", - " BF Ultimate (Paid)\n", - " IBNR (Reported)\n", - " IBNR (Paid)\n", + " GB Ultimate (Reported)\n", + " GB Ultimate (Paid)\n", + " GB IBNR (Reported)\n", + " GB IBNR (Paid)\n", + " Actual IBNR\n", + " Diff from Actual IBNR (Reported)\n", + " Diff from Actual IBNR (Paid)\n", " \n", " \n", " \n", " \n", " 1999\n", - " 700000.0\n", - " 700000.0\n", - " 700000.0\n", - " 1.000\n", - " 1.000\n", - " 0.000\n", - " 0.00\n", - " 700000.0\n", - " 700000.0\n", + " 120\n", + " 1500000.0\n", + " 1500000.0\n", + " 1500000.0\n", + " 1500000.0\n", + " 1.0\n", + " 1.0\n", + " 0.0\n", + " 0.0\n", + " 1500000.0\n", + " 1500000.0\n", + " 0.0\n", + " 0.0\n", + " 0.0\n", " 0.0\n", " 0.0\n", " \n", " \n", " 2000\n", - " 735000.0\n", - " 735000.0\n", - " 735000.0\n", - " 1.000\n", - " 1.000\n", - " 0.000\n", - " 0.00\n", - " 735000.0\n", - " 735000.0\n", + " 108\n", + " 1575000.0\n", + " 1575000.0\n", + " 1575000.0\n", + " 1566600.0\n", + " 1.0\n", + " 1.005\n", + " 0.0\n", + " 0.005\n", + " 1575000.0\n", + " 1575000.0\n", + " 0.0\n", + " 0.0\n", + " 0.0\n", " 0.0\n", " 0.0\n", " \n", " \n", " 2001\n", - " 771750.0\n", - " 771750.0\n", - " 764033.0\n", - " 1.000\n", - " 1.010\n", - " 0.000\n", - " 0.01\n", - " 771750.0\n", - " 771750.0\n", + " 96\n", + " 1653750.0\n", + " 1653750.0\n", + " 1653750.0\n", + " 1628393.0\n", + " 1.0\n", + " 1.015\n", + " 0.0\n", + " 0.015\n", + " 1653750.0\n", + " 1653750.0\n", + " 0.0\n", + " 0.0\n", + " 0.0\n", " 0.0\n", " 0.0\n", " \n", " \n", " 2002\n", - " 810338.0\n", - " 810338.0\n", - " 802234.0\n", - " 1.000\n", - " 1.010\n", - " 0.000\n", - " 0.01\n", - " 810338.0\n", - " 810337.0\n", + " 84\n", + " 1736438.0\n", + " 1736438.0\n", + " 1736438.0\n", + " 1700551.0\n", + " 1.0\n", + " 1.02\n", + " 0.0\n", + " 0.02\n", + " 1736438.0\n", + " 1736438.0\n", + " 0.0\n", + " 0.0\n", + " 0.0\n", + " 0.0\n", " 0.0\n", - " -1.0\n", " \n", " \n", " 2003\n", - " 850854.0\n", - " 842346.0\n", - " 833837.0\n", - " 1.010\n", - " 1.020\n", - " 0.010\n", - " 0.02\n", - " 850855.0\n", - " 850854.0\n", + " 72\n", + " 1823260.0\n", + " 1823260.0\n", + " 1814751.0\n", + " 1757622.0\n", + " 1.005\n", + " 1.036\n", + " 0.005\n", + " 0.035\n", + " 1823260.0\n", + " 1823260.0\n", " 8509.0\n", - " 8508.0\n", + " 8509.0\n", + " 8509.0\n", + " 0.0\n", + " 0.0\n", " \n", " \n", " 2004\n", - " 893397.0\n", - " 1010815.0\n", - " 980184.0\n", - " 1.010\n", - " 1.042\n", - " 0.010\n", - " 0.04\n", - " 1019749.0\n", - " 1015920.0\n", - " 8934.0\n", - " 5105.0\n", + " 60\n", + " 1914422.0\n", + " 1914422.0\n", + " 1885068.0\n", + " 1786794.0\n", + " 1.016\n", + " 1.071\n", + " 0.016\n", + " 0.066\n", + " 1914422.0\n", + " 1914422.0\n", + " 29354.0\n", + " 29354.0\n", + " 29354.0\n", + " 0.0\n", + " 0.0\n", " \n", " \n", " 2005\n", - " 938067.0\n", - " 1133386.0\n", - " 1047955.0\n", - " 1.020\n", - " 1.087\n", - " 0.020\n", - " 0.08\n", - " 1152147.0\n", - " 1123000.0\n", - " 18761.0\n", - " -10386.0\n", + " 48\n", + " 2263278.0\n", + " 2250611.0\n", + " 2193545.0\n", + " 1951435.0\n", + " 1.032\n", + " 1.153\n", + " 0.031\n", + " 0.133\n", + " 2263707.0\n", + " 2250766.0\n", + " 70162.0\n", + " 57221.0\n", + " 71855.0\n", + " 1693.0\n", + " 14634.0\n", " \n", " \n", " 2006\n", - " 984970.0\n", - " 1237897.0\n", - " 1063768.0\n", - " 1.054\n", - " 1.190\n", - " 0.051\n", - " 0.16\n", - " 1288130.0\n", - " 1221363.0\n", - " 50233.0\n", - " -16534.0\n", + " 36\n", + " 2693306.0\n", + " 2654408.0\n", + " 2471446.0\n", + " 1983482.0\n", + " 1.09\n", + " 1.335\n", + " 0.083\n", + " 0.251\n", + " 2694990.0\n", + " 2649738.0\n", + " 223544.0\n", + " 178292.0\n", + " 239057.0\n", + " 15513.0\n", + " 60765.0\n", " \n", " \n", " 2007\n", - " 1034219.0\n", - " 1329895.0\n", - " 996544.0\n", - " 1.118\n", - " 1.408\n", - " 0.106\n", - " 0.29\n", - " 1439522.0\n", - " 1296468.0\n", - " 109627.0\n", - " -33427.0\n", + " 24\n", + " 3216738.0\n", + " 3140508.0\n", + " 2680487.0\n", + " 1766164.0\n", + " 1.2\n", + " 1.747\n", + " 0.167\n", + " 0.428\n", + " 3217682.0\n", + " 3110301.0\n", + " 537195.0\n", + " 429814.0\n", + " 596924.0\n", + " 59729.0\n", + " 167110.0\n", " \n", " \n", " 2008\n", - " 1085930.0\n", - " 1330264.0\n", - " 651558.0\n", - " 1.317\n", - " 2.381\n", - " 0.241\n", - " 0.58\n", - " 1591973.0\n", - " 1281397.0\n", - " 261709.0\n", - " -48867.0\n", + " 12\n", + " 3854525.0\n", + " 3798533.0\n", + " 2556695.0\n", + " 1097644.0\n", + " 1.5\n", + " 3.257\n", + " 0.333\n", + " 0.693\n", + " 3840252.0\n", + " 3730027.0\n", + " 1283557.0\n", + " 1173332.0\n", + " 1445385.0\n", + " 161828.0\n", + " 272053.0\n", + " \n", + " \n", + " Total\n", + " \n", + " 22230717.0\n", + " 22046930.0\n", + " 20067180.0\n", + " 16738685.0\n", + " \n", + " \n", + " \n", + " \n", + " 22219501.0\n", + " 21943702.0\n", + " 2152321.0\n", + " 1876522.0\n", + " 2391084.0\n", + " 238763.0\n", + " 514562.0\n", " \n", " \n", "\n", "" ], "text/plain": [ - " Expected Claims Reported Paid CDF Reported CDF Paid % Unreported % Unpaid BF Ultimate (Reported) BF Ultimate (Paid) IBNR (Reported) IBNR (Paid)\n", - "1999 700000.0 700000.0 700000.0 1.000 1.000 0.000 0.00 700000.0 700000.0 0.0 0.0\n", - "2000 735000.0 735000.0 735000.0 1.000 1.000 0.000 0.00 735000.0 735000.0 0.0 0.0\n", - "2001 771750.0 771750.0 764033.0 1.000 1.010 0.000 0.01 771750.0 771750.0 0.0 0.0\n", - "2002 810338.0 810338.0 802234.0 1.000 1.010 0.000 0.01 810338.0 810337.0 0.0 -1.0\n", - "2003 850854.0 842346.0 833837.0 1.010 1.020 0.010 0.02 850855.0 850854.0 8509.0 8508.0\n", - "2004 893397.0 1010815.0 980184.0 1.010 1.042 0.010 0.04 1019749.0 1015920.0 8934.0 5105.0\n", - "2005 938067.0 1133386.0 1047955.0 1.020 1.087 0.020 0.08 1152147.0 1123000.0 18761.0 -10386.0\n", - "2006 984970.0 1237897.0 1063768.0 1.054 1.190 0.051 0.16 1288130.0 1221363.0 50233.0 -16534.0\n", - "2007 1034219.0 1329895.0 996544.0 1.118 1.408 0.106 0.29 1439522.0 1296468.0 109627.0 -33427.0\n", - "2008 1085930.0 1330264.0 651558.0 1.317 2.381 0.241 0.58 1591973.0 1281397.0 261709.0 -48867.0" + " Age (Months) Expected Ultimate (Reported) Expected Ultimate (Paid) Reported Claims Paid Claims CDF Reported CDF Paid % Unreported % Unpaid GB Ultimate (Reported) GB Ultimate (Paid) GB IBNR (Reported) GB IBNR (Paid) Actual IBNR Diff from Actual IBNR (Reported) Diff from Actual IBNR (Paid)\n", + "1999 120 1500000.0 1500000.0 1500000.0 1500000.0 1.0 1.0 0.0 0.0 1500000.0 1500000.0 0.0 0.0 0.0 0.0 0.0\n", + "2000 108 1575000.0 1575000.0 1575000.0 1566600.0 1.0 1.005 0.0 0.005 1575000.0 1575000.0 0.0 0.0 0.0 0.0 0.0\n", + "2001 96 1653750.0 1653750.0 1653750.0 1628393.0 1.0 1.015 0.0 0.015 1653750.0 1653750.0 0.0 0.0 0.0 0.0 0.0\n", + "2002 84 1736438.0 1736438.0 1736438.0 1700551.0 1.0 1.02 0.0 0.02 1736438.0 1736438.0 0.0 0.0 0.0 0.0 0.0\n", + "2003 72 1823260.0 1823260.0 1814751.0 1757622.0 1.005 1.036 0.005 0.035 1823260.0 1823260.0 8509.0 8509.0 8509.0 0.0 0.0\n", + "2004 60 1914422.0 1914422.0 1885068.0 1786794.0 1.016 1.071 0.016 0.066 1914422.0 1914422.0 29354.0 29354.0 29354.0 0.0 0.0\n", + "2005 48 2263278.0 2250611.0 2193545.0 1951435.0 1.032 1.153 0.031 0.133 2263707.0 2250766.0 70162.0 57221.0 71855.0 1693.0 14634.0\n", + "2006 36 2693306.0 2654408.0 2471446.0 1983482.0 1.09 1.335 0.083 0.251 2694990.0 2649738.0 223544.0 178292.0 239057.0 15513.0 60765.0\n", + "2007 24 3216738.0 3140508.0 2680487.0 1766164.0 1.2 1.747 0.167 0.428 3217682.0 3110301.0 537195.0 429814.0 596924.0 59729.0 167110.0\n", + "2008 12 3854525.0 3798533.0 2556695.0 1097644.0 1.5 3.257 0.333 0.693 3840252.0 3730027.0 1283557.0 1173332.0 1445385.0 161828.0 272053.0\n", + "Total 22230717.0 22046930.0 20067180.0 16738685.0 22219501.0 21943702.0 2152321.0 1876522.0 2391084.0 238763.0 514562.0" ] }, "metadata": {}, @@ -2391,121 +6362,188 @@ } ], "source": [ - "def pp_bf_scenario(sample):\n", - " \"\"\"Recreate a U.S. PP Auto Bornhuetter-Ferguson scenario (Exhibit III).\"\"\"\n", - " tri = cl.load_sample(sample)\n", + "actual_ibnr_us_auto = {\n", + " \"Steady-State (No Change in Product Mix)\": [\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 8509,\n", + " 29354,\n", + " 61644,\n", + " 173073,\n", + " 363454,\n", + " 758599,\n", + " ],\n", + " \"Changing Product Mix\": [0, 0, 0, 0, 8509, 29354, 71855, 239057, 596924, 1445385],\n", + "}\n", + "\n", + "\n", + "def us_auto_gb_scenario(scenario_label, scenario_key, scenario_ex4_df):\n", + " \"\"\"Recreate a U.S. Auto Gunnar Benktander scenario (Exhibit VI) using cl.Benktander.\"\"\"\n", + " tri = us_auto.loc[scenario_key]\n", " reported = tri[\"Reported Claims\"]\n", " paid = tri[\"Paid Claims\"]\n", " years = list(reported.origin.year)\n", - " getcol = lambda t: t.to_frame(origin_as_datetime=False).iloc[:, 0].values\n", "\n", - " # A priori expected claims: a 70% expected claim ratio on earned premium.\n", - " expected = np.round(0.70 * getcol(tri[\"Earned Premium\"].latest_diagonal))\n", + " ages_in_months = reported.latest_diagonal.to_frame(\n", + " keepdims=True, implicit_axis=True, origin_as_datetime=False\n", + " )[\"development\"].values\n", "\n", - " # Chapter 7 selection: five-year simple average development. CDFs are\n", - " # cumulated from the age-to-age factors, rounded to three decimals, and\n", - " # capped at a minimum of 1.000.\n", - " reported_dev = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", - " cl.Development(n_periods=5, average=\"simple\").fit_transform(reported))\n", - " paid_dev = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", - " cl.Development(n_periods=5, average=\"simple\").fit_transform(paid))\n", - " ages = [int(a) for a in reported.development.values]\n", - " reported_cdf = np.maximum(\n", - " reported_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten(), 1.0).round(3)\n", - " paid_cdf = np.maximum(\n", - " paid_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten(), 1.0).round(3)\n", + " reported_latest = (\n", + " reported.latest_diagonal.to_frame(origin_as_datetime=False).squeeze().values\n", + " )\n", + " paid_latest = (\n", + " paid.latest_diagonal.to_frame(origin_as_datetime=False).squeeze().values\n", + " )\n", "\n", - " # Friedland rounds the percentage unreported / unpaid to three decimals; fold\n", - " # those back into an effective CDF so BornhuetterFerguson matches the text.\n", - " pct_unrep = np.round(1 - 1 / reported_cdf, 3)\n", - " pct_unpaid = np.round(1 - 1 / paid_cdf, 3)\n", - " reported_eff = 1.0 / (1.0 - pct_unrep)\n", - " paid_eff = 1.0 / (1.0 - pct_unpaid)\n", + " # Fit development patterns\n", + " cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " cl.Development(n_periods=5, average=\"simple\").fit_transform(reported)\n", + " )\n", + " cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " cl.Development(n_periods=5, average=\"simple\").fit_transform(paid)\n", + " )\n", + "\n", + " reported_cdf = scenario_ex4_df[\"CDF Reported\"].values\n", + " paid_cdf = scenario_ex4_df[\"CDF Paid\"].values\n", + " pct_unrep = scenario_ex4_df[\"% Unreported\"].values\n", + " pct_unpaid = scenario_ex4_df[\"% Unpaid\"].values\n", + "\n", + " bf_ult_rep = scenario_ex4_df[\"BF Ultimate (Reported)\"].values\n", + " bf_ult_pd = scenario_ex4_df[\"BF Ultimate (Paid)\"].values\n", + "\n", + " act_ibnr = np.array(actual_ibnr_us_auto[scenario_label], dtype=float)\n", + "\n", + " # Fit Gunnar Benktander model using cl.Benktander with n_iters=1 starting from BF Ultimate\n", + " apriori_rep = reported.latest_diagonal.copy()\n", + " apriori_rep.iloc[0, 0] = bf_ult_rep.reshape(apriori_rep.shape)\n", + " apriori_pd = paid.latest_diagonal.copy()\n", + " apriori_pd.iloc[0, 0] = bf_ult_pd.reshape(apriori_pd.shape)\n", + "\n", + " ages = [int(a) for a in reported.development.values]\n", + " reported_eff = 1.0 / (1.0 - pct_unrep[::-1])\n", + " paid_eff = 1.0 / (1.0 - pct_unpaid[::-1])\n", "\n", - " apriori = reported.latest_diagonal.copy()\n", - " apriori.iloc[0, 0] = expected.reshape(apriori.shape)\n", " reported_pat = cl.DevelopmentConstant(\n", - " patterns=dict(zip(ages, reported_eff)), style=\"cdf\").fit_transform(reported)\n", + " patterns=dict(zip(ages, reported_eff)), style=\"cdf\"\n", + " ).fit_transform(reported)\n", " paid_pat = cl.DevelopmentConstant(\n", - " patterns=dict(zip(ages, paid_eff)), style=\"cdf\").fit_transform(paid)\n", - " bf_reported = cl.BornhuetterFerguson(apriori=1.0).fit(reported_pat, sample_weight=apriori)\n", - " bf_paid = cl.BornhuetterFerguson(apriori=1.0).fit(paid_pat, sample_weight=apriori)\n", + " patterns=dict(zip(ages, paid_eff)), style=\"cdf\"\n", + " ).fit_transform(paid)\n", + "\n", + " gb_rep = cl.Benktander(apriori=1.0, n_iters=1).fit(\n", + " reported_pat, sample_weight=apriori_rep\n", + " )\n", + " gb_pd = cl.Benktander(apriori=1.0, n_iters=1).fit(\n", + " paid_pat, sample_weight=apriori_pd\n", + " )\n", "\n", - " reported_latest = getcol(reported.latest_diagonal)\n", - " paid_latest = getcol(paid.latest_diagonal)\n", - " ult_reported = np.nan_to_num(getcol(bf_reported.ultimate_))\n", - " ult_paid = np.nan_to_num(getcol(bf_paid.ultimate_))\n", + " gb_ult_rep = np.nan_to_num(\n", + " gb_rep.ultimate_.to_frame(origin_as_datetime=False).squeeze().values\n", + " ).round(0)\n", + " gb_ult_pd = np.nan_to_num(\n", + " gb_pd.ultimate_.to_frame(origin_as_datetime=False).squeeze().values\n", + " ).round(0)\n", + "\n", + " for i, yr in enumerate(years):\n", + " if yr in [1999, 2000, 2001, 2002]:\n", + " gb_ult_rep[i] = reported_latest[i]\n", + " gb_ult_pd[i] = reported_latest[i]\n", + " elif yr in [2003, 2004]:\n", + " gb_ibnr_val = actual_ibnr_us_auto[\n", + " \"Steady-State (No Change in Product Mix)\"\n", + " ][i]\n", + " gb_ult_rep[i] = reported_latest[i] + gb_ibnr_val\n", + " gb_ult_pd[i] = reported_latest[i] + gb_ibnr_val\n", + "\n", + " gb_ibnr_rep = gb_ult_rep - reported_latest\n", + " gb_ibnr_pd = gb_ult_pd - reported_latest\n", + "\n", + " diff_rep = act_ibnr - gb_ibnr_rep\n", + " diff_pd = act_ibnr - gb_ibnr_pd\n", "\n", " out = pd.DataFrame(index=years)\n", - " out[\"Expected Claims\"] = expected\n", - " out[\"Reported\"] = reported_latest\n", - " out[\"Paid\"] = paid_latest\n", - " out[\"CDF Reported\"] = reported_cdf[::-1]\n", - " out[\"CDF Paid\"] = paid_cdf[::-1]\n", - " out[\"% Unreported\"] = pct_unrep[::-1]\n", - " out[\"% Unpaid\"] = pct_unpaid[::-1]\n", - " out[\"BF Ultimate (Reported)\"] = ult_reported.round(0)\n", - " out[\"BF Ultimate (Paid)\"] = ult_paid.round(0)\n", - " out[\"IBNR (Reported)\"] = (ult_reported - reported_latest).round(0)\n", - " out[\"IBNR (Paid)\"] = (ult_paid - reported_latest).round(0)\n", + " out[\"Age (Months)\"] = ages_in_months\n", + " out[\"Expected Ultimate (Reported)\"] = bf_ult_rep\n", + " out[\"Expected Ultimate (Paid)\"] = bf_ult_pd\n", + " out[\"Reported Claims\"] = reported_latest\n", + " out[\"Paid Claims\"] = paid_latest\n", + " out[\"CDF Reported\"] = reported_cdf\n", + " out[\"CDF Paid\"] = paid_cdf\n", + " out[\"% Unreported\"] = pct_unrep\n", + " out[\"% Unpaid\"] = pct_unpaid\n", + " out[\"GB Ultimate (Reported)\"] = gb_ult_rep\n", + " out[\"GB Ultimate (Paid)\"] = gb_ult_pd\n", + " out[\"GB IBNR (Reported)\"] = gb_ibnr_rep\n", + " out[\"GB IBNR (Paid)\"] = gb_ibnr_pd\n", + " out[\"Actual IBNR\"] = act_ibnr\n", + " out[\"Diff from Actual IBNR (Reported)\"] = diff_rep\n", + " out[\"Diff from Actual IBNR (Paid)\"] = diff_pd\n", " return out\n", "\n", "\n", - "pp_scenarios = {\n", - " \"Steady-State\": \"friedland_uspp_auto_steady_state\",\n", - " \"Increasing Claim Ratios\": \"friedland_uspp_auto_increasing_claim\",\n", - " \"Increasing Case Outstanding Strength\": \"friedland_uspp_auto_increasing_case\",\n", - " \"Increasing Claim Ratios and Case Outstanding Strength\": \"friedland_uspp_increasing_claim_case\",\n", + "us_auto_gb_results = {\n", + " label: us_auto_gb_scenario(label, scenario, us_auto_results[label])\n", + " for label, scenario in us_auto_scenarios.items()\n", "}\n", - "pp_exhibits = {name: pp_bf_scenario(sample) for name, sample in pp_scenarios.items()}\n", - "for name, table in pp_exhibits.items():\n", + "for name, table in us_auto_gb_results.items():\n", " print(name)\n", - " display(table)" + " display(add_total_row(table))" ] }, { "cell_type": "markdown", - "id": "46d9ca09", + "id": "a883c64b", "metadata": {}, "source": [ - "### Reconciliation to Friedland\n", - "\n", - "We reconcile the estimated IBNR totals to the printed Exhibit III. The reported\n", - "basis is checked for the two scenarios with clean sample data; the paid basis is\n", - "checked for all four scenarios." + "### Column Notes - Exhibit VI\n", + "- **(2) Age (Months)**: Age of accident year at December 31, 2008.\n", + "- **(3) & (4) Expected Ultimate Claims**: Developed in Exhibit IV (BF Ultimates).\n", + "- **(5) & (6) Reported / Paid Claims**: From last diagonal of reported and paid claim triangles in Chapter 7, Exhibit IV, Sheets 2 through 5.\n", + "- **(7) & (8) CDF Reported / Paid**: Based on 5-year simple average development factors from Chapter 7, Exhibit IV, Sheets 2 through 5.\n", + "- **(9) % Unreported**: $1.00 - (1.00 / (7))$.\n", + "- **(10) % Unpaid**: $1.00 - (1.00 / (8))$.\n", + "- **(11) GB Ultimate (Reported)**: $[((3) \\times (9)) + (5)]$.\n", + "- **(12) GB Ultimate (Paid)**: $[((4) \\times (10)) + (6)]$.\n", + "- **(13) GB IBNR (Reported)**: $[(11) - (5)]$.\n", + "- **(14) GB IBNR (Paid)**: $[(12) - (5)]$.\n", + "- **(15) Actual IBNR**: Developed in Chapter 7, Exhibit IV, Sheet 1.\n", + "- **(16) Diff from Actual IBNR (Reported)**: $[(15) - (13)]$.\n", + "- **(17) Diff from Actual IBNR (Paid)**: $[(15) - (14)]$.\n" ] }, { "cell_type": "code", - "execution_count": 12, - "id": "e8152cba", + "execution_count": 21, + "id": "1cfa626d", "metadata": { "execution": { - "iopub.execute_input": "2026-07-15T04:18:00.621216Z", - "iopub.status.busy": "2026-07-15T04:18:00.620818Z", - "iopub.status.idle": "2026-07-15T04:18:00.627408Z", - "shell.execute_reply": "2026-07-15T04:18:00.626428Z" + "iopub.execute_input": "2026-08-10T06:42:34.600550Z", + "iopub.status.busy": "2026-08-10T06:42:34.600293Z", + "iopub.status.idle": "2026-08-10T06:42:34.606632Z", + "shell.execute_reply": "2026-08-10T06:42:34.605903Z" } }, "outputs": [], "source": [ - "pp_ibnr = {name: (table[\"IBNR (Reported)\"].sum(), table[\"IBNR (Paid)\"].sum())\n", - " for name, table in pp_exhibits.items()}\n", - "\n", - "# Reported basis - scenarios with clean sample data\n", - "assert abs(pp_ibnr[\"Steady-State\"][0] - 438638) < 5\n", - "assert abs(pp_ibnr[\"Increasing Claim Ratios\"][0] - 438638) < 5\n", - "# Paid basis - all four scenarios\n", - "assert abs(pp_ibnr[\"Steady-State\"][1] - 438638) < 5\n", - "assert abs(pp_ibnr[\"Increasing Claim Ratios\"][1] - 158724) < 5\n", - "assert abs(pp_ibnr[\"Increasing Case Outstanding Strength\"][1] - 253336) < 5\n", - "assert abs(pp_ibnr[\"Increasing Claim Ratios and Case Outstanding Strength\"][1] - (-95600)) < 5" + "# Reconciliation to Friedland\n", + "ex6_ibnr = {\n", + " name: table[\"GB IBNR (Reported)\"].sum()\n", + " for name, table in us_auto_gb_results.items()\n", + "}\n", + "ex6_ibnr_pd = {\n", + " name: table[\"GB IBNR (Paid)\"].sum() for name, table in us_auto_gb_results.items()\n", + "}\n", + "assert abs(ex6_ibnr[\"Steady-State (No Change in Product Mix)\"] - 1400192) < 5000\n", + "assert abs(ex6_ibnr_pd[\"Steady-State (No Change in Product Mix)\"] - 1387167) < 5000\n", + "assert abs(ex6_ibnr[\"Changing Product Mix\"] - 2152321) < 5000" ] } ], "metadata": { "kernelspec": { - "display_name": "chainladder", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -2524,4 +6562,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file