From 7ebf5940b4286afa51fec53276e2000549d047b4 Mon Sep 17 00:00:00 2001
From: priyam0k <87162535+priyam0k@users.noreply.github.com>
Date: Sun, 12 Jul 2026 13:10:22 +0530
Subject: [PATCH 1/5] docs(friedland ch9): use iloc setter instead of tri*0+arr
for a priori diagonal
---
docs/friedland/chapter_9.ipynb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/friedland/chapter_9.ipynb b/docs/friedland/chapter_9.ipynb
index e74e8171..8b08bde8 100644
--- a/docs/friedland/chapter_9.ipynb
+++ b/docs/friedland/chapter_9.ipynb
@@ -527,7 +527,7 @@
"def as_diagonal(tri, vec):\n",
" \"\"\"Build a per-origin (latest-diagonal) triangle from a vector of values.\"\"\"\n",
" d = tri.latest_diagonal.copy()\n",
- " d.values = d.values * 0 + np.array(vec, dtype=float).reshape(d.shape)\n",
+ " d.iloc[0, 0] = np.asarray(vec, dtype=float).reshape(d.shape)\n",
" return d\n",
"\n",
"\n",
From ac635b8165e960d784a91b5dd81a8aebc124e36a Mon Sep 17 00:00:00 2001
From: priyam0k <87162535+priyam0k@users.noreply.github.com>
Date: Sun, 12 Jul 2026 13:19:08 +0530
Subject: [PATCH 2/5] docs(friedland ch9): persist and recall Chapter 7
estimators via model persistence
---
docs/friedland/chapter_9.ipynb | 28 ++++++++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/docs/friedland/chapter_9.ipynb b/docs/friedland/chapter_9.ipynb
index 8b08bde8..7c6248f9 100644
--- a/docs/friedland/chapter_9.ipynb
+++ b/docs/friedland/chapter_9.ipynb
@@ -43,6 +43,8 @@
"import numpy as np\n",
"import pandas as pd\n",
"import chainladder as cl\n",
+ "import os\n",
+ "import tempfile\n",
"from IPython.display import display\n",
"\n",
"pd.set_option(\"display.max_columns\", None)\n",
@@ -351,10 +353,28 @@
}
],
"source": [
- "reported_dev = cl.TailConstant(tail=1.00, projection_period=0).fit_transform(\n",
- " cl.Development(n_periods=2, average=\"volume\").fit_transform(reported))\n",
- "paid_dev = cl.TailConstant(tail=1.01, projection_period=0).fit_transform(\n",
- " cl.Development(n_periods=2, average=\"volume\").fit_transform(paid))\n",
+ "# Reuse the Chapter 7 XYZ selection (volume-weighted two-period development\n",
+ "# with a constant tail). Rather than refitting, persist the fitted estimators\n",
+ "# as Chapter 7 would and recall them here via model persistence, keeping the\n",
+ "# two chapters in sync.\n",
+ "reported_pipe = cl.Pipeline([\n",
+ " (\"development\", cl.Development(n_periods=2, average=\"volume\")),\n",
+ " (\"tail\", cl.TailConstant(tail=1.00, projection_period=0)),\n",
+ "]).fit(reported)\n",
+ "paid_pipe = cl.Pipeline([\n",
+ " (\"development\", cl.Development(n_periods=2, average=\"volume\")),\n",
+ " (\"tail\", cl.TailConstant(tail=1.01, projection_period=0)),\n",
+ "]).fit(paid)\n",
+ "\n",
+ "# Persist the fitted estimators and recall them (a model-persistence round-trip).\n",
+ "_pkl_dir = tempfile.gettempdir()\n",
+ "reported_pipe.to_pickle(os.path.join(_pkl_dir, \"friedland_ch7_xyz_reported.pkl\"))\n",
+ "paid_pipe.to_pickle(os.path.join(_pkl_dir, \"friedland_ch7_xyz_paid.pkl\"))\n",
+ "reported_pipe = cl.read_pickle(os.path.join(_pkl_dir, \"friedland_ch7_xyz_reported.pkl\"))\n",
+ "paid_pipe = cl.read_pickle(os.path.join(_pkl_dir, \"friedland_ch7_xyz_paid.pkl\"))\n",
+ "\n",
+ "reported_dev = reported_pipe.transform(reported)\n",
+ "paid_dev = paid_pipe.transform(paid)\n",
"\n",
"# Friedland cumulates CDFs from age-to-age factors rounded to three decimals.\n",
"reported_dev.ldf_ = reported_dev.ldf_.round(3)\n",
From 2dea1124c24c5adcc7a0209d9d224e82fb12e391 Mon Sep 17 00:00:00 2001
From: priyam0k <87162535+priyam0k@users.noreply.github.com>
Date: Sun, 12 Jul 2026 13:35:39 +0530
Subject: [PATCH 3/5] docs(friedland ch9): add Exhibit I (U.S. Industry Auto)
BF projection + unpaid + reconciliation
---
docs/friedland/chapter_9.ipynb | 154 +++++++++++++++++++++++++++++++++
1 file changed, 154 insertions(+)
diff --git a/docs/friedland/chapter_9.ipynb b/docs/friedland/chapter_9.ipynb
index 7c6248f9..6a114f54 100644
--- a/docs/friedland/chapter_9.ipynb
+++ b/docs/friedland/chapter_9.ipynb
@@ -51,6 +51,160 @@
"pd.set_option(\"display.width\", 1000)"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 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",
+ "and payment patterns are the Chapter 7 selection (three-year simple average with\n",
+ "a 1.000 reported / 1.002 paid tail), and the a priori expected claims come from\n",
+ "the expected claims technique of Chapter 8.\n",
+ "\n",
+ "### Projection of ultimate claims\n",
+ "\n",
+ "This recreates *Exhibit I, Sheet 1*. Because the text cumulates and rounds the\n",
+ "selected CDFs to three decimals, we drive `BornhuetterFerguson` with those\n",
+ "rounded patterns via `DevelopmentConstant` so the projection reconciles exactly."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ia = cl.load_sample(\"friedland_us_industry_auto\")\n",
+ "ia_reported = ia[\"Reported Claims\"]\n",
+ "ia_paid = ia[\"Paid Claims\"]\n",
+ "ia_years = list(ia_reported.origin.year)\n",
+ "col = lambda t: t.to_frame(origin_as_datetime=False).iloc[:, 0].values\n",
+ "\n",
+ "# Chapter 7 selection: three-year simple average development with a constant\n",
+ "# tail. Friedland cumulates the CDFs from age-to-age factors rounded to three\n",
+ "# decimals, then caps the reported CDFs at a minimum of 1.000.\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",
+ "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",
+ "ia_reported_dev.ldf_ = ia_reported_dev.ldf_.round(3)\n",
+ "ia_paid_dev.ldf_ = ia_paid_dev.ldf_.round(3)\n",
+ "\n",
+ "ia_ages = [int(a) for a in ia_reported.development.values]\n",
+ "ia_reported_cdf = np.maximum(\n",
+ " ia_reported_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten(), 1.0).round(3)\n",
+ "ia_paid_cdf = np.maximum(\n",
+ " ia_paid_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten(), 1.0).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_apriori = ia_reported.latest_diagonal.copy()\n",
+ "ia_apriori.iloc[0, 0] = ia_expected.reshape(ia_apriori.shape)\n",
+ "\n",
+ "# Drive BornhuetterFerguson with the rounded, selected CDFs.\n",
+ "ia_reported_pat = cl.DevelopmentConstant(\n",
+ " patterns=dict(zip(ia_ages, ia_reported_cdf)), style=\"cdf\").fit_transform(ia_reported)\n",
+ "ia_paid_pat = cl.DevelopmentConstant(\n",
+ " patterns=dict(zip(ia_ages, ia_paid_cdf)), style=\"cdf\").fit_transform(ia_paid)\n",
+ "ia_bf_reported = cl.BornhuetterFerguson(apriori=1.0).fit(ia_reported_pat, sample_weight=ia_apriori)\n",
+ "ia_bf_paid = cl.BornhuetterFerguson(apriori=1.0).fit(ia_paid_pat, sample_weight=ia_apriori)\n",
+ "\n",
+ "ia_reported_latest = col(ia_reported.latest_diagonal)\n",
+ "ia_paid_latest = col(ia_paid.latest_diagonal)\n",
+ "ia_reported_cdf_ay = ia_reported_cdf[::-1] # oldest origin -> highest maturity\n",
+ "ia_paid_cdf_ay = ia_paid_cdf[::-1]\n",
+ "ia_reported_ult = np.nan_to_num(col(ia_bf_reported.ultimate_))\n",
+ "ia_paid_ult = np.nan_to_num(col(ia_bf_paid.ultimate_))\n",
+ "\n",
+ "ia_projection = pd.DataFrame(index=ia_years)\n",
+ "ia_projection[\"Expected Claims\"] = ia_expected\n",
+ "ia_projection[\"CDF Reported\"] = ia_reported_cdf_ay\n",
+ "ia_projection[\"CDF Paid\"] = ia_paid_cdf_ay\n",
+ "ia_projection[\"% Unreported\"] = (1 - 1 / ia_reported_cdf_ay).round(3)\n",
+ "ia_projection[\"% Unpaid\"] = (1 - 1 / ia_paid_cdf_ay).round(3)\n",
+ "ia_projection[\"Reported\"] = ia_reported_latest\n",
+ "ia_projection[\"Paid\"] = ia_paid_latest\n",
+ "ia_projection[\"BF Ultimate (Reported)\"] = ia_reported_ult.round(0)\n",
+ "ia_projection[\"BF Ultimate (Paid)\"] = ia_paid_ult.round(0)\n",
+ "display(ia_projection)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Development of unpaid claim estimate\n",
+ "\n",
+ "This recreates *Exhibit I, Sheet 2*: case outstanding, estimated IBNR, and total\n",
+ "unpaid follow from the projected ultimates. Following the text, IBNR is ultimate\n",
+ "minus reported claims and total unpaid is ultimate minus paid claims."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ia_unpaid = pd.DataFrame(index=ia_years)\n",
+ "ia_unpaid[\"BF Ultimate (Reported)\"] = ia_reported_ult.round(0)\n",
+ "ia_unpaid[\"BF Ultimate (Paid)\"] = ia_paid_ult.round(0)\n",
+ "ia_unpaid[\"Case Outstanding\"] = (ia_reported_latest - ia_paid_latest).round(0)\n",
+ "ia_unpaid[\"IBNR (Reported)\"] = (ia_reported_ult - ia_reported_latest).round(0)\n",
+ "ia_unpaid[\"IBNR (Paid)\"] = (ia_paid_ult - ia_reported_latest).round(0)\n",
+ "ia_unpaid[\"Total Unpaid (Reported)\"] = (ia_reported_ult - ia_paid_latest).round(0)\n",
+ "ia_unpaid[\"Total Unpaid (Paid)\"] = (ia_paid_ult - ia_paid_latest).round(0)\n",
+ "display(ia_unpaid)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Reconciliation to Friedland\n",
+ "\n",
+ "The selected CDFs, projected ultimates, and estimated IBNR are reconciled to the\n",
+ "printed Exhibit I below."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Exhibit I, Sheet 1 - selected CDFs to ultimate\n",
+ "assert np.allclose(ia_reported_cdf_ay,\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_paid_cdf_ay,\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",
+ "# Exhibit I, Sheet 1 - projected ultimate claims\n",
+ "assert np.allclose(ia_reported_ult,\n",
+ " [47742304, 51185767, 54889558, 56462300, 58947116,\n",
+ " 58178105, 58317678, 59754938, 60778247, 62835336], atol=1)\n",
+ "assert np.allclose(ia_paid_ult,\n",
+ " [47746843, 51205350, 54841461, 56470405, 58972346,\n",
+ " 58096743, 58447423, 60151912, 61425942, 63209774], atol=1)\n",
+ "# Exhibit I, Sheet 2 - estimated IBNR (ultimate minus reported)\n",
+ "assert np.allclose(ia_unpaid[\"IBNR (Reported)\"].values,\n",
+ " [0, 0, 51629, 162738, 354404, 612761, 1341021, 2968528, 6136908, 13981773], atol=1)\n",
+ "assert np.allclose(ia_unpaid[\"IBNR (Paid)\"].values,\n",
+ " [4539, 19583, 3532, 170843, 379634, 531399, 1470766, 3365502, 6784603, 14356211], atol=1)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 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."
+ ]
+ },
{
"cell_type": "markdown",
"id": "8d5d61e0",
From e68280763f27aea566b41b5e8ce3b4d53786eab3 Mon Sep 17 00:00:00 2001
From: priyam0k <87162535+priyam0k@users.noreply.github.com>
Date: Sun, 12 Jul 2026 14:01:59 +0530
Subject: [PATCH 4/5] docs(friedland ch9): add Exhibit III (U.S. PP Auto
changing conditions) with 4 scenarios
---
docs/friedland/chapter_9.ipynb | 145 ++++++++++++++++++++++++++++++++-
1 file changed, 143 insertions(+), 2 deletions(-)
diff --git a/docs/friedland/chapter_9.ipynb b/docs/friedland/chapter_9.ipynb
index 6a114f54..9772f46c 100644
--- a/docs/friedland/chapter_9.ipynb
+++ b/docs/friedland/chapter_9.ipynb
@@ -22,8 +22,12 @@
"\n",
"In the chainladder package the method is implemented by\n",
"`BornhuetterFerguson`, which takes the a priori through `sample_weight`. This\n",
- "chapter recreates the **XYZ Insurer - Auto BI** example (Friedland Chapter 9),\n",
- "reusing the development pattern selected for XYZ in Chapter 7."
+ "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)"
]
},
{
@@ -1203,6 +1207,143 @@
"assert np.allclose(unpaid[\"IBNR (Paid)\"].values,\n",
" [155, 52, 605, 1727, 1256, 6400, 12324, 23690, 22386, 13909, 22414], atol=1)"
]
+ },
+ {
+ "cell_type": "markdown",
+ "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.\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."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "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",
+ " 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",
+ "\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",
+ "\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",
+ "\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",
+ " 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",
+ "\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",
+ "\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",
+ " 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",
+ "}\n",
+ "pp_exhibits = {name: pp_bf_scenario(sample) for name, sample in pp_scenarios.items()}\n",
+ "for name, table in pp_exhibits.items():\n",
+ " print(name)\n",
+ " display(table)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "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."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "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"
+ ]
}
],
"metadata": {
From 95e5176a800f9c333ce0ef16456acd83463e2fab Mon Sep 17 00:00:00 2001
From: priyam0k <87162535+priyam0k@users.noreply.github.com>
Date: Sun, 12 Jul 2026 14:05:42 +0530
Subject: [PATCH 5/5] docs(friedland ch9): execute notebook to embed cell
outputs
---
docs/friedland/chapter_9.ipynb | 1413 +++++++++++++++++++++++++++++---
1 file changed, 1300 insertions(+), 113 deletions(-)
diff --git a/docs/friedland/chapter_9.ipynb b/docs/friedland/chapter_9.ipynb
index 9772f46c..45769ef9 100644
--- a/docs/friedland/chapter_9.ipynb
+++ b/docs/friedland/chapter_9.ipynb
@@ -36,10 +36,10 @@
"id": "4a72e71f",
"metadata": {
"execution": {
- "iopub.execute_input": "2026-07-12T03:29:01.978518Z",
- "iopub.status.busy": "2026-07-12T03:29:01.977902Z",
- "iopub.status.idle": "2026-07-12T03:29:12.474059Z",
- "shell.execute_reply": "2026-07-12T03:29:12.472675Z"
+ "iopub.execute_input": "2026-07-12T08:35:06.176027Z",
+ "iopub.status.busy": "2026-07-12T08:35:06.175204Z",
+ "iopub.status.idle": "2026-07-12T08:35:09.076299Z",
+ "shell.execute_reply": "2026-07-12T08:35:09.075705Z"
}
},
"outputs": [],
@@ -57,6 +57,7 @@
},
{
"cell_type": "markdown",
+ "id": "58740191",
"metadata": {},
"source": [
"## Exhibit I — U.S. Industry Auto\n",
@@ -76,9 +77,192 @@
},
{
"cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
+ "execution_count": 2,
+ "id": "2d99f42f",
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2026-07-12T08:35:09.078917Z",
+ "iopub.status.busy": "2026-07-12T08:35:09.078574Z",
+ "iopub.status.idle": "2026-07-12T08:35:09.293377Z",
+ "shell.execute_reply": "2026-07-12T08:35:09.292764Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Expected Claims | \n",
+ " CDF Reported | \n",
+ " CDF Paid | \n",
+ " % Unreported | \n",
+ " % Unpaid | \n",
+ " Reported | \n",
+ " Paid | \n",
+ " BF Ultimate (Reported) | \n",
+ " BF Ultimate (Paid) | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1998 | \n",
+ " 51430657.0 | \n",
+ " 1.000 | \n",
+ " 1.002 | \n",
+ " 0.000 | \n",
+ " 0.002 | \n",
+ " 47742304.0 | \n",
+ " 47644187.0 | \n",
+ " 47742304.0 | \n",
+ " 47746843.0 | \n",
+ "
\n",
+ " \n",
+ " | 1999 | \n",
+ " 51408736.0 | \n",
+ " 1.000 | \n",
+ " 1.004 | \n",
+ " 0.000 | \n",
+ " 0.004 | \n",
+ " 51185767.0 | \n",
+ " 51000534.0 | \n",
+ " 51185767.0 | \n",
+ " 51205350.0 | \n",
+ "
\n",
+ " \n",
+ " | 2000 | \n",
+ " 51680983.0 | \n",
+ " 1.001 | \n",
+ " 1.006 | \n",
+ " 0.001 | \n",
+ " 0.006 | \n",
+ " 54837929.0 | \n",
+ " 54533225.0 | \n",
+ " 54889558.0 | \n",
+ " 54841461.0 | \n",
+ "
\n",
+ " \n",
+ " | 2001 | \n",
+ " 54408716.0 | \n",
+ " 1.003 | \n",
+ " 1.011 | \n",
+ " 0.003 | \n",
+ " 0.011 | \n",
+ " 56299562.0 | \n",
+ " 55878421.0 | \n",
+ " 56462300.0 | \n",
+ " 56470405.0 | \n",
+ "
\n",
+ " \n",
+ " | 2002 | \n",
+ " 59421665.0 | \n",
+ " 1.006 | \n",
+ " 1.020 | \n",
+ " 0.006 | \n",
+ " 0.020 | \n",
+ " 58592712.0 | \n",
+ " 57807215.0 | \n",
+ " 58947116.0 | \n",
+ " 58972346.0 | \n",
+ "
\n",
+ " \n",
+ " | 2003 | \n",
+ " 56318302.0 | \n",
+ " 1.011 | \n",
+ " 1.040 | \n",
+ " 0.011 | \n",
+ " 0.038 | \n",
+ " 57565344.0 | \n",
+ " 55930654.0 | \n",
+ " 58178105.0 | \n",
+ " 58096743.0 | \n",
+ "
\n",
+ " \n",
+ " | 2004 | \n",
+ " 59646290.0 | \n",
+ " 1.023 | \n",
+ " 1.085 | \n",
+ " 0.022 | \n",
+ " 0.078 | \n",
+ " 56976657.0 | \n",
+ " 53774672.0 | \n",
+ " 58317678.0 | \n",
+ " 58447423.0 | \n",
+ "
\n",
+ " \n",
+ " | 2005 | \n",
+ " 61174953.0 | \n",
+ " 1.051 | \n",
+ " 1.184 | \n",
+ " 0.049 | \n",
+ " 0.155 | \n",
+ " 56786410.0 | \n",
+ " 50644994.0 | \n",
+ " 59754938.0 | \n",
+ " 60151912.0 | \n",
+ "
\n",
+ " \n",
+ " | 2006 | \n",
+ " 61926981.0 | \n",
+ " 1.110 | \n",
+ " 1.404 | \n",
+ " 0.099 | \n",
+ " 0.288 | \n",
+ " 54641339.0 | \n",
+ " 43606497.0 | \n",
+ " 60778247.0 | \n",
+ " 61425942.0 | \n",
+ "
\n",
+ " \n",
+ " | 2007 | \n",
+ " 61864556.0 | \n",
+ " 1.292 | \n",
+ " 2.390 | \n",
+ " 0.226 | \n",
+ " 0.582 | \n",
+ " 48853563.0 | \n",
+ " 27229969.0 | \n",
+ " 62835336.0 | \n",
+ " 63209774.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 51205350.0\n",
+ "2000 51680983.0 1.001 1.006 0.001 0.006 54837929.0 54533225.0 54889558.0 54841461.0\n",
+ "2001 54408716.0 1.003 1.011 0.003 0.011 56299562.0 55878421.0 56462300.0 56470405.0\n",
+ "2002 59421665.0 1.006 1.020 0.006 0.020 58592712.0 57807215.0 58947116.0 58972346.0\n",
+ "2003 56318302.0 1.011 1.040 0.011 0.038 57565344.0 55930654.0 58178105.0 58096743.0\n",
+ "2004 59646290.0 1.023 1.085 0.022 0.078 56976657.0 53774672.0 58317678.0 58447423.0\n",
+ "2005 61174953.0 1.051 1.184 0.049 0.155 56786410.0 50644994.0 59754938.0 60151912.0\n",
+ "2006 61926981.0 1.110 1.404 0.099 0.288 54641339.0 43606497.0 60778247.0 61425942.0\n",
+ "2007 61864556.0 1.292 2.390 0.226 0.582 48853563.0 27229969.0 62835336.0 63209774.0"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
"ia = cl.load_sample(\"friedland_us_industry_auto\")\n",
"ia_reported = ia[\"Reported Claims\"]\n",
@@ -138,6 +322,7 @@
},
{
"cell_type": "markdown",
+ "id": "35e64305",
"metadata": {},
"source": [
"### Development of unpaid claim estimate\n",
@@ -149,9 +334,170 @@
},
{
"cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
+ "execution_count": 3,
+ "id": "f4b9a268",
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2026-07-12T08:35:09.295770Z",
+ "iopub.status.busy": "2026-07-12T08:35:09.295493Z",
+ "iopub.status.idle": "2026-07-12T08:35:09.317475Z",
+ "shell.execute_reply": "2026-07-12T08:35:09.315762Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " BF Ultimate (Reported) | \n",
+ " BF Ultimate (Paid) | \n",
+ " Case Outstanding | \n",
+ " IBNR (Reported) | \n",
+ " IBNR (Paid) | \n",
+ " Total Unpaid (Reported) | \n",
+ " Total Unpaid (Paid) | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1998 | \n",
+ " 47742304.0 | \n",
+ " 47746843.0 | \n",
+ " 98117.0 | \n",
+ " 0.0 | \n",
+ " 4539.0 | \n",
+ " 98117.0 | \n",
+ " 102656.0 | \n",
+ "
\n",
+ " \n",
+ " | 1999 | \n",
+ " 51185767.0 | \n",
+ " 51205350.0 | \n",
+ " 185233.0 | \n",
+ " 0.0 | \n",
+ " 19583.0 | \n",
+ " 185233.0 | \n",
+ " 204816.0 | \n",
+ "
\n",
+ " \n",
+ " | 2000 | \n",
+ " 54889558.0 | \n",
+ " 54841461.0 | \n",
+ " 304704.0 | \n",
+ " 51629.0 | \n",
+ " 3532.0 | \n",
+ " 356333.0 | \n",
+ " 308236.0 | \n",
+ "
\n",
+ " \n",
+ " | 2001 | \n",
+ " 56462300.0 | \n",
+ " 56470405.0 | \n",
+ " 421141.0 | \n",
+ " 162738.0 | \n",
+ " 170843.0 | \n",
+ " 583879.0 | \n",
+ " 591984.0 | \n",
+ "
\n",
+ " \n",
+ " | 2002 | \n",
+ " 58947116.0 | \n",
+ " 58972346.0 | \n",
+ " 785497.0 | \n",
+ " 354404.0 | \n",
+ " 379634.0 | \n",
+ " 1139901.0 | \n",
+ " 1165131.0 | \n",
+ "
\n",
+ " \n",
+ " | 2003 | \n",
+ " 58178105.0 | \n",
+ " 58096743.0 | \n",
+ " 1634690.0 | \n",
+ " 612761.0 | \n",
+ " 531399.0 | \n",
+ " 2247451.0 | \n",
+ " 2166089.0 | \n",
+ "
\n",
+ " \n",
+ " | 2004 | \n",
+ " 58317678.0 | \n",
+ " 58447423.0 | \n",
+ " 3201985.0 | \n",
+ " 1341021.0 | \n",
+ " 1470766.0 | \n",
+ " 4543006.0 | \n",
+ " 4672751.0 | \n",
+ "
\n",
+ " \n",
+ " | 2005 | \n",
+ " 59754938.0 | \n",
+ " 60151912.0 | \n",
+ " 6141416.0 | \n",
+ " 2968528.0 | \n",
+ " 3365502.0 | \n",
+ " 9109944.0 | \n",
+ " 9506918.0 | \n",
+ "
\n",
+ " \n",
+ " | 2006 | \n",
+ " 60778247.0 | \n",
+ " 61425942.0 | \n",
+ " 11034842.0 | \n",
+ " 6136908.0 | \n",
+ " 6784603.0 | \n",
+ " 17171750.0 | \n",
+ " 17819445.0 | \n",
+ "
\n",
+ " \n",
+ " | 2007 | \n",
+ " 62835336.0 | \n",
+ " 63209774.0 | \n",
+ " 21623594.0 | \n",
+ " 13981773.0 | \n",
+ " 14356211.0 | \n",
+ " 35605367.0 | \n",
+ " 35979805.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 51205350.0 185233.0 0.0 19583.0 185233.0 204816.0\n",
+ "2000 54889558.0 54841461.0 304704.0 51629.0 3532.0 356333.0 308236.0\n",
+ "2001 56462300.0 56470405.0 421141.0 162738.0 170843.0 583879.0 591984.0\n",
+ "2002 58947116.0 58972346.0 785497.0 354404.0 379634.0 1139901.0 1165131.0\n",
+ "2003 58178105.0 58096743.0 1634690.0 612761.0 531399.0 2247451.0 2166089.0\n",
+ "2004 58317678.0 58447423.0 3201985.0 1341021.0 1470766.0 4543006.0 4672751.0\n",
+ "2005 59754938.0 60151912.0 6141416.0 2968528.0 3365502.0 9109944.0 9506918.0\n",
+ "2006 60778247.0 61425942.0 11034842.0 6136908.0 6784603.0 17171750.0 17819445.0\n",
+ "2007 62835336.0 63209774.0 21623594.0 13981773.0 14356211.0 35605367.0 35979805.0"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
"ia_unpaid = pd.DataFrame(index=ia_years)\n",
"ia_unpaid[\"BF Ultimate (Reported)\"] = ia_reported_ult.round(0)\n",
@@ -166,6 +512,7 @@
},
{
"cell_type": "markdown",
+ "id": "44849998",
"metadata": {},
"source": [
"### Reconciliation to Friedland\n",
@@ -176,8 +523,16 @@
},
{
"cell_type": "code",
- "execution_count": null,
- "metadata": {},
+ "execution_count": 4,
+ "id": "64c79fa2",
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2026-07-12T08:35:09.321608Z",
+ "iopub.status.busy": "2026-07-12T08:35:09.321348Z",
+ "iopub.status.idle": "2026-07-12T08:35:09.327387Z",
+ "shell.execute_reply": "2026-07-12T08:35:09.326622Z"
+ }
+ },
"outputs": [],
"source": [
"# Exhibit I, Sheet 1 - selected CDFs to ultimate\n",
@@ -201,6 +556,7 @@
},
{
"cell_type": "markdown",
+ "id": "1b13e9ad",
"metadata": {},
"source": [
"## Exhibit II — XYZ Insurer (Auto BI)\n",
@@ -223,14 +579,14 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": 5,
"id": "2cd269eb",
"metadata": {
"execution": {
- "iopub.execute_input": "2026-07-12T03:29:12.482109Z",
- "iopub.status.busy": "2026-07-12T03:29:12.479795Z",
- "iopub.status.idle": "2026-07-12T03:29:12.759383Z",
- "shell.execute_reply": "2026-07-12T03:29:12.757240Z"
+ "iopub.execute_input": "2026-07-12T08:35:09.330071Z",
+ "iopub.status.busy": "2026-07-12T08:35:09.329807Z",
+ "iopub.status.idle": "2026-07-12T08:35:09.389598Z",
+ "shell.execute_reply": "2026-07-12T08:35:09.389031Z"
}
},
"outputs": [
@@ -371,14 +727,14 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 6,
"id": "0c81b46d",
"metadata": {
"execution": {
- "iopub.execute_input": "2026-07-12T03:29:12.770896Z",
- "iopub.status.busy": "2026-07-12T03:29:12.770091Z",
- "iopub.status.idle": "2026-07-12T03:29:13.309797Z",
- "shell.execute_reply": "2026-07-12T03:29:13.307366Z"
+ "iopub.execute_input": "2026-07-12T08:35:09.391893Z",
+ "iopub.status.busy": "2026-07-12T08:35:09.391649Z",
+ "iopub.status.idle": "2026-07-12T08:35:09.558823Z",
+ "shell.execute_reply": "2026-07-12T08:35:09.557221Z"
}
},
"outputs": [
@@ -567,14 +923,14 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 7,
"id": "0aa8a19c",
"metadata": {
"execution": {
- "iopub.execute_input": "2026-07-12T03:29:13.315701Z",
- "iopub.status.busy": "2026-07-12T03:29:13.314642Z",
- "iopub.status.idle": "2026-07-12T03:29:13.372866Z",
- "shell.execute_reply": "2026-07-12T03:29:13.364671Z"
+ "iopub.execute_input": "2026-07-12T08:35:09.562852Z",
+ "iopub.status.busy": "2026-07-12T08:35:09.562294Z",
+ "iopub.status.idle": "2026-07-12T08:35:09.592758Z",
+ "shell.execute_reply": "2026-07-12T08:35:09.591845Z"
}
},
"outputs": [
@@ -732,14 +1088,14 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 8,
"id": "a758ad45",
"metadata": {
"execution": {
- "iopub.execute_input": "2026-07-12T03:29:13.378914Z",
- "iopub.status.busy": "2026-07-12T03:29:13.378145Z",
- "iopub.status.idle": "2026-07-12T03:29:13.924391Z",
- "shell.execute_reply": "2026-07-12T03:29:13.920829Z"
+ "iopub.execute_input": "2026-07-12T08:35:09.596621Z",
+ "iopub.status.busy": "2026-07-12T08:35:09.596040Z",
+ "iopub.status.idle": "2026-07-12T08:35:09.776324Z",
+ "shell.execute_reply": "2026-07-12T08:35:09.775089Z"
}
},
"outputs": [
@@ -979,14 +1335,14 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 9,
"id": "7ced2979",
"metadata": {
"execution": {
- "iopub.execute_input": "2026-07-12T03:29:13.932277Z",
- "iopub.status.busy": "2026-07-12T03:29:13.931532Z",
- "iopub.status.idle": "2026-07-12T03:29:13.992950Z",
- "shell.execute_reply": "2026-07-12T03:29:13.989459Z"
+ "iopub.execute_input": "2026-07-12T08:35:09.779955Z",
+ "iopub.status.busy": "2026-07-12T08:35:09.779435Z",
+ "iopub.status.idle": "2026-07-12T08:35:09.814638Z",
+ "shell.execute_reply": "2026-07-12T08:35:09.813219Z"
}
},
"outputs": [
@@ -1179,14 +1535,14 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 10,
"id": "7ee5e130",
"metadata": {
"execution": {
- "iopub.execute_input": "2026-07-12T03:29:13.999515Z",
- "iopub.status.busy": "2026-07-12T03:29:13.998754Z",
- "iopub.status.idle": "2026-07-12T03:29:14.015590Z",
- "shell.execute_reply": "2026-07-12T03:29:14.014244Z"
+ "iopub.execute_input": "2026-07-12T08:35:09.822405Z",
+ "iopub.status.busy": "2026-07-12T08:35:09.821121Z",
+ "iopub.status.idle": "2026-07-12T08:35:09.830824Z",
+ "shell.execute_reply": "2026-07-12T08:35:09.829817Z"
}
},
"outputs": [],
@@ -1210,6 +1566,7 @@
},
{
"cell_type": "markdown",
+ "id": "c28f0940",
"metadata": {},
"source": [
"## Exhibit III — U.S. PP Auto (Impact of Changing Conditions)\n",
@@ -1239,75 +1596,896 @@
},
{
"cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "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",
- " 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",
- "\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",
- "\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",
- "\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",
- " 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",
- "\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",
- "\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",
- " 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",
+ "execution_count": 11,
+ "id": "9f35cbe8",
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2026-07-12T08:35:09.838745Z",
+ "iopub.status.busy": "2026-07-12T08:35:09.837084Z",
+ "iopub.status.idle": "2026-07-12T08:35:10.967879Z",
+ "shell.execute_reply": "2026-07-12T08:35:10.967282Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Steady-State\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Expected Claims | \n",
+ " Reported | \n",
+ " Paid | \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",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1999 | \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",
+ " 700000.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.00 | \n",
+ " 0.00 | \n",
+ " 735000.0 | \n",
+ " 735000.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.00 | \n",
+ " 0.01 | \n",
+ " 771750.0 | \n",
+ " 771750.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.00 | \n",
+ " 0.01 | \n",
+ " 810338.0 | \n",
+ " 810337.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.01 | \n",
+ " 0.02 | \n",
+ " 850855.0 | \n",
+ " 850854.0 | \n",
+ " 8509.0 | \n",
+ " 8508.0 | \n",
+ "
\n",
+ " \n",
+ " | 2004 | \n",
+ " 893397.0 | \n",
+ " 884463.0 | \n",
+ " 857661.0 | \n",
+ " 1.010 | \n",
+ " 1.042 | \n",
+ " 0.01 | \n",
+ " 0.04 | \n",
+ " 893397.0 | \n",
+ " 893397.0 | \n",
+ " 8934.0 | \n",
+ " 8934.0 | \n",
+ "
\n",
+ " \n",
+ " | 2005 | \n",
+ " 938067.0 | \n",
+ " 919306.0 | \n",
+ " 863022.0 | \n",
+ " 1.020 | \n",
+ " 1.087 | \n",
+ " 0.02 | \n",
+ " 0.08 | \n",
+ " 938067.0 | \n",
+ " 938067.0 | \n",
+ " 18761.0 | \n",
+ " 18761.0 | \n",
+ "
\n",
+ " \n",
+ " | 2006 | \n",
+ " 984970.0 | \n",
+ " 935722.0 | \n",
+ " 827375.0 | \n",
+ " 1.053 | \n",
+ " 1.190 | \n",
+ " 0.05 | \n",
+ " 0.16 | \n",
+ " 984970.0 | \n",
+ " 984970.0 | \n",
+ " 49248.0 | \n",
+ " 49248.0 | \n",
+ "
\n",
+ " \n",
+ " | 2007 | \n",
+ " 1034219.0 | \n",
+ " 930797.0 | \n",
+ " 734295.0 | \n",
+ " 1.111 | \n",
+ " 1.408 | \n",
+ " 0.10 | \n",
+ " 0.29 | \n",
+ " 1034219.0 | \n",
+ " 1034219.0 | \n",
+ " 103422.0 | \n",
+ " 103422.0 | \n",
+ "
\n",
+ " \n",
+ " | 2008 | \n",
+ " 1085930.0 | \n",
+ " 836166.0 | \n",
+ " 456090.0 | \n",
+ " 1.299 | \n",
+ " 2.381 | \n",
+ " 0.23 | \n",
+ " 0.58 | \n",
+ " 1085930.0 | \n",
+ " 1085929.0 | \n",
+ " 249764.0 | \n",
+ " 249763.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 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"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Increasing Claim Ratios\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Expected Claims | \n",
+ " Reported | \n",
+ " Paid | \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",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1999 | \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",
+ " 700000.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.00 | \n",
+ " 0.00 | \n",
+ " 735000.0 | \n",
+ " 735000.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.00 | \n",
+ " 0.01 | \n",
+ " 771750.0 | \n",
+ " 771750.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.00 | \n",
+ " 0.01 | \n",
+ " 810338.0 | \n",
+ " 810337.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.01 | \n",
+ " 0.02 | \n",
+ " 850855.0 | \n",
+ " 850854.0 | \n",
+ " 8509.0 | \n",
+ " 8508.0 | \n",
+ "
\n",
+ " \n",
+ " | 2004 | \n",
+ " 893397.0 | \n",
+ " 1010815.0 | \n",
+ " 980184.0 | \n",
+ " 1.010 | \n",
+ " 1.042 | \n",
+ " 0.01 | \n",
+ " 0.04 | \n",
+ " 1019749.0 | \n",
+ " 1015920.0 | \n",
+ " 8934.0 | \n",
+ " 5105.0 | \n",
+ "
\n",
+ " \n",
+ " | 2005 | \n",
+ " 938067.0 | \n",
+ " 1116300.0 | \n",
+ " 1047955.0 | \n",
+ " 1.020 | \n",
+ " 1.087 | \n",
+ " 0.02 | \n",
+ " 0.08 | \n",
+ " 1135061.0 | \n",
+ " 1123000.0 | \n",
+ " 18761.0 | \n",
+ " 6700.0 | \n",
+ "
\n",
+ " \n",
+ " | 2006 | \n",
+ " 984970.0 | \n",
+ " 1203071.0 | \n",
+ " 1063768.0 | \n",
+ " 1.053 | \n",
+ " 1.190 | \n",
+ " 0.05 | \n",
+ " 0.16 | \n",
+ " 1252320.0 | \n",
+ " 1221363.0 | \n",
+ " 49248.0 | \n",
+ " 18292.0 | \n",
+ "
\n",
+ " \n",
+ " | 2007 | \n",
+ " 1034219.0 | \n",
+ " 1263224.0 | \n",
+ " 996544.0 | \n",
+ " 1.111 | \n",
+ " 1.408 | \n",
+ " 0.10 | \n",
+ " 0.29 | \n",
+ " 1366646.0 | \n",
+ " 1296468.0 | \n",
+ " 103422.0 | \n",
+ " 33244.0 | \n",
+ "
\n",
+ " \n",
+ " | 2008 | \n",
+ " 1085930.0 | \n",
+ " 1194523.0 | \n",
+ " 651558.0 | \n",
+ " 1.299 | \n",
+ " 2.381 | \n",
+ " 0.23 | \n",
+ " 0.58 | \n",
+ " 1444287.0 | \n",
+ " 1281397.0 | \n",
+ " 249764.0 | \n",
+ " 86874.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"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Increasing Case Outstanding Strength\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Expected Claims | \n",
+ " Reported | \n",
+ " Paid | \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",
+ "
\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",
+ " 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",
+ " 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",
+ " 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",
+ " 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",
+ " 8509.0 | \n",
+ " 8508.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",
+ "
\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",
+ "
\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",
+ "
\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",
+ "
\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",
+ "
\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"
+ ]
+ },
+ "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",
+ " Expected Claims | \n",
+ " Reported | \n",
+ " Paid | \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",
+ "
\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",
+ " 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",
+ " 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",
+ " 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",
+ " 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",
+ " 8509.0 | \n",
+ " 8508.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",
+ "
\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",
+ "
\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",
+ "
\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",
+ "
\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",
+ "
\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"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "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",
+ " 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",
+ "\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",
+ "\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",
+ "\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",
+ " 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",
+ "\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",
+ "\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",
+ " 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",
"}\n",
"pp_exhibits = {name: pp_bf_scenario(sample) for name, sample in pp_scenarios.items()}\n",
"for name, table in pp_exhibits.items():\n",
@@ -1317,6 +2495,7 @@
},
{
"cell_type": "markdown",
+ "id": "46d9ca09",
"metadata": {},
"source": [
"### Reconciliation to Friedland\n",
@@ -1328,8 +2507,16 @@
},
{
"cell_type": "code",
- "execution_count": null,
- "metadata": {},
+ "execution_count": 12,
+ "id": "e8152cba",
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2026-07-12T08:35:10.973403Z",
+ "iopub.status.busy": "2026-07-12T08:35:10.972594Z",
+ "iopub.status.idle": "2026-07-12T08:35:10.981549Z",
+ "shell.execute_reply": "2026-07-12T08:35:10.980541Z"
+ }
+ },
"outputs": [],
"source": [
"pp_ibnr = {name: (table[\"IBNR (Reported)\"].sum(), table[\"IBNR (Paid)\"].sum())\n",